Creating access permissions for the object instances

This article describes how to assign access permissions to the custom object instances, as well as to access the module. The example shows how to create such access permissions as view object instance and edit object instance. We also demonstrate how to add custom roles or use the existing ones. As an example, we have developed a module with an object and implemented access permissions. We have implemented the following extensions points/base classes of extension points for this example:

  1. IPermissionProvider (Extension interface for the permissions list).
  2. IModuleAccessPermissionProvider (Provider interface, which defines access permissions to the modules).
  3. InstanceSettingsPermissionBase (Base class interface of the access permissions to the object instance (IInstanceSettingsPermission) based on the settings).
  4. IPermissionsDelegate (This interface is implemented for each object type, whose instances are related to the permissions).
  5. IEntityInstanceDefaultPermission (default permissions for the entity objects).
  6. IPermissionRoleTypeProvider (permissions for the object type, permission roles).

Example of Data Display

Fig. 1. List of instances

Fig. 1. List of instances of the IMyObject object. If a user doesn’t have respective access permissions, they will not be able to see the instance.

Fig. 2. Access permissions for the instance object

Fig. 2. Access permissions for the instance object

Fig. 3. Roles when granting access permissions

Fig. 3. Roles when granting access permissions

Fig. 4. Access permissions for the module in the Global Access Section

Fig. 4. Access permissions for the module in the Global Access Section

Methods of the IPermissionProvider Interface

/// <summary>
/// Get the list of permissions
/// </summary>
/// <returns></returns>
IEnumerable<Permission> GetPermissions();
 
/// <summary>
/// Get the default permissions for the role 
/// </summary>
/// <returns></returns>
IEnumerable<PermissionStereotype> GetPermissionStereotypes();

Methods of the IModuleAccessPermissionProvider Interface

/// <summary>
/// Get the dictionary with the values "Module Identifier -> Module access permission"
/// </summary>
/// <returns></returns>
Dictionary<string, Permission> GetModuleAccessPermissions();

Methods of the IInstanceSettingsPermission Interface

/// <summary>
/// Entity Type
/// </summary>
Type EntityType { get; }
 
/// <summary>
/// Get the collection of objects, which stores information about the holders of permissions 
/// </summary>
/// <returns></returns>
ICollection<IInstanceSettingsPermissionHolder> GetPermissionCollection(object target); 
 
/// <summary>
/// Whether to filter object instances when downloading automatically
/// </summary>
bool Filtering { get; }
 
/// <summary>
/// Name of the entity reference-property in the collection, which stores the granted permissions
/// </summary>
string TargetPropetyName { get; }
 
/// <summary>
/// Name of the entity that stores granted permissions (in the collection)
/// </summary>
Type PermissionHolderType { get; }
 
/// <summary>
/// Permission to manage entities; if the permission is granted,
///a user with this permission gets all permissions for the entity EntityType
/// </summary>
Permission AdminPermission { get; }
 
/// <summary>
/// Check whether the user can grant object permissions to other users 
/// </summary>
/// <param name="user">User</param>
/// <param name="target"> The object on which the permissions are granted </param>
/// <returns><c>true</c>, if the user can grant object permissions to other users </returns>
bool CanGrandPermissions(IUser user, object target);
 
/// <summary>
/// Define permissions that the user can grant 
/// If null, a user can grant any permissions
/// </summary>
/// <param name="user">User</param>
/// <param name="target"> The object on which the permissions are granted </param>
/// <returns></returns>
ICollection<Permission> CanGrandLevel(IUser user, object target);
 
/// <summary>
/// Whether it is allowed to grant item permissions 
/// </summary>
/// <param name="elementPermission"></param>
/// <param name="userPermission"></param>
/// <param name="permissionToCheck"></param>
/// <param name="permissionRoleTypeId">Role Type Identifier</param>
/// <param name="permissionRole">Role</param>
/// <param name="target"> The object for which the permissions are set.</param>
/// <returns><c>true</c>, if the item permissions can be granted</returns>
bool CanGrandToElement(Permission[] elementPermission, Permission[] userPermission, Permission permissionToCheck, Guid permissionRoleTypeId, IEntity permissionRole, object target);
 
