diff --git a/examples/broker/facebook-authentication/facebook-identity-provider-realm.json b/examples/broker/facebook-authentication/facebook-identity-provider-realm.json
index 0baa7170bb..5243879de2 100644
--- a/examples/broker/facebook-authentication/facebook-identity-provider-realm.json
+++ b/examples/broker/facebook-authentication/facebook-identity-provider-realm.json
@@ -39,6 +39,9 @@
"baseUrl": "/facebook-authentication",
"redirectUris": [
"/facebook-authentication/*"
+ ],
+ "webOrigins": [
+ "http://localhost:8080"
]
}
],
diff --git a/examples/broker/google-authentication/google-identity-provider-realm.json b/examples/broker/google-authentication/google-identity-provider-realm.json
index 751bbf064e..22df7e42a3 100644
--- a/examples/broker/google-authentication/google-identity-provider-realm.json
+++ b/examples/broker/google-authentication/google-identity-provider-realm.json
@@ -39,6 +39,9 @@
"baseUrl": "/google-authentication",
"redirectUris": [
"/google-authentication/*"
+ ],
+ "webOrigins": [
+ "http://localhost:8080"
]
}
],
diff --git a/examples/broker/saml-broker-authentication/saml-broker-authentication-realm.json b/examples/broker/saml-broker-authentication/saml-broker-authentication-realm.json
index 5433cf0eb0..3369dafea0 100644
--- a/examples/broker/saml-broker-authentication/saml-broker-authentication-realm.json
+++ b/examples/broker/saml-broker-authentication/saml-broker-authentication-realm.json
@@ -38,8 +38,7 @@
"adminUrl": "/saml-broker-authentication",
"baseUrl": "/saml-broker-authentication",
"redirectUris": [
- "/saml-broker-authentication/*",
- "http://localhost:8080/saml-broker-authentication/*"
+ "/saml-broker-authentication/*"
],
"webOrigins": [
"http://localhost:8080"
@@ -55,7 +54,7 @@
"updateProfileFirstLogin" : "true",
"storeToken" : "true",
"config": {
- "singleSignOnServiceUrl": "http://localhost:8081/auth/realms/saml-broker-realm/protocol/saml",
+ "singleSignOnServiceUrl": "http://localhost:8080/auth/realms/saml-broker-realm/protocol/saml",
"nameIDPolicyFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
"signingCertificate": "MIIDdzCCAl+gAwIBAgIEbySuqTANBgkqhkiG9w0BAQsFADBsMRAwDgYDVQQGEwdVbmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYDVQQKEwdVbmtub3duMRAwDgYDVQQLEwdVbmtub3duMRAwDgYDVQQDEwdVbmtub3duMB4XDTE1MDEyODIyMTYyMFoXDTE3MTAyNDIyMTYyMFowbDEQMA4GA1UEBhMHVW5rbm93bjEQMA4GA1UECBMHVW5rbm93bjEQMA4GA1UEBxMHVW5rbm93bjEQMA4GA1UEChMHVW5rbm93bjEQMA4GA1UECxMHVW5rbm93bjEQMA4GA1UEAxMHVW5rbm93bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAII/K9NNvXi9IySl7+l2zY/kKrGTtuR4WdCI0xLW/Jn4dLY7v1/HOnV4CC4ecFOzhdNFPtJkmEhP/q62CpmOYOKApXk3tfmm2rwEz9bWprVxgFGKnbrWlz61Z/cjLAlhD3IUj2ZRBquYgSXQPsYfXo1JmSWF5pZ9uh1FVqu9f4wvRqY20ZhUN+39F+1iaBsoqsrbXypCn1HgZkW1/9D9GZug1c3vB4wg1TwZZWRNGtxwoEhdK6dPrNcZ+6PdanVilWrbQFbBjY4wz8/7IMBzssoQ7Usmo8F1Piv0FGfaVeJqBrcAvbiBMpk8pT+27u6p8VyIX6LhGvnxIwM07NByeSUCAwEAAaMhMB8wHQYDVR0OBBYEFFlcNuTYwI9W0tQ224K1gFJlMam0MA0GCSqGSIb3DQEBCwUAA4IBAQB5snl1KWOJALtAjLqD0mLPg1iElmZP82Lq1htLBt3XagwzU9CaeVeCQ7lTp+DXWzPa9nCLhsC3QyrV3/+oqNli8C6NpeqI8FqN2yQW/QMWN1m5jWDbmrWwtQzRUn/rh5KEb5m3zPB+tOC6e/2bV3QeQebxeW7lVMD0tSCviUg1MQf1l2gzuXQo60411YwqrXwk6GMkDOhFDQKDlMchO3oRbQkGbcP8UeiKAXjMeHfzbiBr+cWz8NYZEtxUEDYDjTpKrYCSMJBXpmgVJCZ00BswbksxJwaGqGMPpUKmCV671pf3m8nq3xyiHMDGuGwtbU+GE8kVx85menmp8+964nin",
"wantAuthnRequestsSigned": true,
diff --git a/examples/broker/saml-broker-authentication/saml-broker-realm.json b/examples/broker/saml-broker-authentication/saml-broker-realm.json
index 0fc06434df..016b843e3a 100644
--- a/examples/broker/saml-broker-authentication/saml-broker-realm.json
+++ b/examples/broker/saml-broker-authentication/saml-broker-realm.json
@@ -28,10 +28,10 @@
},
"applications": [
{
- "name": "http://localhost:8081/auth/",
+ "name": "http://localhost:8080/auth/",
"enabled": true,
"redirectUris": [
- "http://localhost:8081/auth/realms/saml-broker-authentication-realm/broker/saml-identity-provider"
+ "http://localhost:8080/auth/realms/saml-broker-authentication-realm/broker/saml-identity-provider"
],
"attributes": {
"saml.assertion.signature": "true",
diff --git a/examples/broker/saml-broker-authentication/src/main/webapp/keycloak.json b/examples/broker/saml-broker-authentication/src/main/webapp/keycloak.json
index dccd4a337f..5c86ef013e 100644
--- a/examples/broker/saml-broker-authentication/src/main/webapp/keycloak.json
+++ b/examples/broker/saml-broker-authentication/src/main/webapp/keycloak.json
@@ -2,7 +2,7 @@
"realm" : "saml-broker-authentication-realm",
"resource" : "saml-broker-authentication",
"realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
- "auth-server-url": "http://localhost:8081/auth",
+ "auth-server-url": "/auth",
"ssl-required" : "external",
"public-client" : true
}
diff --git a/examples/broker/twitter-authentication/src/main/webapp/keycloak.json b/examples/broker/twitter-authentication/src/main/webapp/keycloak.json
new file mode 100644
index 0000000000..7243636390
--- /dev/null
+++ b/examples/broker/twitter-authentication/src/main/webapp/keycloak.json
@@ -0,0 +1,8 @@
+{
+ "realm" : "twitter-identity-provider-realm",
+ "resource" : "twitter-authentication",
+ "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+ "auth-server-url": "/auth",
+ "ssl-required" : "external",
+ "public-client" : true
+}
diff --git a/examples/broker/twitter-authentication/twitter-identity-provider-realm.json b/examples/broker/twitter-authentication/twitter-identity-provider-realm.json
index 10c6b55ea0..90aef3fe20 100644
--- a/examples/broker/twitter-authentication/twitter-identity-provider-realm.json
+++ b/examples/broker/twitter-authentication/twitter-identity-provider-realm.json
@@ -40,6 +40,9 @@
"baseUrl": "/twitter-authentication",
"redirectUris": [
"/twitter-authentication/*"
+ ],
+ "webOrigins": [
+ "http://localhost:8080"
]
},
{
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/app.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/app.js
index 47c3fcac07..59f169fa77 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/app.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/app.js
@@ -171,7 +171,7 @@ module.config([ '$routeProvider', function($routeProvider) {
controller : 'RealmIdentityProviderCtrl'
})
.when('/realms/:realm/identity-provider-settings/provider/:provider_id/:id', {
- templateUrl : function(params){ return 'partials/realm-identity-provider-' + params.provider_id + '.html'; },
+ templateUrl : function(params){ return resourceUrl + '/partials/realm-identity-provider-' + params.provider_id + '.html'; },
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/applications.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/applications.js
index 569c8662b8..b4122885a1 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/applications.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/applications.js
@@ -49,75 +49,95 @@ module.controller('ApplicationCredentialsCtrl', function($scope, $location, real
});
});
-module.controller('ApplicationIdentityProviderCtrl', function($scope, $location, realm, application, Application, $location, Notifications) {
+module.controller('ApplicationIdentityProviderCtrl', function($scope, $location, $route, realm, application, Application, $location, Notifications) {
$scope.realm = realm;
$scope.application = angular.copy(application);
+ var length = 0;
+
+ if ($scope.application.identityProviders) {
+ length = $scope.application.identityProviders.length;
+ } else {
+ $scope.application.identityProviders = new Array(realm.identityProviders.length);
+ }
+
+ for (j = length; j < realm.identityProviders.length; j++) {
+ $scope.application.identityProviders[j] = {};
+ }
$scope.identityProviders = [];
- if (!$scope.application.allowedIdentityProviders) {
- $scope.application.allowedIdentityProviders = [];
- }
-
for (j = 0; j < realm.identityProviders.length; j++) {
var identityProvider = realm.identityProviders[j];
var match = false;
+ var applicationProvider;
- for (i = 0; i < $scope.application.allowedIdentityProviders.length; i++) {
- var appProvider = $scope.application.allowedIdentityProviders[i];
+ for (i = 0; i < $scope.application.identityProviders.length; i++) {
+ applicationProvider = $scope.application.identityProviders[i];
- if (appProvider == identityProvider.id) {
- $scope.identityProviders[i] = identityProvider;
- match = true;
+ if (applicationProvider) {
+ if (applicationProvider.retrieveToken) {
+ applicationProvider.retrieveToken = applicationProvider.retrieveToken.toString();
+ } else {
+ applicationProvider.retrieveToken = false.toString();
+ }
+
+ if (applicationProvider.id == identityProvider.id) {
+ $scope.identityProviders[i] = {};
+ $scope.identityProviders[i].identityProvider = identityProvider;
+ $scope.identityProviders[i].retrieveToken = applicationProvider.retrieveToken.toString();
+ break;
+ }
+
+ applicationProvider = null;
}
}
- if (!match) {
- var length = $scope.identityProviders.length;
+ if (applicationProvider == null) {
+ var length = $scope.identityProviders.length + $scope.application.identityProviders.length;
- length = length + $scope.application.allowedIdentityProviders.length;
-
- $scope.identityProviders[length] = identityProvider;
+ $scope.identityProviders[length] = {};
+ $scope.identityProviders[length].identityProvider = identityProvider;
+ $scope.identityProviders[length].retrieveToken = false.toString();
}
}
$scope.identityProviders = $scope.identityProviders.filter(function(n){ return n != undefined });
+ var oldCopy = angular.copy($scope.application);
+
$scope.save = function() {
var selectedProviders = [];
- for (i = 0; i < $scope.application.allowedIdentityProviders.length; i++) {
- var appProvider = $scope.application.allowedIdentityProviders[i];
+ for (i = 0; i < $scope.application.identityProviders.length; i++) {
+ var appProvider = $scope.application.identityProviders[i];
- if (appProvider) {
+ if (appProvider.id != null && appProvider.id != false) {
selectedProviders[selectedProviders.length] = appProvider;
}
}
- $scope.allowedIdentityProviders = $scope.application.allowedIdentityProviders;
- $scope.application.allowedIdentityProviders = selectedProviders;
+ $scope.application.identityProviders = selectedProviders;
Application.update({
realm : realm.realm,
application : application.id
}, $scope.application, function() {
$scope.changed = false;
- $scope.application.allowedIdentityProviders = $scope.allowedIdentityProviders;
- $location.url("/realms/" + realm.realm + "/applications/" + application.id + "/identity-provider");
+ $route.reload();
Notifications.success("Your changes have been saved to the application.");
});
};
$scope.reset = function() {
- $scope.application = angular.copy(application);
+ $scope.application = angular.copy(oldCopy);
$scope.changed = false;
};
- $scope.$watch(function() {
- return $location.path();
- }, function() {
- $scope.path = $location.path().substring(1).split("/");
- });
+ $scope.$watch('application', function() {
+ if (!angular.equals($scope.application, oldCopy)) {
+ $scope.changed = true;
+ }
+ }, true);
});
module.controller('ApplicationSamlKeyCtrl', function($scope, $location, $http, $upload, realm, application,
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/oauth-clients.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/oauth-clients.js
index dec42f56f3..1237f57301 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/oauth-clients.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/oauth-clients.js
@@ -324,74 +324,94 @@ module.controller('OAuthClientRevocationCtrl', function($scope, realm, oauth, OA
}
});
-module.controller('OAuthClientIdentityProviderCtrl', function($scope, realm, oauth, OAuthClient, $location, Notifications) {
+module.controller('OAuthClientIdentityProviderCtrl', function($scope, $route, realm, oauth, OAuthClient, $location, Notifications) {
$scope.realm = realm;
$scope.oauth = angular.copy(oauth);
+ var length = 0;
+
+ if ($scope.oauth.identityProviders) {
+ length = $scope.oauth.identityProviders.length;
+ } else {
+ $scope.oauth.identityProviders = new Array(realm.identityProviders.length);
+ }
+
+ for (j = length; j < realm.identityProviders.length; j++) {
+ $scope.oauth.identityProviders[j] = {};
+ }
$scope.identityProviders = [];
- if (!$scope.oauth.allowedIdentityProviders) {
- $scope.oauth.allowedIdentityProviders = [];
- }
-
for (j = 0; j < realm.identityProviders.length; j++) {
var identityProvider = realm.identityProviders[j];
var match = false;
+ var applicationProvider;
- for (i = 0; i < $scope.oauth.allowedIdentityProviders.length; i++) {
- var appProvider = $scope.oauth.allowedIdentityProviders[i];
+ for (i = 0; i < $scope.oauth.identityProviders.length; i++) {
+ applicationProvider = $scope.oauth.identityProviders[i];
- if (appProvider == identityProvider.id) {
- $scope.identityProviders[i] = identityProvider;
- match = true;
+ if (applicationProvider) {
+ if (applicationProvider.retrieveToken) {
+ applicationProvider.retrieveToken = applicationProvider.retrieveToken.toString();
+ } else {
+ applicationProvider.retrieveToken = false.toString();
+ }
+
+ if (applicationProvider.id == identityProvider.id) {
+ $scope.identityProviders[i] = {};
+ $scope.identityProviders[i].identityProvider = identityProvider;
+ $scope.identityProviders[i].retrieveToken = applicationProvider.retrieveToken.toString();
+ break;
+ }
+
+ applicationProvider = null;
}
}
- if (!match) {
- var length = $scope.identityProviders.length;
+ if (applicationProvider == null) {
+ var length = $scope.identityProviders.length + $scope.oauth.identityProviders.length;
- length = length + $scope.oauth.allowedIdentityProviders.length;
-
- $scope.identityProviders[length] = identityProvider;
+ $scope.identityProviders[length] = {};
+ $scope.identityProviders[length].identityProvider = identityProvider;
+ $scope.identityProviders[length].retrieveToken = false.toString();
}
}
$scope.identityProviders = $scope.identityProviders.filter(function(n){ return n != undefined });
+ var oldCopy = angular.copy($scope.oauth);
+
$scope.save = function() {
var selectedProviders = [];
- for (i = 0; i < $scope.oauth.allowedIdentityProviders.length; i++) {
- var appProvider = $scope.oauth.allowedIdentityProviders[i];
+ for (i = 0; i < $scope.oauth.identityProviders.length; i++) {
+ var appProvider = $scope.oauth.identityProviders[i];
- if (appProvider) {
+ if (appProvider.id != null && appProvider.id != false) {
selectedProviders[selectedProviders.length] = appProvider;
}
}
- $scope.allowedIdentityProviders = $scope.oauth.allowedIdentityProviders;
- $scope.oauth.allowedIdentityProviders = selectedProviders;
+ $scope.oauth.identityProviders = selectedProviders;
OAuthClient.update({
realm : realm.realm,
oauth : oauth.id
}, $scope.oauth, function() {
$scope.changed = false;
- $scope.oauth.allowedIdentityProviders = $scope.allowedIdentityProviders;
- $location.url("/realms/" + realm.realm + "/oauth-clients/" + oauth.id + "/identity-provider");
+ $route.reload();
Notifications.success("Your changes have been saved to the application.");
});
};
$scope.reset = function() {
- $scope.oauth = angular.copy(oauth);
+ $scope.oauth = angular.copy(oldCopy);
$scope.changed = false;
};
- $scope.$watch(function() {
- return $location.path();
- }, function() {
- $scope.path = $location.path().substring(1).split("/");
- });
+ $scope.$watch('oauth', function() {
+ if (!angular.equals($scope.oauth, oldCopy)) {
+ $scope.changed = true;
+ }
+ }, true);
});
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-identity-provider.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-identity-provider.html
index 57adcd3f35..aeb3b8d961 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-identity-provider.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-identity-provider.html
@@ -7,17 +7,24 @@
{{application.name}}
Identity Provider
- {{application.name}} Identity Provider Settings
+ {{application.name}} Identity Provider Settings
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/oauth-client-identity-provider.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/oauth-client-identity-provider.html
index 92f3e87124..619ce331ea 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/oauth-client-identity-provider.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/oauth-client-identity-provider.html
@@ -10,14 +10,21 @@
{{oauth.name}} Identity Provider Settings
diff --git a/model/api/src/main/java/org/keycloak/models/ClientIdentityProviderMappingModel.java b/model/api/src/main/java/org/keycloak/models/ClientIdentityProviderMappingModel.java
new file mode 100644
index 0000000000..e3b84014a8
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/ClientIdentityProviderMappingModel.java
@@ -0,0 +1,43 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.models;
+
+/**
+ * @author pedroigor
+ */
+public class ClientIdentityProviderMappingModel {
+
+ private String identityProvider;
+ private boolean retrieveToken;
+
+ public String getIdentityProvider() {
+ return this.identityProvider;
+ }
+
+ public void setIdentityProvider(String identityProviderModel) {
+ this.identityProvider = identityProviderModel;
+ }
+
+ public boolean isRetrieveToken() {
+ return this.retrieveToken;
+ }
+
+ public void setRetrieveToken(boolean retrieveToken) {
+ this.retrieveToken = retrieveToken;
+ }
+}
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 5354d04225..3ebdc2aac7 100755
--- a/model/api/src/main/java/org/keycloak/models/ClientModel.java
+++ b/model/api/src/main/java/org/keycloak/models/ClientModel.java
@@ -98,11 +98,10 @@ public interface ClientModel {
void setNotBefore(int notBefore);
- void updateAllowedIdentityProviders(List identityProviders);
-
- List getAllowedIdentityProviders();
-
+ void updateAllowedIdentityProviders(List identityProviders);
+ List getIdentityProviders();
boolean hasIdentityProvider(String providerId);
+ boolean isAllowedRetrieveTokenFromIdentityProvider(String providerId);
Set getProtocolMappers();
void addProtocolMappers(Set mapperIds);
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 3d43feeb3d..134d1bfaf1 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
@@ -29,7 +29,7 @@ public class ClientEntity extends AbstractIdentifiableEntity {
private List webOrigins = new ArrayList();
private List redirectUris = new ArrayList();
private List scopeIds = new ArrayList();
- private List allowedIdentityProviders = new ArrayList();
+ private List identityProviders = new ArrayList();
private Set protocolMappers = new HashSet();
public String getName() {
@@ -144,12 +144,12 @@ public class ClientEntity extends AbstractIdentifiableEntity {
this.frontchannelLogout = frontchannelLogout;
}
- public List getAllowedIdentityProviders() {
- return this.allowedIdentityProviders;
+ public List getIdentityProviders() {
+ return this.identityProviders;
}
- public void setAllowedIdentityProviders(List allowedIdentityProviders) {
- this.allowedIdentityProviders = allowedIdentityProviders;
+ public void setIdentityProviders(List identityProviders) {
+ this.identityProviders = identityProviders;
}
public Set getProtocolMappers() {
diff --git a/model/api/src/main/java/org/keycloak/models/entities/ClientIdentityProviderMappingEntity.java b/model/api/src/main/java/org/keycloak/models/entities/ClientIdentityProviderMappingEntity.java
new file mode 100644
index 0000000000..a788aacf4b
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/entities/ClientIdentityProviderMappingEntity.java
@@ -0,0 +1,44 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.models.entities;
+
+/**
+ * @author pedroigor
+ */
+public class ClientIdentityProviderMappingEntity {
+
+ private String id;
+ private Boolean retrieveToken;
+
+ public String getId() {
+ return this.id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public Boolean isRetrieveToken() {
+ return this.retrieveToken;
+ }
+
+ public void setRetrieveToken(Boolean retrieveToken) {
+ this.retrieveToken = retrieveToken;
+ }
+
+}
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 522c96572b..7bd7cfaecc 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
@@ -3,6 +3,7 @@ package org.keycloak.models.utils;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.ClaimMask;
import org.keycloak.models.ClaimTypeModel;
+import org.keycloak.models.ClientIdentityProviderMappingModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.FederatedIdentityModel;
@@ -19,6 +20,7 @@ import org.keycloak.models.UserSessionModel;
import org.keycloak.representations.idm.ApplicationRepresentation;
import org.keycloak.representations.idm.ClaimRepresentation;
import org.keycloak.representations.idm.ClaimTypeRepresentation;
+import org.keycloak.representations.idm.ClientIdentityProviderMappingRepresentation;
import org.keycloak.representations.idm.ClientProtocolMappingRepresentation;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.FederatedIdentityRepresentation;
@@ -262,8 +264,8 @@ public class ModelToRepresentation {
rep.setRegisteredNodes(new HashMap(applicationModel.getRegisteredNodes()));
}
- if (!applicationModel.getAllowedIdentityProviders().isEmpty()) {
- rep.setAllowedIdentityProviders(applicationModel.getAllowedIdentityProviders());
+ if (!applicationModel.getIdentityProviders().isEmpty()) {
+ rep.setIdentityProviders(toRepresentation(applicationModel.getIdentityProviders()));
}
if (!applicationModel.getProtocolMappers().isEmpty()) {
@@ -279,6 +281,21 @@ public class ModelToRepresentation {
return rep;
}
+ private static List toRepresentation(List identityProviders) {
+ ArrayList representations = new ArrayList();
+
+ for (ClientIdentityProviderMappingModel model : identityProviders) {
+ ClientIdentityProviderMappingRepresentation representation = new ClientIdentityProviderMappingRepresentation();
+
+ representation.setId(model.getIdentityProvider());
+ representation.setRetrieveToken(model.isRetrieveToken());
+
+ representations.add(representation);
+ }
+
+ return representations;
+ }
+
public static OAuthClientRepresentation toRepresentation(OAuthClientModel model) {
OAuthClientRepresentation rep = new OAuthClientRepresentation();
rep.setId(model.getId());
@@ -301,8 +318,8 @@ public class ModelToRepresentation {
}
rep.setNotBefore(model.getNotBefore());
- if (!model.getAllowedIdentityProviders().isEmpty()) {
- rep.setAllowedIdentityProviders(model.getAllowedIdentityProviders());
+ if (!model.getIdentityProviders().isEmpty()) {
+ rep.setIdentityProviders(toRepresentation(model.getIdentityProviders()));
}
if (!model.getProtocolMappers().isEmpty()) {
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 f296764114..2bf61e2395 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
@@ -7,6 +7,7 @@ import org.keycloak.models.ApplicationModel;
import org.keycloak.models.BrowserSecurityHeaders;
import org.keycloak.models.ClaimMask;
import org.keycloak.models.ClaimTypeModel;
+import org.keycloak.models.ClientIdentityProviderMappingModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.IdentityProviderModel;
@@ -23,6 +24,7 @@ import org.keycloak.models.UserModel;
import org.keycloak.representations.idm.ApplicationRepresentation;
import org.keycloak.representations.idm.ClaimRepresentation;
import org.keycloak.representations.idm.ClaimTypeRepresentation;
+import org.keycloak.representations.idm.ClientIdentityProviderMappingRepresentation;
import org.keycloak.representations.idm.ClientProtocolMappingRepresentation;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.FederatedIdentityRepresentation;
@@ -473,17 +475,7 @@ public class RepresentationToModel {
applicationModel.setProtocolMappers(ids);
}
- List allowedIdentityProviders = resourceRep.getAllowedIdentityProviders();
-
- if (allowedIdentityProviders == null || allowedIdentityProviders.isEmpty()) {
- allowedIdentityProviders = new ArrayList();
-
- for (IdentityProviderModel identityProvider : realm.getIdentityProviders()) {
- allowedIdentityProviders.add(identityProvider.getId());
- }
- }
-
- applicationModel.updateAllowedIdentityProviders(allowedIdentityProviders);
+ applicationModel.updateAllowedIdentityProviders(toModel(resourceRep.getIdentityProviders(), realm));
return applicationModel;
}
@@ -536,9 +528,7 @@ public class RepresentationToModel {
setClaims(resource, rep.getClaims());
}
- if (rep.getAllowedIdentityProviders() != null) {
- resource.updateAllowedIdentityProviders(rep.getAllowedIdentityProviders());
- }
+ updateClientIdentityProvides(rep.getIdentityProviders(), resource);
}
public static void setClaims(ClientModel model, ClaimRepresentation rep) {
@@ -613,17 +603,7 @@ public class RepresentationToModel {
public static OAuthClientModel createOAuthClient(OAuthClientRepresentation rep, RealmModel realm) {
OAuthClientModel model = createOAuthClient(rep.getId(), rep.getName(), realm);
- List allowedIdentityProviders = rep.getAllowedIdentityProviders();
-
- if (allowedIdentityProviders == null || allowedIdentityProviders.isEmpty()) {
- allowedIdentityProviders = new ArrayList();
-
- for (IdentityProviderModel identityProvider : realm.getIdentityProviders()) {
- allowedIdentityProviders.add(identityProvider.getId());
- }
- }
-
- model.updateAllowedIdentityProviders(allowedIdentityProviders);
+ model.updateAllowedIdentityProviders(toModel(rep.getIdentityProviders(), realm));
updateOAuthClient(rep, model);
return model;
@@ -667,9 +647,7 @@ public class RepresentationToModel {
}
}
- if (rep.getAllowedIdentityProviders() != null) {
- model.updateAllowedIdentityProviders(rep.getAllowedIdentityProviders());
- }
+ updateClientIdentityProvides(rep.getIdentityProviders(), model);
if (rep.getProtocolMappers() != null) {
Set ids = new HashSet();
@@ -868,4 +846,48 @@ public class RepresentationToModel {
model.setConfig(rep.getConfig());
return model;
}
+
+ private static List toModel(List repIdentityProviders, RealmModel realm) {
+ List allowedIdentityProviders = new ArrayList();
+
+ if (repIdentityProviders == null || repIdentityProviders.isEmpty()) {
+ allowedIdentityProviders = new ArrayList();
+
+ for (IdentityProviderModel identityProvider : realm.getIdentityProviders()) {
+ ClientIdentityProviderMappingModel identityProviderMapping = new ClientIdentityProviderMappingModel();
+
+ identityProviderMapping.setIdentityProvider(identityProvider.getId());
+
+ allowedIdentityProviders.add(identityProviderMapping);
+ }
+ } else {
+ for (ClientIdentityProviderMappingRepresentation rep : repIdentityProviders) {
+ ClientIdentityProviderMappingModel identityProviderMapping = new ClientIdentityProviderMappingModel();
+
+ identityProviderMapping.setIdentityProvider(rep.getId());
+ identityProviderMapping.setRetrieveToken(rep.isRetrieveToken());
+
+ allowedIdentityProviders.add(identityProviderMapping);
+ }
+ }
+
+ return allowedIdentityProviders;
+ }
+
+ private static void updateClientIdentityProvides(List identityProviders, ClientModel resource) {
+ if (identityProviders != null) {
+ List allowedIdentityProviders = new ArrayList();
+
+ for (ClientIdentityProviderMappingRepresentation mappingRepresentation : identityProviders) {
+ ClientIdentityProviderMappingModel identityProviderMapping = new ClientIdentityProviderMappingModel();
+
+ identityProviderMapping.setIdentityProvider(mappingRepresentation.getId());
+ identityProviderMapping.setRetrieveToken(mappingRepresentation.isRetrieveToken());
+
+ allowedIdentityProviders.add(identityProviderMapping);
+ }
+
+ resource.updateAllowedIdentityProviders(allowedIdentityProviders);
+ }
+ }
}
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/ClientAdapter.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/ClientAdapter.java
index 0fc38bcc9d..a17cc7c14a 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/ClientAdapter.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/ClientAdapter.java
@@ -1,5 +1,6 @@
package org.keycloak.models.cache;
+import org.keycloak.models.ClientIdentityProviderMappingModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
@@ -263,15 +264,15 @@ public abstract class ClientAdapter implements ClientModel {
}
@Override
- public void updateAllowedIdentityProviders(List identityProviders) {
+ public void updateAllowedIdentityProviders(List identityProviders) {
getDelegateForUpdate();
updatedClient.updateAllowedIdentityProviders(identityProviders);
}
@Override
- public List getAllowedIdentityProviders() {
- if (updatedClient != null) return updatedClient.getAllowedIdentityProviders();
- return cachedClient.getAllowedIdentityProviders();
+ public List getIdentityProviders() {
+ if (updatedClient != null) return updatedClient.getIdentityProviders();
+ return cachedClient.getIdentityProviders();
}
@Override
@@ -280,6 +281,12 @@ public abstract class ClientAdapter implements ClientModel {
return cachedClient.hasIdentityProvider(providerId);
}
+ @Override
+ public boolean isAllowedRetrieveTokenFromIdentityProvider(String providerId) {
+ if (updatedClient != null) return updatedClient.isAllowedRetrieveTokenFromIdentityProvider(providerId);
+ return cachedClient.isAllowedRetrieveTokenFromIdentityProvider(providerId);
+ }
+
@Override
public Set getProtocolMappers() {
if (updatedClient != null) return updatedClient.getProtocolMappers();
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 1029713ba6..e9a8a89b74 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
@@ -1,5 +1,6 @@
package org.keycloak.models.cache.entities;
+import org.keycloak.models.ClientIdentityProviderMappingModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
@@ -35,7 +36,7 @@ public class CachedClient {
protected int notBefore;
protected Set scope = new HashSet();
protected Set webOrigins = new HashSet();
- private List allowedIdentityProviders = new ArrayList();
+ private List identityProviders = new ArrayList();
private Set protocolClaimMappings = new HashSet();
public CachedClient(RealmCache cache, RealmProvider delegate, RealmModel realm, ClientModel model) {
@@ -57,7 +58,7 @@ public class CachedClient {
for (RoleModel role : model.getScopeMappings()) {
scope.add(role.getId());
}
- this.allowedIdentityProviders = model.getAllowedIdentityProviders();
+ this.identityProviders = model.getIdentityProviders();
protocolClaimMappings.addAll(model.getProtocolMappers());
}
@@ -125,15 +126,31 @@ public class CachedClient {
return frontchannelLogout;
}
- public List getAllowedIdentityProviders() {
- return this.allowedIdentityProviders;
+ public List getIdentityProviders() {
+ return this.identityProviders;
}
public boolean hasIdentityProvider(String providerId) {
- return this.allowedIdentityProviders.contains(providerId);
+ for (ClientIdentityProviderMappingModel model : getIdentityProviders()) {
+ if (model.getIdentityProvider().equals(providerId)) {
+ return true;
+ }
+ }
+
+ return false;
}
public Set getProtocolClaimMappings() {
return protocolClaimMappings;
}
+
+ public boolean isAllowedRetrieveTokenFromIdentityProvider(String providerId) {
+ for (ClientIdentityProviderMappingModel model : getIdentityProviders()) {
+ if (model.getIdentityProvider().equals(providerId)) {
+ return model.isRetrieveToken();
+ }
+ }
+
+ return false;
+ }
}
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 c437014cbc..04af5ee4a4 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
@@ -1,14 +1,15 @@
package org.keycloak.models.jpa;
+import org.keycloak.models.ClientIdentityProviderMappingModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleContainerModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.jpa.entities.ClientEntity;
+import org.keycloak.models.jpa.entities.ClientIdentityProviderMappingEntity;
import org.keycloak.models.jpa.entities.IdentityProviderEntity;
import org.keycloak.models.jpa.entities.ProtocolMapperEntity;
-import org.keycloak.models.jpa.entities.RealmEntity;
import org.keycloak.models.jpa.entities.RoleEntity;
import org.keycloak.models.jpa.entities.ScopeMappingEntity;
@@ -303,47 +304,96 @@ public abstract class ClientAdapter implements ClientModel {
}
@Override
- public void updateAllowedIdentityProviders(List identityProviders) {
- Collection entities = entity.getAllowedIdentityProviders();
+ public void updateAllowedIdentityProviders(List identityProviders) {
+ Collection entities = entity.getIdentityProviders();
Set already = new HashSet();
- List remove = new ArrayList();
- for (IdentityProviderEntity rel : entities) {
- if (!contains(rel.getId(), identityProviders.toArray(new String[identityProviders.size()]))) {
- remove.add(rel);
+ List remove = new ArrayList();
+
+ for (ClientIdentityProviderMappingEntity entity : entities) {
+ IdentityProviderEntity identityProvider = entity.getIdentityProvider();
+ boolean toRemove = true;
+
+ for (ClientIdentityProviderMappingModel model : identityProviders) {
+ if (model.getIdentityProvider().equals(identityProvider.getId())) {
+ toRemove = false;
+ break;
+ }
+ }
+
+ if (toRemove) {
+ remove.add(entity);
} else {
- already.add(rel.getId());
+ already.add(entity.getIdentityProvider().getId());
}
}
- for (IdentityProviderEntity entity : remove) {
+ for (ClientIdentityProviderMappingEntity entity : remove) {
entities.remove(entity);
+ em.remove(entity);
}
em.flush();
- for (String providerId : identityProviders) {
- if (!already.contains(providerId)) {
- TypedQuery query = em.createNamedQuery("findIdentityProviderById", IdentityProviderEntity.class).setParameter("id", providerId);
- IdentityProviderEntity providerEntity = query.getSingleResult();
- entities.add(providerEntity);
+ for (ClientIdentityProviderMappingModel model : identityProviders) {
+ ClientIdentityProviderMappingEntity mappingEntity = null;
+
+ if (!already.contains(model.getIdentityProvider())) {
+ mappingEntity = new ClientIdentityProviderMappingEntity();
+ entities.add(mappingEntity);
+ } else {
+ for (ClientIdentityProviderMappingEntity entity : entities) {
+ if (entity.getIdentityProvider().getId().equals(model.getIdentityProvider())) {
+ mappingEntity = entity;
+ break;
+ }
+ }
}
+
+ TypedQuery query = em.createNamedQuery("findIdentityProviderById", IdentityProviderEntity.class).setParameter("id", model.getIdentityProvider());
+ IdentityProviderEntity identityProviderEntity = query.getSingleResult();
+
+ mappingEntity.setIdentityProvider(identityProviderEntity);
+ mappingEntity.setClient(this.entity);
+ mappingEntity.setRetrieveToken(model.isRetrieveToken());
+
+ em.persist(mappingEntity);
}
em.flush();
}
@Override
- public List getAllowedIdentityProviders() {
- Collection entities = entity.getAllowedIdentityProviders();
- List providers = new ArrayList();
+ public List getIdentityProviders() {
+ List models = new ArrayList();
- for (IdentityProviderEntity entity : entities) {
- providers.add(entity.getId());
+ for (ClientIdentityProviderMappingEntity entity : this.entity.getIdentityProviders()) {
+ ClientIdentityProviderMappingModel model = new ClientIdentityProviderMappingModel();
+
+ model.setIdentityProvider(entity.getIdentityProvider().getId());
+ model.setRetrieveToken(entity.isRetrieveToken());
+
+ models.add(model);
}
- return providers;
+ return models;
}
@Override
public boolean hasIdentityProvider(String providerId) {
- List allowedIdentityProviders = getAllowedIdentityProviders();
- return allowedIdentityProviders.contains(providerId);
+ for (ClientIdentityProviderMappingModel model : getIdentityProviders()) {
+ if (model.getIdentityProvider().equals(providerId)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean isAllowedRetrieveTokenFromIdentityProvider(String providerId) {
+ for (ClientIdentityProviderMappingModel model : getIdentityProviders()) {
+ if (model.getIdentityProvider().equals(providerId)) {
+ return model.isRetrieveToken();
+ }
+ }
+
+ return false;
}
public static boolean contains(String str, String[] array) {
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 817fd8085c..dcf33cb769 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
@@ -73,9 +73,8 @@ public abstract class ClientEntity {
@CollectionTable(name="CLIENT_ATTRIBUTES", joinColumns={ @JoinColumn(name="CLIENT_ID") })
protected Map attributes = new HashMap();
- @OneToMany(fetch = FetchType.LAZY)
- @JoinTable(name="CLIENT_ALLOWED_IDENTITY_PROVIDER", joinColumns = { @JoinColumn(name="CLIENT_ID")}, inverseJoinColumns = { @JoinColumn(name="INTERNAL_ID")})
- Collection allowedIdentityProviders = new ArrayList();
+ @OneToMany(fetch = FetchType.LAZY, mappedBy = "client", cascade = CascadeType.REMOVE)
+ Collection identityProviders = new ArrayList();
@OneToMany(fetch = FetchType.LAZY)
@JoinTable(name="CLIENT_PROTOCOL_MAPPER", joinColumns = { @JoinColumn(name="CLIENT_ID")}, inverseJoinColumns = { @JoinColumn(name="MAPPING_ID")})
@@ -193,12 +192,12 @@ public abstract class ClientEntity {
this.frontchannelLogout = frontchannelLogout;
}
- public Collection getAllowedIdentityProviders() {
- return this.allowedIdentityProviders;
+ public Collection getIdentityProviders() {
+ return this.identityProviders;
}
- public void setAllowedIdentityProviders(Collection allowedIdentityProviders) {
- this.allowedIdentityProviders = allowedIdentityProviders;
+ public void setIdentityProviders(Collection identityProviders) {
+ this.identityProviders = identityProviders;
}
public Collection getProtocolMappers() {
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientIdentityProviderMappingEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientIdentityProviderMappingEntity.java
new file mode 100755
index 0000000000..fe8485d608
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientIdentityProviderMappingEntity.java
@@ -0,0 +1,121 @@
+package org.keycloak.models.jpa.entities;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.IdClass;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import java.io.Serializable;
+
+/**
+ * @author pedroigor
+ */
+@Table(name="CLIENT_IDENTITY_PROVIDER_MAPPING")
+@Entity
+@IdClass(ClientIdentityProviderMappingEntity.Key.class)
+public class ClientIdentityProviderMappingEntity {
+
+ @Id
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "CLIENT_ID")
+ private ClientEntity client;
+
+ @Id
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "IDENTITY_PROVIDER_ID")
+ private IdentityProviderEntity identityProvider;
+
+ @Column(name = "RETRIEVE_TOKEN")
+ private boolean retrieveToken;
+
+ public ClientEntity getClient() {
+ return this.client;
+ }
+
+ public void setClient(ClientEntity client) {
+ this.client = client;
+ }
+
+ public IdentityProviderEntity getIdentityProvider() {
+ return this.identityProvider;
+ }
+
+ public void setIdentityProvider(IdentityProviderEntity identityProvider) {
+ this.identityProvider = identityProvider;
+ }
+
+ public void setRetrieveToken(boolean retrieveToken) {
+ this.retrieveToken = retrieveToken;
+ }
+
+ public boolean isRetrieveToken() {
+ return retrieveToken;
+ }
+
+ public static class Key implements Serializable {
+
+ private ClientEntity client;
+ private IdentityProviderEntity identityProvider;
+
+ public Key() {
+ }
+
+ public Key(ClientEntity client, IdentityProviderEntity identityProvider) {
+ this.client = client;
+ this.identityProvider = identityProvider;
+ }
+
+ public ClientEntity getUser() {
+ return client;
+ }
+
+ public IdentityProviderEntity getIdentityProvider() {
+ return identityProvider;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Key key = (Key) o;
+
+ if (identityProvider != null ? !identityProvider.getId().equals(key.identityProvider.getId()) : key.identityProvider != null)
+ return false;
+ if (client != null ? !client.getId().equals(key.client != null ? key.client.getId() : null) : key.client != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = client != null ? client.getId().hashCode() : 0;
+ result = 31 * result + (identityProvider != null ? identityProvider.hashCode() : 0);
+ return result;
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ ClientIdentityProviderMappingEntity key = (ClientIdentityProviderMappingEntity) o;
+
+ if (identityProvider != null ? !identityProvider.getId().equals(key.identityProvider.getId()) : key.identityProvider != null)
+ return false;
+ if (client != null ? !client.getId().equals(key.client != null ? key.client.getId() : null) : key.client != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = client != null ? client.getId().hashCode() : 0;
+ result = 31 * result + (identityProvider != null ? identityProvider.hashCode() : 0);
+ return result;
+ }
+}
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 3c472821f6..c0202c1f52 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
@@ -2,6 +2,7 @@ package org.keycloak.models.mongo.keycloak.adapters;
import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
+import org.keycloak.models.ClientIdentityProviderMappingModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
@@ -9,6 +10,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.RealmProvider;
import org.keycloak.models.RoleModel;
import org.keycloak.models.entities.ClientEntity;
+import org.keycloak.models.entities.ClientIdentityProviderMappingEntity;
import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity;
import org.keycloak.models.mongo.utils.MongoModelUtils;
@@ -323,24 +325,58 @@ public abstract class ClientAdapter extends A
}
@Override
- public void updateAllowedIdentityProviders(List identityProviders) {
- List providerIds = new ArrayList();
- for (String providerId : identityProviders) {
- providerIds.add(providerId);
+ public void updateAllowedIdentityProviders(List identityProviders) {
+ List stored = getMongoEntityAsClient().getIdentityProviders();
+
+ for (ClientIdentityProviderMappingModel model : identityProviders) {
+ ClientIdentityProviderMappingEntity entity = new ClientIdentityProviderMappingEntity();
+
+ entity.setId(model.getIdentityProvider());
+ entity.setRetrieveToken(model.isRetrieveToken());
}
- getMongoEntityAsClient().setAllowedIdentityProviders(identityProviders);
+ getMongoEntityAsClient().setIdentityProviders(stored);
updateMongoEntity();
}
@Override
- public List getAllowedIdentityProviders() {
- return getMongoEntityAsClient().getAllowedIdentityProviders();
+ public List getIdentityProviders() {
+ List models = new ArrayList();
+
+ for (ClientIdentityProviderMappingEntity entity : getMongoEntityAsClient().getIdentityProviders()) {
+ ClientIdentityProviderMappingModel model = new ClientIdentityProviderMappingModel();
+
+ model.setIdentityProvider(entity.getId());
+ model.setRetrieveToken(entity.isRetrieveToken());
+
+ models.add(model);
+ }
+
+ return models;
}
@Override
public boolean hasIdentityProvider(String providerId) {
- List allowedIdentityProviders = getMongoEntityAsClient().getAllowedIdentityProviders();
- return allowedIdentityProviders.contains(providerId);
+ for (ClientIdentityProviderMappingEntity identityProviderMappingModel : getMongoEntityAsClient().getIdentityProviders()) {
+ String identityProvider = identityProviderMappingModel.getId();
+
+ if (identityProvider.equals(providerId)) {
+ return true;
+ }
+ }
+
+ return false;
}
+
+ @Override
+ public boolean isAllowedRetrieveTokenFromIdentityProvider(String providerId) {
+ for (ClientIdentityProviderMappingEntity identityProviderMappingModel : getMongoEntityAsClient().getIdentityProviders()) {
+ if (identityProviderMappingModel.getId().equals(providerId)) {
+ return identityProviderMappingModel.isRetrieveToken();
+ }
+ }
+
+ return false;
+ }
+
}
diff --git a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
index 5de4015ddb..7fb4209ca4 100644
--- a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
+++ b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
@@ -186,6 +186,10 @@ public class IdentityBrokerService {
return corsResponse(badRequest("Client [" + audience + "] not authorized."), clientModel);
}
+ if (!clientModel.isAllowedRetrieveTokenFromIdentityProvider(providerId)) {
+ return corsResponse(badRequest("Client [" + audience + "] not authorized to retrieve tokens from identity provider [" + providerId + "]."), clientModel);
+ }
+
if (OAuthClientModel.class.isInstance(clientModel) && !forceRetrieval) {
return corsResponse(Flows.forms(this.session, this.realmModel, clientModel, this.uriInfo)
.setClientSessionCode(authManager.extractAuthorizationHeaderToken(this.request.getHttpHeaders()))
@@ -323,7 +327,7 @@ public class IdentityBrokerService {
federatedUser.addRequiredAction(UPDATE_PROFILE);
}
} catch (Exception e) {
- return redirectToLoginPage(e.getMessage(), clientCode);
+ return redirectToLoginPage(e, clientCode);
}
}
@@ -439,16 +443,21 @@ public class IdentityBrokerService {
}
private Response redirectToErrorPage(String message, Throwable throwable) {
+ if (message == null) {
+ message = "Unexpected error when authenticating with identity provider";
+ }
+
fireErrorEvent(message, throwable);
return Flows.forwardToSecurityFailurePage(this.session, this.realmModel, this.uriInfo, message);
}
- private Response badRequest(String message) {
- fireErrorEvent(message);
- return Flows.errors().error(message, Status.BAD_REQUEST);
- }
+ private Response redirectToLoginPage(Throwable t, ClientSessionCode clientCode) {
+ String message = t.getMessage();
+
+ if (message == null) {
+ message = "Unexpected error when authenticating with identity provider";
+ }
- private Response redirectToLoginPage(String message, ClientSessionCode clientCode) {
fireErrorEvent(message);
return Flows.forms(this.session, this.realmModel, clientCode.getClientSession().getClient(), this.uriInfo)
.setClientSessionCode(clientCode.getCode())
@@ -456,6 +465,11 @@ public class IdentityBrokerService {
.createLogin();
}
+ private Response badRequest(String message) {
+ fireErrorEvent(message);
+ return Flows.errors().error(message, Status.BAD_REQUEST);
+ }
+
private IdentityProvider getIdentityProvider(String providerId) {
IdentityProviderModel identityProviderModel = this.realmModel.getIdentityProviderById(providerId);
@@ -513,7 +527,11 @@ public class IdentityBrokerService {
FederatedIdentityModel federatedIdentityModel = new FederatedIdentityModel(updatedIdentity.getIdentityProviderId(), updatedIdentity.getId(),
updatedIdentity.getUsername(), updatedIdentity.getToken());
// Check if no user already exists with this username or email
- UserModel existingUser = this.session.users().getUserByEmail(updatedIdentity.getEmail(), this.realmModel);
+ UserModel existingUser = null;
+
+ if (updatedIdentity.getEmail() != null) {
+ existingUser = this.session.users().getUserByEmail(updatedIdentity.getEmail(), this.realmModel);
+ }
if (existingUser != null) {
fireErrorEvent(Errors.FEDERATED_IDENTITY_EMAIL_EXISTS);
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java b/services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java
index e8b184edce..bac9b03027 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java
@@ -1,6 +1,7 @@
package org.keycloak.services.resources.admin;
import org.jboss.resteasy.annotations.cache.NoCache;
+import org.keycloak.models.ClientIdentityProviderMappingModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.KeycloakSession;
@@ -17,6 +18,7 @@ import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -63,11 +65,15 @@ public class IdentityProviderResource {
private void removeClientIdentityProviders(List extends ClientModel> clients, IdentityProviderModel identityProvider) {
for (ClientModel clientModel : clients) {
- List allowedIdentityProviders = clientModel.getAllowedIdentityProviders();
+ List identityProviders = clientModel.getIdentityProviders();
- allowedIdentityProviders.remove(identityProvider.getId());
+ for (ClientIdentityProviderMappingModel providerMappingModel : new ArrayList(identityProviders)) {
+ if (providerMappingModel.getIdentityProvider().equals(identityProvider.getId())) {
+ identityProviders.remove(providerMappingModel);
+ }
+ }
- clientModel.updateAllowedIdentityProviders(allowedIdentityProviders);
+ clientModel.updateAllowedIdentityProviders(identityProviders);
}
}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/IdentityProvidersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/IdentityProvidersResource.java
index d44e464c44..d7d1a679b3 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/IdentityProvidersResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/IdentityProvidersResource.java
@@ -7,6 +7,7 @@ import org.jboss.resteasy.spi.NotFoundException;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.broker.provider.IdentityProvider;
import org.keycloak.broker.provider.IdentityProviderFactory;
+import org.keycloak.models.ClientIdentityProviderMappingModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.KeycloakSession;
@@ -179,9 +180,12 @@ public class IdentityProvidersResource {
private void updateClientIdentityProviders(List extends ClientModel> clients, IdentityProviderRepresentation identityProvider) {
for (ClientModel clientModel : clients) {
- List allowedIdentityProviders = clientModel.getAllowedIdentityProviders();
+ List allowedIdentityProviders = clientModel.getIdentityProviders();
+ ClientIdentityProviderMappingModel providerMappingModel = new ClientIdentityProviderMappingModel();
- allowedIdentityProviders.add(identityProvider.getId());
+ providerMappingModel.setIdentityProvider(identityProvider.getId());
+
+ allowedIdentityProviders.add(providerMappingModel);
clientModel.updateAllowedIdentityProviders(allowedIdentityProviders);
}
diff --git a/testsuite/integration/README.md b/testsuite/integration/README.md
index a8d06711ce..f76392ed54 100644
--- a/testsuite/integration/README.md
+++ b/testsuite/integration/README.md
@@ -100,12 +100,12 @@ To start a ApacheDS based LDAP server for testing LDAP sending run:
There are additional system properties you can use to configure (See EmbeddedServersFactory class for details). Once done, you can create LDAP Federation provider
in Keycloak admin console with the settings like:
-Vendor: Other
-Connection URL: ldap://localhost:10389
-Base DN: dc=keycloak,dc=org
-User DN Suffix: ou=People,dc=keycloak,dc=org
-Bind DN: uid=admin,ou=system
-Bind credential: secret
+* Vendor: Other
+* Connection URL: ldap://localhost:10389
+* Base DN: dc=keycloak,dc=org
+* User DN Suffix: ou=People,dc=keycloak,dc=org
+* Bind DN: uid=admin,ou=system
+* Bind credential: secret
Kerberos server
---------------
@@ -114,10 +114,40 @@ To start a ApacheDS based Kerberos server for testing Kerberos + LDAP sending ru
mvn exec:java -Pkerberos
-There are additional system properties you can use to configure (See EmbeddedServersFactory class for details). Once done, you can create LDAP Federation provider
-in Keycloak admin console with same settings like mentioned in previous LDAP section. And you can enable Kerberos with the settings like:
+There are additional system properties you can use to configure (See EmbeddedServersFactory class for details) but for testing purposes default values should be good.
+By default ApacheDS LDAP server will be running on localhost:10389 and Kerberos KDC on localhost:6088 . LDAP will import initial data from [src/main/resources/kerberos/users-kerberos.ldif](src/main/resources/kerberos/users-kerberos.ldif) .
-Server Principal: HTTP/localhost@KEYCLOAK.ORG
-KeyTab: $KEYCLOAK_SOURCES/testsuite/integration/src/main/resources/kerberos/http.keytab
+Once kerberos is running, you can create LDAP Federation provider in Keycloak admin console with same settings like mentioned in previous LDAP section.
+But additionally you can enable Kerberos authentication in LDAP provider with the settings like:
+
+* Kerberos realm: KEYCLOAK.ORG
+* Server Principal: HTTP/localhost@KEYCLOAK.ORG
+* KeyTab: $KEYCLOAK_SOURCES/testsuite/integration/src/main/resources/kerberos/http.keytab (Replace $KEYCLOAK_SOURCES with correct absolute path of your sources)
+
+Once you do this, you should also ensure that your Kerberos client configuration file is properly configured with KEYCLOAK.ORG domain.
+See [src/main/resources/kerberos/test-krb5.conf](src/main/resources/kerberos/test-krb5.conf) for inspiration. The location of Kerberos configuration file
+is platform dependent (In linux it's file `/etc/krb5.conf` )
+
+Then you need to configure your browser to allow SPNEGO/Kerberos login from `localhost` .
+
+Exact steps are again browser dependent. For Firefox see for example [http://www.microhowto.info/howto/configure_firefox_to_authenticate_using_spnego_and_kerberos.html](http://www.microhowto.info/howto/configure_firefox_to_authenticate_using_spnego_and_kerberos.html) .
+URI `localhost` must be allowed in `network.negotiate-auth.trusted-uris` config option.
+
+For Chrome, you just need to run the browser with command similar to this (more details in Chrome documentation):
+
+```
+/usr/bin/google-chrome-stable --auth-server-whitelist="localhost"
+```
+
+
+Finally test the integration by retrieve kerberos ticket. In many OS you can achieve this by running command from CMD like:
+
+```
+kinit hnelson@KEYCLOAK.ORG
+```
+
+and provide password `secret`
+
+Now when you access `http://localhost:8081/auth/realms/master/account` you should be logged in automatically as user `hnelson` .
diff --git a/testsuite/integration/src/main/java/org/keycloak/testutils/ldap/KerberosEmbeddedServer.java b/testsuite/integration/src/main/java/org/keycloak/testutils/ldap/KerberosEmbeddedServer.java
index ff5b7791e1..527c9b3807 100644
--- a/testsuite/integration/src/main/java/org/keycloak/testutils/ldap/KerberosEmbeddedServer.java
+++ b/testsuite/integration/src/main/java/org/keycloak/testutils/ldap/KerberosEmbeddedServer.java
@@ -54,7 +54,7 @@ public class KerberosEmbeddedServer extends LDAPEmbeddedServer {
public void init() throws Exception {
super.init();
- log.info("Creating KDC server");
+ log.info("Creating KDC server. kerberosRealm: " + kerberosRealm + ", kdcPort: " + kdcPort);
createAndStartKdcServer();
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java
index 6921ec3132..dd034c1e82 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java
@@ -25,6 +25,8 @@ import org.junit.Rule;
import org.junit.Test;
import org.keycloak.OAuth2Constants;
import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.ClientIdentityProviderMappingModel;
+import org.keycloak.models.ClientModel;
import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.KeycloakSession;
@@ -156,11 +158,18 @@ public abstract class AbstractIdentityProviderTest {
IdentityProviderModel identityProviderModel = getIdentityProviderModel();
RealmModel realm = getRealm();
ApplicationModel applicationModel = realm.getApplicationByName("test-app");
- List allowedIdentityProviders = applicationModel.getAllowedIdentityProviders();
+ List allowedIdentityProviders = applicationModel.getIdentityProviders();
+ ClientIdentityProviderMappingModel mapping = null;
- assertTrue(allowedIdentityProviders.contains(identityProviderModel.getId()));
+ for (ClientIdentityProviderMappingModel model : allowedIdentityProviders) {
+ if (model.getIdentityProvider().equals(identityProviderModel.getId())) {
+ mapping = model;
+ }
+ }
- allowedIdentityProviders.remove(identityProviderModel.getId());
+ assertNotNull(mapping);
+
+ allowedIdentityProviders.remove(mapping);
this.driver.navigate().to("http://localhost:8081/test-app/");
@@ -173,7 +182,7 @@ public abstract class AbstractIdentityProviderTest {
}
- allowedIdentityProviders.add(identityProviderModel.getId());
+ allowedIdentityProviders.add(mapping);
applicationModel.updateAllowedIdentityProviders(allowedIdentityProviders);
@@ -317,6 +326,18 @@ public abstract class AbstractIdentityProviderTest {
assertNotNull(identityModel.getToken());
+ ClientModel clientModel = realm.findClient("test-app");
+ ClientIdentityProviderMappingModel providerMappingModel = null;
+
+ for (ClientIdentityProviderMappingModel identityProviderMappingModel : clientModel.getIdentityProviders()) {
+ if (identityProviderMappingModel.getIdentityProvider().equals(getProviderId())) {
+ providerMappingModel = identityProviderMappingModel;
+ break;
+ }
+ }
+
+ providerMappingModel.setRetrieveToken(false);
+
UserSessionStatus userSessionStatus = retrieveSessionStatus();
String accessToken = userSessionStatus.getAccessTokenString();
URI tokenEndpointUrl = Urls.identityProviderRetrieveToken(BASE_URI, getProviderId(), realm.getName());
@@ -331,6 +352,14 @@ public abstract class AbstractIdentityProviderTest {
WebTarget tokenEndpoint = client.target(tokenEndpointUrl);
Response response = tokenEndpoint.request().get();
+ assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
+
+ providerMappingModel.setRetrieveToken(true);
+
+ client = ClientBuilder.newBuilder().register(authFilter).build();
+ tokenEndpoint = client.target(tokenEndpointUrl);
+ response = tokenEndpoint.request().get();
+
assertEquals(Status.OK.getStatusCode(), response.getStatus());
assertNotNull(response.readEntity(String.class));
@@ -375,6 +404,18 @@ public abstract class AbstractIdentityProviderTest {
assertTrue(oauth.getCurrentQuery().containsKey(OAuth2Constants.CODE));
+ ClientModel clientModel = getRealm().findClient("third-party");
+ ClientIdentityProviderMappingModel providerMappingModel = null;
+
+ for (ClientIdentityProviderMappingModel identityProviderMappingModel : clientModel.getIdentityProviders()) {
+ if (identityProviderMappingModel.getIdentityProvider().equals(getProviderId())) {
+ providerMappingModel = identityProviderMappingModel;
+ break;
+ }
+ }
+
+ providerMappingModel.setRetrieveToken(true);
+
AccessTokenResponse accessToken = oauth.doAccessTokenRequest(oauth.getCurrentQuery().get(OAuth2Constants.CODE), "password");
URI tokenEndpointUrl = Urls.identityProviderRetrieveToken(BASE_URI, getProviderId(), getRealm().getName());
String authHeader = "Bearer " + accessToken.getAccessToken();
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/ImportIdentityProviderTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/ImportIdentityProviderTest.java
index d654d1e5d9..8f43860e05 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/ImportIdentityProviderTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/ImportIdentityProviderTest.java
@@ -25,6 +25,8 @@ import org.keycloak.broker.oidc.OIDCIdentityProviderFactory;
import org.keycloak.broker.saml.SAMLIdentityProvider;
import org.keycloak.broker.saml.SAMLIdentityProviderConfig;
import org.keycloak.broker.saml.SAMLIdentityProviderFactory;
+import org.keycloak.models.ClientIdentityProviderMappingModel;
+import org.keycloak.models.ClientModel;
import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.RealmModel;
import org.keycloak.representations.idm.RealmRepresentation;
@@ -113,6 +115,31 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
assertFalse(identityProviderModel.isAuthenticateByDefault());
}
+ @Test
+ public void testApplicationIdentityProviders() throws Exception {
+ RealmModel realm = installTestRealm();
+
+ ClientModel client = realm.findClient("test-app-with-allowed-providers");
+ List identityProviders = client.getIdentityProviders();
+
+ assertEquals(1, identityProviders.size());
+
+ ClientIdentityProviderMappingModel identityProviderMappingModel = identityProviders.get(0);
+
+ assertEquals("kc-oidc-idp", identityProviderMappingModel.getIdentityProvider());
+ assertEquals(false, identityProviderMappingModel.isRetrieveToken());
+
+ identityProviders.remove(identityProviderMappingModel);
+
+ client.updateAllowedIdentityProviders(identityProviders);
+
+ client = realm.findClientById(client.getId());
+ identityProviders = client.getIdentityProviders();
+
+ assertEquals(0, identityProviders.size());
+ }
+
+
private void assertIdentityProviderConfig(List identityProviders) {
assertFalse(identityProviders.isEmpty());
diff --git a/testsuite/integration/src/test/resources/broker-test/test-realm-with-broker.json b/testsuite/integration/src/test/resources/broker-test/test-realm-with-broker.json
index 322cebe423..c567c177df 100755
--- a/testsuite/integration/src/test/resources/broker-test/test-realm-with-broker.json
+++ b/testsuite/integration/src/test/resources/broker-test/test-realm-with-broker.json
@@ -197,8 +197,11 @@
"/test-app/*"
],
"webOrigins": [],
- "allowedIdentityProviders": [
- "kc-oidc-idp"
+ "identityProviders": [
+ {
+ "id": "kc-oidc-idp",
+ "retrieveToken": false
+ }
]
}
],