[KEYCLOAK-883] - Configuration option to disable token retrieval from applications.

This commit is contained in:
pedroigor 2015-02-27 02:13:27 -03:00
parent cd39d5292a
commit b45d6b896a
28 changed files with 723 additions and 181 deletions

View file

@ -73,13 +73,14 @@
<constraints nullable="false"/>
</column>
</createTable>
<createTable tableName="CLIENT_ALLOWED_IDENTITY_PROVIDER">
<createTable tableName="CLIENT_IDENTITY_PROVIDER_MAPPING">
<column name="CLIENT_ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
<column name="INTERNAL_ID" type="VARCHAR(36)">
<column name="IDENTITY_PROVIDER_ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
<column name="RETRIEVE_TOKEN" type="BOOLEAN(1)"/>
</createTable>
<createTable tableName="CLIENT_PROTOCOL_MAPPER">
<column name="CLIENT_ID" type="VARCHAR(36)">
@ -104,10 +105,11 @@
<addForeignKeyConstraint baseColumnNames="USER_ID" baseTableName="FEDERATED_IDENTITY" constraintName="FK404288B92EF007A6" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="USER_ENTITY"/>
<addForeignKeyConstraint baseColumnNames="IDENTITY_PROVIDER_ID" baseTableName="IDENTITY_PROVIDER_CONFIG" constraintName="FKDC4897CF864C4E43" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="INTERNAL_ID" referencedTableName="IDENTITY_PROVIDER"/>
<addForeignKeyConstraint baseColumnNames="PROTOCOL_MAPPER_ID" baseTableName="PROTOCOL_MAPPER_CONFIG" constraintName="FK_PMConfig" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="PROTOCOL_MAPPER"/>
<addForeignKeyConstraint baseColumnNames="INTERNAL_ID" baseTableName="CLIENT_ALLOWED_IDENTITY_PROVIDER" constraintName="FK_7CELWNIBJI49AVXSRTUF6XJ12" referencedColumnNames="INTERNAL_ID" referencedTableName="IDENTITY_PROVIDER"/>
<addUniqueConstraint columnNames="INTERNAL_ID,CLIENT_ID" constraintName="UK_7CAELWNIBJI49AVXSRTUF6XJ12" tableName="CLIENT_ALLOWED_IDENTITY_PROVIDER"/>
<addForeignKeyConstraint baseColumnNames="IDENTITY_PROVIDER_ID" baseTableName="CLIENT_IDENTITY_PROVIDER_MAPPING" constraintName="FK_7CELWNIBJI49AVXSRTUF6XJ12" referencedColumnNames="INTERNAL_ID" referencedTableName="IDENTITY_PROVIDER"/>
<addForeignKeyConstraint baseColumnNames="CLIENT_ID" baseTableName="CLIENT_IDENTITY_PROVIDER_MAPPING" constraintName="FK_56ELWNIBJI49AVXSRTUF6XJ23" referencedColumnNames="ID" referencedTableName="CLIENT"/>
<addForeignKeyConstraint baseColumnNames="MAPPING_ID" baseTableName="CLIENT_PROTOCOL_MAPPER" constraintName="FK_CPCM" referencedColumnNames="ID" referencedTableName="PROTOCOL_MAPPER"/>
<addUniqueConstraint columnNames="CLIENT_ID,MAPPING_ID" constraintName="UK_CPCM" tableName="CLIENT_PROTOCOL_MAPPER"/>
<addUniqueConstraint columnNames="PROVIDER_NONIMAL_ID" constraintName="UK_2DAELWNIBJI49AVXSRTUF6XJ33" tableName="IDENTITY_PROVIDER"/>
<addUniqueConstraint columnNames="IDENTITY_PROVIDER_ID,CLIENT_ID" constraintName="UK_7CAELWNIBJI49AVXSRTUF6XJ12" tableName="CLIENT_IDENTITY_PROVIDER_MAPPING"/>
</changeSet>
</databaseChangeLog>

View file