/// <summary>
/// System role which can not be deleted.
/// </summary>
/// <param name="permissionRoleTypeId"> Role Type Identifier.</param>
/// <param name="permissionRole">Role</param>
/// <param name="target"> The object for which the permissions are set.</param>
/// <returns><c>true</c>, if it is a system role</returns>
bool IsSystemRole(Guid permissionRoleTypeId, IEntity permissionRole, object target);

Methods of the IPermissionsDelegate Interface

/// <summary>
/// Check whether the type correspond to the permission
/// </summary>
/// <param name="type">Type</param>
/// <param name="permission">Permission</param>
/// <returns><c>true</c>, if the type corresponds to the specified permission</returns>
bool CanCheckPermissions(Type type, Permission permission);
 
/// <summary>
/// Check the permission 
/// </summary>
/// <param name="user">User</param>
/// <param name="permission"> Permission </param>
/// <param name="target">Target Object</param>
/// <param name="skipAdmin"> skip admin permission check</param>
/// <returns><c>true</c>, if the permission exists </returns>
bool HasPermission(IUser user, Permission permission, object target, bool skipAdmin = false);

Methods of the IEntityInstanceDefaultPermission Interface

/// <summary>
/// Entity type with permissions
/// </summary>
Type EntityType { get; }
 
/// <summary>
/// Create permissions
/// </summary>
/// <param name="entity">Entity</param>
void CreatePermissions(IEntity entity);

Methods of the IPermissionRoleTypeProvider interface

/// <summary>
/// Get the type-permission-role correspondence. 
/// This method determines which permissions can be set for an object type and which roles can be set for permissions 
/// </summary>
IEnumerable<PermissionRoleTypeStereotype> GetTypePermissionRoleStereotypes();
 
/// <summary>
/// Get roles, related to the permissions
/// </summary>
IEnumerable<PermissionRoleType> GetRoleTypes();
Warning 
Avoid using GUIDS shown in the example; always generate your own GUIDS when possible. For this purpose, you can use VS2013 GUID generation utility (see fig. 5). To generate a new GUID you need to click "Create GUID", and then click "Copy." The GUID will be generated in the Result field and copied to the clipboard, then you can use in your code.

Fig. 5 Generating GUID

Creating a new permission

To create a new permission it is necessary to put four mandatory parameters:

  1. Permission GUID.
  2. The name of the permission.
  3. The description of the permission.
  4. Example: the CRM has the following permissions´ categories: Common module settings, Companies, Contacts, etc.

Also, when creating a permission, you can specify optional parameters which you can call using the structure: parameter_name: parameter_value. Example: moduleUid: Module

Optional parameters:

  1. The identifier of the moduleUid module (the default is null). Example: moduleUid: Module
  2. A permission type related to the permissionType (the default PermissionType.Global). Example: permissionType: PermissionType.Instance

There are three types of permissions:

- PermissionType.Global - Global permissions, for example, "Module Access" or "User Administration"

- PermissionType.EntityType - Permissions for an entity type, such as the editing permissions for the Cities Object

- PermissionType.Instance – Object Instance Permissions, for example, "Edit Document ID = 124".

  1. An entity type related to the entityType (default null). An object, which has this permission. Example: InterfaceActivator.TypeOf <IMyObject> ().
  2. A base permission @base (the default is null). The reference to the base system permission. Example: @base: CommonPermissions.View

If you need to implement your own permissions for an object, the permission type will be PermissionType.Instance. When you create an instance permission, the base View permission is searched for an object. If the View permission is not found an error message appears, telling that you must implement the base View permission.

