Access Groups: Why only one per user?

Probably, but the problem is I don’t even have a fragment of an inkling on how to do it. I need records a user doesn’t have access to, to appear to not exist - which is what access groups do… but only in the limited fashion like we’ve been discussing.

I have no idea how to do it… I’m guessing one would have to extend DataManager as I’m guessing all the access group stuff is done there?

I wouldn’t even know where to start.

I need records a user doesn’t have access to, to disappear, to basically not exist… everywhere in the system that records for that entity are accessed. It needs to be central… like the access groups are.

My 2 cents…

I completely agree with @mario, CUBA security subsystem rocks :smiley:

Something that I noticed some time ago (after the initial “confusion” I had about access groups), is that your constraints don’t have to be “hardcoded” as constraints in the AccessGroup. You can use database joins and even call standard services (spring beans, etc.) to define if the constraint has to be applied or not for the given user.

Where I work we created a Status Component (something like a simpler BPM) just to define which user (or group of users) is responsible for a given record in a given status. As you can imagine, there are a lot of possible constraints to be applied.

It ended up being very easy to implement: We created a single group for all users with the same constraint, that would call a service (spring bean) that, for any given entity (we created a superclass) would check the entity status, and if the user was assigned as responsible for this specific status, he would be allowed to manipulate the record.

@jon.craig and ray1, if you two create a test project with your domain classes, I can try to help you setting the constraints to achieve what you are calling " “set of keys”.

I did a quick search and there are some information in the docs about it:

https://doc.cuba-platform.com/manual-7.2/local_admins_example.html
https://doc.cuba-platform.com/manual-7.2/constraints.html#constraints_run_time

Regards,
Peteson

1 Like

Take a look at this database script in the addon @mario created:

insert into SEC_CONSTRAINT
(ID, VERSION, CREATE_TS, CREATED_BY, UPDATE_TS, UPDATED_BY, DELETE_TS, DELETED_BY, CHECK_TYPE, OPERATION_TYPE, CODE, ENTITY_NAME, JOIN_CLAUSE, WHERE_CLAUSE, GROOVY_SCRIPT, FILTER_XML, IS_ACTIVE, GROUP_ID)
values ('a122a042-c314-bfb8-affa-d0d31aab2596', 1, '2017-03-09 09:38:17', 'admin', '2017-03-09 09:38:17', null, null, null, 'db', 'read', null, 'entitypin$Order', null, '{E}.customer.id = :session$pinEntity', null, null, true, 'cde8e540-8f74-4c95-7bf4-f6b14e927eb0');

The user will only be able to read (view) orders of the selected (‘pinned’) user. In this case @mario is using a session parameter for the rule. But you can use other entities tables (joins, etc.) and even a service if a complex logic is required.

Regards,
Peterson.

1 Like

Is there an example you could share of how this works? I’m still new enough to this that I can’t work out how that would work, without an example.

I can try to make a test project, but it would end up being extremely complex for a test project. If you look back at my initial post you can see a small example of what our system does.

And keep in mind - a user of the system (an admin user of course!) needs to define all of this, at runtime, to specify which users can see and work with what data.

In order to dynamically inject constraints into the user session you don’t need to touch the dataManager implementation.DataManger is more of a delegation class. For the heavy lifting a lot of the stuff goes on in the RdbmsStore. For the constraints it somehow does a lookup of the constraints to apply through the UserSession class. Therefore the only thing you have to do is to change the constraints that are currently set for the user session.

In the entity pin example (and with the database script @peterson.br mentioned) I really just auto-populated the definition of the constraint configuration. If you start the entity-pin example and look into the Access Groups you see a group called “PinDefinitions”. Below you will find the definitions of what constraints to apply for each Entity (the name of the Entity is actually the name of the Access Group). These are just the configuration that should be applied when a user clicks pin. But this was just my easy way to somewhere store the constraint configuration. It could be anywhere in the application (hard-coded, in another entity etc.).

What happens at the time the user clicks on the screen is that the addon programmatically adds constraints to the user session.

Here you see the one magic line in the whole entity pin example that applies and removes security constraints on the fly: cuba-component-entity-pin/PinEntityServiceBean.groovy at master · mariodavid/cuba-component-entity-pin · GitHub

