Support for granular permissions in Account Console#10568
Support for granular permissions in Account Console#10568cgeorgilakis wants to merge 1 commit intokeycloak:mainfrom
Conversation
|
@ssilvert could you review this PR? It is serious problem that only manage-account users can view/manage anything in account console. We do not want to give users the possibility to change their basic info. Do you want to rebase it? Do you want to remove extra account roles and discuss account roles in another issue? |
|
@cgeorgilakis Have you looked at the user profile (https://www.keycloak.org/docs/latest/server_admin/#defining-a-user-profile?) You can use it to prevent users from changing a defined set of attributes. |
|
@sschu We have checked User profile feature in version 18 and it is interesting. The main problem is that you can not change permissions in main attributes ( email, first name, last name). |
|
At least the configuration for main attributes was added, see #12620. You can also forbid changing password by disabling this action on the realm. But indeed, all of these things are realm-level settings. |
|
Good news for Keycloak 20 version! |
|
Hi @cgeorgilakis, I definitely like the idea of having some fine-grained authorization in account console. Has this been discussed on the discussions board or anywhere else with members of the Keycloak team? I was going to try running your code but there are conflicts. Can you resolve them? |
|
@ssilvert I will rebase, resolve conflicts and I will inform you. I have open a github discussion where I have not given any reply after mentioning about this PR. |
| import static org.keycloak.models.AccountRoles.MANAGE_ACCOUNT_2FA; | ||
| import static org.keycloak.models.AccountRoles.MANAGE_ACCOUNT_BASIC_AUTH; |
There was a problem hiding this comment.
I'd rather have a single role "MANAGE_CREDENTIALS" than these two.
There was a problem hiding this comment.
When user is from external IdP, he must not be able to add Keycloak password. However, he must be able to add two factor credentials. Can we block external users from login via Keycloak Username form in another way?
| isTotpConfigured = session.userCredentialManager().isConfiguredFor(realm, user, realm.getOTPPolicy().getType()); | ||
| RoleModel deleteAccountRole = realm.getClientByClientId(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID).getRole(AccountRoles.DELETE_ACCOUNT); | ||
| deleteAccountAllowed = deleteAccountRole != null && user.hasRole(deleteAccountRole) && realm.getRequiredActionProviderByAlias(DeleteAccount.PROVIDER_ID).isEnabled(); | ||
| RoleModel manageAccount = realm.getClientByClientId(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID).getRole(AccountRoles.MANAGE_ACCOUNT); |
There was a problem hiding this comment.
By adding scope on all the account client roles to account-console client the roles are available in the token and can be read directly by the admin console rather than having to pass these as variables in the landing page for the console.
There was a problem hiding this comment.
Although I agree that this is best solution, I need Keycloak team help.
getToken() function exists in 'themes/src/main/resources/theme/keycloak.v2/account/src/app/keycloak-service/keycloak.service.ts' for getting token. How can I use this function or something similar in index.ftl in order to hide some parts of themes/src/main/resources/theme/keycloak.v2/account/resources/content.json? For tsx files, I believe change can be implemented.
There was a problem hiding this comment.
@cgeorgilakis As you know, there is a "hidden" attribute on each ContentItem in content.json.
When the account console starts up, the value of that attribute is evaluated as javascript. So you should be able to call out to a function that reads the token from there. As long as your function returns a boolean value true, the ContentItem will be hidden.
| @@ -0,0 +1,52 @@ | |||
| package org.keycloak.migration.migrators; | |||
There was a problem hiding this comment.
Need to update to MigrateTo20_0_0
There was a problem hiding this comment.
Done during rebase
| RoleModel manageAccount2fa = accountClient.addRole(MANAGE_ACCOUNT_2FA); | ||
| manageAccount2fa.setDescription("${role_" + MANAGE_ACCOUNT_2FA + "}"); | ||
| } | ||
| } |
There was a problem hiding this comment.
Need to add scope on all roles in account client to account-console client
stianst
left a comment
There was a problem hiding this comment.
See comments. Haven't done a full review yet, but let's start with deciding what the credentials role should be, and have the account-console read roles directly from the token rather than passing as variables to the landing page.
31e0a3a to
64e1d5d
Compare
|
I have rebased it resolving conflicts. I will check tests. |
7d152ef to
697f579
Compare
beb7d8b to
17c4ef8
Compare
|
Originally, |
There was a problem hiding this comment.
Let's not add commented out code.
| // kc.hasAtLeastOneResourceRoles = function(resource, ...roles) { | |
| // if (!kc.resourceAccess) { | |
| // return false; | |
| // } | |
| // | |
| // var access = kc.resourceAccess[resource || kc.clientId]; | |
| // if (!!access) { | |
| // return roles.some(function(role) { | |
| // return access.roles.indexOf(role) >= 0; | |
| // }); | |
| // } else { | |
| // return false; | |
| // } | |
| // } |
There was a problem hiding this comment.
Sorry, it is temporary.
Although Keycloak have been working with this code, some tests related to ui were failing.
I try in another way. Finnally, I will remove it.
There was a problem hiding this comment.
The obj variable does not exist.
There was a problem hiding this comment.
Since all cases here rely on the existence of the access variable, it would be best to add early return to avoid repeating logic.
if (!access) {
return false;
}There was a problem hiding this comment.
ok I will do the change
a2c8f1d to
dc9d5a6
Compare
There was a problem hiding this comment.
Since all of the branches of this statement terminate (the all return). There is no need to use else if or else.
| if (!access) { | |
| return false; | |
| } else if (Array.isArray(role)) { | |
| return role.some(function(r) { | |
| return access.roles.indexOf(r) >= 0; | |
| }); | |
| } else{ | |
| return !!access && access.roles.indexOf(role) >= 0; | |
| } | |
| if (!access) { | |
| return false; | |
| } | |
| if (Array.isArray(role)) { | |
| return role.some(function(r) { | |
| return access.roles.indexOf(r) >= 0; | |
| }); | |
| } | |
| return access.roles.indexOf(role) >= 0; |
jonkoops
left a comment
There was a problem hiding this comment.
The type definition for the hasResourceRole() method should be updated as well.
You are right. How could I define that role could be either String either String Array? Or should I create a seperate method? @ssilvert @stianst I have kept interface features in account console. I have only changed that its values is taken from account role from user access token. Are we ok with this functionality? |
|
You can provide the type as either a string, or a string array so |
Done |
There was a problem hiding this comment.
Hmmm, taking a closer look at this change I am left wondering if it's really needed. If the consumer of this would like to check an array one could simply do the following:
const hasRole = roles.some(role => keycloak.hasResourceRole(role))Apologies for the back an forth, but I think I'd rather keep the implementation of Keycloak JS itself unchanged. Could you remove this change from it?
There was a problem hiding this comment.
Hmmm, taking a closer look at this change I am left wondering if it's really needed. If the consumer of this would like to check an array one could simply do the following:
const hasRole = roles.some(role => keycloak.hasResourceRole(role))Apologies for the back an forth, but I think I'd rather keep the implementation of Keycloak JS itself unchanged. Could you remove this change from it?
@jonkoops I agree that it is more clear. My only thought was that keycloak.hasResourceRole will be call 2-3 times even if user has no account roles. However, this is rare. So I will procceed with requested change soon.
a8cf426 to
141181f
Compare
96f5923 to
5b2dcdd
Compare
|
I have made requested change. keycloak.js does not change. |
jonkoops
left a comment
There was a problem hiding this comment.
Changes for the front-end look good, can't say anything about the code changes on the Java side.
|
@ssilvert @stianst Could we proceed with the review of this PR? |
|
@cgeorgilakis I'm sorry this is taking so long and I do owe you an explanation. We are working on a refactored version of the account console that will use modern React (hooks instead of classes). Also, we are switching to Vite instead of using the dead project, Snowpack. We've had this new version almost ready for quite awhile now but progress has been held up by our migration of all UI code from the keycloak/keycloak-ui repo to keycloak/keycloak. So the main reason this PR has not gotten more traction is because it doesn't make sense to make big changes to account console V2 while we've been expecting V3 to come out soon. Unfortunately, soon has turned into much longer than anticipated. Thanks for your incredible patience and again, I'm sorry I didn't explain this sooner. I do think it won't be long before the migration is complete. Then we can work to get fine grained permissions into V3. |
|
@cgeorgilakis Just noticed this old PR. We have completed V3 of account console. So if you still want to get this done I suggest we start from scratch with a new PR. I'm going to close this one though. |
|
😥 |
Closes #8734
PR for supporting granular permissions in Account Console.
We propose to add two extra roles (‘manage-account-basic-auth’ and ‘manage-account-2fa’). A user from external Identity Provider can not be able to login as Keycloak User (add password) but he will be able to add two factor authedicator. However, we can remove them if you want or add others instead.
My PR works with adding a composite role containing all account roles in account-console client. I want to add all scopes of "account" client to "account-console" client in order not to needed adding extra role. However, such a change make testAccountConsoleClient method of AbstractMigrationTest works different for masterRealm and migrationRealm. What else change should I do? Could you help me with it?