Note: When loading several objects, a table with permissions will be applied to check the objects´ permissions. When loading one object, an additional condition will be applied to the selection. This should be considered when implementing permissions with a base permissions because it affects the overall performance.

  1. Showing permissions in the global settings showInGlobalSettings (the default is true). Example: showInGlobalSettings: false 
  1. The read only permission readOnly (default false). Means that the list of permissions owners cannot be edited.

 Permissions on which the created permission depends. If the permission is assigned to a role, the system automatically assigns all dependent permissions and vice versa, if the dependent permission is removed from the role, the system will remove the main permission. Example: The "Edit Users" permission depends on the "View Users"permission. Example: dependencies: new [] {UserViewPermission}

Example of creating a new permission

new Permission(ViewItemPermissionId,
                        SR.M("View"),
                        "",
                        SR.M("Permission to view objet instance"),
                        permissionType: PermissionType.Instance,
                        entityType: InterfaceActivator.TypeOf<IMyObject>(),
                        @base: CommonPermissions.View);

Permissions life cycles

Fig. 6 Working with a permission when opening an object

Fig. 6 Working with a permission when opening an object

Fig. 7 Working with a permission in ELMA when creating a new object instance IMyObject

Fig. 7 Working with a permission in ELMA when creating a new object instance IMyObject        

Fig. 8 Working with a permission in ELMA when assigning access permissions

Fig. 8 Working with a permission in ELMA when assigning access permissions

An example of the IPermissionProvider interface implementation

[Component]
public sealed class ModulePermissions : IPermissionProvider
{
    public const string Module = __ModuleInfo.ModuleId;
 
    public static string ModuleName = SR.M("Your Module");
 
    /// <summary>
    /// ID for "Access to Your Unit"
    /// </summary>
    public const string ModuleAccessPermissionId = "{184A1311-8CCF-49c6-A1C1-C11825232389}";
 
    /// <summary>
    /// ID for “View Permissions”
    /// </summary>
    public const string ViewItemPermissionId = "{81E3A934-AB85-4970-8CF4-E38913BB6DCB}";
 
    /// <summary>
    /// ID for “Edit Permissions”
    /// </summary>
    public const string EditItemPermissionId = "{702F4CFB-B1D4-4102-8F6A-4E36DBCED8E3}";
 
    /// <summary>
    /// ID for admin permissions for an object 
    /// </summary>
    public const string AdminPermissionId = "{2D53DE58-A90D-4fc8-A937-AFEEFDB0AFB3}";
 
 
 
    /// <summary>
    /// Access Permissions To Your Module 
    /// </summary>
    public static readonly Permission ModuleAccessPermission =
        new Permission(ModuleAccessPermissionId,
                        SR.M("Access Permissions To Your Module"),
                        SR.M("Capability to work with the module"),
                        ModuleName,
                        moduleUid: Module
        //You can add ,readOnly: true – then the permission will contain the specified group.
                        );
 
    /// <summary>
    /// View Permissions
    /// </summary>
    public static readonly Permission ViewItemPermission =
        new Permission(ViewItemPermissionId,
                        SR.M("View"),
                        "",
                        SR.M("Permission to view object instance"),
                        permissionType: PermissionType.Instance,
                        entityType: InterfaceActivator.TypeOf<IMyObject>(),
                        @base: CommonPermissions.View);
 
    /// <summary>
    /// Edit Permissions
    /// </summary>
    public static readonly Permission EditItemPermission =
        new Permission(EditItemPermissionId,
                        SR.M("Edit"),
                        "",
                        SR.M("Permission to edit object instance "),
                        permissionType: PermissionType.Instance,
                        entityType: InterfaceActivator.TypeOf<IMyObject>(),
                        @base: CommonPermissions.Edit);
         
    /// <summary>
    /// Full access permissions
    /// </summary>
    public static readonly Permission AdminPermission =
        new Permission(AdminPermissionId,
                        SR.M("Admin permissions for the object"),
                            "",
                        SR.M("Full permissions for the object"),
                        permissionType: PermissionType.Instance,
                        entityType: InterfaceActivator.TypeOf<IMyObject>(),
                        @base: CommonPermissions.AdminPermission);
 
