Skip to content

Commit 7e55408

Browse files
martin-kanispedroigor
authored andcommitted
Add Client resource type and its scopes to authorization schema and evaluation implementation for ClientsPermissionsV2
Closes #35564 Signed-off-by: Martin Kanis <[email protected]>
1 parent baa6ebb commit 7e55408

File tree

10 files changed

+866
-138
lines changed

10 files changed

+866
-138
lines changed

server-spi-private/src/main/java/org/keycloak/authorization/AdminPermissionsSchema.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,22 +50,27 @@
5050
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
5151

5252
public class AdminPermissionsSchema extends AuthorizationSchema {
53-
53+
5454
public static final String USERS_RESOURCE_TYPE = "Users";
55+
public static final String CLIENTS_RESOURCE_TYPE = "Clients";
5556

5657
//scopes
5758
public static final String MANAGE = "manage";
5859
public static final String VIEW = "view";
5960
public static final String IMPERSONATE = "impersonate";
6061
public static final String MAP_ROLES = "map-roles";
6162
public static final String MANAGE_GROUP_MEMBERSHIP = "manage-group-membership";
63+
public static final String CONFIGURE = "configure";
64+
public static final String MAP_ROLES_CLIENT_SCOPE = "map-roles-client-scope";
65+
public static final String MAP_ROLES_COMPOSITE = "map-roles-composite";
6266

6367
public static final ResourceType USERS = new ResourceType(USERS_RESOURCE_TYPE, Set.of(MANAGE, VIEW, IMPERSONATE, MAP_ROLES, MANAGE_GROUP_MEMBERSHIP));
68+
public static final ResourceType CLIENTS = new ResourceType(CLIENTS_RESOURCE_TYPE, Set.of(CONFIGURE, MANAGE, MAP_ROLES, MAP_ROLES_CLIENT_SCOPE, MAP_ROLES_COMPOSITE, VIEW));
6469

6570
public static final AdminPermissionsSchema SCHEMA = new AdminPermissionsSchema();
6671

6772
private AdminPermissionsSchema() {
68-
super(Map.of(USERS_RESOURCE_TYPE, USERS));
73+
super(Map.of(USERS_RESOURCE_TYPE, USERS, CLIENTS_RESOURCE_TYPE, CLIENTS));
6974
}
7075

7176
public Resource getOrCreateResource(KeycloakSession session, ResourceServer resourceServer, String policyType, String resourceType, String id) {
@@ -85,6 +90,8 @@ public Resource getOrCreateResource(KeycloakSession session, ResourceServer reso
8590

8691
if (USERS.getType().equals(resourceType)) {
8792
name = resolveUser(session, id);
93+
} else if (CLIENTS.getType().equals(resourceType)) {
94+
name = resolveClient(session, id);
8895
}
8996

9097
if (name == null) {
@@ -165,6 +172,17 @@ private String resolveUser(KeycloakSession session, String id) {
165172
return user == null ? null : user.getId();
166173
}
167174

175+
private String resolveClient(KeycloakSession session, String id) {
176+
RealmModel realm = session.getContext().getRealm();
177+
ClientModel client = session.clients().getClientById(realm, id);
178+
179+
if (client == null) {
180+
client = session.clients().getClientByClientId(realm, id);
181+
}
182+
183+
return client == null ? null : client.getId();
184+
}
185+
168186
private StoreFactory getStoreFactory(KeycloakSession session) {
169187
AuthorizationProvider authzProvider = session.getProvider(AuthorizationProvider.class);
170188
return authzProvider.getStoreFactory();

services/src/main/java/org/keycloak/services/resources/admin/permissions/AdminPermissions.java

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -74,23 +74,25 @@ public static void registerListener(ProviderEventManager manager) {
7474
manager.register(new ProviderEventListener() {
7575
@Override
7676
public void onEvent(ProviderEvent event) {
77-
if (event instanceof RoleContainerModel.RoleRemovedEvent) {
78-
RoleContainerModel.RoleRemovedEvent cast = (RoleContainerModel.RoleRemovedEvent)event;
79-
RoleModel role = cast.getRole();
80-
RealmModel realm;
81-
if (role.getContainer() instanceof ClientModel) {
82-
realm = ((ClientModel)role.getContainer()).getRealm();
77+
if (Profile.isFeatureEnabled(Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ)) {
78+
if (event instanceof RoleContainerModel.RoleRemovedEvent) {
79+
RoleContainerModel.RoleRemovedEvent cast = (RoleContainerModel.RoleRemovedEvent) event;
80+
RoleModel role = cast.getRole();
81+
RealmModel realm;
82+
if (role.getContainer() instanceof ClientModel) {
83+
realm = ((ClientModel) role.getContainer()).getRealm();
8384

84-
} else {
85-
realm = (RealmModel)role.getContainer();
85+
} else {
86+
realm = (RealmModel) role.getContainer();
87+
}
88+
management(cast.getKeycloakSession(), realm).roles().setPermissionsEnabled(role, false);
89+
} else if (event instanceof ClientModel.ClientRemovedEvent) {
90+
ClientModel.ClientRemovedEvent cast = (ClientModel.ClientRemovedEvent) event;
91+
management(cast.getKeycloakSession(), cast.getClient().getRealm()).clients().setPermissionsEnabled(cast.getClient(), false);
92+
} else if (event instanceof GroupModel.GroupRemovedEvent) {
93+
GroupModel.GroupRemovedEvent cast = (GroupModel.GroupRemovedEvent) event;
94+
management(cast.getKeycloakSession(), cast.getRealm()).groups().setPermissionsEnabled(cast.getGroup(), false);
8695
}
87-
management(cast.getKeycloakSession(), realm).roles().setPermissionsEnabled(role, false);
88-
} else if (event instanceof ClientModel.ClientRemovedEvent) {
89-
ClientModel.ClientRemovedEvent cast = (ClientModel.ClientRemovedEvent)event;
90-
management(cast.getKeycloakSession(), cast.getClient().getRealm()).clients().setPermissionsEnabled(cast.getClient(), false);
91-
} else if (event instanceof GroupModel.GroupRemovedEvent) {
92-
GroupModel.GroupRemovedEvent cast = (GroupModel.GroupRemovedEvent)event;
93-
management(cast.getKeycloakSession(), cast.getRealm()).groups().setPermissionsEnabled(cast.getGroup(), false);
9496
}
9597
}
9698
});

services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionEvaluator.java

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
*/
1717
package org.keycloak.services.resources.admin.permissions;
1818

19+
import org.keycloak.models.AdminRoles;
1920
import org.keycloak.models.ClientModel;
2021
import org.keycloak.models.ClientScopeModel;
2122

@@ -31,54 +32,161 @@ public interface ClientPermissionEvaluator {
3132

3233
void setPermissionsEnabled(ClientModel client, boolean enable);
3334

35+
/**
36+
* Throws ForbiddenException if {@link #canListClientScopes()} returns {@code false}.
37+
*/
3438
void requireListClientScopes();
3539

40+
/**
41+
* Returns {@code true} if the caller has {@link org.keycloak.models.AdminRoles#MANAGE_CLIENTS} role.
42+
* <p/>
43+
* For V2 only: Also if it has permission to {@link org.keycloak.authorization.AdminPermissionsSchema#MANAGE}.
44+
*/
3645
boolean canManage();
3746

47+
/**
48+
* Throws ForbiddenException if {@link #canManage()} returns {@code false}.
49+
*/
3850
void requireManage();
3951

52+
/**
53+
* Returns {@code true} if the caller has {@link org.keycloak.models.AdminRoles#MANAGE_CLIENTS} role.
54+
* <p/>
55+
* For V2 only: Also if it has permission to {@link org.keycloak.authorization.AdminPermissionsSchema#MANAGE}.
56+
*/
4057
boolean canManageClientScopes();
4158

59+
/**
60+
* Throws ForbiddenException if {@link #canManageClientScopes()} returns {@code false}.
61+
*/
4262
void requireManageClientScopes();
4363

64+
/**
65+
* Returns {@code true} if the caller has at least one of the {@link org.keycloak.models.AdminRoles#MANAGE_CLIENTS} or {@link org.keycloak.models.AdminRoles#VIEW_CLIENTS} roles.
66+
* <p/>
67+
* For V2 only: Also if it has permission to {@link org.keycloak.authorization.AdminPermissionsSchema#VIEW}.
68+
*/
4469
boolean canView();
4570

71+
/**
72+
* Returns {@code true} if {@link #canView()} returns {@code true}.
73+
* <p/>
74+
* Or if the caller has at least one of the {@link AdminRoles#QUERY_CLIENTS} or {@link AdminRoles#QUERY_USERS} roles.
75+
*/
4676
boolean canList();
4777

78+
/**
79+
* Returns {@code true} if {@link #canView()} returns {@code true}.
80+
*/
4881
boolean canViewClientScopes();
4982

83+
/**
84+
* Throws ForbiddenException if {@link #canList()} returns {@code false}.
85+
*/
5086
void requireList();
5187

88+
/**
89+
* Returns {@code true} if {@link #canView()} returns {@code true}.
90+
* <p/>
91+
* Or if the caller has {@link AdminRoles#QUERY_CLIENTS} role.
92+
*/
5293
boolean canListClientScopes();
5394

95+
/**
96+
* Returns {@code true} if {@link #canView()} returns {@code true}.
97+
*/
5498
void requireView();
5599

100+
/**
101+
* Returns {@code true} if {@link #canViewClientScopes()} returns {@code true}.
102+
*/
56103
void requireViewClientScopes();
57104

105+
/**
106+
* Returns {@code true} if the caller has {@link org.keycloak.models.AdminRoles#MANAGE_CLIENTS} role.
107+
* <p/>
108+
* Or if the caller has a permission to {@link AdminPermissionManagement#MANAGE_SCOPE} the client.
109+
* <p/>
110+
* For V2 only: Also if the caller has a permission to {@link org.keycloak.authorization.AdminPermissionsSchema#MANAGE} all clients.
111+
*/
58112
boolean canManage(ClientModel client);
59113

114+
/**
115+
* Returns {@code true} if {@link #canManage(ClientModel)} returns {@code true}.
116+
* <p/>
117+
* Or if the caller has a permission to {@link ClientPermissionManagement#CONFIGURE_SCOPE} the client.
118+
* <p/>
119+
* For V2 only: Also if the caller has a permission to {@link org.keycloak.authorization.AdminPermissionsSchema#CONFIGURE} all clients.
120+
*/
60121
boolean canConfigure(ClientModel client);
61122

123+
/**
124+
* Throws ForbiddenException if {@link #canConfigure(ClientModel)} returns {@code false}.
125+
*/
62126
void requireConfigure(ClientModel client);
63127

128+
/**
129+
* Throws ForbiddenException if {@link #canManage(ClientModel)} returns {@code false}.
130+
*/
64131
void requireManage(ClientModel client);
65132

133+
/**
134+
* Returns {@code true} if {@link #canView()} or {@link #canConfigure(ClientModel)} returns {@code true}.
135+
* <p/>
136+
* Or if the caller has a permission to {@link AdminPermissionManagement#VIEW_SCOPE} the client.
137+
* <p/>
138+
* For V2 only: Also if the caller has a permission to {@link org.keycloak.authorization.AdminPermissionsSchema#VIEW} all clients.
139+
*/
66140
boolean canView(ClientModel client);
67141

142+
/**
143+
* Throws ForbiddenException if {@link #canView(ClientModel)} returns {@code false}.
144+
*/
68145
void requireView(ClientModel client);
69146

147+
/**
148+
* Returns {@code true} if the caller has {@link org.keycloak.models.AdminRoles#MANAGE_CLIENTS} role.
149+
* <p/>
150+
* For V2 only: Also if it has permission to {@link org.keycloak.authorization.AdminPermissionsSchema#MANAGE}.
151+
*/
70152
boolean canManage(ClientScopeModel clientScope);
71153

154+
/**
155+
* Throws ForbiddenException if {@link #canManage(ClientScopeModel)} returns {@code false}.
156+
*/
72157
void requireManage(ClientScopeModel clientScope);
73158

159+
/**
160+
* Returns {@code true} if the caller has at least one of the {@link org.keycloak.models.AdminRoles#VIEW_CLIENTS} or {@link org.keycloak.models.AdminRoles#MANAGE_CLIENTS} roles.
161+
* <p/>
162+
* For V2 only: Also if it has permission to {@link org.keycloak.authorization.AdminPermissionsSchema#VIEW} or {@link org.keycloak.authorization.AdminPermissionsSchema#MANAGE}.
163+
*/
74164
boolean canView(ClientScopeModel clientScope);
75165

166+
/**
167+
* Throws ForbiddenException if {@link #canView(ClientScopeModel)} returns {@code false}.
168+
*/
76169
void requireView(ClientScopeModel clientScope);
77170

171+
/**
172+
* Returns {@code true} if the caller has a permission to {@link ClientPermissionManagement#MAP_ROLES_SCOPE} for the client.
173+
* <p/>
174+
* For V2 only: Also if the caller has a permission to {@link org.keycloak.authorization.AdminPermissionsSchema#MAP_ROLES} for all clients.
175+
*/
78176
boolean canMapRoles(ClientModel client);
79177

178+
/**
179+
* Returns {@code true} if the caller has a permission to {@link ClientPermissionManagement#MAP_ROLES_COMPOSITE_SCOPE} for the client.
180+
* <p/>
181+
* For V2 only: Also if the caller has a permission to {@link org.keycloak.authorization.AdminPermissionsSchema#MAP_ROLES_COMPOSITE} for all clients.
182+
*/
80183
boolean canMapCompositeRoles(ClientModel client);
81184

185+
/**
186+
* Returns {@code true} if the caller has a permission to {@link ClientPermissionManagement#MAP_ROLES_CLIENT_SCOPE} for the client.
187+
* <p/>
188+
* For V2 only: Also if the caller has a permission to {@link org.keycloak.authorization.AdminPermissionsSchema#MAP_ROLES_CLIENT_SCOPE} for all clients.
189+
*/
82190
boolean canMapClientScopeRoles(ClientModel client);
83191

84192
Map<String, Boolean> getAccess(ClientModel client);

0 commit comments

Comments
 (0)