In CUBA 7.2 this method was replaced by cuba/UserSession.java at master · cuba-platform/cuba · GitHub (it was never really part of any kind of official API). But the newly created concept of ConstraintsContainer will be the better replacement.

From that point onwards - the DataManager will take this constraint into consideration and apply it all over the application. See the screencast when pinning a particular order and then try to use the standard filter component with a dropdown: only the customers that are related to that order are visible in the application:

pin-order

With that understanding you might be able to abstract away from the concrete use-case of entity-pin and see a path to create a dedicated object where you store your constraint-to-some-context configurations.

Then at login time of the user you load all constraints and put them to the user. This is probably one possible path.

If I find some time in the next days, I will give it a shot and create an example app.

Cheers
Mario

1 Like

I’ll have to look into this when I’m back at work on Monday (and @peterson.br’s info too).

Thank you for the pointers for sure!

This is another point-cut where you can define your own constraints - User Logged In event:

package com.company.test2342342134.listeners;

import com.company.test2342342134.core.MyAccessGroup;
import com.company.test2342342134.entity.Foo;
import com.haulmont.cuba.security.app.group.AccessConstraintsBuilder;
import com.haulmont.cuba.security.app.group.AccessGroupDefinitionsComposer;
import com.haulmont.cuba.security.auth.events.UserLoggedInEvent;
import com.haulmont.cuba.security.global.UserSession;
import com.haulmont.cuba.security.group.ConstraintsContainer;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

import javax.inject.Inject;

@Component("test2342342134_AuthenticationEventListener")
public class AuthenticationEventListener {

    @Inject
    private AccessGroupDefinitionsComposer accessGroupDefinitionsComposer;

    @EventListener
    public void userLoggedIn(UserLoggedInEvent event) {
        UserSession session = event
                .getAuthenticationDetails()
                .getSession();


        if (session.getUser().getLogin().startsWith("a")) {
            ConstraintsContainer newConstraints = AccessConstraintsBuilder.create()
                    .join(session.getConstraints())
                    .withJpql(Foo.class, String.format("{E}.createdBy = '%s'", session.getUser().getLogin()))
                    .build();
            session.setConstraints(newConstraints);
        }
    }
}

In the sample above all users starting with a will be able to see only instances of Foo which were created by them.

Technically here you can apply constraints from different AccessGroups:

ConstraintsContainer newConstraints = AccessConstraintsBuilder.create()
      .join(session.getConstraints())
      .join(accessGroupDefinitionsComposer.composeGroupDefinitionFromDb(myAccessGroupUuid))
      .build();

, where AccessGroupDefinitionsComposer can be simply injected.

Hello guys,

I did a quick test project/app component that implements what I called “Role Constraints”. The idea is very simple: be able to link Access Groups and Roles.

https://github.com/petersonbr/cuba-role-constraints

Key implementation details:

Test steps:

  1. I created a Role to assign Access Groups to:
    image

  2. I created two Access Groups and added constraints:

OnlyAdminUser: Only ‘admin’ user is allowed.

OnlyCreatedByUser: Only users created by the current user are allowed

  1. I linked userRole to OnlyAdminUser Access Group:

  2. I created two new users: test and createdByTest (I used the first user to create the second). Notice that all users are on the default Access Group:

  3. I assigned userRole Role to test User:

  4. I logged in with test User and opened the user browse screen. Only the admin user is shown:
    image

  5. I linked userRole to OnlyCreatedByUser Access Group:
    image

  6. I logged in again with test User and opened the user browse screen. No user was shown, because when these two constraints are applied, no user is allowed.

  7. I removed the link userRole - OnlyAdminUser, logged in again with test User and opened the user browse screen. Now, only the userCreatedByTest User is show!!

image

I hope this quick test I’ve done help you guys somehow. Even complex topics as this one are easily implemented (an can become addon if reuse is required) in CUBA Platform. As @mario told, the sky is the limit :smiley:

Regards,
Peterson.

3 Likes

This is ultimately how I solved this. I used .withInMemory constraints that use custom Predicates to handle what I needed.

1 Like