    /// <summary>
    /// Get list of permission 
    /// </summary>
    /// <returns></returns>
    public IEnumerable<Permission> GetPermissions()
    {
        return new[]
                    {
                        ViewItemPermission,
                        EditItemPermission,
                        ModuleAccessPermission,
                        AdminPermission
                    };
    }
 
    /// <summary>
    /// Get information about the default roles 
    /// </summary>
    /// <returns></returns>
    public IEnumerable<PermissionStereotype> GetPermissionStereotypes()
    {
        return EmptyArray<PermissionStereotype>.Instance;
    }
 
    public List<string> LocalizedItemsNames
    {
        get { return null; }
    }
 
    public List<string> LocalizedItemsDescriptions
    {
        get { return null; }
    }
 
    public List<string> LocalizedItemsCategories
    {
        get { return null; }
    }
}

These permissions will be used in the module and in the further implementation of the extension points. As you may have noticed, ViewItemPermission, EditItemPermission and AdminPermission use the base access permissions to the object, while ModuleAccessPermission is the access permission to the module.

An example of the IModuleAccessPermissionProvider interface implementation

[Component]
internal class ModuleAccessPermission : IModuleAccessPermissionProvider
{
    public const string Module = __ModuleInfo.ModuleId;
 
    public Dictionary<string, Permission> GetModuleAccessPermissions()
    {
        return new Dictionary<string, Permission> { { Module, ModulePermissions.ModuleAccessPermission } };
    }
}

The permission “ModulePermissions.ModuleAccessPermission” will be displayed in the "Global access settings" section (see fig. 4). This example shows the implementation of the access to the module (depending on access permissions the corresponding menu item is shown or hidden). Learn how to create a menu item in this article.

 Once the module is activated, the section with your permissions will be displayed in the global access settings. There are two ways to apply permissions:

  1. Add a permission attribute to the controller or controller method.
  2. Check permissions in separate methods.

 An example of the controller code with the attribute:

public class HomeController : BPMController
{
    [ContentItem]
    [Permission(ModuleAccessPermission.ModuleAccessPermissionId)]
    public ActionResult ViewItem()
    {
        return View("View");
    }
}
 
An example of the permission check in methods/views:
if (SecurityService.HasPermission(UserManager.Instance.GetCurrentUser(),
    ModuleAccessPermission.ModulePermissionAccess))
{
    //Your code
}

Example of implementation of the IInstanceSettingsPermission base class

[Component]
internal class InstanceSettingsPermission : InstanceSettingsPermissionBase<IMyObject, IMyObjectPermissions>
{
    public InstanceSettingsPermission()
        : base(access => access.MyObject) // expression referring to the entity
    {
    }
 
    protected override ICollection<IMyObjectPermissions> GetPermissionHolderCollection(IMyObject target)
    {
        return target.Permissions; // collection that stores the permission settings 
    }
}

To implement setting the IMyObject object is necessary, as well as the object to store the IMyObjectPermissions settings. The structure of the IMyObject object: Fig.10 Structure of the object with the settings

Fig.9 IMyObject Structure

 

 

Fig.10 Structure of the object with the settings

On the "Advanced" tab you must specify that the object implements the interface EleWise.ELMA.Security.Services.ISecuritySetIdHolder. You must also check the box "Additional code." This must be done to implement the ISecuritySetIdHolder interface.

Properties of the ISecuritySetIdHolder interface:

/// <summary>
/// The permission holder (user, group, organizational structure element, etc.).
/// </summary>
IEntity Assigned { get; set; }
 
/// <summary>
/// The role of the permission holder, it may be an author, a process initiator or a group of users, and so forth.
/// </summary>
Guid TypeRoleId { get; set; }
 
/// <summary>
/// Permission ID
/// </summary>
Guid PermissionId { get; set; }
 
/// <summary>
/// Permission object
/// </summary>
object Target { get; set; }

Additional code for an object with the settings:

using System;
using EleWise.ELMA.Model.Entities;
using EleWise.ELMA.Security.Models;
using EleWise.ELMA.Security.Services;
 
