Advise on restricting entity editing based on owner field

Hello, I am looking for some advise on how to proceed on the following.

Within the application there’s a role that restricts users to allow editing and saving certain entities only if they are the assigned owner. The owner is a field on the entity.

This is done using a constraint like this:

{E}.owner.id == userSession.user.id

This works ok, although on objects that the user is not assigned to as an owner, the user is able to assign his or her selves as an owner and still be able to save the entity. Likewise, when the user would like to transfer the entity to someone different, it cannot be saved.

What would be the best way to solve this issue elegantly? This scenario is specific to a certain customer and I would like to have it applied as much as possible through configuration thus avoiding a lot of coding for this customer only.

Summarized, the best solution would need to work as follows:

  • allow for editing & saving when the user is initially assigned as an owner (but being able to reassign the entity)
  • prevent the user from re-assigning the entity if it is not the initial owner; best case: show the entity read-only
  • require a minimum of coding, mostly by configuration

Any help appreciated. Thanks.

Hi Berend,

I think you can do it as follows:

  • Create an update constraint which allows for editing the entity only if the owner is the current user or is not assigned yet:

    ({E}.owner == null) || ({E}.owner.id == userSession.user.id)
    
  • In the editor screen, make the edit action to check the constraint:

    <actions>
        <action id="edit" constraintOperationType="update"/>
    </actions>
    
  • Add a button to the edit screen that allows users to assign the entity to themselves:

    <button id="assignBtn" caption="Assign to me" invoke="assignToMe"/>
    
    package com.company.sample.web.foo;
    
    import com.haulmont.cuba.core.global.UserSessionSource;
    import com.haulmont.cuba.gui.components.AbstractEditor;
    import com.company.sample.entity.Foo;
    import com.haulmont.cuba.gui.data.Datasource;
    
    import javax.inject.Inject;
    
    public class FooEdit extends AbstractEditor<Foo> {
        @Inject
        private Datasource<Foo> fooDs;
        @Inject
        private UserSessionSource userSessionSource;
    
        public void assignToMe() {
            fooDs.getItem().setOwner(userSessionSource.getUserSession().getCurrentOrSubstitutedUser());
        }
    }
    

The sample project is attached.
owner-access.zip (82.5 KB)

1 Like

Hello Konstantin.

Thanks for the elaborate follow-up. I have taken a look at your approach which is great but it doesn’t cover the idea I want to achieve yet.

Your approach allows a user to edit the entity only if it is assigned to it, which is great. When editing however and assigning it to someone else, it cannot be saved (as is the case in my setup as well). An error appears.

Also, the button ‘Assign to me’ is only available to the user if it is already assigned to it. So that doesn’t help much either, sorry.

Moreover, I would like the user to view the details of the entity if it is not assigned to it.

On a side note, seeing the edit button disabled if the entity is not assigned to the current user is something I can use in other situations - so have picked that one up - thanks!

Anyway, any ideas how to approach this problem?

Then you probably cannot use constraints and do all the work programmatically. For example, define a bean which encapsulates your access rules like this:

package com.company.sample.web.foo;

import com.company.sample.entity.Foo;
import com.haulmont.cuba.core.global.UserSessionSource;
import org.springframework.stereotype.Component;

import javax.inject.Inject;

@Component("sample_OwnerAccessControl")
public class OwnerAccessControl {

    @Inject
    private UserSessionSource uss;

    boolean isEditAvailable(Foo foo) {
        return foo.getOwner() == null || foo.getOwner().equals(uss.getUserSession().getCurrentOrSubstitutedUser());
    }
}

And use it in the screens:

public class FooBrowse extends AbstractLookup {

    @Named("foosTable.edit")
    private EditAction foosTableEdit;

    @Inject
    private GroupDatasource<Foo, UUID> foosDs;

    @Inject
    private OwnerAccessControl accessControl;

    @Override
    public void init(Map<String, Object> params) {
        super.init(params);

        // change EditAction caption depending on access
        foosDs.addItemChangeListener(e -> {
            if (e.getItem() != null && accessControl.isEditAvailable(e.getItem())) {
                foosTableEdit.setCaption("Edit");
            } else {
                foosTableEdit.setCaption("View");
            }
        });
    }
}
public class FooEdit extends AbstractEditor<Foo> {

    @Inject
    private Datasource<Foo> fooDs;

    @Inject
    private UserSessionSource userSessionSource;

    @Inject
    private OwnerAccessControl accessControl;

    @Override
    protected void postInit() {
        if (!accessControl.isEditAvailable(getItem())) {
            for (Component component : getComponents()) {
                if (component instanceof Editable)
                    ((Editable) component).setEditable(false);
                else
                    component.setEnabled(false);
            }
        }
    }

    public void assignToMe() {
        fooDs.getItem().setOwner(userSessionSource.getUserSession().getCurrentOrSubstitutedUser());
    }
}

Of course, you can make this approach more generic by creating a MappedSuperclass for your entities containing owner attribute. Then the OwnerAccessControl bean will work with this superclass. Screen controllers logic can also be encapsulated in appropriate base classes or delegates.

owner-access.zip (86.4 KB)

1 Like

Hi Konstantin,

Yes - this works as required! Excellent.

Although I will need some adjustments as it only applies to a certain role - not all users will have this restriction. In fact, there are roles that allow for editing these entities regardless of the ownership and other roles that may not edit the entities at all.

But I can see how this could work, so thanks.

Regards, Berend.