Skip to content

Commit

Permalink
Merge pull request adorsys#149 from adorsys/fine-grained-authorization
Browse files Browse the repository at this point in the history
Add support for clients with fine-grained authorization
  • Loading branch information
jkroepke authored Jul 26, 2020
2 parents af969ff + 421e395 commit f04dff0
Show file tree
Hide file tree
Showing 16 changed files with 1,865 additions and 11 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- Add support for clients with fine-grained authorization

### Changed

### Fixed
Expand Down
221 changes: 221 additions & 0 deletions contrib/example-config/moped.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,18 @@
"description": "My second realm role",
"composite": false,
"clientRole": false
},
{
"name": "user",
"description": "User privileges"
},
{
"name": "admin",
"description": "Administrator privileges"
},
{
"name": "user_premium",
"description": "User Premium privileges"
}
],
"client": {
Expand Down Expand Up @@ -174,6 +186,215 @@
}
],
"clients": [
{
"clientId": "auth-moped-client",
"name": "auth-moped-client",
"description": "Auth-Moped-Client",
"enabled": true,
"clientAuthenticatorType": "client-secret",
"secret": "changed-special-client-secret",
"redirectUris": [
"https://moped-client.org/redirect"
],
"webOrigins": [
"https://moped-client.org/webOrigin"
],
"protocolMappers": [
{
"name": "BranchCodeMapper",
"protocol": "openid-connect",
"protocolMapper": "oidc-usermodel-attribute-mapper",
"consentRequired": false,
"config": {
"aggregate.attrs": "false",
"userinfo.token.claim": "true",
"multivalued": "false",
"user.attribute": "branch",
"id.token.claim": "false",
"access.token.claim": "true",
"claim.name": "branch",
"jsonType.label": "String"
}
}
],
"serviceAccountsEnabled": true,
"authorizationServicesEnabled": true,
"authorizationSettings": {
"allowRemoteResourceManagement": true,
"policyEnforcementMode": "PERMISSIVE",
"decisionStrategy": "UNANIMOUS",
"resources": [
{
"name": "Admin Resource",
"uri": "/protected/admin/*",
"type": "http://servlet-authz/protected/admin",
"scopes": [
{
"name": "urn:servlet-authz:protected:admin:access",
"iconUri": "https://www.keycloak.org/resources/favicon.ico"
}
]
},
{
"name": "Protected Resource",
"uris": [
"/*"
],
"type": "http://servlet-authz/protected/resource",
"scopes": [
{
"name": "urn:servlet-authz:protected:resource:access"
}
],
"attributes": {},
"ownerManagedAccess": false
},
{
"name": "Premium Resource",
"uri": "/protected/premium/*",
"type": "urn:servlet-authz:protected:resource",
"scopes": [
{
"name": "urn:servlet-authz:protected:premium:access"
}
]
},
{
"name": "Main Page",
"type": "urn:servlet-authz:protected:resource",
"scopes": [
{
"name": "urn:servlet-authz:page:main:actionForPremiumUser"
},
{
"name": "urn:servlet-authz:page:main:actionForAdmin"
},
{
"name": "urn:servlet-authz:page:main:actionForUser"
}
]
}
],
"policies": [
{
"name": "Any Admin Policy",
"description": "Defines that adminsitrators can do something",
"type": "role",
"logic": "POSITIVE",
"decisionStrategy": "UNANIMOUS",
"config": {
"roles": "[{\"id\":\"admin\"}]"
}
},
{
"name": "Any User Policy",
"description": "Defines that any user can do something",
"type": "role",
"logic": "POSITIVE",
"decisionStrategy": "UNANIMOUS",
"config": {
"roles": "[{\"id\":\"user\"}]"
}
},
{
"name": "Only Premium User Policy",
"description": "Defines that only premium users can do something",
"type": "role",
"logic": "POSITIVE",
"decisionStrategy": "UNANIMOUS",
"config": {
"roles": "[{\"id\":\"user_premium\"}]"
}
},
{
"name": "All Users Policy",
"description": "Defines that all users can do something",
"type": "aggregate",
"logic": "POSITIVE",
"decisionStrategy": "AFFIRMATIVE",
"config": {
"applyPolicies": "[\"Any User Policy\",\"Any Admin Policy\",\"Only Premium User Policy\"]"
}
},
{
"name": "Administrative Resource Permission",
"description": "A policy that defines access to administrative resources",
"type": "resource",
"logic": "POSITIVE",
"decisionStrategy": "UNANIMOUS",
"config": {
"resources": "[\"Admin Resource\"]",
"applyPolicies": "[\"Any Admin Policy\"]"
}
},
{
"name": "Premium User Scope Permission",
"description": "A policy that defines access to a premium scope",
"type": "scope",
"logic": "POSITIVE",
"decisionStrategy": "UNANIMOUS",
"config": {
"scopes": "[\"urn:servlet-authz:page:main:actionForPremiumUser\"]",
"applyPolicies": "[\"Only Premium User Policy\"]"
}
},
{
"name": "User Action Scope Permission",
"description": "A policy that defines access to a user scope",
"type": "scope",
"logic": "POSITIVE",
"decisionStrategy": "UNANIMOUS",
"config": {
"scopes": "[\"urn:servlet-authz:page:main:actionForUser\"]",
"applyPolicies": "[\"Any User Policy\"]"
}
},
{
"name": "Administrator Action Scope Permission",
"description": "A policy that defines access to an administrator scope",
"type": "scope",
"logic": "POSITIVE",
"decisionStrategy": "UNANIMOUS",
"config": {
"scopes": "[\"urn:servlet-authz:page:main:actionForAdmin\"]",
"applyPolicies": "[\"Any Admin Policy\"]"
}
},
{
"name": "Protected Resource Permission",
"description": "A policy that defines access to any protected resource",
"type": "resource",
"logic": "POSITIVE",
"decisionStrategy": "UNANIMOUS",
"config": {
"resources": "[\"Protected Resource\"]",
"applyPolicies": "[\"All Users Policy\"]"
}
}
],
"scopes": [
{
"name": "urn:servlet-authz:protected:admin:access"
},
{
"name": "urn:servlet-authz:protected:resource:access"
},
{
"name": "urn:servlet-authz:protected:premium:access"
},
{
"name": "urn:servlet-authz:page:main:actionForPremiumUser"
},
{
"name": "urn:servlet-authz:page:main:actionForAdmin"
},
{
"name": "urn:servlet-authz:page:main:actionForUser",
"iconUri": "https://www.keycloak.org/resources/favicon.ico"
}
]
}
},
{
"clientId": "moped-client",
"name": "moped-client",
Expand Down
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,7 @@
--initialize-at-run-time=org.hibernate.validator.internal.engine.messageinterpolation.ElTermResolver
-H:+ReportExceptionStackTraces
-Dspring.native.dump-config=feature-reflect-config.json
-Dspring.native.verify=true
-Dspring.native.verbose=false
-Dspring.native.remove-unused-autoconfig=true
-Dspring.native.remove-jmx-support=true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
import org.keycloak.representations.idm.authorization.ScopeRepresentation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

Expand Down Expand Up @@ -75,8 +79,12 @@ public ClientRepresentation getClientById(String realm, String id) {
return loadClientById(realm, id).toRepresentation();
}

public ResourceServerRepresentation getAuthorizationConfigById(String realm, String id) {
return loadClientById(realm, id).authorization().exportSettings();
}

public String getClientSecret(String realm, String clientId) {
ClientResource clientResource = getClientResource(realm, clientId);
ClientResource clientResource = getClientResourceByClientId(realm, clientId);
return clientResource.getSecret().getValue();
}

Expand Down Expand Up @@ -131,7 +139,7 @@ private ClientResource loadClientById(String realm, String id) {
return client;
}

final ClientResource getClientResource(String realm, String clientId) {
final ClientResource getClientResourceByClientId(String realm, String clientId) {
ClientRepresentation client = loadClientByClientId(realm, clientId);
return realmRepository.loadRealm(realm)
.clients()
Expand Down Expand Up @@ -175,8 +183,8 @@ public void removeProtocolMappers(String realm, String clientId, List<ProtocolMa
}
}

public void updateProtocolMappers(String realm, String clientId, List<ProtocolMapperRepresentation> protocolMappers) {
ClientResource clientResource = loadClientById(realm, clientId);
public void updateProtocolMappers(String realm, String id, List<ProtocolMapperRepresentation> protocolMappers) {
ClientResource clientResource = loadClientById(realm, id);
ProtocolMappersResource protocolMappersResource = clientResource.getProtocolMappers();

for (ProtocolMapperRepresentation protocolMapper : protocolMappers) {
Expand All @@ -194,4 +202,60 @@ public void updateProtocolMappers(String realm, String clientId, List<ProtocolMa
}
}
}

public void updateAuthorizationSettings(String realm, String id, ResourceServerRepresentation authorizationSettings) {
ClientResource clientResource = loadClientById(realm, id);
clientResource.authorization().update(authorizationSettings);
}

public void createAuthorizationResource(String realm, String id, ResourceRepresentation resource) {
ClientResource clientResource = loadClientById(realm, id);

Response response = clientResource.authorization().resources().create(resource);
ResponseUtil.validate(response);
}

public void updateAuthorizationResource(String realm, String id, ResourceRepresentation resource) {
ClientResource clientResource = loadClientById(realm, id);
clientResource.authorization().resources().resource(resource.getId()).update(resource);
}

public void removeAuthorizationResource(String realm, String id, String resourceId) {
ClientResource clientResource = loadClientById(realm, id);
clientResource.authorization().resources().resource(resourceId).remove();
}

public void addAuthorizationScope(String realm, String id, String name) {
ClientResource clientResource = loadClientById(realm, id);

Response response = clientResource.authorization().scopes().create(new ScopeRepresentation(name));
ResponseUtil.validate(response);
}

public void updateAuthorizationScope(String realm, String id, ScopeRepresentation scope) {
ClientResource clientResource = loadClientById(realm, id);
clientResource.authorization().scopes().scope(scope.getId()).update(scope);
}

public void removeAuthorizationScope(String realm, String id, String scopeId) {
ClientResource clientResource = loadClientById(realm, id);
clientResource.authorization().scopes().scope(scopeId).remove();
}

public void createAuthorizationPolicy(String realm, String id, PolicyRepresentation policy) {
ClientResource clientResource = loadClientById(realm, id);

Response response = clientResource.authorization().policies().create(policy);
ResponseUtil.validate(response);
}

public void updateAuthorizationPolicy(String realm, String id, PolicyRepresentation policy) {
ClientResource clientResource = loadClientById(realm, id);
clientResource.authorization().policies().policy(policy.getId()).update(policy);
}

public void removeAuthorizationPolicy(String realm, String id, String policyId) {
ClientResource clientResource = loadClientById(realm, id);
clientResource.authorization().policies().policy(policyId).remove();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public ScopeMappingRepository(
}

public void addScopeMappingRolesForClient(String realm, String clientId, Collection<String> roles) {
ClientResource clientResource = clientRepository.getClientResource(realm, clientId);
ClientResource clientResource = clientRepository.getClientResourceByClientId(realm, clientId);
RoleMappingResource scopeMappingsResource = clientResource.getScopeMappings();
RoleScopeResource roleScopeResource = scopeMappingsResource.realmLevel();

Expand All @@ -68,7 +68,7 @@ public void addScopeMappingRolesForClientScope(String realm, String clientScopeN
}

public void removeScopeMappingRolesForClient(String realm, String clientId, Collection<String> roles) {
RoleMappingResource scopeMappingsResource = clientRepository.getClientResource(realm, clientId).getScopeMappings();
RoleMappingResource scopeMappingsResource = clientRepository.getClientResourceByClientId(realm, clientId).getScopeMappings();

List<RoleRepresentation> realmRoles = roles.stream()
.map(role -> roleRepository.findRealmRole(realm, role))
Expand Down
Loading

0 comments on commit f04dff0

Please sign in to comment.