namespace ModuleAccessPermissionProvider.Models
{
    public partial class MyModuleSettings
    {
        public virtual IEntity Assigned
        {
            get //Get a parameter value depending on the permission holder 
            {
                if (User != null)
                    return User;
                if (Group != null)
                    return Group;
                if (OrganizationItem != null)
                    return OrganizationItem;
                if (this.OrganizationItemEmployee != null)
                    return OrganizationItemEmployee;
                return null;
            }
            set // Determine the value type and set it to the created  property of the permission holder 
            {
                if (value is User)
                    User = (User)value;
                else if (value is UserGroup)
                    Group = (UserGroup)value;
                else if (value is OrganizationItem)
                {
                    var organizationItem = (OrganizationItem)value;
                    if (organizationItem.ItemType == OrganizationItemType.Position)
                        OrganizationItem = organizationItem;
                    else
                        OrganizationItemEmployee = organizationItem;
                }
                else
                    throw new ArgumentException(value.ToString());
            }
        }
         
        public virtual object Target
        {
            get { return MyObject; } Get the object value, for which the permission is granted  
            set { MyObject = (MyObject)value; } // Set the property value converting it to type
        }
        
       public virtual Guid TypeRoleId
        {
            get { return PermissionRole; } // Get value
            set { PermissionRole = value; } // Set property value
        }
   }
}

Example of implementation of the IPermissionsDelegate base class

To access the instance of the specified type (IMyObject) it is necessary to implement the extension point. If you do not implement this extension point, a user when opening the object, will receive the error message  telling that it is necessary to delegate permissions.

[Component]
internal class MyObjectPermissionsDelegate : IPermissionsDelegate
{
    public ISecurityService SecurityService { get { return Locator.GetService<ISecurityService>(); } }
 
    public InstanceSettingsPermissionsDelegate DefaultInstanceSettingsPermissionsDelegate { get; set; }
 
    private readonly Permission[] _permissions = new[]
                        {
                            ModulePermissions.ViewItemPermission,
                                        ModulePermissions.EditItemPermission
                        };
 
    public bool CanCheckPermissions(Type type, Permission permission)
    {
        return typeof(IMyObject).IsAssignableFrom(type) && _permissions.Contains(permission);
    }
 
    public bool HasPermission(IUser user, Permission permission, object target, bool skipAdmin = false)
    {
        var obj = target as IMyObject;
 
        if (permission == null || obj == null)
            return false;
 
        if (permission.Id == ModulePermissions.ViewItemPermission.Id)
        {
            return DefaultInstanceSettingsPermissionsDelegate.HasPermission(user, permission, target);
        }
 
        if (permission.Id == ModulePermissions.EditItemPermission.Id)
        {
            return DefaultInstanceSettingsPermissionsDelegate.HasPermission(user, permission, target);
        }
 
        return false;
    }
}

Example of implementation of the IEntityInstanceDefaultPermission base class

[Component]
internal class DefaultPermission : IEntityInstanceDefaultPermission
{
    public Type EntityType 
    {
        get { return typeof(IMyObject); }
    }
 
    /// <summary>
    /// Create permissions
    /// </summary>
    /// <param name="entity">Entity</param>
    public void CreatePermissions(IEntity entity)
    {
        var obj = (IMyObject) entity;
        if (obj.Permissions.Count == 0)
        {
 
            var h = new PermissionHelper(obj);
 
            // Access permissions to the object
            h.AddPermission(ModulePermissions.ViewItemPermissionId, CommonRoleTypes.Author);
            h.AddPermission(ModulePermissions.EditItemPermissionId, CommonRoleTypes.Author);
 
        }
    }
 
    private class PermissionHelper
    {
 
        public PermissionHelper(IMyObject myObject)
        {
            this.myObject = myObject;
        }
 
