The Configuration

Every call your endpoint makes to the Rule Repository carries a configuration: an object that describes the current request's user and what your application allows. It says who the user is, which permissions they have, which language RR shows, which AI models are offered, how notifications reach people, and which limits apply. RR reads nothing about users, roles or settings from anywhere else.

You need it because RR has no user directory, no roles and no settings file of its own. Your application already knows who is signed in and what they may do; the configuration is how it tells RR, request by request.

One Configuration per Request

Build a new configuration for every request, from the signed-in user, and pass it to Context.GetResponse. Do not keep one in a static field or share one between users: its properties are the current user's.

Most applications use the Configuration class. An application with its own authorization service can implement the IRepositoryConfiguration interface instead (see below).

Every Property

Property Type Default What it decides
CurrentUserId string (none) Required. The signed-in user's ID in your application: a login, an email address, a directory object ID. Up to 256 characters. Stored against everything the user creates or changes. See Users and the Acting User.
CurrentUserName string null The user's display name, shown in RR. Optional; a name stored earlier is kept when you leave it out.
CurrentUserType UserType User What the caller is: a person (User), an AI agent acting on its own (Agent) or automation (Service). Only a person can make versions live, approve or delete.
CurrentAgentId, CurrentAgentName string null The AI agent acting for the person in CurrentUserId, when there is one. Set only from your own authentication of the caller.
AccessMode ActionPermissionMode ViewOnly How ActionList is read: ViewOnly (no permissions), AllowAllExcept (every permission but those listed) or AllowOnly (only those listed). See Permissions.
ActionList ISet<ActionPermissionType> empty The permissions withheld (AllowAllExcept) or granted (AllowOnly).
CanPerform(action) method from the two above Whether the user has a permission. Configuration answers from AccessMode and ActionList; your own implementation can answer any way you like.
UiXml XmlDocument null A translated or reworded user interface text document. See User Interface Text and Themes.
UiXmlFile string null The full physical path to a file holding that document; its parsed text is cached by content. Ignored when UiXml is set.
EditorHelpXml XmlDocument null The rule editors' Help XML in another language: their menus, operators and Help String. See Help XML and Multilingual Support.
EditorHelpXmlFile string null The full physical path to a file holding that document. Ignored when EditorHelpXml is set. Unreadable: CE10262.
SourceLabelsXml XmlDocument null Source labels: your source objects' field, method and action names and descriptions in another language, laid over every source version on display.
SourceLabelsXmlFile string null The full physical path to a file holding that document. Ignored when SourceLabelsXml is set. Unreadable: CE10263.
TypeResolver GetTypeDelegate null Finds a .NET type by name when the runtime cannot: needed only when a type a stored source definition names has moved to another assembly or been renamed.
Notify NotificationDelegate null Called after approvals are requested, granted or declined and after versions go live or offline, so your application can tell the right people. See Notifications.
Assistant AssistantSettings null The AI models for the AI Assistant and the AI Rule Editor. Null or no models: neither feature appears. See AI Assistant and AI Rule Editor.
Agents AgentSettings defaults What AI agents may do beyond their permissions: test limits and data source access. See AI Agents.
Limits RequestLimits defaults Request and payload sizes, filter and option counts, test runs at once (below).

Building It from the Signed-In User

Put the mapping from your users and roles to RR in one class of your own, and use it from your endpoint. A typical one:

using System.Security.Claims;
using CodeEffects.Rule.Common.Models;
using CodeEffects.Rule.Repository;
using CodeEffects.Rule.Repository.Common;

public sealed class RepositoryConfigurationFactory
{
	// What each of your roles may do in the Rule Repository
	private static readonly ISet<ActionPermissionType> Authors = new HashSet<ActionPermissionType>
	{
		ActionPermissionType.RuleCreate, ActionPermissionType.RuleUpdateDetails,
		ActionPermissionType.RuleVersionCreate, ActionPermissionType.RuleVersionUpdateDetails,
		ActionPermissionType.RuleVersionRequestApproval,
		ActionPermissionType.DraftView, ActionPermissionType.DraftCreate, ActionPermissionType.DraftUpdate,
		ActionPermissionType.DraftConvert, ActionPermissionType.DraftDelete,
		ActionPermissionType.TestView, ActionPermissionType.TestRun, ActionPermissionType.TestCreate,
		ActionPermissionType.TestUpdate, ActionPermissionType.TestDelete, ActionPermissionType.TestRunBatch,
		ActionPermissionType.ValueTemplateCreate, ActionPermissionType.ValueTemplateUpdate,
		ActionPermissionType.ActivityView, ActionPermissionType.NoteView, ActionPermissionType.NoteCreate
	};

	private readonly NotificationDelegate notify;
	private readonly AssistantSettings assistant;

	public RepositoryConfigurationFactory(NotificationDelegate notify = null, AssistantSettings assistant = null)
	{
		this.notify = notify;
		this.assistant = assistant;
	}

	public Configuration Create(ClaimsPrincipal user)
	{
		var config = new Configuration
		{
			CurrentUserId = user.FindFirst(ClaimTypes.NameIdentifier)?.Value,
			CurrentUserName = user.FindFirst(ClaimTypes.Name)?.Value,
			Notify = this.notify,
			Assistant = this.assistant
		};

		if (user.IsInRole("RuleManager"))
		{
			// Everything except reading other people's AI conversations
			config.AccessMode = ActionPermissionMode.AllowAllExcept;
			config.ActionList = new HashSet<ActionPermissionType> { ActionPermissionType.AiConversationViewAll };
		}
		else if (user.IsInRole("RuleAuthor"))
		{
			config.AccessMode = ActionPermissionMode.AllowOnly;
			config.ActionList = new HashSet<ActionPermissionType>(Authors);
		}
		else
		{
			config.AccessMode = ActionPermissionMode.ViewOnly;   // sees sources and rules, changes nothing
		}

		return config;
	}
}

Register it once (builder.Services.AddSingleton<RepositoryConfigurationFactory>(), or with a notification handler and AI settings) and call Create(http.User) in the endpoint, as The Server Endpoint does.

A role mapping is easy to get subtly wrong (granting DraftUpdate without DraftView, for instance), and an incomplete set fails every request with CE10218. Check each of your role sets in a unit test:

var problems = Context.ValidatePermissions(new RepositoryConfigurationFactory().Create(TestUsers.Author));
Assert.Empty(problems);   // each problem is one English sentence, such as "DraftUpdate (52) requires DraftView (50)."

ValidatePermissions reads no database.

The Context Class

Context is the whole server-side API of the Rule Repository. It is static: nothing to create, set up or dispose of. Each method takes the database provider and, where a user is involved, a configuration.

Method When you call it
GetResponse(config, db, requestJson) In your endpoint, for every request from the browser.
SyncSources(db, settings) At startup, at deployment or on demand, to put your source objects into the repository. See Source Sync and Versioning.
Reindex(db, aliases) After an upgrade or a bulk import, when SyncSources reports source objects to reindex.
GetAliasNames(db) To see which source objects the repository already holds.
GetRules(db, query) In production code that evaluates live rules without Audit. See Live Versions and Approvals.
CheckDatabase(db) Once at startup. See The Database.
ValidatePermissions(config) In your tests of role mappings.
GetDefaultUiXml() Once, to get the English text document you translate.
GetDefaultEditorHelpXml() Once, to get the rule editors' English Help XML you translate.
GetRequestToken(config, db) Only when your own server code builds repository requests.

Implementing the Interface Yourself

If your application already has an authorization service (policies, a permissions table, an external policy engine), implement IRepositoryConfiguration and answer CanPerform from it. RR asks CanPerform once for every permission on every request, so keep it fast (cache the user's permissions for the request).

using System.Xml;
using CodeEffects.Rule.Common.Models;
using CodeEffects.Rule.Repository;
using CodeEffects.Rule.Repository.Common;

public interface IMyPermissions { bool Allows(string userId, string permission); }

public sealed class PolicyConfiguration : IRepositoryConfiguration
{
	private readonly IMyPermissions permissions;

	public PolicyConfiguration(IMyPermissions permissions, string userId, string userName)
	{
		this.permissions = permissions;
		this.CurrentUserId = userId;
		this.CurrentUserName = userName;
	}

	// Answered by your own service; RR only asks
	public bool CanPerform(ActionPermissionType action) =>
		this.permissions.Allows(this.CurrentUserId, "rules:" + action);

	// Not used when CanPerform answers by itself, but part of the interface
	public ActionPermissionMode AccessMode { get; set; } = ActionPermissionMode.ViewOnly;
	public ISet<ActionPermissionType> ActionList { get; set; } = new HashSet<ActionPermissionType>();

	public string CurrentUserId { get; set; }
	public string CurrentUserName { get; set; }
	public UserType CurrentUserType { get; set; } = UserType.User;
	public string CurrentAgentId { get; set; }
	public string CurrentAgentName { get; set; }

	public XmlDocument UiXml { get; set; }
	public string UiXmlFile { get; set; }
	public XmlDocument EditorHelpXml { get; set; }
	public string EditorHelpXmlFile { get; set; }
	public XmlDocument SourceLabelsXml { get; set; }
	public string SourceLabelsXmlFile { get; set; }
	public GetTypeDelegate TypeResolver { get; set; }
	public NotificationDelegate Notify { get; set; }
	public AgentSettings Agents { get; set; }
	public AssistantSettings Assistant { get; set; }
	public RequestLimits Limits { get; set; }
}

Your answers must form a complete set: a permission granted without one it requires fails every request with CE10218. Test them with Context.ValidatePermissions.

Request Limits

Every request is held to limits that protect your server. The defaults suit most sites; change them with Limits:

config.Limits = new RequestLimits { LookupOptions = 5000, TestRunsAtOnce = 4 };
Property Default What it limits
RequestLength 3 MB (characters) The longest request accepted (CE10205). An AI Assistant file upload has its own limit, AssistantSettings.MaxAttachmentBytes.
PayloadLength 1 MB (characters) The longest stored payload: a draft's rule, a test's values and last run, a value template (CE10207).
Filters 50 Filters on one grid request (CE10206).
LookupOptions 1,000 Options returned for one drop-down list.
TestRunsAtOnce 8 Test runs under way at once in the whole process, for all users together (CE10209).

Your endpoint's own body-size limit should be a little larger than RequestLength, and larger still if you allow AI attachments (see AI Assistant and AI Rule Editor).

Ask on Stack Overflow - Yes, it's still active
Ask in GitHub Discussions

p102

l097 --

l102

p101

×