From 8d2e4c03167b22ee6c27a27555f29ab2321b423c Mon Sep 17 00:00:00 2001 From: mposolda Date: Mon, 23 Nov 2015 14:26:47 +0100 Subject: [PATCH] KEYCLOAK-2061 Add switches to enable/disable grant types for clients --- .../META-INF/jpa-changelog-1.7.0.xml | 26 +++++++++++ .../impl/DefaultMongoUpdaterProvider.java | 3 +- .../updater/impl/updates/Update1_7_0.java | 39 ++++++++++++++++ .../idm/ClientRepresentation.java | 27 +++++++++++ .../messages/admin-messages_en.properties | 10 ++-- .../admin/resources/js/controllers/clients.js | 9 +++- .../resources/partials/client-detail.html | 30 ++++++++---- .../login/messages/messages_en.properties | 2 +- .../java/org/keycloak/models/ClientModel.java | 12 +++-- .../models/entities/ClientEntity.java | 27 +++++++++++ .../models/utils/ModelToRepresentation.java | 4 +- .../models/utils/RepresentationToModel.java | 15 +++++- .../cache/infinispan/ClientAdapter.java | 46 +++++++++++++++---- .../models/cache/entities/CachedClient.java | 24 +++++++--- .../keycloak/models/jpa/ClientAdapter.java | 40 ++++++++++++---- .../org/keycloak/models/jpa/RealmAdapter.java | 2 + .../models/jpa/entities/ClientEntity.java | 44 +++++++++++++----- .../keycloak/adapters/ClientAdapter.java | 44 +++++++++++++----- .../mongo/keycloak/adapters/RealmAdapter.java | 2 + .../keycloak/protocol/saml/SamlService.java | 6 +-- .../oidc/endpoints/AuthorizationEndpoint.java | 4 +- .../keycloak/services/messages/Messages.java | 2 +- .../keycloak/testsuite/model/ImportTest.java | 7 +++ .../src/test/resources/model/testrealm.json | 3 ++ 24 files changed, 353 insertions(+), 75 deletions(-) create mode 100644 connections/mongo-update/src/main/java/org/keycloak/connections/mongo/updater/impl/updates/Update1_7_0.java diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.7.0.xml b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.7.0.xml index c81b062099..1418dc490d 100755 --- a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.7.0.xml +++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.7.0.xml @@ -69,11 +69,37 @@ + + + + + + + + + + + + + DIRECT_GRANTS_ONLY = :value + + + + + + + + + + + + + \ No newline at end of file diff --git a/connections/mongo-update/src/main/java/org/keycloak/connections/mongo/updater/impl/DefaultMongoUpdaterProvider.java b/connections/mongo-update/src/main/java/org/keycloak/connections/mongo/updater/impl/DefaultMongoUpdaterProvider.java index fa75d6b087..e66e16d28d 100644 --- a/connections/mongo-update/src/main/java/org/keycloak/connections/mongo/updater/impl/DefaultMongoUpdaterProvider.java +++ b/connections/mongo-update/src/main/java/org/keycloak/connections/mongo/updater/impl/DefaultMongoUpdaterProvider.java @@ -28,7 +28,8 @@ public class DefaultMongoUpdaterProvider implements MongoUpdaterProvider { Update1_2_0_Beta1.class, Update1_2_0_CR1.class, Update1_3_0.class, - Update1_4_0.class + Update1_4_0.class, + Update1_7_0.class }; @Override diff --git a/connections/mongo-update/src/main/java/org/keycloak/connections/mongo/updater/impl/updates/Update1_7_0.java b/connections/mongo-update/src/main/java/org/keycloak/connections/mongo/updater/impl/updates/Update1_7_0.java new file mode 100644 index 0000000000..666a3af9c9 --- /dev/null +++ b/connections/mongo-update/src/main/java/org/keycloak/connections/mongo/updater/impl/updates/Update1_7_0.java @@ -0,0 +1,39 @@ +package org.keycloak.connections.mongo.updater.impl.updates; + +import com.mongodb.BasicDBObject; +import com.mongodb.DBCollection; +import com.mongodb.DBCursor; +import org.keycloak.models.KeycloakSession; + +/** + * @author Marek Posolda + */ +public class Update1_7_0 extends Update { + + @Override + public String getId() { + return "1.7.0"; + } + + @Override + public void update(KeycloakSession session) throws ClassNotFoundException { + DBCollection clients = db.getCollection("clients"); + DBCursor clientsCursor = clients.find(); + + try { + while (clientsCursor.hasNext()) { + BasicDBObject client = (BasicDBObject) clientsCursor.next(); + + boolean directGrantsOnly = client.getBoolean("directGrantsOnly", false); + client.append("standardFlowEnabled", !directGrantsOnly); + client.append("implicitFlowEnabled", false); + client.append("directAccessGrantsEnabled", true); + client.removeField("directGrantsOnly"); + + clients.save(client); + } + } finally { + clientsCursor.close(); + } + } +} diff --git a/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java index 514c0fb953..aef643c180 100755 --- a/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java +++ b/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java @@ -26,6 +26,9 @@ public class ClientRepresentation { protected Integer notBefore; protected Boolean bearerOnly; protected Boolean consentRequired; + protected Boolean standardFlowEnabled; + protected Boolean implicitFlowEnabled; + protected Boolean directAccessGrantsEnabled; protected Boolean serviceAccountsEnabled; protected Boolean directGrantsOnly; protected Boolean publicClient; @@ -181,6 +184,30 @@ public class ClientRepresentation { this.consentRequired = consentRequired; } + public Boolean isStandardFlowEnabled() { + return standardFlowEnabled; + } + + public void setStandardFlowEnabled(Boolean standardFlowEnabled) { + this.standardFlowEnabled = standardFlowEnabled; + } + + public Boolean isImplicitFlowEnabled() { + return implicitFlowEnabled; + } + + public void setImplicitFlowEnabled(Boolean implicitFlowEnabled) { + this.implicitFlowEnabled = implicitFlowEnabled; + } + + public Boolean isDirectAccessGrantsEnabled() { + return directAccessGrantsEnabled; + } + + public void setDirectAccessGrantsEnabled(Boolean directAccessGrantsEnabled) { + this.directAccessGrantsEnabled = directAccessGrantsEnabled; + } + public Boolean isServiceAccountsEnabled() { return serviceAccountsEnabled; } diff --git a/forms/common-themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties b/forms/common-themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties index bf1af109fe..d1764e88fe 100755 --- a/forms/common-themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties +++ b/forms/common-themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties @@ -169,14 +169,18 @@ client.name.tooltip=Specifies display name of the client. For example 'My Client client.enabled.tooltip=Disabled clients cannot initiate a login or have obtain access tokens. consent-required=Consent Required consent-required.tooltip=If enabled users have to consent to client access. -direct-grants-only=Direct Grants Only -direct-grants-only.tooltip=When enabled, client can only obtain grants from grant REST API. client-protocol=Client Protocol client-protocol.tooltip='OpenID connect' allows Clients to verify the identity of the End-User based on the authentication performed by an Authorization Server.'SAML' enables web-based authentication and authorization scenarios including cross-domain single sign-on (SSO) and uses security tokens containing assertions to pass information. access-type=Access Type access-type.tooltip='Confidential' clients require a secret to initiate login protocol. 'Public' clients do not require a secret. 'Bearer-only' clients are web services that never initiate a login. +standard-flow-enabled=Standard Flow Enabled +standard-flow-enabled.tooltip=This enables standard OpenID Connect redirect based authentication with authorization code. In terms of OpenID Connect or OAuth2 specifications, this enables support of 'Authorization Code Flow' for this client. +implicit-flow-enabled=Implicit Flow Enabled +implicit-flow-enabled.tooltip=This enables support for OpenID Connect redirect based authentication without authorization code. In terms of OpenID Connect or OAuth2 specifications, this enables support of 'Implicit Flow' for this client. +direct-access-grants-enabled=Direct Access Grants Enabled +direct-access-grants-enabled.tooltip=This enables support for Direct Access Grants, which means that client has access to username/password of user and exchange it directly with Keycloak server for access token. In terms of OAuth2 specification, this enables support of 'Resource Owner Password Credentials Grant' for this client. service-accounts-enabled=Service Accounts Enabled -service-accounts-enabled.tooltip=Allows you to authenticate this client to Keycloak and retrieve access token dedicated to this client. +service-accounts-enabled.tooltip=Allows you to authenticate this client to Keycloak and retrieve access token dedicated to this client. In terms of OAuth2 specification, this enables support of 'Client Credentials Grant' for this client. include-authnstatement=Include AuthnStatement include-authnstatement.tooltip=Should a statement specifying the method and timestamp be included in login responses? sign-documents=Sign Documents diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js index 2f3ef6366a..11bb11b3b7 100755 --- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js +++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js @@ -862,7 +862,12 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, $route, se $scope.client = angular.copy(client); updateProperties(); } else { - $scope.client = { enabled: true, attributes: {}}; + $scope.client = { + enabled: true, + standardFlowEnabled: true, + directAccessGrantsEnabled: true, + attributes: {} + }; $scope.client.attributes['saml_signature_canonicalization_method'] = $scope.canonicalization[0].value; $scope.client.redirectUris = []; $scope.accessType = $scope.accessTypes[0]; @@ -1039,7 +1044,7 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, $route, se $scope.client.attributes['saml.signature.algorithm'] = $scope.signatureAlgorithm; $scope.client.attributes['saml_name_id_format'] = $scope.nameIdFormat; - if ($scope.client.protocol != 'saml' && !$scope.client.bearerOnly && !$scope.client.directGrantsOnly && (!$scope.client.redirectUris || $scope.client.redirectUris.length == 0)) { + if ($scope.client.protocol != 'saml' && !$scope.client.bearerOnly && ($scope.client.standardFlowEnabled || $scope.client.implicitFlowEnabled) && (!$scope.client.redirectUris || $scope.client.redirectUris.length == 0)) { Notifications.error("You must specify at least one redirect uri"); } else { if ($scope.create) { diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html index 443cbe94ca..eb54741514 100755 --- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html +++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html @@ -59,13 +59,6 @@ {{:: 'consent-required.tooltip' | translate}} -
- -
- -
- {{:: 'direct-grants-only.tooltip' | translate}} -
@@ -92,6 +85,27 @@
{{:: 'access-type.tooltip' | translate}}
+
+ + {{:: 'standard-flow-enabled.tooltip' | translate}} +
+ +
+
+
+ + {{:: 'implicit-flow-enabled.tooltip' | translate}} +
+ +
+
+
+ + {{:: 'direct-access-grants-enabled.tooltip' | translate}} +
+ +
+
{{:: 'service-accounts-enabled.tooltip' | translate}} @@ -202,7 +216,7 @@ {{:: 'root-url.tooltip' | translate}}
-
+
diff --git a/forms/common-themes/src/main/resources/theme/base/login/messages/messages_en.properties b/forms/common-themes/src/main/resources/theme/base/login/messages/messages_en.properties index 68853082ca..21175a09dc 100644 --- a/forms/common-themes/src/main/resources/theme/base/login/messages/messages_en.properties +++ b/forms/common-themes/src/main/resources/theme/base/login/messages/messages_en.properties @@ -176,7 +176,7 @@ failedLogout=Logout failed unknownLoginRequesterMessage=Unknown login requester loginRequesterNotEnabledMessage=Login requester not enabled bearerOnlyMessage=Bearer-only applications are not allowed to initiate browser login -directGrantsOnlyMessage=Direct-grants-only clients are not allowed to initiate browser login +standardFlowDisabledMessage=Client is not allowed to initiate browser login because standard flow is disabled for the client. invalidRedirectUriMessage=Invalid redirect uri unsupportedNameIdFormatMessage=Unsupported NameIDFormat invlidRequesterMessage=Invalid requester diff --git a/model/api/src/main/java/org/keycloak/models/ClientModel.java b/model/api/src/main/java/org/keycloak/models/ClientModel.java index 051493e90f..492c2e1ef4 100755 --- a/model/api/src/main/java/org/keycloak/models/ClientModel.java +++ b/model/api/src/main/java/org/keycloak/models/ClientModel.java @@ -111,12 +111,18 @@ public interface ClientModel extends RoleContainerModel { boolean isPublicClient(); void setPublicClient(boolean flag); - boolean isDirectGrantsOnly(); - void setDirectGrantsOnly(boolean flag); - boolean isConsentRequired(); void setConsentRequired(boolean consentRequired); + boolean isStandardFlowEnabled(); + void setStandardFlowEnabled(boolean standardFlowEnabled); + + boolean isImplicitFlowEnabled(); + void setImplicitFlowEnabled(boolean implicitFlowEnabled); + + boolean isDirectAccessGrantsEnabled(); + void setDirectAccessGrantsEnabled(boolean directAccessGrantsEnabled); + boolean isServiceAccountsEnabled(); void setServiceAccountsEnabled(boolean serviceAccountsEnabled); diff --git a/model/api/src/main/java/org/keycloak/models/entities/ClientEntity.java b/model/api/src/main/java/org/keycloak/models/entities/ClientEntity.java index d04699f16c..b5edbaf9ee 100755 --- a/model/api/src/main/java/org/keycloak/models/entities/ClientEntity.java +++ b/model/api/src/main/java/org/keycloak/models/entities/ClientEntity.java @@ -30,6 +30,9 @@ public class ClientEntity extends AbstractIdentifiableEntity { private String baseUrl; private boolean bearerOnly; private boolean consentRequired; + private boolean standardFlowEnabled; + private boolean implicitFlowEnabled; + private boolean directAccessGrantsEnabled; private boolean serviceAccountsEnabled; private boolean directGrantsOnly; private int nodeReRegistrationTimeout; @@ -243,6 +246,30 @@ public class ClientEntity extends AbstractIdentifiableEntity { this.consentRequired = consentRequired; } + public boolean isStandardFlowEnabled() { + return standardFlowEnabled; + } + + public void setStandardFlowEnabled(boolean standardFlowEnabled) { + this.standardFlowEnabled = standardFlowEnabled; + } + + public boolean isImplicitFlowEnabled() { + return implicitFlowEnabled; + } + + public void setImplicitFlowEnabled(boolean implicitFlowEnabled) { + this.implicitFlowEnabled = implicitFlowEnabled; + } + + public boolean isDirectAccessGrantsEnabled() { + return directAccessGrantsEnabled; + } + + public void setDirectAccessGrantsEnabled(boolean directAccessGrantsEnabled) { + this.directAccessGrantsEnabled = directAccessGrantsEnabled; + } + public boolean isServiceAccountsEnabled() { return serviceAccountsEnabled; } diff --git a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java index 723617b7a6..fd88b26395 100755 --- a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java +++ b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java @@ -418,8 +418,10 @@ public class ModelToRepresentation { rep.setFullScopeAllowed(clientModel.isFullScopeAllowed()); rep.setBearerOnly(clientModel.isBearerOnly()); rep.setConsentRequired(clientModel.isConsentRequired()); + rep.setStandardFlowEnabled(clientModel.isStandardFlowEnabled()); + rep.setImplicitFlowEnabled(clientModel.isImplicitFlowEnabled()); + rep.setDirectAccessGrantsEnabled(clientModel.isDirectAccessGrantsEnabled()); rep.setServiceAccountsEnabled(clientModel.isServiceAccountsEnabled()); - rep.setDirectGrantsOnly(clientModel.isDirectGrantsOnly()); rep.setSurrogateAuthRequired(clientModel.isSurrogateAuthRequired()); rep.setRootUrl(clientModel.getRootUrl()); rep.setBaseUrl(clientModel.getBaseUrl()); diff --git a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java index dd82098242..82fd2c792c 100755 --- a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java +++ b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java @@ -772,8 +772,17 @@ public class RepresentationToModel { if (resourceRep.getBaseUrl() != null) client.setBaseUrl(resourceRep.getBaseUrl()); if (resourceRep.isBearerOnly() != null) client.setBearerOnly(resourceRep.isBearerOnly()); if (resourceRep.isConsentRequired() != null) client.setConsentRequired(resourceRep.isConsentRequired()); + if (resourceRep.isStandardFlowEnabled() != null) client.setStandardFlowEnabled(resourceRep.isStandardFlowEnabled()); + if (resourceRep.isImplicitFlowEnabled() != null) client.setImplicitFlowEnabled(resourceRep.isImplicitFlowEnabled()); + if (resourceRep.isDirectAccessGrantsEnabled() != null) client.setDirectAccessGrantsEnabled(resourceRep.isDirectAccessGrantsEnabled()); if (resourceRep.isServiceAccountsEnabled() != null) client.setServiceAccountsEnabled(resourceRep.isServiceAccountsEnabled()); - if (resourceRep.isDirectGrantsOnly() != null) client.setDirectGrantsOnly(resourceRep.isDirectGrantsOnly()); + + // Backwards compatibility only + if (resourceRep.isDirectGrantsOnly() != null) { + logger.warn("Using deprecated 'directGrantsOnly' configuration in JSON representation. It will be removed in future versions"); + client.setStandardFlowEnabled(!resourceRep.isDirectGrantsOnly()); + } + if (resourceRep.isPublicClient() != null) client.setPublicClient(resourceRep.isPublicClient()); if (resourceRep.isFrontchannelLogout() != null) client.setFrontchannelLogout(resourceRep.isFrontchannelLogout()); if (resourceRep.getProtocol() != null) client.setProtocol(resourceRep.getProtocol()); @@ -869,8 +878,10 @@ public class RepresentationToModel { if (rep.isEnabled() != null) resource.setEnabled(rep.isEnabled()); if (rep.isBearerOnly() != null) resource.setBearerOnly(rep.isBearerOnly()); if (rep.isConsentRequired() != null) resource.setConsentRequired(rep.isConsentRequired()); + if (rep.isStandardFlowEnabled() != null) resource.setStandardFlowEnabled(rep.isStandardFlowEnabled()); + if (rep.isImplicitFlowEnabled() != null) resource.setImplicitFlowEnabled(rep.isImplicitFlowEnabled()); + if (rep.isDirectAccessGrantsEnabled() != null) resource.setDirectAccessGrantsEnabled(rep.isDirectAccessGrantsEnabled()); if (rep.isServiceAccountsEnabled() != null) resource.setServiceAccountsEnabled(rep.isServiceAccountsEnabled()); - if (rep.isDirectGrantsOnly() != null) resource.setDirectGrantsOnly(rep.isDirectGrantsOnly()); if (rep.isPublicClient() != null) resource.setPublicClient(rep.isPublicClient()); if (rep.isFullScopeAllowed() != null) resource.setFullScopeAllowed(rep.isFullScopeAllowed()); if (rep.isFrontchannelLogout() != null) resource.setFrontchannelLogout(rep.isFrontchannelLogout()); diff --git a/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java b/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java index e03452ad2b..20c87251a1 100755 --- a/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java +++ b/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java @@ -163,16 +163,6 @@ public class ClientAdapter implements ClientModel { } - public boolean isDirectGrantsOnly() { - if (updated != null) return updated.isDirectGrantsOnly(); - return cached.isDirectGrantsOnly(); - } - - public void setDirectGrantsOnly(boolean flag) { - getDelegateForUpdate(); - updated.setDirectGrantsOnly(flag); - } - public Set getScopeMappings() { if (updated != null) return updated.getScopeMappings(); Set roles = new HashSet(); @@ -451,6 +441,42 @@ public class ClientAdapter implements ClientModel { updated.setConsentRequired(consentRequired); } + @Override + public boolean isStandardFlowEnabled() { + if (updated != null) return updated.isStandardFlowEnabled(); + return cached.isStandardFlowEnabled(); + } + + @Override + public void setStandardFlowEnabled(boolean standardFlowEnabled) { + getDelegateForUpdate(); + updated.setStandardFlowEnabled(standardFlowEnabled); + } + + @Override + public boolean isImplicitFlowEnabled() { + if (updated != null) return updated.isImplicitFlowEnabled(); + return cached.isImplicitFlowEnabled(); + } + + @Override + public void setImplicitFlowEnabled(boolean implicitFlowEnabled) { + getDelegateForUpdate(); + updated.setImplicitFlowEnabled(implicitFlowEnabled); + } + + @Override + public boolean isDirectAccessGrantsEnabled() { + if (updated != null) return updated.isDirectAccessGrantsEnabled(); + return cached.isDirectAccessGrantsEnabled(); + } + + @Override + public void setDirectAccessGrantsEnabled(boolean directAccessGrantsEnabled) { + getDelegateForUpdate(); + updated.setDirectAccessGrantsEnabled(directAccessGrantsEnabled); + } + @Override public boolean isServiceAccountsEnabled() { if (updated != null) return updated.isServiceAccountsEnabled(); diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClient.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClient.java index 7c7b97d796..a68395603c 100755 --- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClient.java +++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClient.java @@ -36,7 +36,6 @@ public class CachedClient implements Serializable { private Map attributes = new HashMap(); private boolean publicClient; private boolean fullScopeAllowed; - private boolean directGrantsOnly; private boolean frontchannelLogout; private int notBefore; private Set scope = new HashSet(); @@ -49,6 +48,9 @@ public class CachedClient implements Serializable { private List defaultRoles = new LinkedList(); private boolean bearerOnly; private boolean consentRequired; + private boolean standardFlowEnabled; + private boolean implicitFlowEnabled; + private boolean directAccessGrantsEnabled; private boolean serviceAccountsEnabled; private Map roles = new HashMap(); private int nodeReRegistrationTimeout; @@ -67,7 +69,6 @@ public class CachedClient implements Serializable { protocol = model.getProtocol(); attributes.putAll(model.getAttributes()); notBefore = model.getNotBefore(); - directGrantsOnly = model.isDirectGrantsOnly(); frontchannelLogout = model.isFrontchannelLogout(); publicClient = model.isPublicClient(); fullScopeAllowed = model.isFullScopeAllowed(); @@ -86,6 +87,9 @@ public class CachedClient implements Serializable { defaultRoles.addAll(model.getDefaultRoles()); bearerOnly = model.isBearerOnly(); consentRequired = model.isConsentRequired(); + standardFlowEnabled = model.isStandardFlowEnabled(); + implicitFlowEnabled = model.isImplicitFlowEnabled(); + directAccessGrantsEnabled = model.isDirectAccessGrantsEnabled(); serviceAccountsEnabled = model.isServiceAccountsEnabled(); for (RoleModel role : model.getRoles()) { roles.put(role.getName(), role.getId()); @@ -139,10 +143,6 @@ public class CachedClient implements Serializable { return publicClient; } - public boolean isDirectGrantsOnly() { - return directGrantsOnly; - } - public int getNotBefore() { return notBefore; } @@ -203,6 +203,18 @@ public class CachedClient implements Serializable { return consentRequired; } + public boolean isStandardFlowEnabled() { + return standardFlowEnabled; + } + + public boolean isImplicitFlowEnabled() { + return implicitFlowEnabled; + } + + public boolean isDirectAccessGrantsEnabled() { + return directAccessGrantsEnabled; + } + public boolean isServiceAccountsEnabled() { return serviceAccountsEnabled; } diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java index 6cb24f8dce..f6778594c9 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java @@ -498,6 +498,36 @@ public class ClientAdapter implements ClientModel { entity.setConsentRequired(consentRequired); } + @Override + public boolean isStandardFlowEnabled() { + return entity.isStandardFlowEnabled(); + } + + @Override + public void setStandardFlowEnabled(boolean standardFlowEnabled) { + entity.setStandardFlowEnabled(standardFlowEnabled); + } + + @Override + public boolean isImplicitFlowEnabled() { + return entity.isImplicitFlowEnabled(); + } + + @Override + public void setImplicitFlowEnabled(boolean implicitFlowEnabled) { + entity.setImplicitFlowEnabled(implicitFlowEnabled); + } + + @Override + public boolean isDirectAccessGrantsEnabled() { + return entity.isDirectAccessGrantsEnabled(); + } + + @Override + public void setDirectAccessGrantsEnabled(boolean directAccessGrantsEnabled) { + entity.setDirectAccessGrantsEnabled(directAccessGrantsEnabled); + } + @Override public boolean isServiceAccountsEnabled() { return entity.isServiceAccountsEnabled(); @@ -508,16 +538,6 @@ public class ClientAdapter implements ClientModel { entity.setServiceAccountsEnabled(serviceAccountsEnabled); } - @Override - public boolean isDirectGrantsOnly() { - return entity.isDirectGrantsOnly(); - } - - @Override - public void setDirectGrantsOnly(boolean flag) { - entity.setDirectGrantsOnly(flag); - } - @Override public RoleModel getRole(String name) { TypedQuery query = em.createNamedQuery("getClientRoleByName", RoleEntity.class); diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java index acc5182f60..8c1bb87c2b 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java @@ -715,6 +715,8 @@ public class RealmAdapter implements RealmModel { entity.setId(id); entity.setClientId(clientId); entity.setEnabled(true); + entity.setStandardFlowEnabled(true); + entity.setDirectAccessGrantsEnabled(true); entity.setRealm(realm); realm.getClients().add(entity); em.persist(entity); diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java index 6218e26d4b..1569875955 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java @@ -95,15 +95,21 @@ public class ClientEntity { @Column(name="MANAGEMENT_URL") private String managementUrl; - @Column(name="DIRECT_GRANTS_ONLY") - protected boolean directGrantsOnly; - @Column(name="BEARER_ONLY") private boolean bearerOnly; @Column(name="CONSENT_REQUIRED") private boolean consentRequired; + @Column(name="STANDARD_FLOW_ENABLED") + private boolean standardFlowEnabled; + + @Column(name="IMPLICIT_FLOW_ENABLED") + private boolean implicitFlowEnabled; + + @Column(name="DIRECT_ACCESS_GRANTS_ENABLED") + private boolean directAccessGrantsEnabled; + @Column(name="SERVICE_ACCOUNTS_ENABLED") private boolean serviceAccountsEnabled; @@ -339,6 +345,30 @@ public class ClientEntity { this.consentRequired = consentRequired; } + public boolean isStandardFlowEnabled() { + return standardFlowEnabled; + } + + public void setStandardFlowEnabled(boolean standardFlowEnabled) { + this.standardFlowEnabled = standardFlowEnabled; + } + + public boolean isImplicitFlowEnabled() { + return implicitFlowEnabled; + } + + public void setImplicitFlowEnabled(boolean implicitFlowEnabled) { + this.implicitFlowEnabled = implicitFlowEnabled; + } + + public boolean isDirectAccessGrantsEnabled() { + return directAccessGrantsEnabled; + } + + public void setDirectAccessGrantsEnabled(boolean directAccessGrantsEnabled) { + this.directAccessGrantsEnabled = directAccessGrantsEnabled; + } + public boolean isServiceAccountsEnabled() { return serviceAccountsEnabled; } @@ -347,14 +377,6 @@ public class ClientEntity { this.serviceAccountsEnabled = serviceAccountsEnabled; } - public boolean isDirectGrantsOnly() { - return directGrantsOnly; - } - - public void setDirectGrantsOnly(boolean directGrantsOnly) { - this.directGrantsOnly = directGrantsOnly; - } - public int getNodeReRegistrationTimeout() { return nodeReRegistrationTimeout; } diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java index fc1f13defb..eb6bcd9544 100755 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java @@ -504,6 +504,39 @@ public class ClientAdapter extends AbstractMongoAdapter imple updateMongoEntity(); } + @Override + public boolean isStandardFlowEnabled() { + return getMongoEntity().isStandardFlowEnabled(); + } + + @Override + public void setStandardFlowEnabled(boolean standardFlowEnabled) { + getMongoEntity().setStandardFlowEnabled(standardFlowEnabled); + updateMongoEntity(); + } + + @Override + public boolean isImplicitFlowEnabled() { + return getMongoEntity().isImplicitFlowEnabled(); + } + + @Override + public void setImplicitFlowEnabled(boolean implicitFlowEnabled) { + getMongoEntity().setImplicitFlowEnabled(implicitFlowEnabled); + updateMongoEntity(); + } + + @Override + public boolean isDirectAccessGrantsEnabled() { + return getMongoEntity().isDirectAccessGrantsEnabled(); + } + + @Override + public void setDirectAccessGrantsEnabled(boolean directAccessGrantsEnabled) { + getMongoEntity().setDirectAccessGrantsEnabled(directAccessGrantsEnabled); + updateMongoEntity(); + } + @Override public boolean isServiceAccountsEnabled() { return getMongoEntity().isServiceAccountsEnabled(); @@ -515,17 +548,6 @@ public class ClientAdapter extends AbstractMongoAdapter imple updateMongoEntity(); } - @Override - public boolean isDirectGrantsOnly() { - return getMongoEntity().isDirectGrantsOnly(); - } - - @Override - public void setDirectGrantsOnly(boolean flag) { - getMongoEntity().setDirectGrantsOnly(flag); - updateMongoEntity(); - } - @Override public RoleAdapter getRole(String name) { DBObject query = new QueryBuilder() diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java index d31503cf40..9f42cb586d 100755 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java @@ -799,6 +799,8 @@ public class RealmAdapter extends AbstractMongoAdapter impleme clientEntity.setClientId(clientId); clientEntity.setRealmId(getId()); clientEntity.setEnabled(true); + clientEntity.setStandardFlowEnabled(true); + clientEntity.setDirectAccessGrantsEnabled(true); getMongoStore().insertEntity(clientEntity, invocationContext); final ClientModel model = new ClientAdapter(session, this, clientEntity, invocationContext); diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java index ff1275a986..34025933d1 100755 --- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java +++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java @@ -142,15 +142,15 @@ public class SamlService extends AuthorizationEndpointBase { event.error(Errors.CLIENT_DISABLED); return ErrorPage.error(session, Messages.LOGIN_REQUESTER_NOT_ENABLED); } - if ((client instanceof ClientModel) && ((ClientModel) client).isBearerOnly()) { + if (client.isBearerOnly()) { event.event(EventType.LOGIN); event.error(Errors.NOT_ALLOWED); return ErrorPage.error(session, Messages.BEARER_ONLY); } - if (client.isDirectGrantsOnly()) { + if (!client.isStandardFlowEnabled()) { event.event(EventType.LOGIN); event.error(Errors.NOT_ALLOWED); - return ErrorPage.error(session, Messages.DIRECT_GRANTS_ONLY); + return ErrorPage.error(session, Messages.STANDARD_FLOW_DISABLED); } session.getContext().setClient(client); diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java index c8d56563d0..250de124b4 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java @@ -172,9 +172,9 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase { throw new ErrorPageException(session, Messages.BEARER_ONLY); } - if (client.isDirectGrantsOnly()) { + if (!client.isStandardFlowEnabled()) { event.error(Errors.NOT_ALLOWED); - throw new ErrorPageException(session, Messages.DIRECT_GRANTS_ONLY); + throw new ErrorPageException(session, Messages.STANDARD_FLOW_DISABLED); } session.getContext().setClient(client); diff --git a/services/src/main/java/org/keycloak/services/messages/Messages.java b/services/src/main/java/org/keycloak/services/messages/Messages.java index 5f2eeb3760..6b22606525 100755 --- a/services/src/main/java/org/keycloak/services/messages/Messages.java +++ b/services/src/main/java/org/keycloak/services/messages/Messages.java @@ -110,7 +110,7 @@ public class Messages { public static final String BEARER_ONLY = "bearerOnlyMessage"; - public static final String DIRECT_GRANTS_ONLY = "directGrantsOnlyMessage"; + public static final String STANDARD_FLOW_DISABLED = "standardFlowDisabledMessage"; public static final String INVALID_REDIRECT_URI = "invalidRedirectUriMessage"; diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ImportTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ImportTest.java index b2d14b9019..c11286aa76 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ImportTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ImportTest.java @@ -327,6 +327,13 @@ public class ImportTest extends AbstractModelTest { Assert.assertFalse(otherAppAdminConsent.isRoleGranted(application.getRole("app-admin"))); Assert.assertTrue(otherAppAdminConsent.isProtocolMapperGranted(gssCredentialMapper)); + Assert.assertTrue(application.isStandardFlowEnabled()); + Assert.assertTrue(application.isImplicitFlowEnabled()); + Assert.assertTrue(application.isDirectAccessGrantsEnabled()); + Assert.assertFalse(otherApp.isStandardFlowEnabled()); + Assert.assertFalse(otherApp.isImplicitFlowEnabled()); + Assert.assertFalse(otherApp.isDirectAccessGrantsEnabled()); + // Test service accounts Assert.assertFalse(application.isServiceAccountsEnabled()); Assert.assertTrue(otherApp.isServiceAccountsEnabled()); diff --git a/testsuite/integration/src/test/resources/model/testrealm.json b/testsuite/integration/src/test/resources/model/testrealm.json index 68ec4d3327..2935ff6214 100755 --- a/testsuite/integration/src/test/resources/model/testrealm.json +++ b/testsuite/integration/src/test/resources/model/testrealm.json @@ -154,6 +154,7 @@ "clientId": "Application", "name": "Applicationn", "enabled": true, + "implicitFlowEnabled": true, "nodeReRegistrationTimeout": 50, "registeredNodes": { "node1": 10, @@ -164,6 +165,8 @@ "clientId": "OtherApp", "name": "Other Application", "enabled": true, + "standardFlowEnabled": false, + "directAccessGrantsEnabled": false, "serviceAccountsEnabled": true, "clientAuthenticatorType": "client-jwt", "protocolMappers" : [