Skip to content

OBPIH-6232 show hyperlink to source edit and stockcard on sources list page when user has sufficient roles#4539

Merged
awalkowiak merged 7 commits intofeature/product-supplier-list-redesignfrom
OBPIH-6232-hyperlink-to-source-edit-and-stockcard-on-sources-list-page
Mar 12, 2024
Merged

OBPIH-6232 show hyperlink to source edit and stockcard on sources list page when user has sufficient roles#4539
awalkowiak merged 7 commits intofeature/product-supplier-list-redesignfrom
OBPIH-6232-hyperlink-to-source-edit-and-stockcard-on-sources-list-page

Conversation

@drodzewicz
Copy link
Collaborator

An additional case that I had to include in this ticket was conditionally rendered link to product sources edit if the user has sufficient roles (user is at least an admin and has product manager roles).

I have decided to create a custom hook useUserHasPermission to encapsulate the logic of retrieving user role data from redux and performing appropriate checks.

I also had to return location roles in the AppContext since I noticed that in a previous version of hasPermission util function we don't check for location roles on front end, only global roles.

const allUserRoles = new Set([...userRoles, ...userLocationRoles]);

return _.every(roles, (role) => allUserRoles.has(role));
}, [currentUser]);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't the currentLocation also be a dependency?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, true.
And currentUser dependancy should be [currentUser.id]

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exactly :D and the currentLocation -> currentLocation?.id

Copy link
Member

@jmiranda jmiranda left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll let @awalkowiak decide on what to do here but I recommend moving the auth logic to the server and making it configurable.

User user = User.get(session?.user?.id)
Location location = Location.get(session.warehouse?.id)
String highestRole = user.getHighestRole(location)
List currentLocationRoles = user.getRolesByCurrentLocation(location)?.roleType*.name()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@awalkowiak can approve this if he decides, but there's some tech debt here that we need to deal with in the future.

  • Resolve all roles / permissions on the server-side and use the app context as the vehicle to deliver them. That means, if you're writing auth code on the client we need to refactor.
  • Hard-coded roles should not exist anywhere in the application, but definitely not on the client-side
    • Use configuration properties where possible

As I mentioned on the tech huddle, it would be better to allow the server to resolve access rules on its own. The client shouldn't really be responsible for this. It should just ask "given what you know about me, do I have access to a certain resource". That should happen on-demand, for the most part (i.e. when accessign a URL / controller+action) but in the case where we need to determine whether we display a particular thing to a user, then it probably makes sense to have a have a permission in the appContext.

In this case we really just want to check if the user has the product manager "role" (i.e. permission) within the current context (i.e. location). In my opinion, I don't think the "admin" role should have anything to do with this. Some implementation out there probably wants to be able to allow managers to also be product managers. So we need to make that a configurable property.

It'll be similar to this permission in the App Context (which should also be configurable).

boolean isUserRequestApprover = userService.isUserInAllRoles(session?.user?.id, [RoleType.ROLE_REQUISITION_APPROVER], session.warehouse?.id)

so something like this, but configurable since we don't want roles to be hard-coded

List<Role> defaultRoleTypes = grailsApplication.config.openboxes.product.manager.defaultRoleTypes ?: [RoleType.ROLE_PRODUCT_MANAGER]
boolean defaultRoleTypes = userService.isUserInAllRoles(session?.user?.id, productManagerRoles, session.warehouse?.id)

and then runtime.groovy

openboxes.product.manager.defaultRoleTypes = [RoleType.ROLE_PRODUCT_MANAGER]

If PIH's requirements specify that we need to include the "admin" role, then the configurable code above will allow PIH to make a config change in their Ansible configuration to allow this to work.

Aside: You could also move the "authorization" logic to a service to ensure there is a centralized declaration of permission logic. In the case of purchase approval we added that to the OrderService.

    boolean canApproveOrder(Order order, User userInstance) {
        if (isApprovalRequired(order)) {
            List<RoleType> defaultRoleTypes = grailsApplication.config.openboxes.purchasing.approval.defaultRoleTypes
            return userService.hasAnyRoles(userInstance, defaultRoleTypes)
        }
        return Boolean.TRUE
    }

For this case we probably want to move it to the ProductService. I would also be ok with creating a new SecurityService but we can make that change when we migrate to a security framework like Spring Security or Shiro.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isAdmin: state.session.isUserAdmin,
}));

const isUserAdminWithProductManager = useUserHasPermissions({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you don't end up moving the code to the server-side and pulling this permission from the app context, then we need to at least change the name of the variable to canManageProducts or something like that.

Convention: Variable names should describe the what, not the how.

Copy link
Collaborator

@awalkowiak awalkowiak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@drodzewicz see my comments for the change requests (at least change the name of variable, and do a second one if will be a quick one, otherwise let me know and I'll put it under the TD ticket that I am going to create for this one)

@jmiranda I agree with you here, that it would be better to handle this on the backend, but on the other hand if we'd need to do a separate property for each combination of canDoSomething this feels odd to me too. I prefer moving this froward as is and do a Spike together with future permissions/security refactor to investigate how we should and want to resolve this. I am going to create a TD ticket and we can discuss it further on the tech huddle.

* @param supplementalRoles {RoleType[]} [d=[]] - list of supplemental required roles
* @returns {boolean}
*/
const useUserHasPermissions = ({ minRequiredRole, supplementalRoles = [] }) => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be a replacement for the permisionUtil that we already have? Is there a case that we want to have both? I think we should have only one thing if that's possible.

exportProductSuppliers,
} = useProductSupplierActions({ fireFetchData });

const isUserAdminWithProductManager = useUserHasPermissions({
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's change it to the suggested convention canManageProducts and let's stick to that version of variable names moving forward.

…ils in other places and remove permission util
@drodzewicz drodzewicz requested a review from awalkowiak March 12, 2024 11:30
@awalkowiak awalkowiak merged commit 3737017 into feature/product-supplier-list-redesign Mar 12, 2024
@awalkowiak awalkowiak deleted the OBPIH-6232-hyperlink-to-source-edit-and-stockcard-on-sources-list-page branch March 12, 2024 12:00
awalkowiak pushed a commit that referenced this pull request Mar 12, 2024
…t page when user has sufficient roles (#4539)

* OBPIH-6232 created useUserHasPermissions hook for checking if user is authorized with minimum required roles

* OBPIH-6232 return user current logged in location roles in app context

* OBPIH-6232 Update UserHasPermission hook with users current location roles

* OBPIH-6232 use useUserHasPermission hook on list actions

* OBPIH-6232 added comments to useuserHasPermission hook to document its functionality

* OBPIH-6232 update hook dependency array

* OBPIH-6232 use new useUserHasPermission hook instead of permissionsUtils in other places and remove permission util
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants