To implement field-level editing restrictions based on both the user’s role and the entity status, you should combine role checking and component management in your view controller.
Let me describe the solution using the Onboarding example. Suppose that the task is to enable updating the user’s “Active” field only for “ExtendedUserManagementRole” and if the OnboardingStatus of the user is “Completed”.
First approach is to check if the user has a particular role.
The role:
@ResourceRole(name = "ExtendedUserManagementRole", code = ExtendedUserManagementRole.CODE, scope = "UI")
public interface ExtendedUserManagementRole {
String CODE = "extended-user-management-role";
}
User detail view controller:
public class UserDetailView extends StandardDetailView<User> {
// ...
@Autowired
private CurrentAuthentication currentAuthentication;
@ViewComponent
private JmixCheckbox activeField;
@ViewComponent
private InstanceContainer<User> userDc;
@Subscribe
public void onReady(final ReadyEvent event) {
// ...
// Update field on view opening
updateActiveField();
}
private void updateActiveField() {
OnboardingStatus onboardingStatus = userDc.getItem().getOnboardingStatus();
boolean editable = OnboardingStatus.COMPLETED.equals(onboardingStatus) && hasExtendedUserManagerRole();
activeField.setReadOnly(!editable);
}
// Check if the current user has "extended-user-management-role"
private boolean hasExtendedUserManagerRole() {
return currentAuthentication.getAuthentication().getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.anyMatch(authority -> authority.equals("ROLE_extended-user-management-role"));
}
// Update state when the entity state is changed
@Subscribe(id = "userDc", target = Target.DATA_CONTAINER)
public void onUserDcItemPropertyChange(final InstanceContainer.ItemPropertyChangeEvent<User> event) {
if (event.getProperty().equals("onboardingStatus")) {
updateActiveField();
}
}
Another approach is not tied to a particular role. Instead, we’ll check if the user has a specific policy. This is more flexible, because you can enable the policy in any role. For example, it can be in a base role which is not directly assigned to the user.
A role that enables the specific policy:
@ResourceRole(name = "ExtendedUserManagementRole", code = ExtendedUserManagementRole.CODE, scope = "UI")
public interface ExtendedUserManagementRole {
String CODE = "extended-user-management-role";
@SpecificPolicy(resources = "onboarding.canUpdateUserActiveField")
void specific();
}
The specific policy context to check in the app code:
package com.company.onboarding.security.specific;
import io.jmix.core.accesscontext.SpecificOperationAccessContext;
public class UpdatingUserActiveFieldContext extends SpecificOperationAccessContext {
public static final String NAME = "onboarding.canUpdateUserActiveField";
public UpdatingUserActiveFieldContext() {
super(NAME);
}
}
User detail view controller:
public class UserDetailView extends StandardDetailView<User> {
// ...
@Autowired
private AccessManager accessManager;
@Autowired
private CurrentAuthentication currentAuthentication;
@ViewComponent
private JmixCheckbox activeField;
@ViewComponent
private InstanceContainer<User> userDc;
@Subscribe
public void onReady(final ReadyEvent event) {
// ...
// Update field on view opening
updateActiveField();
}
private void updateActiveField() {
OnboardingStatus onboardingStatus = userDc.getItem().getOnboardingStatus();
boolean editable = OnboardingStatus.COMPLETED.equals(onboardingStatus) && hasSpecificPermission();
activeField.setReadOnly(!editable);
}
// Check if the current user has "onboarding.canUpdateUserActiveField" specific permission
private boolean hasSpecificPermission() {
UpdatingUserActiveFieldContext updatingUserActiveFieldContext = new UpdatingUserActiveFieldContext();
accessManager.applyRegisteredConstraints(updatingUserActiveFieldContext);
return updatingUserActiveFieldContext.isPermitted();
}
// Update state when the entity state is changed
@Subscribe(id = "userDc", target = Target.DATA_CONTAINER)
public void onUserDcItemPropertyChange(final InstanceContainer.ItemPropertyChangeEvent<User> event) {
if (event.getProperty().equals("onboardingStatus")) {
updateActiveField();
}
}
Regards,
Konstantin