        public void AddPermission(string permissionId, PermissionRoleType role)
        {
            var item = new InstanceOf<IMyObjectPermissions>
            {
                New =
                {
                    PermissionRole = role.UID,
                    MyObject = myObject,
                    PermissionId = new Guid(permissionId)
                }
            }.New;
            if (role == CommonRoleTypes.Author)
            {
                item.User = myObject.CreationAuthor;
                item.TypeRoleId = CommonRoleTypes.Author.Id;
            }
            if (role == RoleType.AllUsers)
            {
                item.Group = UserGroupManager.Instance.Load(SecurityConstants.AllUsersGroupUid);
                item.TypeRoleId = RoleType.AllUsers.Id;
            }
            myObject.Permissions.Add(item);
        }
 
        private IMyObject myObject;
 
    }
}

When you create an object, the default object permissions will be added to it. The object author will be granted the permissions to view and edit .

Example of implementation of the IPermissionRoleTypeProvider base class

[Component]
internal class RoleType : IPermissionRoleTypeProvider
{
    /// <summary>
    /// All users
    /// </summary>
    public static readonly PermissionRoleType ObjectRole =
        new PermissionRoleType(new Guid("58DEC298-AB48-468a-9A59-E245826A4B1F"), "Role of your object", InterfaceActivator.TypeOf<IUserGroup>(), "#x16/User.png", "javascript:showUserGroupInfo({0});");
         
    public IEnumerable<PermissionRoleTypeStereotype> GetTypePermissionRoleStereotypes()
    {
        var organizationItem = CommonRoleTypes.OrganizationItem;
        var group = CommonRoleTypes.Group;
        var user = CommonRoleTypes.User;
        var author = CommonRoleTypes.Author;
 
        var allTypes = new[] { ObjectRole, group, organizationItem, user, author, user };
 
        return new[]
                    {
                        new PermissionRoleTypeStereotype(InterfaceActivator.TypeOf<IMyObject>(), 
                            ModulePermissions.ViewItemPermission, allTypes),
                        new PermissionRoleTypeStereotype(InterfaceActivator.TypeOf<IMyObject>(), 
                            ModulePermissions.EditItemPermission, allTypes), 
                        new PermissionRoleTypeStereotype(InterfaceActivator.TypeOf<IMyObject>(), 
                            ModulePermissions.AdminPermission, new [] {author})
                    };
    }
         
    public IEnumerable<PermissionRoleType> GetRoleTypes()
    {
        return new[]
                    {
                        ObjectRole
                    };
    }
 
    public List<string> LocalizedItemNames()
    {
        return new List<string>()
                    {
                        SR.T("Role of your object")
                    };
    }
}

As you may have noticed in the example, we use the existing system roles; we also create a new role (see figure 3).We have defined particular roles that can be selected for each permission (view, edit, administrator permissions). For example, for the View permission, you can select all five roles: all users, groups, organizational structure element, author, and user, whereas for the "Object Administrator" permission you can select only the author role.

To be able to assign access permissions you need to implement this capability in the web application of your module. In this example, it is implemented by the action in the controller. We have implemented the object page, whose toolbar has two buttons "Back" and "Settings."

Code of the toolbar:

@(Html.Toolbar()
          .Group("tb-group1")
          .Button(b => b
                .Uid("toolbar-action-Back")
                .Text(SR.Back)
                .IconUrl("#x32/Prev.png")
                .Click("javascript:history.back(-1);")
          )
          .Button(b => b
                .Uid("toolbar-action-Settings")
                .Text(SR.T("Settings"))
                .IconUrl("#x32/settings.png")
                .Url(Url.Action("EntityPermissionSettings", "PermissionManagment", new { area = "EleWise.ELMA.BPM.Web.Security", id = Model.Id, type = EleWise.ELMA.Model.Services.InterfaceActivator.TypeOf<IMyObject>().AssemblyQualifiedName }))
          )
      )

When clicking the button a user will see access permissions shown on the fig. 2

 Links to API elements 

IPermissionProvider
IModuleAccessPermissionProvider
IInstanceSettingsPermission
IPermissionsDelegate
IEntityInstanceDefaultPermission
IPermissionRoleTypeProvider