@ -18,6 +18,7 @@
<class>org.keycloak.models.jpa.entities.UserRoleMappingEntity</class>
<class>org.keycloak.models.jpa.entities.ScopeMappingEntity</class>
<class>org.keycloak.models.jpa.entities.IdentityProviderEntity</class>
<class>org.keycloak.models.jpa.entities.ClientIdentityProviderMappingEntity</class>
<class>org.keycloak.models.jpa.entities.ClaimTypeEntity</class>
<class>org.keycloak.models.jpa.entities.ProtocolMapperEntity</class>

View file

@ -2,7 +2,6 @@ package org.keycloak.representations.idm;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -29,7 +28,7 @@ public class ApplicationRepresentation {
protected Boolean fullScopeAllowed;
protected Integer nodeReRegistrationTimeout;
protected Map<String, Integer> registeredNodes;
protected List<String> allowedIdentityProviders;
protected List<ClientIdentityProviderMappingRepresentation> identityProviders;
protected List<ClientProtocolMappingRepresentation> protocolMappers;
public String getId() {
@ -192,12 +191,12 @@ public class ApplicationRepresentation {
this.frontchannelLogout = frontchannelLogout;
}
public List<String> getAllowedIdentityProviders() {
return this.allowedIdentityProviders;
public List<ClientIdentityProviderMappingRepresentation> getIdentityProviders() {
return this.identityProviders;
}
public void setAllowedIdentityProviders(List<String> allowedIdentityProviders) {
this.allowedIdentityProviders = allowedIdentityProviders;
public void setIdentityProviders(List<ClientIdentityProviderMappingRepresentation> identityProviders) {
this.identityProviders = identityProviders;
}
public List<ClientProtocolMappingRepresentation> getProtocolMappers() {

View file

@ -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.representations.idm;
/**
* @author pedroigor
*/
public class ClientIdentityProviderMappingRepresentation {
protected String id;
protected boolean retrieveToken;
public String getId() {
return this.id;
}
public void setId(String identityProviderId) {
this.id = identityProviderId;
}
public boolean isRetrieveToken() {
return this.retrieveToken;
}
public void setRetrieveToken(boolean retrieveToken) {
this.retrieveToken = retrieveToken;
}
}

View file

@ -2,7 +2,6 @@ package org.keycloak.representations.idm;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -23,8 +22,8 @@ public class OAuthClientRepresentation {
protected Boolean directGrantsOnly;
protected Boolean fullScopeAllowed;
protected Boolean frontchannelLogout;
protected List<String> allowedIdentityProviders;
protected List<ClientProtocolMappingRepresentation> protocolMappers;
private List<ClientIdentityProviderMappingRepresentation> identityProviders;
public String getId() {
@ -139,12 +138,12 @@ public class OAuthClientRepresentation {
this.frontchannelLogout = frontchannelLogout;
}
public List<String> getAllowedIdentityProviders() {
return this.allowedIdentityProviders;
public List<ClientIdentityProviderMappingRepresentation> getIdentityProviders() {
return this.identityProviders;
}
public void setAllowedIdentityProviders(List<String> allowedIdentityProviders) {
this.allowedIdentityProviders = allowedIdentityProviders;
public void setIdentityProviders(List<ClientIdentityProviderMappingRepresentation> identityProviders) {
this.identityProviders = identityProviders;
}
public List<ClientProtocolMappingRepresentation> getProtocolMappers() {

View file

@ -962,7 +962,7 @@ Authorization: Bearer {keycloak_access_token}]]></programlisting>
</section>
<section>
<title>Enabling/Disabling Identity Providers for Service Providers</title>
<title>Configuring Identity Providers for Applications</title>
<para>
By default, all identity providers enabled for a particular realm are also available to all its applications.
However, you can also specify which identity providers should be available when
@ -993,6 +993,10 @@ Authorization: Bearer {keycloak_access_token}]]></programlisting>
</para>
</listitem>
</orderedlist>
<para>
From this page you can also configure if an application is allowed to retrieve tokens from an specific identity provider. For that,
just click on the <emphasis>Can Retrieve Token</emphasis> button.
</para>
</section>
<section>

View file

@ -43,75 +43,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,

View file

@ -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);
});

View file

@ -7,17 +7,24 @@
<li><a href="#/realms/{{realm.realm}}/applications/{{application.id}}">{{application.name}}</a></li>
<li class="active">Identity Provider</li>
</ol>
<h2 data-ng-hide="create"><span>{{application.name}}</span> Identity Provider Settings<span tooltip-placement="right" tooltip="Select which identity providers can be used for this application. By default, all identity providers are enabled. Select one or more to restrict." class="fa fa-info-circle"></span></h2>
<h2 data-ng-hide="create"><span>{{application.name}}</span> Identity Provider Settings</h2>
<form class="form-horizontal" name="identityProviderForm" novalidate>
<div class="form-group" ng-repeat="identityProvider in identityProviders">
<legend><span class="text">{{identityProvider.name}}</span></legend>
<label class="col-sm-2 control-label" for="{{identityProvider.id}}">Enable</label>
<legend><span class="text">{{identityProvider.identityProvider.name}}</span></legend>
<label class="col-sm-2 control-label" for="{{identityProvider.identityProvider.id}}">Enable&nbsp;<span tooltip-placement="right" tooltip="If disabled, users can not login to the application using this identity provider." class="fa fa-info-circle"></span></label>
<div class="col-sm-4">
<input ng-model="application.allowedIdentityProviders[$index]" name="identityProvider.id" id="identityProvider.id" value="identityProvider.id" onoffswitchmodel />
<input ng-model="application.identityProviders[$index].id" name="identityProvider.identityProvider.id" id="identityProvider.identityProvider.id" value="identityProvider.identityProvider.id" onoffswitchmodel />
</div>
<div data-ng-show="application.identityProviders[$index].id">
<label class="col-sm-2 control-label" for="{{identityProvider.identityProvider.id}}retrieveToken">Can Retrieve Token&nbsp;<span tooltip-placement="right" tooltip="If disabled, the application can not retrieve tokens from the identity provider." class="fa fa-info-circle"></span></label>
<div class="col-sm-4">
<input ng-model="application.identityProviders[$index].retrieveToken" name="identityProvider.identityProvider.id + 'retrieveToken'" id="identityProvider.identityProvider.id + 'retrieveToken'" value="true" onoffswitchmodel />
</div>
</div>
</div>
<div class="pull-right form-actions">
<button kc-save>Save</button>
<button kc-reset data-ng-show="changed">Clear changes</button>
<button kc-save data-ng-show="changed">Save</button>
</div>
</form>
</div>

View file

@ -10,14 +10,21 @@
<h2 data-ng-hide="create"><span>{{oauth.name}}</span> Identity Provider Settings</h2>
<form class="form-horizontal" name="identityProviderForm" novalidate>
<div class="form-group" ng-repeat="identityProvider in identityProviders">
<legend><span class="text">{{identityProvider.name}}</span></legend>
<label class="col-sm-2 control-label" for="{{identityProvider.id}}">Enable</label>
<legend><span class="text">{{identityProvider.identityProvider.name}}</span></legend>
<label class="col-sm-2 control-label" for="{{identityProvider.identityProvider.id}}">Enable&nbsp;<span tooltip-placement="right" tooltip="If disabled, users can not login to the application using this identity provider." class="fa fa-info-circle"></span></label>
<div class="col-sm-4">
<input ng-model="oauth.allowedIdentityProviders[$index]" name="identityProvider.id" id="identityProvider.id" value="identityProvider.id" kc-onoffswitch-model />
<input ng-model="oauth.identityProviders[$index].id" name="identityProvider.identityProvider.id" id="identityProvider.identityProvider.id" value="identityProvider.identityProvider.id" onoffswitchmodel />
</div>
<div data-ng-show="oauth.identityProviders[$index].id">
<label class="col-sm-2 control-label" for="{{identityProvider.identityProvider.id}}retrieveToken">Can Retrieve Token&nbsp;<span tooltip-placement="right" tooltip="If disabled, the application can not retrieve tokens from the identity provider." class="fa fa-info-circle"></span></label>
<div class="col-sm-4">
<input ng-model="oauth.identityProviders[$index].retrieveToken" name="identityProvider.identityProvider.id + 'retrieveToken'" id="identityProvider.identityProvider.id + 'retrieveToken'" value="true" onoffswitchmodel />
</div>
</div>
</div>
<div class="pull-right form-actions">
<button kc-save>Save</button>
<button kc-reset data-ng-show="changed">Clear changes</button>
<button kc-save data-ng-show="changed">Save</button>
</div>
</form>
</div>

View file

@ -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;
}
}

View file

@ -98,11 +98,10 @@ public interface ClientModel {
void setNotBefore(int notBefore);
void updateAllowedIdentityProviders(List<String> identityProviders);
List<String> getAllowedIdentityProviders();
void updateAllowedIdentityProviders(List<ClientIdentityProviderMappingModel> identityProviders);
List<ClientIdentityProviderMappingModel> getIdentityProviders();
boolean hasIdentityProvider(String providerId);
boolean isAllowedRetrieveTokenFromIdentityProvider(String providerId);
Set<ProtocolMapperModel> getProtocolMappers();
void addProtocolMappers(Set<String> mapperIds);

View file

@ -29,7 +29,7 @@ public class ClientEntity extends AbstractIdentifiableEntity {
private List<String> webOrigins = new ArrayList<String>();
private List<String> redirectUris = new ArrayList<String>();
private List<String> scopeIds = new ArrayList<String>();
private List<String> allowedIdentityProviders = new ArrayList<String>();
private List<ClientIdentityProviderMappingEntity> identityProviders = new ArrayList<ClientIdentityProviderMappingEntity>();
private Set<String> protocolMappers = new HashSet<String>();
public String getName() {
@ -144,12 +144,12 @@ public class ClientEntity extends AbstractIdentifiableEntity {
this.frontchannelLogout = frontchannelLogout;
}
public List<String> getAllowedIdentityProviders() {
return this.allowedIdentityProviders;
public List<ClientIdentityProviderMappingEntity> getIdentityProviders() {
return this.identityProviders;
}
public void setAllowedIdentityProviders(List<String> allowedIdentityProviders) {
this.allowedIdentityProviders = allowedIdentityProviders;
public void setIdentityProviders(List<ClientIdentityProviderMappingEntity> identityProviders) {
this.identityProviders = identityProviders;
}
public Set<String> getProtocolMappers() {

View file

@ -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;
}
}

View file

@ -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<String, Integer>(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<ClientIdentityProviderMappingRepresentation> toRepresentation(List<ClientIdentityProviderMappingModel> identityProviders) {
ArrayList<ClientIdentityProviderMappingRepresentation> representations = new ArrayList<ClientIdentityProviderMappingRepresentation>();
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()) {

View file

@ -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<String> allowedIdentityProviders = resourceRep.getAllowedIdentityProviders();
if (allowedIdentityProviders == null || allowedIdentityProviders.isEmpty()) {
allowedIdentityProviders = new ArrayList<String>();
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<String> allowedIdentityProviders = rep.getAllowedIdentityProviders();
if (allowedIdentityProviders == null || allowedIdentityProviders.isEmpty()) {
allowedIdentityProviders = new ArrayList<String>();
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<String> ids = new HashSet<String>();
@ -868,4 +846,48 @@ public class RepresentationToModel {
model.setConfig(rep.getConfig());
return model;
}
private static List<ClientIdentityProviderMappingModel> toModel(List<ClientIdentityProviderMappingRepresentation> repIdentityProviders, RealmModel realm) {
List<ClientIdentityProviderMappingModel> allowedIdentityProviders = new ArrayList<ClientIdentityProviderMappingModel>();
if (repIdentityProviders == null || repIdentityProviders.isEmpty()) {
allowedIdentityProviders = new ArrayList<ClientIdentityProviderMappingModel>();
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<ClientIdentityProviderMappingRepresentation> identityProviders, ClientModel resource) {
if (identityProviders != null) {
List<ClientIdentityProviderMappingModel> allowedIdentityProviders = new ArrayList<ClientIdentityProviderMappingModel>();
for (ClientIdentityProviderMappingRepresentation mappingRepresentation : identityProviders) {
ClientIdentityProviderMappingModel identityProviderMapping = new ClientIdentityProviderMappingModel();
identityProviderMapping.setIdentityProvider(mappingRepresentation.getId());
identityProviderMapping.setRetrieveToken(mappingRepresentation.isRetrieveToken());
allowedIdentityProviders.add(identityProviderMapping);
}
resource.updateAllowedIdentityProviders(allowedIdentityProviders);
}
}
}

View file

@ -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<String> identityProviders) {
public void updateAllowedIdentityProviders(List<ClientIdentityProviderMappingModel> identityProviders) {
getDelegateForUpdate();
updatedClient.updateAllowedIdentityProviders(identityProviders);
}
@Override
public List<String> getAllowedIdentityProviders() {
if (updatedClient != null) return updatedClient.getAllowedIdentityProviders();
return cachedClient.getAllowedIdentityProviders();
public List<ClientIdentityProviderMappingModel> 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<ProtocolMapperModel> getProtocolMappers() {
if (updatedClient != null) return updatedClient.getProtocolMappers();

View file

@ -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<String> scope = new HashSet<String>();
protected Set<String> webOrigins = new HashSet<String>();
private List<String> allowedIdentityProviders = new ArrayList<String>();
private List<ClientIdentityProviderMappingModel> identityProviders = new ArrayList<ClientIdentityProviderMappingModel>();
private Set<ProtocolMapperModel> protocolClaimMappings = new HashSet<ProtocolMapperModel>();
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<String> getAllowedIdentityProviders() {
return this.allowedIdentityProviders;
public List<ClientIdentityProviderMappingModel> 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<ProtocolMapperModel> getProtocolClaimMappings() {
return protocolClaimMappings;
}
public boolean isAllowedRetrieveTokenFromIdentityProvider(String providerId) {
for (ClientIdentityProviderMappingModel model : getIdentityProviders()) {
if (model.getIdentityProvider().equals(providerId)) {
return model.isRetrieveToken();
}
}
return false;
}
}

View file

@ -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<String> identityProviders) {
Collection<IdentityProviderEntity> entities = entity.getAllowedIdentityProviders();
public void updateAllowedIdentityProviders(List<ClientIdentityProviderMappingModel> identityProviders) {
Collection<ClientIdentityProviderMappingEntity> entities = entity.getIdentityProviders();
Set<String> already = new HashSet<String>();
List<IdentityProviderEntity> remove = new ArrayList<IdentityProviderEntity>();
for (IdentityProviderEntity rel : entities) {
if (!contains(rel.getId(), identityProviders.toArray(new String[identityProviders.size()]))) {
remove.add(rel);
List<ClientIdentityProviderMappingEntity> remove = new ArrayList<ClientIdentityProviderMappingEntity>();
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<IdentityProviderEntity> 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<IdentityProviderEntity> 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<String> getAllowedIdentityProviders() {
Collection<IdentityProviderEntity> entities = entity.getAllowedIdentityProviders();
List<String> providers = new ArrayList<String>();
public List<ClientIdentityProviderMappingModel> getIdentityProviders() {
List<ClientIdentityProviderMappingModel> models = new ArrayList<ClientIdentityProviderMappingModel>();
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<String> 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) {

View file

@ -73,9 +73,8 @@ public abstract class ClientEntity {
@CollectionTable(name="CLIENT_ATTRIBUTES", joinColumns={ @JoinColumn(name="CLIENT_ID") })
protected Map<String, String> attributes = new HashMap<String, String>();
@OneToMany(fetch = FetchType.LAZY)
@JoinTable(name="CLIENT_ALLOWED_IDENTITY_PROVIDER", joinColumns = { @JoinColumn(name="CLIENT_ID")}, inverseJoinColumns = { @JoinColumn(name="INTERNAL_ID")})
Collection<IdentityProviderEntity> allowedIdentityProviders = new ArrayList<IdentityProviderEntity>();
@OneToMany(fetch = FetchType.LAZY, mappedBy = "client", cascade = CascadeType.REMOVE)
Collection<ClientIdentityProviderMappingEntity> identityProviders = new ArrayList<ClientIdentityProviderMappingEntity>();
@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<IdentityProviderEntity> getAllowedIdentityProviders() {
return this.allowedIdentityProviders;
public Collection<ClientIdentityProviderMappingEntity> getIdentityProviders() {
return this.identityProviders;
}
public void setAllowedIdentityProviders(Collection<IdentityProviderEntity> allowedIdentityProviders) {
this.allowedIdentityProviders = allowedIdentityProviders;
public void setIdentityProviders(Collection<ClientIdentityProviderMappingEntity> identityProviders) {
this.identityProviders = identityProviders;
}
public Collection<ProtocolMapperEntity> getProtocolMappers() {

View file

@ -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;
}
}

View file

@ -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<T extends MongoIdentifiableEntity> extends A
}
@Override
public void updateAllowedIdentityProviders(List<String> identityProviders) {
List<String> providerIds = new ArrayList<String>();
for (String providerId : identityProviders) {
providerIds.add(providerId);
public void updateAllowedIdentityProviders(List<ClientIdentityProviderMappingModel> identityProviders) {
List<ClientIdentityProviderMappingEntity> 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<String> getAllowedIdentityProviders() {
return getMongoEntityAsClient().getAllowedIdentityProviders();
public List<ClientIdentityProviderMappingModel> getIdentityProviders() {
List<ClientIdentityProviderMappingModel> models = new ArrayList<ClientIdentityProviderMappingModel>();
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<String> 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;
}
}

View file

@ -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()))

View file

@ -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<String> allowedIdentityProviders = clientModel.getAllowedIdentityProviders();
List<ClientIdentityProviderMappingModel> identityProviders = clientModel.getIdentityProviders();
allowedIdentityProviders.remove(identityProvider.getId());
for (ClientIdentityProviderMappingModel providerMappingModel : new ArrayList<ClientIdentityProviderMappingModel>(identityProviders)) {
if (providerMappingModel.getIdentityProvider().equals(identityProvider.getId())) {
identityProviders.remove(providerMappingModel);
}
}
clientModel.updateAllowedIdentityProviders(allowedIdentityProviders);
clientModel.updateAllowedIdentityProviders(identityProviders);
}
}

View file

@ -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<String> allowedIdentityProviders = clientModel.getAllowedIdentityProviders();
List<ClientIdentityProviderMappingModel> allowedIdentityProviders = clientModel.getIdentityProviders();
ClientIdentityProviderMappingModel providerMappingModel = new ClientIdentityProviderMappingModel();
allowedIdentityProviders.add(identityProvider.getId());
providerMappingModel.setIdentityProvider(identityProvider.getId());
allowedIdentityProviders.add(providerMappingModel);
clientModel.updateAllowedIdentityProviders(allowedIdentityProviders);
}

View file

@ -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<String> allowedIdentityProviders = applicationModel.getAllowedIdentityProviders();
List<ClientIdentityProviderMappingModel> 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();

View file

@ -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<ClientIdentityProviderMappingModel> 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<IdentityProviderModel> identityProviders) {
assertFalse(identityProviders.isEmpty());

View file

@ -197,8 +197,11 @@
"/test-app/*"
],
"webOrigins": [],
"allowedIdentityProviders": [
"kc-oidc-idp"
"identityProviders": [
{
"id": "kc-oidc-idp",
"retrieveToken": false
}
]
}
],