Skip to main content

Create Predefined Users, Roles and Permissions in the Database

  • 6 minutes to read

This topic describes how to define built-in user accounts and their permissions, which should be created automatically when the application database is initialized.

Create an Administrative Account

Standard Authentication requires:

  • An administrative role (a role with PermissionPolicyRoleBase.IsAdministrative = true).
  • A user with the administrative role.

The XAF Solution Wizard automatically adds these objects. To add them manually, do the following:

  • In the module project, reference the DevExpress.ExpressApp.Security.v23.2.dll assembly.
  • Edit the Updater.cs file located in the DatabaseUpdate folder of your module project. Override the ModuleUpdater.UpdateDatabaseAfterUpdateSchema method as follows:

    File: MySolution.Module\DatabaseUpdate\Updater.cs

    using DevExpress.Persistent.BaseImpl.PermissionPolicy;
    // ...
    public override void UpdateDatabaseAfterUpdateSchema() {
        if(userAdmin == null) {
            userAdmin = ObjectSpace.CreateObject<ApplicationUser>();
            userAdmin.UserName = "Admin";
            // Set a password if the standard authentication type is used
            userAdmin.SetPassword("");
    
            // The UserLoginInfo object requires a user object Id (Oid).
            // Commit the user object to the database before you create a UserLoginInfo object. This will correctly initialize the user key property.
            ObjectSpace.CommitChanges(); //This line persists created object(s).
            ((ISecurityUserWithLoginInfo)userAdmin).CreateUserLoginInfo(SecurityDefaults.PasswordAuthentication, ObjectSpace.GetKeyValueAsString(userAdmin));
        }
        // If a role with the Administrators name doesn't exist in the database, create this role.
        PermissionPolicyRole adminRole = ObjectSpace.FirstOrDefault<PermissionPolicyRole>(r => r.Name == "Administrators");
        if(adminRole == null) {
            adminRole = ObjectSpace.CreateObject<PermissionPolicyRole>();
            adminRole.Name = "Administrators";
        }
        adminRole.IsAdministrative = true;
        userAdmin.Roles.Add(adminRole);
        ObjectSpace.CommitChanges(); //This line persists created object(s).
    }
    

ApplicationUser and PermissionPolicyRole persistent objects store users and roles. The ApplicationUser class is generated by the XAF Solution Wizard. This class extends PermissionPolicyUser and implements the ISecurityUserWithLoginInfo interface required to store user login information in applications with multiple authentication methods.

Running the application now displays a login dialog at startup. You can log in as “Administrator” with an empty password (you can pass an alternate password to the SecurityUserBase.SetPassword method), add more users and roles at runtime, or specify more predefined users and roles in the Updater class code.

Create a User (Non-Administrative) Account

A user account is created in the same way as an administrative account, but the user role’s IsAdministrative property must be set to false (the default setting). Instead, you need to manually grant the required access rights to a user role as the following section describes: Set Permissions for Non-Administrative Roles.

File: MySolution.Module\DatabaseUpdate\Updater.cs

using DevExpress.Persistent.BaseImpl.PermissionPolicy;
// ...
public override void UpdateDatabaseAfterUpdateSchema() {
    // ...
    ApplicationUser sampleUser = ObjectSpace.FirstOrDefault<ApplicationUser>(u => u.UserName == "User");
        if(sampleUser == null) {
            sampleUser = ObjectSpace.CreateObject<ApplicationUser>();
            sampleUser.UserName = "User";
            // Set a password if the standard authentication type is used.
            sampleUser.SetPassword("");

            // The UserLoginInfo object requires a user object Id (Oid).
            // Commit the user object to the database before you create a UserLoginInfo object. This will correctly initialize the user key property.
            ObjectSpace.CommitChanges(); //This line persists created object(s).
            ((ISecurityUserWithLoginInfo)sampleUser).CreateUserLoginInfo(SecurityDefaults.PasswordAuthentication, ObjectSpace.GetKeyValueAsString(sampleUser));
        }
        PermissionPolicyRole defaultRole = CreateDefaultRole();
        sampleUser.Roles.Add(defaultRole);
    ObjectSpace.CommitChanges(); //This line persists created object(s).
    // ...
}

private PermissionPolicyRole CreateDefaultRole() {
    PermissionPolicyRole defaultRole = ObjectSpace.FirstOrDefault<PermissionPolicyRole>(role => role.Name == "Default");
    if(defaultRole == null) {
        // Configure the role's permission here.
        // See the "Set Permissions for Non-Administrative Roles" section for more information.
    }
    return defaultRole;
}

Important

In applications that support multiple authentication schemes, if the SecurityStrategy.AssociationPermissionsMode setting is set to Manual, you need to grant user access to their ApplicationUserLoginInfo object for authentication to work correctly. The code sample below demonstrates how to do this:

File: MySolution.Module\DatabaseUpdate\Updater.cs

private PermissionPolicyRole CreateDefaultRole() {
   PermissionPolicyRole defaultRole = ObjectSpace.FirstOrDefault<PermissionPolicyRole>(role => role.Name == "Default");
   if(defaultRole == null) {
       defaultRole.AddObjectPermissionFromLambda<ApplicationUserLoginInfo>(
          SecurityOperations.Read, 
          li => li.User.Oid == (Guid)CurrentUserIdOperator.CurrentUserId(), 
          SecurityPermissionState.Allow
       );
      // ...
  }
  return defaultRole;
}

