Developer Docs

Custom C#Bot ACL Security Filtering

Scenario

This is a staff management app. Users have the ability to apply for leave by submitting a leave application . From the perspective of the user:

Entity Model

The entity model consists of two entities:

entityDiagram.png

Security Model

User has been given full CRUD permissions over the target application, as well as admin backend access. This is the first step of us implementing custom security, we will give full permissions in the model, and then limit permissions in special circumstances. Visitors have no permissions.

securityModel.png

Backend

UserApplicationEntity.cs

The class UserApplicationEntity is an ACL (Access Control List) that contains the security rules for CRUD access of Application entities by Users.

C#Bot will write out our security rules to reflect what we have specified in the security diagram. A typical security rule will look something like this:

public bool GetUpdate(User user, IEnumerable<IAbstractModel> models, SecurityContext context)
{
    // % protected region % [Override update rule contents here here] off begin
    return true;
    // % protected region % [Override update rule contents here here] end
}

The function name GetUpdate is called when the server-side is performing an update on an Application, and returns a boolean indicating if the user should be allowed to perform the action. In the case above, it is returning true, since we specified that Users can update Applications in the security model.

Each ACL will contain methods for create, update, read and delete that look like the example above, since they all implement the interface IAcl which describes what an ACL should contain.

Each of these CRUD ACL methods are given a copy of the current user, a list of the models/entities (in this case applications) and a SecurityContext which contains other services like the DbContext (which we can use to access the database).

The ACLs are checked after the entities/updates have been added the the DbContext change tracker, so the changes are queued, but have not yet been executed.

We can perform custom security logic in the Bot-Written ACLs to return true or false for particular cases we want to check. In our example, we want to make sure that a user who has created an application or is the applicant (through the applicant reference) of the application can make updates to their applications.

We can do that by turning the protected region on, and updating the GetUpdate method to look something like this:

public bool GetUpdate(User user, IEnumerable<IAbstractModel> models, SecurityContext context)
{
    // % protected region % [Override update rule contents here here] on begin
    if (context.DbContext.ChangeTracker.Entries().Any(x => x.Entity is ApplicationEntity))
    {
        var applicationEntityEntries = context.DbContext.ChangeTracker.Entries<ApplicationEntity>().ToList();

        if (applicationEntityEntries.All(x => x.Entity.Owner == user.Id || x.Entity.ApplicantId == user.Id))
        {
            return true;
        }
    }

    return false;
    // % protected region % [Override update rule contents here here] end
}

So now the new ACL update logic works like this:

If I wanted to include the conditions to prevent a reviewer from updating certain fields on the application, I would use the following code:

if (applicationEntityEntries.All(x => x.Entity.ReviewerId == user.Id))
{
    var changedEntities = applicationEntityEntries.Select(x => x.Entity).ToList();

    var entityIds = changedEntities.Select(x => x.Id);

    var originalEntities = context.DbContext.ApplicationEntity
        .AsNoTracking()
        .Where(x => entityIds.Contains(x.Id))
        .ToList();

    var allJustificationsUnchanged = changedEntities
        .All(x => x.Justification == originalEntities.First(o => o.Id == x.Id).Justification);

    if (allJustificationsUnchanged)
    {
        return true;
    }
}

The logic is as follows:

Check that the user who is attempting the update is the nominated reviewer of the applications (by comparing the reviewer id on the application to the user id).

  1. Pull out a copy of the ApplicationEntitys.
  2. Get a list of IDs of all the ApplicationEntitys that are being changed.
  3. Retrieve the original versions of there entities from the database.
  4. Check that the Justification attributes have not been changed.
  5. If Justification property has not been changed, allow the changes to go through.

With all the changes together, the code looks like this:

public bool GetUpdate(User user, IEnumerable<IAbstractModel> models, SecurityContext context)
{
    // % protected region % [Override update rule contents here here] off begin
    if(context.DbContext.ChangeTracker.Entries().Any(x => x.Entity is ApplicationEntity))
    {
        var applicationEntityEntries = context.DbContext.ChangeTracker.Entries<ApplicationEntity>().ToList();

        if (applicationEntityEntries.All(x => x.Entity.Owner == user.Id || x.Entity.ApplicantId == user.Id))
        {
            return true;
        }

        if (applicationEntityEntries.All(x => x.Entity.ReviewerId == user.Id))
        {
            var changedEntities = applicationEntityEntries.Select(x => x.Entity).ToList();

            var entityIds = changedEntities.Select(x => x.Id);

            var originalEntities = context.DbContext.ApplicationEntity
                .AsNoTracking()
                .Where(x => entityIds.Contains(x.Id))
                .ToList();

            var allJustificationsUnchanged = changedEntities
                .All(x => x.Justification == originalEntities.First(o => o.Id == x.Id).Justification);

            if (allJustificationsUnchanged)
            {
                return true;
            }
        }
    }
    return false;
    // % protected region % [Override update rule contents here here] end
}

After all our changes have been made, we can log in to the target application and test them out. The image below shows, through a network request, a review getting an error when trying to update an application they have changed the Justification property on.

failedUpdate.png

Was this article helpful?

Thanks for your feedback!

If you would like to tell us more, please click on the link below to send us a message with more details.

Tool:

Generate

Iterate

Bot:

C#Bot

SpringBot

On this page

New to Codebots?

We know our software can be complicated, so we are always happy to have a chat if you have any questions.