Skip to content

OBPIH-5135 Browsers cannot change their password#4444

Merged
awalkowiak merged 4 commits intofeature/upgrade-to-grails-3.3.10from
OBPIH-5135
Jan 16, 2024
Merged

OBPIH-5135 Browsers cannot change their password#4444
awalkowiak merged 4 commits intofeature/upgrade-to-grails-3.3.10from
OBPIH-5135

Conversation

@alannadolny
Copy link
Collaborator

'update', 'importData', 'receive', 'showRecordInventory', 'withdraw', 'cancel', 'change', 'toggle', 'exportAsCsv']
def static changeControllers = ['createProductFromTemplate']

def static omittedControllersWithChangeActions = [
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think it should be handled by the proper configuration of openboxes.security.rbac.rules in the runtime.groovy

Copy link
Member

Choose a reason for hiding this comment

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

Thank you @awalkowiak

@alannadolny alannadolny requested a review from awalkowiak January 9, 2024 15:41
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.

LGTM

@jmiranda
Copy link
Member

jmiranda commented Jan 9, 2024

Actually, I'm having second thoughts about approving this. I'll add some discussion to the ticket.

@jmiranda jmiranda self-requested a review January 9, 2024 18:01
def userInstance = User.get(params.id)
if (userInstance) {
// user is not allowed to edit another user's account unless the user has at least an admin role
if (authService?.currentUser?.id != params.id && !userService?.isUserAdmin(authService?.currentUser)) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

@jmiranda Initially I suggested this as a further improvement to the issue here, but it still does not resolve the issue 100%, we still might have some potential problems with unwanted changes made by users to their accounts and their authorization parameters (through the postman or something like that).
I think that the easiest would be to introduce a separate user edit page stripped of the authorization tab for editing personal data and a password (or even only a simple "change password" form with dedicated action) that could be accessed by the user himself and an admin+ user and a separate (current version of) user edit page for admin+ users.

Copy link
Collaborator

Choose a reason for hiding this comment

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

What do you think? Or we are moving this into 9.1, and skipping this as a quick hotfix for now, since Manon said in a ticket that it is not as pressing as it was while Lesotho was going live?

Copy link
Member

Choose a reason for hiding this comment

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

@awalkowiak Yeah, the separate form / action for changing the password is what I was thinking we could do for now. In fact, I think we should do this regardless of how we implement the record-level authorization rules.

Regarding the release, I would still move this to 0.9.1 because it doesn't seem urgent enough to hotfix. But that's just my vote. I would check again with Kelsey and Manon to see if they still agree that it can wait until March.

Copy link
Member

Choose a reason for hiding this comment

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

We also need to add a ticket to think through how we want to handle the record-level authorization. I actually think this might be possible by adding a ROLE_SELF to the minimum required roles or adding a boolean property to the map to allow the user to edit their own profile and/or password.

Note: I imagine we still want to allow superusers (maybe admins) as well as the user themself to edit their password and update profile information. We also probably need to take user active flag into account.

So I guess for now we don't need to do anything fancy in the interceptor, we can just implement logic in the controller action. But consider delegating the auth logic to a service.

userService.canUserEditUser()

🤷

@alannadolny alannadolny changed the base branch from release/0.9.0-hotfix1 to feature/upgrade-to-grails-3.3.10 January 15, 2024 16:21
} catch (ValidationException e) {
// This read function is used to avoid getting lazy initialization exceptions in
// rendering the edit page, it is done like in the update function above
user = User.read(params.id)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Good practices:

If we considered good practices, it shouldn't be the way to go (solving LazyInitializationException by re-fetching an entity), but join the needed associations at the query level.
Seeing this code, something tells me that we must have some kind of OSIV mechanism that is associated with a session, and maybe it is turned off when catching an exception? 🤔
I've mentioned it recently in my PR: #4435 (comment)
So in a nutshell - I would expect that when we have the OSIV filter disabled, we should get the Lazy every time we want to access some associations when not in a transaction boundary.
I'd consider creating a spike ticket to investigate this part, because it's weird that in the catch we lose the associations.
Or maybe it's maybe related to render vs redirect?
Could you check that?

Not considering good practices:

Obviously we won't experience any performance issues due to that additional fetch, so I won't force you to go with the join query route, especially if you had just followed the code from the method above.

user.errors = e.errors
render(view: "edit", model: [userInstance: user])
} catch (AuthenticationException e) {
flash.message = e.message
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think flash.error would fit better. Used in view like:

<g:if test="${flash.error}">
  <div class="errors">${flash.error}</div>
</g:if>

// Needed to bypass the password == passwordConfirm validation
userInstance.passwordConfirm = userInstance.password
}

Copy link
Collaborator

Choose a reason for hiding this comment

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

have you made sure this method is not used for imports where we probably wouldn't want to remove this part?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I was able to import users, in the import users template there is no password column
image

if (user.password != password) {
user.password = password?.encodeAsPassword()
user.passwordConfirm = passwordConfirm?.encodeAsPassword()
user.save(failOnError: true)
Copy link
Collaborator

Choose a reason for hiding this comment

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

an alternative way could be to use the save from the data service, as it throws validation exception when validation fails by default. (no need to change, just titbit).

@awalkowiak awalkowiak merged commit e000cae into feature/upgrade-to-grails-3.3.10 Jan 16, 2024
@awalkowiak awalkowiak deleted the OBPIH-5135 branch January 16, 2024 19:02
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.

5 participants