Set Permissions for Non-Administrative Roles

When the role is not administrative (the IsAdministrative property value is false), you need to grant required permissions to this role explicitly. You can specify permissions for Create, Read, Write, and Delete data operations, and manage access to navigation items.

Permission Policy

First, you should set the Role’s Permission Policy. The IPermissionPolicyRole.PermissionPolicy property specifies the Security System behavior when there are no explicitly specified permissions for a specific type, object, or member. The available behaviors are listed in the SecurityPermissionPolicy enumeration (DenyAllByDefault, AllowAllByDefault, and ReadOnlyAllByDefault).

File: MySolution.Module\DatabaseUpdate\Updater.cs

defaultRole.PermissionPolicy = SecurityPermissionPolicy.AllowAllByDefault;

Access to Navigation Items

The Role’s Navigation Permissions manage navigation item access. You can grant or deny permissions for a single navigation item or the whole navigation group using the PermissionSettingHelper.AddNavigationPermission method.

File: MySolution.Module\DatabaseUpdate\Updater.cs

defaultRole.AddNavigationPermission(
    @"Application/NavigationItems/Items/Default/Items/MyDetails",
    SecurityPermissionState.Allow
);

CRUD Access to all Objects of a Specific Type

To specify permissions for the create, read, update, and delete (CRUD) operations with a certain business class, use the PermissionSettingHelper.AddTypePermission<T> method:

File: MySolution.Module\DatabaseUpdate\Updater.cs

defaultRole.AddTypePermission<Contact>(SecurityOperations.Read, SecurityPermissionState.Deny);

The method parameters set is standard for all PermissionSettingHelper methods managing CRUD access:

  • The operations parameter is a string containing a semicolon-separated list of security operations. The SecurityOperations class exposes string constants with operation names and their combinations.
  • The state parameter takes a SecurityPermissionState enumeration value (Allow or Deny), which specifies if access is granted or denied.
  • The T generic parameter specifies the business object type for which permissions are assigned.

To override the existing Type Permissions, use the PermissionSettingHelper.SetTypePermission<T> method. It searches for the specified type’s first permission in the current role and updates it according to the specified parameters. If the corresponding type permission is not found, it is created.

File: MySolution.Module\DatabaseUpdate\Updater.cs

defaultRole.SetTypePermission<Contact>(SecurityOperations.Read, SecurityPermissionState.Deny);

CRUD Access to an Object that Fits a Criteria

Object Permissions manage access to business object instances that fit a specified criterion. The AddObjectPermission<T> and AddObjectPermissionFromLambda<T> methods find the given type’s first type permission in the current role and add the object permission to it. If the appropriate type permission is not found, the methods create it. The additional criteria/lambda parameter specifies which expression the target object should match.

File: MySolution.Module\DatabaseUpdate\Updater.cs

defaultRole.AddObjectPermission<ApplicationUser>(
    SecurityOperations.Read, 
    "[Oid] = CurrentUserId()", 
    SecurityPermissionState.Allow
);
// or
defaultRole.AddObjectPermissionFromLambda<ApplicationUser>(
    SecurityOperations.Read, 
    u => u.Oid == (Guid)CurrentUserIdOperator.CurrentUserId(), 
    SecurityPermissionState.Allow
);

CRUD Access to Business Class Properties

You can manage access to individual members of the business class using Member Permissions. The AddMemberPermission<T> and AddMemberPermissionFromLambda<T> methods find the given type’s first type permission in the current role and add the member permission to it. If the appropriate type permission is not found, the methods create it. The members parameter is a string containing a semicolon-separated list of target member names, and the criteria/lambda parameter is a string containing the expression that specifies the target objects. If the expression is null (Nothing in VB), the member permission is applied to an object of the given type.

File: MySolution.Module\DatabaseUpdate\Updater.cs

defaultRole.AddMemberPermission<ApplicationUser>(
    SecurityOperations.Write, 
    "ChangePasswordOnFirstLogon",
    null, 
    SecurityPermissionState.Allow
);
// or
defaultRole.AddMemberPermissionFromLambda<ApplicationUser>(
    SecurityOperations.Write, 
    "ChangePasswordOnFirstLogon", 
    null, 
    SecurityPermissionState.Allow
);

You can also specify the criteria or lambda expression to apply member permissions to objects that fit this criteria:

File: MySolution.Module\DatabaseUpdate\Updater.cs

defaultRole.AddMemberPermission<ApplicationUser>(
    SecurityOperations.Write, 
    "ChangePasswordOnFirstLogon",
    "[Oid] = CurrentUserId()", 
    SecurityPermissionState.Allow
);
// or
defaultRole.AddMemberPermissionFromLambda<ApplicationUser>(
    SecurityOperations.Write, 
    "ChangePasswordOnFirstLogon", 
    u => u.Oid == (Guid)CurrentUserIdOperator.CurrentUserId(), 
    SecurityPermissionState.Allow
);

Refer to the SecurityDemo sources for more examples on how to create predefined users and roles. This demo application is available in the %PUBLIC%\Documents\DevExpress Demos 23.2\Components\XAF\SecurityDemo.NETFramework.XPO folder.

See Also