[KEYCLOAK-992] - Token retrieval from brokered idps.
This commit is contained in:
parent
9b6254399d
commit
4b1ba704ec
64 changed files with 1193 additions and 170 deletions
|
@ -30,6 +30,7 @@ public class FederatedIdentity {
|
|||
private String firstName;
|
||||
private String lastName;
|
||||
private String email;
|
||||
private String token;
|
||||
|
||||
public FederatedIdentity(String id) {
|
||||
if (id == null) {
|
||||
|
@ -84,4 +85,11 @@ public class FederatedIdentity {
|
|||
}
|
||||
|
||||
|
||||
public void setToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
return this.token;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,9 +17,12 @@
|
|||
*/
|
||||
package org.keycloak.broker.provider;
|
||||
|
||||
import org.keycloak.models.FederatedIdentityModel;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.provider.Provider;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
/**
|
||||
* @author Pedro Igor
|
||||
*/
|
||||
|
@ -64,4 +67,6 @@ public interface IdentityProvider<C extends IdentityProviderModel> extends Provi
|
|||
* @return
|
||||
*/
|
||||
AuthenticationResponse handleResponse(AuthenticationRequest request);
|
||||
|
||||
Response retrieveToken(FederatedIdentityModel identity);
|
||||
}
|
||||
|
|
|
@ -25,7 +25,9 @@ import org.keycloak.broker.provider.AbstractIdentityProvider;
|
|||
import org.keycloak.broker.provider.AuthenticationRequest;
|
||||
import org.keycloak.broker.provider.AuthenticationResponse;
|
||||
import org.keycloak.broker.provider.FederatedIdentity;
|
||||
import org.keycloak.models.FederatedIdentityModel;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import java.io.IOException;
|
||||
|
@ -100,7 +102,13 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
|
|||
.param(OAUTH2_PARAMETER_REDIRECT_URI, request.getRedirectUri())
|
||||
.param(OAUTH2_PARAMETER_GRANT_TYPE, OAUTH2_GRANT_TYPE_AUTHORIZATION_CODE).asString();
|
||||
|
||||
return doHandleResponse(response);
|
||||
FederatedIdentity federatedIdentity = getFederatedIdentity(response);
|
||||
|
||||
if (getConfig().isStoreToken()) {
|
||||
federatedIdentity.setToken(response);
|
||||
}
|
||||
|
||||
return AuthenticationResponse.end(federatedIdentity);
|
||||
}
|
||||
|
||||
throw new RuntimeException("No authorization code from identity provider.");
|
||||
|
@ -109,24 +117,23 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response retrieveToken(FederatedIdentityModel identity) {
|
||||
return Response.ok(identity.getToken()).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public C getConfig() {
|
||||
return super.getConfig();
|
||||
}
|
||||
|
||||
protected AuthenticationResponse doHandleResponse(String response) throws IOException {
|
||||
String token = extractTokenFromResponse(response, OAUTH2_PARAMETER_ACCESS_TOKEN);
|
||||
|
||||
if (token == null) {
|
||||
throw new RuntimeException("No access token from server.");
|
||||
}
|
||||
|
||||
return AuthenticationResponse.end(getFederatedIdentity(token));
|
||||
}
|
||||
|
||||
protected String extractTokenFromResponse(String response, String tokenName) throws IOException {
|
||||
protected String extractTokenFromResponse(String response, String tokenName) {
|
||||
if (response.startsWith("{")) {
|
||||
return mapper.readTree(response).get(tokenName).getTextValue();
|
||||
try {
|
||||
return mapper.readTree(response).get(tokenName).getTextValue();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Could not extract token [" + tokenName + "] from response [" + response + "].", e);
|
||||
}
|
||||
} else {
|
||||
Matcher matcher = Pattern.compile(tokenName + "=([^&]+)").matcher(response);
|
||||
|
||||
|
@ -138,9 +145,21 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
|
|||
return null;
|
||||
}
|
||||
|
||||
protected FederatedIdentity getFederatedIdentity(String accessToken) {
|
||||
throw new RuntimeException("Not implemented.");
|
||||
};
|
||||
protected FederatedIdentity getFederatedIdentity(String response) {
|
||||
String accessToken = extractTokenFromResponse(response, OAUTH2_PARAMETER_ACCESS_TOKEN);
|
||||
|
||||
if (accessToken == null) {
|
||||
throw new RuntimeException("No access token from server.");
|
||||
}
|
||||
|
||||
return doGetFederatedIdentity(accessToken);
|
||||
}
|
||||
|
||||
protected FederatedIdentity doGetFederatedIdentity(String accessToken) {
|
||||
return null;
|
||||
}
|
||||
|
||||
;
|
||||
|
||||
protected UriBuilder createAuthorizationUrl(AuthenticationRequest request) {
|
||||
return UriBuilder.fromPath(getConfig().getAuthorizationUrl())
|
||||
|
|
|
@ -20,7 +20,6 @@ package org.keycloak.broker.oidc;
|
|||
import org.codehaus.jackson.JsonNode;
|
||||
import org.keycloak.broker.oidc.util.SimpleHttp;
|
||||
import org.keycloak.broker.provider.AuthenticationRequest;
|
||||
import org.keycloak.broker.provider.AuthenticationResponse;
|
||||
import org.keycloak.broker.provider.FederatedIdentity;
|
||||
import org.keycloak.jose.jws.JWSInput;
|
||||
|
||||
|
@ -59,7 +58,7 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
|
|||
}
|
||||
|
||||
@Override
|
||||
protected AuthenticationResponse doHandleResponse(String response) throws IOException {
|
||||
protected FederatedIdentity getFederatedIdentity(String response) {
|
||||
String accessToken = extractTokenFromResponse(response, OAUTH2_PARAMETER_ACCESS_TOKEN);
|
||||
|
||||
if (accessToken == null) {
|
||||
|
@ -96,7 +95,11 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
|
|||
|
||||
identity.setUsername(preferredUsername);
|
||||
|
||||
return AuthenticationResponse.end(identity);
|
||||
if (getConfig().isStoreToken()) {
|
||||
identity.setToken(response);
|
||||
}
|
||||
|
||||
return identity;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Could not fetch attributes from userinfo endpoint.", e);
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import org.keycloak.broker.provider.AbstractIdentityProvider;
|
|||
import org.keycloak.broker.provider.AuthenticationRequest;
|
||||
import org.keycloak.broker.provider.AuthenticationResponse;
|
||||
import org.keycloak.broker.provider.FederatedIdentity;
|
||||
import org.keycloak.models.FederatedIdentityModel;
|
||||
import org.keycloak.protocol.saml.SAML2AuthnRequestBuilder;
|
||||
import org.keycloak.protocol.saml.SAML2NameIDPolicyBuilder;
|
||||
import org.picketlink.common.constants.JBossSAMLConstants;
|
||||
|
@ -54,6 +55,7 @@ import org.w3c.dom.Element;
|
|||
import org.w3c.dom.Node;
|
||||
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import javax.xml.namespace.QName;
|
||||
|
@ -140,8 +142,14 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
|
|||
|
||||
@Override
|
||||
public AuthenticationResponse handleResponse(AuthenticationRequest request) {
|
||||
String samlResponse = getRequestParameter(request, SAML_RESPONSE_PARAMETER);
|
||||
|
||||
if (samlResponse == null) {
|
||||
throw new RuntimeException("No response from SAML identity provider.");
|
||||
}
|
||||
|
||||
try {
|
||||
AssertionType assertion = getAssertion(request);
|
||||
AssertionType assertion = getAssertion(samlResponse, request);
|
||||
SubjectType subject = assertion.getSubject();
|
||||
STSubType subType = subject.getSubType();
|
||||
NameIDType subjectNameID = (NameIDType) subType.getBaseID();
|
||||
|
@ -153,19 +161,22 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
|
|||
identity.setEmail(subjectNameID.getValue());
|
||||
}
|
||||
|
||||
if (getConfig().isStoreToken()) {
|
||||
identity.setToken(samlResponse);
|
||||
}
|
||||
|
||||
return AuthenticationResponse.end(identity);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Could not process response from SAML identity provider.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private AssertionType getAssertion(AuthenticationRequest request) throws Exception {
|
||||
String samlResponse = getRequestParameter(request, SAML_RESPONSE_PARAMETER);
|
||||
|
||||
if (samlResponse == null) {
|
||||
throw new RuntimeException("No response from SAML identity provider.");
|
||||
}
|
||||
@Override
|
||||
public Response retrieveToken(FederatedIdentityModel identity) {
|
||||
return Response.ok(identity.getToken()).build();
|
||||
}
|
||||
|
||||
private AssertionType getAssertion(String samlResponse, AuthenticationRequest request) throws Exception {
|
||||
SAML2Request saml2Request = new SAML2Request();
|
||||
ResponseType responseType;
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
<column name="REALM_ID" type="VARCHAR(255)"/>
|
||||
<column name="FEDERATED_USER_ID" type="VARCHAR(255)"/>
|
||||
<column name="FEDERATED_USERNAME" type="VARCHAR(255)"/>
|
||||
<column name="TOKEN" type="TEXT"/>
|
||||
<column name="USER_ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
|
@ -21,6 +22,7 @@
|
|||
<column name="PROVIDER_NAME" type="VARCHAR(255)"/>
|
||||
<column name="PROVIDER_ID" type="VARCHAR(255)"/>
|
||||
<column name="UPDATE_PROFILE_FIRST_LOGIN" type="BOOLEAN(1)"/>
|
||||
<column name="STORE_TOKEN" type="BOOLEAN(1)"/>
|
||||
<column name="REALM_ID" type="VARCHAR(36)"/>
|
||||
</createTable>
|
||||
<createTable tableName="IDENTITY_PROVIDER_CONFIG">
|
||||
|
@ -32,6 +34,14 @@
|
|||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</createTable>
|
||||
<createTable tableName="CLIENT_ALLOWED_IDENTITY_PROVIDER">
|
||||
<column name="CLIENT_ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="INTERNAL_ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</createTable>
|
||||
<addColumn tableName="CLIENT">
|
||||
<column name="FRONTCHANNEL_LOGOUT" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||
</addColumn>
|
||||
|
@ -41,5 +51,8 @@
|
|||
<addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="IDENTITY_PROVIDER" constraintName="FK2B4EBC52AE5C3B34" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="REALM"/>
|
||||
<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="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"/>
|
||||
<addUniqueConstraint columnNames="PROVIDER_NONIMAL_ID" constraintName="UK_2DAELWNIBJI49AVXSRTUF6XJ33" tableName="IDENTITY_PROVIDER"/>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
|
|
|
@ -28,6 +28,7 @@ public class ApplicationRepresentation {
|
|||
protected Boolean fullScopeAllowed;
|
||||
protected Integer nodeReRegistrationTimeout;
|
||||
protected Map<String, Integer> registeredNodes;
|
||||
protected List<String> allowedIdentityProviders;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
|
@ -188,4 +189,12 @@ public class ApplicationRepresentation {
|
|||
public void setFrontchannelLogout(Boolean frontchannelLogout) {
|
||||
this.frontchannelLogout = frontchannelLogout;
|
||||
}
|
||||
|
||||
public List<String> getAllowedIdentityProviders() {
|
||||
return this.allowedIdentityProviders;
|
||||
}
|
||||
|
||||
public void setAllowedIdentityProviders(List<String> allowedIdentityProviders) {
|
||||
this.allowedIdentityProviders = allowedIdentityProviders;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ public class IdentityProviderRepresentation {
|
|||
protected String name;
|
||||
protected boolean enabled = true;
|
||||
protected boolean updateProfileFirstLogin = true;
|
||||
protected boolean storeToken;
|
||||
protected String groupName;
|
||||
protected Map<String, String> config = new HashMap<String, String>();
|
||||
|
||||
|
@ -65,14 +66,6 @@ public class IdentityProviderRepresentation {
|
|||
this.config = config;
|
||||
}
|
||||
|
||||
public String getGroupName() {
|
||||
return this.groupName;
|
||||
}
|
||||
|
||||
public void setGroupName(String groupName) {
|
||||
this.groupName = groupName;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return this.enabled;
|
||||
}
|
||||
|
@ -88,4 +81,20 @@ public class IdentityProviderRepresentation {
|
|||
public void setUpdateProfileFirstLogin(boolean updateProfileFirstLogin) {
|
||||
this.updateProfileFirstLogin = updateProfileFirstLogin;
|
||||
}
|
||||
|
||||
public boolean isStoreToken() {
|
||||
return this.storeToken;
|
||||
}
|
||||
|
||||
public void setStoreToken(boolean storeToken) {
|
||||
this.storeToken = storeToken;
|
||||
}
|
||||
|
||||
public String getGroupName() {
|
||||
return this.groupName;
|
||||
}
|
||||
|
||||
public void setGroupName(String groupName) {
|
||||
this.groupName = groupName;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ public class OAuthClientRepresentation {
|
|||
protected Boolean directGrantsOnly;
|
||||
protected Boolean fullScopeAllowed;
|
||||
protected Boolean frontchannelLogout;
|
||||
protected List<String> allowedIdentityProviders;
|
||||
|
||||
|
||||
public String getId() {
|
||||
|
@ -135,4 +136,12 @@ public class OAuthClientRepresentation {
|
|||
public void setFrontchannelLogout(Boolean frontchannelLogout) {
|
||||
this.frontchannelLogout = frontchannelLogout;
|
||||
}
|
||||
|
||||
public List<String> getAllowedIdentityProviders() {
|
||||
return this.allowedIdentityProviders;
|
||||
}
|
||||
|
||||
public void setAllowedIdentityProviders(List<String> allowedIdentityProviders) {
|
||||
this.allowedIdentityProviders = allowedIdentityProviders;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -464,6 +464,18 @@ module.config([ '$routeProvider', function($routeProvider) {
|
|||
},
|
||||
controller : 'ApplicationCredentialsCtrl'
|
||||
})
|
||||
.when('/realms/:realm/applications/:application/identity-provider', {
|
||||
templateUrl : 'partials/application-identity-provider.html',
|
||||
resolve : {
|
||||
realm : function(RealmLoader) {
|
||||
return RealmLoader();
|
||||
},
|
||||
application : function(ApplicationLoader) {
|
||||
return ApplicationLoader();
|
||||
}
|
||||
},
|
||||
controller : 'ApplicationIdentityProviderCtrl'
|
||||
})
|
||||
.when('/realms/:realm/applications/:application/clustering', {
|
||||
templateUrl : 'partials/application-clustering.html',
|
||||
resolve : {
|
||||
|
@ -750,6 +762,18 @@ module.config([ '$routeProvider', function($routeProvider) {
|
|||
},
|
||||
controller : 'OAuthClientDetailCtrl'
|
||||
})
|
||||
.when('/realms/:realm/oauth-clients/:oauth/identity-provider', {
|
||||
templateUrl : 'partials/oauth-client-identity-provider.html',
|
||||
resolve : {
|
||||
realm : function(RealmLoader) {
|
||||
return RealmLoader();
|
||||
},
|
||||
oauth : function(OAuthClientLoader) {
|
||||
return OAuthClientLoader();
|
||||
}
|
||||
},
|
||||
controller : 'OAuthClientIdentityProviderCtrl'
|
||||
})
|
||||
.when('/realms/:realm/oauth-clients', {
|
||||
templateUrl : 'partials/oauth-client-list.html',
|
||||
resolve : {
|
||||
|
@ -1050,6 +1074,48 @@ module.directive('onoffswitch', function() {
|
|||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Directive for presenting an ON-OFF switch for checkbox.
|
||||
* This directive provides some additional capabilities to the default onoffswitch such as:
|
||||
*
|
||||
* - Dynamic values for id and name attributes. Useful if you need to use this directive inside a ng-repeat
|
||||
* - Specific scope to specify the value. Instead of just true or false.
|
||||
*
|
||||
* Usage: <input ng-model="mmm" name="nnn" id="iii" kc-onoffswitch-model [on-text="ooo" off-text="fff"] />
|
||||
*/
|
||||
module.directive('kc-onoffswitch-model', function() {
|
||||
return {
|
||||
restrict: "EA",
|
||||
replace: true,
|
||||
scope: {
|
||||
name: '=',
|
||||
id: '=',
|
||||
value: '=',
|
||||
ngModel: '=',
|
||||
ngDisabled: '=',
|
||||
kcOnText: '@onText',
|
||||
kcOffText: '@offText'
|
||||
},
|
||||
// TODO - The same code acts differently when put into the templateURL. Find why and move the code there.
|
||||
//templateUrl: "templates/kc-switch.html",
|
||||
template: "<span><div class='onoffswitch' tabindex='0'><input type='checkbox' ng-true-value='{{value}}' ng-model='ngModel' ng-disabled='ngDisabled' class='onoffswitch-checkbox' name='kc{{name}}' id='kc{{id}}'><label for='kc{{id}}' class='onoffswitch-label'><span class='onoffswitch-inner'><span class='onoffswitch-active'>{{kcOnText}}</span><span class='onoffswitch-inactive'>{{kcOffText}}</span></span><span class='onoffswitch-switch'></span></label></div></span>",
|
||||
compile: function(element, attrs) {
|
||||
|
||||
if (!attrs.onText) { attrs.onText = "ON"; }
|
||||
if (!attrs.offText) { attrs.offText = "OFF"; }
|
||||
|
||||
element.bind('keydown click', function(e){
|
||||
var code = e.keyCode || e.which;
|
||||
if (code === 32 || code === 13) {
|
||||
e.stopImmediatePropagation();
|
||||
e.preventDefault();
|
||||
$(e.target).find('input').click();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
module.directive('kcInput', function() {
|
||||
var d = {
|
||||
scope : true,
|
||||
|
|
|
@ -43,6 +43,77 @@ module.controller('ApplicationCredentialsCtrl', function($scope, $location, real
|
|||
});
|
||||
});
|
||||
|
||||
module.controller('ApplicationIdentityProviderCtrl', function($scope, $location, realm, application, Application, $location, Notifications) {
|
||||
$scope.realm = realm;
|
||||
$scope.application = angular.copy(application);
|
||||
|
||||
$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;
|
||||
|
||||
for (i = 0; i < $scope.application.allowedIdentityProviders.length; i++) {
|
||||
var appProvider = $scope.application.allowedIdentityProviders[i];
|
||||
|
||||
if (appProvider == identityProvider.id) {
|
||||
$scope.identityProviders[i] = identityProvider;
|
||||
match = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!match) {
|
||||
var length = $scope.identityProviders.length;
|
||||
|
||||
length = length + $scope.application.allowedIdentityProviders.length;
|
||||
|
||||
$scope.identityProviders[length] = identityProvider;
|
||||
}
|
||||
}
|
||||
|
||||
$scope.identityProviders = $scope.identityProviders.filter(function(n){ return n != undefined });
|
||||
|
||||
$scope.save = function() {
|
||||
var selectedProviders = [];
|
||||
|
||||
for (i = 0; i < $scope.application.allowedIdentityProviders.length; i++) {
|
||||
var appProvider = $scope.application.allowedIdentityProviders[i];
|
||||
|
||||
if (appProvider) {
|
||||
selectedProviders[selectedProviders.length] = appProvider;
|
||||
}
|
||||
}
|
||||
|
||||
$scope.allowedIdentityProviders = $scope.application.allowedIdentityProviders;
|
||||
$scope.application.allowedIdentityProviders = 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");
|
||||
Notifications.success("Your changes have been saved to the application.");
|
||||
});
|
||||
};
|
||||
|
||||
$scope.reset = function() {
|
||||
$scope.application = angular.copy(application);
|
||||
$scope.changed = false;
|
||||
};
|
||||
|
||||
$scope.$watch(function() {
|
||||
return $location.path();
|
||||
}, function() {
|
||||
$scope.path = $location.path().substring(1).split("/");
|
||||
});
|
||||
});
|
||||
|
||||
module.controller('ApplicationSamlKeyCtrl', function($scope, $location, $http, $upload, realm, application,
|
||||
ApplicationCertificate, ApplicationCertificateGenerate,
|
||||
ApplicationCertificateDownload, Notifications) {
|
||||
|
|
|
@ -324,4 +324,74 @@ module.controller('OAuthClientRevocationCtrl', function($scope, realm, oauth, OA
|
|||
}
|
||||
});
|
||||
|
||||
module.controller('OAuthClientIdentityProviderCtrl', function($scope, realm, oauth, OAuthClient, $location, Notifications) {
|
||||
$scope.realm = realm;
|
||||
$scope.oauth = angular.copy(oauth);
|
||||
|
||||
$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;
|
||||
|
||||
for (i = 0; i < $scope.oauth.allowedIdentityProviders.length; i++) {
|
||||
var appProvider = $scope.oauth.allowedIdentityProviders[i];
|
||||
|
||||
if (appProvider == identityProvider.id) {
|
||||
$scope.identityProviders[i] = identityProvider;
|
||||
match = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!match) {
|
||||
var length = $scope.identityProviders.length;
|
||||
|
||||
length = length + $scope.oauth.allowedIdentityProviders.length;
|
||||
|
||||
$scope.identityProviders[length] = identityProvider;
|
||||
}
|
||||
}
|
||||
|
||||
$scope.identityProviders = $scope.identityProviders.filter(function(n){ return n != undefined });
|
||||
|
||||
$scope.save = function() {
|
||||
var selectedProviders = [];
|
||||
|
||||
for (i = 0; i < $scope.oauth.allowedIdentityProviders.length; i++) {
|
||||
var appProvider = $scope.oauth.allowedIdentityProviders[i];
|
||||
|
||||
if (appProvider) {
|
||||
selectedProviders[selectedProviders.length] = appProvider;
|
||||
}
|
||||
}
|
||||
|
||||
$scope.allowedIdentityProviders = $scope.oauth.allowedIdentityProviders;
|
||||
$scope.oauth.allowedIdentityProviders = 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");
|
||||
Notifications.success("Your changes have been saved to the application.");
|
||||
});
|
||||
};
|
||||
|
||||
$scope.reset = function() {
|
||||
$scope.oauth = angular.copy(oauth);
|
||||
$scope.changed = false;
|
||||
};
|
||||
|
||||
$scope.$watch(function() {
|
||||
return $location.path();
|
||||
}, function() {
|
||||
$scope.path = $location.path().substring(1).split("/");
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
<div class="bs-sidebar col-md-3 clearfix" data-ng-include data-src="'partials/realm-menu.html'"></div>
|
||||
<div id="content-area" class="col-md-9" role="main">
|
||||
<kc-navigation-application></kc-navigation-application>
|
||||
<div id="content">
|
||||
<ol class="breadcrumb" data-ng-hide="create">
|
||||
<li><a href="#/realms/{{realm.realm}}/applications">Applications</a></li>
|
||||
<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</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>
|
||||
<div class="col-sm-4">
|
||||
<input ng-model="application.allowedIdentityProviders[$index]" name="identityProvider.id" id="identityProvider.id" value="identityProvider.id" kc-onoffswitch-model />
|
||||
</div>
|
||||
</div>
|
||||
<div class="pull-right form-actions">
|
||||
<button kc-save>Save</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,24 @@
|
|||
<div class="bs-sidebar col-md-3 clearfix" data-ng-include data-src="'partials/realm-menu.html'"></div>
|
||||
<div id="content-area" class="col-md-9" role="main">
|
||||
<kc-navigation-oauth-client></kc-navigation-oauth-client>
|
||||
<div id="content">
|
||||
<ol class="breadcrumb" data-ng-hide="create">
|
||||
<li><a href="#/realms/{{realm.realm}}/oauth-clients">OAuth Clients</a></li>
|
||||
<li><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}">{{oauth.name}}</a></li>
|
||||
<li class="active">Identity Provider</li>
|
||||
</ol>
|
||||
<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>
|
||||
<div class="col-sm-4">
|
||||
<input ng-model="oauth.allowedIdentityProviders[$index]" name="identityProvider.id" id="identityProvider.id" value="identityProvider.id" kc-onoffswitch-model />
|
||||
</div>
|
||||
</div>
|
||||
<div class="pull-right form-actions">
|
||||
<button kc-save>Save</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
|
@ -92,6 +92,13 @@
|
|||
</div>
|
||||
<span tooltip-placement="right" tooltip="Specifies whether the Authorization Server prompts the End-User for reauthentication and consent." class="fa fa-info-circle"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="enabled">Store Tokens</label>
|
||||
<div class="col-sm-4">
|
||||
<input ng-model="identityProvider.storeToken" id="storeToken" onoffswitch />
|
||||
</div>
|
||||
<span tooltip-placement="right" tooltip="Enable/disable if tokens must be stored when authenticating users." class="fa fa-info-circle"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="enabled">Enabled</label>
|
||||
<div class="col-sm-4">
|
||||
|
|
|
@ -93,6 +93,13 @@
|
|||
</div>
|
||||
<span tooltip-placement="right" tooltip="Indicates whether the AuthnRequest must be sent using HTTP-POST binding. If false, HTTP-REDIRECT binding will be used." class="fa fa-info-circle"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="enabled">Store Tokens</label>
|
||||
<div class="col-sm-4">
|
||||
<input ng-model="identityProvider.storeToken" id="storeToken" onoffswitch />
|
||||
</div>
|
||||
<span tooltip-placement="right" tooltip="Enable/disable if tokens must be stored when authenticating users." class="fa fa-info-circle"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="enabled">Enabled</label>
|
||||
<div class="col-sm-4">
|
||||
|
|
|
@ -49,6 +49,13 @@
|
|||
</div>
|
||||
<span tooltip-placement="right" tooltip="The scopes to be sent when asking for authorization. It can be a space-separated list of scopes. Defaults to 'openid'." class="fa fa-info-circle"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="enabled">Store Tokens</label>
|
||||
<div class="col-sm-4">
|
||||
<input ng-model="identityProvider.storeToken" id="storeToken" onoffswitch />
|
||||
</div>
|
||||
<span tooltip-placement="right" tooltip="Enable/disable if tokens must be stored when authenticating users." class="fa fa-info-circle"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="enabled">Enabled</label>
|
||||
<div class="col-sm-4">
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
<li ng-class="{active: path[4] == 'claims'}" data-ng-show="!application.bearerOnly"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/claims">Claims</a></li>
|
||||
<li ng-class="{active: path[4] == 'scope-mappings'}" data-ng-show="!application.bearerOnly"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/scope-mappings">Scope</a></li>
|
||||
<li ng-class="{active: path[4] == 'revocation'}"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/revocation">Revocation</a></li>
|
||||
<li ng-class="{active: path[4] == 'identity-provider'}" data-ng-show="realm.identityFederationEnabled"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/identity-provider">Identity Provider</a></li>
|
||||
<li ng-class="{active: path[4] == 'sessions'}" data-ng-show="!application.bearerOnly"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/sessions">Sessions</a></li>
|
||||
<li ng-class="{active: path[4] == 'clustering'}" data-ng-show="!application.publicClient"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/clustering">Clustering</a></li>
|
||||
<li ng-class="{active: path[4] == 'installation'}" data-ng-show="application.protocol != 'saml'"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/installation">Installation</a></li>
|
||||
|
|
|
@ -4,5 +4,6 @@
|
|||
<li ng-class="{active: path[4] == 'claims'}"><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/claims">Claims</a></li>
|
||||
<li ng-class="{active: path[4] == 'scope-mappings'}"><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/scope-mappings">Scope</a></li>
|
||||
<li ng-class="{active: path[4] == 'revocation'}"><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/revocation">Revocation</a></li>
|
||||
<li ng-class="{active: path[4] == 'identity-provider'}" data-ng-show="realm.identityFederationEnabled"><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/identity-provider">Identity Provider</a></li>
|
||||
<li ng-class="{active: path[4] == 'installation'}"><a href="#/realms/{{realm.realm}}/oauth-clients/{{oauth.id}}/installation">Installation</a></li>
|
||||
</ul>
|
|
@ -14,25 +14,35 @@
|
|||
<span>
|
||||
Personal Info:
|
||||
<#list oauth.claimsRequested as claim>
|
||||
${claim}
|
||||
${claim}
|
||||
</#list>
|
||||
</span>
|
||||
</li>
|
||||
</#if>
|
||||
<#list oauth.realmRolesRequested as role>
|
||||
<#if oauth.accessRequestMessage??>
|
||||
<li>
|
||||
<span><#if role.description??>${role.description}<#else>${role.name}</#if></span>
|
||||
<span>
|
||||
${oauth.accessRequestMessage}
|
||||
</span>
|
||||
</li>
|
||||
</#list>
|
||||
|
||||
<#list oauth.resourceRolesRequested?keys as resource>
|
||||
<#list oauth.resourceRolesRequested[resource] as role>
|
||||
</#if>
|
||||
<#if oauth.realmRolesRequested??>
|
||||
<#list oauth.realmRolesRequested as role>
|
||||
<li>
|
||||
<span class="kc-role"><#if role.description??>${role.description}<#else>${role.name}</#if></span>
|
||||
<span class="kc-resource">in <strong>${resource}</strong></span>
|
||||
<span><#if role.description??>${role.description}<#else>${role.name}</#if></span>
|
||||
</li>
|
||||
</#list>
|
||||
</#list>
|
||||
</#if>
|
||||
<#if oauth.resourceRolesRequested??>
|
||||
<#list oauth.resourceRolesRequested?keys as resource>
|
||||
<#list oauth.resourceRolesRequested[resource] as role>
|
||||
<li>
|
||||
<span class="kc-role"><#if role.description??>${role.description}<#else>${role.name}</#if></span>
|
||||
<span class="kc-resource">in <strong>${resource}</strong></span>
|
||||
</li>
|
||||
</#list>
|
||||
</#list>
|
||||
</#if>
|
||||
</ul>
|
||||
|
||||
<form class="form-actions" action="${url.oauthAction}" method="POST">
|
||||
|
|
|
@ -9,6 +9,7 @@ import org.keycloak.provider.Provider;
|
|||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
@ -39,6 +40,7 @@ public interface LoginFormsProvider extends Provider {
|
|||
public LoginFormsProvider setClientSessionCode(String accessCode);
|
||||
|
||||
public LoginFormsProvider setAccessRequest(List<RoleModel> realmRolesRequested, MultivaluedMap<String,RoleModel> resourceRolesRequested);
|
||||
public LoginFormsProvider setAccessRequest(String message);
|
||||
|
||||
public LoginFormsProvider setError(String message);
|
||||
|
||||
|
@ -56,4 +58,5 @@ public interface LoginFormsProvider extends Provider {
|
|||
|
||||
public LoginFormsProvider setStatus(Response.Status status);
|
||||
|
||||
LoginFormsProvider setActionUri(URI requestUri);
|
||||
}
|
||||
|
|
|
@ -57,6 +57,8 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
private List<RoleModel> realmRolesRequested;
|
||||
private MultivaluedMap<String, RoleModel> resourceRolesRequested;
|
||||
private MultivaluedMap<String, String> queryParams;
|
||||
private String accessRequestMessage;
|
||||
private URI actionUri;
|
||||
|
||||
public static enum MessageType {SUCCESS, WARNING, ERROR}
|
||||
|
||||
|
@ -188,8 +190,8 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
|
||||
if (realm != null) {
|
||||
attributes.put("realm", new RealmBean(realm));
|
||||
attributes.put("social", new IdentityProviderBean(realm, baseUri));
|
||||
attributes.put("url", new UrlBean(realm, theme, baseUri));
|
||||
attributes.put("social", new IdentityProviderBean(realm, baseUri, this.uriInfo));
|
||||
attributes.put("url", new UrlBean(realm, theme, baseUri, this.actionUri));
|
||||
}
|
||||
|
||||
if (client != null) {
|
||||
|
@ -209,7 +211,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
attributes.put("register", new RegisterBean(formData));
|
||||
break;
|
||||
case OAUTH_GRANT:
|
||||
attributes.put("oauth", new OAuthGrantBean(accessCode, client, realmRolesRequested, resourceRolesRequested));
|
||||
attributes.put("oauth", new OAuthGrantBean(accessCode, client, realmRolesRequested, resourceRolesRequested, this.accessRequestMessage));
|
||||
break;
|
||||
case CODE:
|
||||
attributes.put(OAuth2Constants.CODE, new CodeBean(accessCode, messageType == MessageType.ERROR ? message : null));
|
||||
|
@ -303,6 +305,12 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoginFormsProvider setAccessRequest(String accessRequestMessage) {
|
||||
this.accessRequestMessage = accessRequestMessage;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoginFormsProvider setStatus(Response.Status status) {
|
||||
this.status = status;
|
||||
|
@ -315,6 +323,12 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoginFormsProvider setActionUri(URI actionUri) {
|
||||
this.actionUri = actionUri;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
|
|
@ -21,10 +21,13 @@
|
|||
*/
|
||||
package org.keycloak.login.freemarker.model;
|
||||
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.services.resources.flows.Urls;
|
||||
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import java.net.URI;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
@ -39,7 +42,7 @@ public class IdentityProviderBean {
|
|||
private List<IdentityProvider> providers;
|
||||
private RealmModel realm;
|
||||
|
||||
public IdentityProviderBean(RealmModel realm, URI baseURI) {
|
||||
public IdentityProviderBean(RealmModel realm, URI baseURI, UriInfo uriInfo) {
|
||||
this.realm = realm;
|
||||
List<IdentityProviderModel> identityProviders = realm.getIdentityProviders();
|
||||
|
||||
|
@ -48,6 +51,16 @@ public class IdentityProviderBean {
|
|||
|
||||
for (IdentityProviderModel identityProvider : identityProviders) {
|
||||
if (identityProvider.isEnabled()) {
|
||||
String clientId = uriInfo.getQueryParameters().getFirst(OAuth2Constants.CLIENT_ID);
|
||||
|
||||
if (clientId != null) {
|
||||
ClientModel clientModel = realm.findClient(clientId);
|
||||
|
||||
if (clientModel != null && !clientModel.hasIdentityProvider(identityProvider.getId())) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
String loginUrl = Urls.identityProviderAuthnRequest(baseURI, identityProvider, realm).toString();
|
||||
providers.add(new IdentityProvider(identityProvider.getId(), identityProvider.getName(), loginUrl));
|
||||
}
|
||||
|
|
|
@ -34,17 +34,19 @@ import java.util.List;
|
|||
*/
|
||||
public class OAuthGrantBean {
|
||||
|
||||
private final String accessRequestMessage;
|
||||
private List<RoleModel> realmRolesRequested;
|
||||
private MultivaluedMap<String, RoleModel> resourceRolesRequested;
|
||||
private String code;
|
||||
private ClientModel client;
|
||||
private List<String> claimsRequested;
|
||||
|
||||
public OAuthGrantBean(String code, ClientModel client, List<RoleModel> realmRolesRequested, MultivaluedMap<String, RoleModel> resourceRolesRequested) {
|
||||
public OAuthGrantBean(String code, ClientModel client, List<RoleModel> realmRolesRequested, MultivaluedMap<String, RoleModel> resourceRolesRequested, String accessRequestMessage) {
|
||||
this.code = code;
|
||||
this.client = client;
|
||||
this.realmRolesRequested = realmRolesRequested;
|
||||
this.resourceRolesRequested = resourceRolesRequested;
|
||||
this.accessRequestMessage = accessRequestMessage;
|
||||
|
||||
// todo support locale
|
||||
List<String> claims = new LinkedList<String>();
|
||||
|
@ -101,4 +103,8 @@ public class OAuthGrantBean {
|
|||
public List<String> getClaimsRequested() {
|
||||
return claimsRequested;
|
||||
}
|
||||
|
||||
public String getAccessRequestMessage() {
|
||||
return this.accessRequestMessage;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,16 +32,18 @@ import java.net.URI;
|
|||
*/
|
||||
public class UrlBean {
|
||||
|
||||
private final URI actionuri;
|
||||
private URI baseURI;
|
||||
|
||||
private Theme theme;
|
||||
|
||||
private String realm;
|
||||
|
||||
public UrlBean(RealmModel realm, Theme theme, URI baseURI) {
|
||||
public UrlBean(RealmModel realm, Theme theme, URI baseURI, URI actionUri) {
|
||||
this.realm = realm.getName();
|
||||
this.theme = theme;
|
||||
this.baseURI = baseURI;
|
||||
this.actionuri = actionUri;
|
||||
}
|
||||
|
||||
public String getLoginAction() {
|
||||
|
@ -85,6 +87,10 @@ public class UrlBean {
|
|||
}
|
||||
|
||||
public String getOauthAction() {
|
||||
if (this.actionuri != null) {
|
||||
return this.actionuri.getPath();
|
||||
}
|
||||
|
||||
return Urls.realmOauthAction(baseURI, realm).toString();
|
||||
}
|
||||
|
||||
|
@ -92,5 +98,4 @@ public class UrlBean {
|
|||
URI uri = Urls.themeRoot(baseURI);
|
||||
return uri.getPath() + "/" + theme.getType().toString().toLowerCase() +"/" + theme.getName();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -53,5 +53,4 @@ public interface ApplicationModel extends RoleContainerModel, ClientModel {
|
|||
void registerNode(String nodeHost, int registrationTime);
|
||||
|
||||
void unregisterNode(String nodeHost);
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.keycloak.models;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -97,4 +98,9 @@ public interface ClientModel {
|
|||
|
||||
void setNotBefore(int notBefore);
|
||||
|
||||
void updateAllowedIdentityProviders(List<String> identityProviders);
|
||||
|
||||
List<String> getAllowedIdentityProviders();
|
||||
|
||||
boolean hasIdentityProvider(String providerId);
|
||||
}
|
||||
|
|
|
@ -5,16 +5,20 @@ package org.keycloak.models;
|
|||
*/
|
||||
public class FederatedIdentityModel {
|
||||
|
||||
private String userId;
|
||||
private String identityProvider;
|
||||
private String userName;
|
||||
|
||||
public FederatedIdentityModel() {};
|
||||
private final String token;
|
||||
private final String userId;
|
||||
private final String identityProvider;
|
||||
private final String userName;
|
||||
|
||||
public FederatedIdentityModel(String identityProvider, String userId, String userName) {
|
||||
this(identityProvider, userId, userName, null);
|
||||
}
|
||||
|
||||
public FederatedIdentityModel(String providerId, String userId, String userName, String token) {
|
||||
this.identityProvider = providerId;
|
||||
this.userId = userId;
|
||||
this.identityProvider = identityProvider;
|
||||
this.userName = userName;
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
|
@ -28,4 +32,8 @@ public class FederatedIdentityModel {
|
|||
public String getUserName() {
|
||||
return userName;
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
return this.token;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,6 +50,8 @@ public class IdentityProviderModel {
|
|||
|
||||
private boolean updateProfileFirstLogin = true;
|
||||
|
||||
private boolean storeToken;
|
||||
|
||||
/**
|
||||
* <p>A map containing the configuration and properties for a specific identity provider instance and implementation. The items
|
||||
* in the map are understood by the identity provider implementation.</p>
|
||||
|
@ -67,6 +69,7 @@ public class IdentityProviderModel {
|
|||
this.config = new HashMap<String, String>(model.getConfig());
|
||||
this.enabled = model.isEnabled();
|
||||
this.updateProfileFirstLogin = model.isUpdateProfileFirstLogin();
|
||||
this.storeToken = model.isStoreToken();
|
||||
}
|
||||
|
||||
public String getInternalId() {
|
||||
|
@ -117,6 +120,14 @@ public class IdentityProviderModel {
|
|||
this.updateProfileFirstLogin = updateProfileFirstLogin;
|
||||
}
|
||||
|
||||
public boolean isStoreToken() {
|
||||
return this.storeToken;
|
||||
}
|
||||
|
||||
public void setStoreToken(boolean storeToken) {
|
||||
this.storeToken = storeToken;
|
||||
}
|
||||
|
||||
public Map<String, String> getConfig() {
|
||||
return this.config;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,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;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
|
@ -139,4 +140,12 @@ public class ClientEntity extends AbstractIdentifiableEntity {
|
|||
public void setFrontchannelLogout(boolean frontchannelLogout) {
|
||||
this.frontchannelLogout = frontchannelLogout;
|
||||
}
|
||||
|
||||
public List<String> getAllowedIdentityProviders() {
|
||||
return this.allowedIdentityProviders;
|
||||
}
|
||||
|
||||
public void setAllowedIdentityProviders(List<String> allowedIdentityProviders) {
|
||||
this.allowedIdentityProviders = allowedIdentityProviders;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ public class FederatedIdentityEntity {
|
|||
private String userId;
|
||||
private String userName;
|
||||
private String identityProvider;
|
||||
private String token;
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
|
@ -59,4 +60,12 @@ public class FederatedIdentityEntity {
|
|||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,11 +26,13 @@ import java.util.Map;
|
|||
public class IdentityProviderEntity {
|
||||
|
||||
private String internalId;
|
||||
private String id;
|
||||
private String providerId;
|
||||
private String name;
|
||||
private boolean enabled;
|
||||
private boolean updateProfileFirstLogin;
|
||||
private String providerId;
|
||||
private String id;
|
||||
private boolean storeToken;
|
||||
|
||||
private Map<String, String> config = new HashMap<String, String>();
|
||||
|
||||
public String getInternalId() {
|
||||
|
@ -65,6 +67,14 @@ public class IdentityProviderEntity {
|
|||
this.updateProfileFirstLogin = updateProfileFirstLogin;
|
||||
}
|
||||
|
||||
public boolean isStoreToken() {
|
||||
return this.storeToken;
|
||||
}
|
||||
|
||||
public void setStoreToken(boolean storeToken) {
|
||||
this.storeToken = storeToken;
|
||||
}
|
||||
|
||||
public String getProviderId() {
|
||||
return providerId;
|
||||
}
|
||||
|
|
|
@ -258,6 +258,10 @@ public class ModelToRepresentation {
|
|||
rep.setRegisteredNodes(new HashMap<String, Integer>(applicationModel.getRegisteredNodes()));
|
||||
}
|
||||
|
||||
if (!applicationModel.getAllowedIdentityProviders().isEmpty()) {
|
||||
rep.setAllowedIdentityProviders(applicationModel.getAllowedIdentityProviders());
|
||||
}
|
||||
|
||||
return rep;
|
||||
}
|
||||
|
||||
|
@ -282,6 +286,11 @@ public class ModelToRepresentation {
|
|||
rep.setWebOrigins(new LinkedList<String>(webOrigins));
|
||||
}
|
||||
rep.setNotBefore(model.getNotBefore());
|
||||
|
||||
if (!model.getAllowedIdentityProviders().isEmpty()) {
|
||||
rep.setAllowedIdentityProviders(model.getAllowedIdentityProviders());
|
||||
}
|
||||
|
||||
return rep;
|
||||
}
|
||||
|
||||
|
|
|
@ -112,6 +112,8 @@ public class RepresentationToModel {
|
|||
|
||||
if (rep.getPasswordPolicy() != null) newRealm.setPasswordPolicy(new PasswordPolicy(rep.getPasswordPolicy()));
|
||||
|
||||
importIdentityProviders(rep, newRealm);
|
||||
|
||||
if (rep.getApplications() != null) {
|
||||
Map<String, ApplicationModel> appMap = createApplications(rep, newRealm);
|
||||
}
|
||||
|
@ -231,21 +233,6 @@ public class RepresentationToModel {
|
|||
UserModel user = createUser(session, newRealm, userRep, appMap);
|
||||
}
|
||||
}
|
||||
|
||||
if (rep.getIdentityProviders() != null) {
|
||||
for (IdentityProviderRepresentation identityProviderRepresentation : rep.getIdentityProviders()) {
|
||||
IdentityProviderModel identityProviderModel = new IdentityProviderModel();
|
||||
|
||||
identityProviderModel.setId(identityProviderRepresentation.getId());
|
||||
identityProviderModel.setProviderId(identityProviderRepresentation.getProviderId());
|
||||
identityProviderModel.setName(identityProviderRepresentation.getName());
|
||||
identityProviderModel.setEnabled(identityProviderRepresentation.isEnabled());
|
||||
identityProviderModel.setUpdateProfileFirstLogin(identityProviderRepresentation.isUpdateProfileFirstLogin());
|
||||
identityProviderModel.setConfig(identityProviderRepresentation.getConfig());
|
||||
|
||||
newRealm.addIdentityProvider(identityProviderModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void updateRealm(RealmRepresentation rep, RealmModel realm) {
|
||||
|
@ -517,6 +504,10 @@ public class RepresentationToModel {
|
|||
if (rep.getClaims() != null) {
|
||||
setClaims(resource, rep.getClaims());
|
||||
}
|
||||
|
||||
if (rep.getAllowedIdentityProviders() != null) {
|
||||
resource.updateAllowedIdentityProviders(rep.getAllowedIdentityProviders());
|
||||
}
|
||||
}
|
||||
|
||||
public static void setClaims(ClientModel model, ClaimRepresentation rep) {
|
||||
|
@ -632,6 +623,9 @@ public class RepresentationToModel {
|
|||
}
|
||||
}
|
||||
|
||||
if (rep.getAllowedIdentityProviders() != null) {
|
||||
model.updateAllowedIdentityProviders(rep.getAllowedIdentityProviders());
|
||||
}
|
||||
}
|
||||
|
||||
// Scope mappings
|
||||
|
@ -747,4 +741,22 @@ public class RepresentationToModel {
|
|||
|
||||
}
|
||||
}
|
||||
|
||||
private static void importIdentityProviders(RealmRepresentation rep, RealmModel newRealm) {
|
||||
if (rep.getIdentityProviders() != null) {
|
||||
for (IdentityProviderRepresentation identityProviderRepresentation : rep.getIdentityProviders()) {
|
||||
IdentityProviderModel identityProviderModel = new IdentityProviderModel();
|
||||
|
||||
identityProviderModel.setId(identityProviderRepresentation.getId());
|
||||
identityProviderModel.setProviderId(identityProviderRepresentation.getProviderId());
|
||||
identityProviderModel.setName(identityProviderRepresentation.getName());
|
||||
identityProviderModel.setEnabled(identityProviderRepresentation.isEnabled());
|
||||
identityProviderModel.setUpdateProfileFirstLogin(identityProviderRepresentation.isUpdateProfileFirstLogin());
|
||||
identityProviderModel.setStoreToken(identityProviderRepresentation.isStoreToken());
|
||||
identityProviderModel.setConfig(identityProviderRepresentation.getConfig());
|
||||
|
||||
newRealm.addIdentityProvider(identityProviderModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import org.keycloak.models.cache.entities.CachedClient;
|
|||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -259,4 +260,22 @@ public abstract class ClientAdapter implements ClientModel {
|
|||
copy.putAll(cachedClient.getAttributes());
|
||||
return copy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAllowedIdentityProviders(List<String> identityProviders) {
|
||||
getDelegateForUpdate();
|
||||
updatedClient.updateAllowedIdentityProviders(identityProviders);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getAllowedIdentityProviders() {
|
||||
if (updatedClient != null) return updatedClient.getAllowedIdentityProviders();
|
||||
return cachedClient.getAllowedIdentityProviders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasIdentityProvider(String providerId) {
|
||||
if (updatedClient != null) return updatedClient.hasIdentityProvider(providerId);
|
||||
return cachedClient.hasIdentityProvider(providerId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,8 +6,10 @@ import org.keycloak.models.RealmProvider;
|
|||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.cache.RealmCache;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -32,6 +34,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>();
|
||||
|
||||
public CachedClient(RealmCache cache, RealmProvider delegate, RealmModel realm, ClientModel model) {
|
||||
id = model.getId();
|
||||
|
@ -52,7 +55,7 @@ public class CachedClient {
|
|||
for (RoleModel role : model.getScopeMappings()) {
|
||||
scope.add(role.getId());
|
||||
}
|
||||
|
||||
this.allowedIdentityProviders = model.getAllowedIdentityProviders();
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
|
@ -122,4 +125,16 @@ public class CachedClient {
|
|||
public void setFrontchannelLogout(boolean frontchannelLogout) {
|
||||
this.frontchannelLogout = frontchannelLogout;
|
||||
}
|
||||
|
||||
public List<String> getAllowedIdentityProviders() {
|
||||
return this.allowedIdentityProviders;
|
||||
}
|
||||
|
||||
public boolean hasIdentityProvider(String providerId) {
|
||||
if (this.allowedIdentityProviders.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.allowedIdentityProviders.contains(providerId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,9 +7,9 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.models.RoleContainerModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.jpa.entities.ApplicationEntity;
|
||||
import org.keycloak.models.jpa.entities.IdentityProviderEntity;
|
||||
import org.keycloak.models.jpa.entities.RoleEntity;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.util.Time;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.TypedQuery;
|
||||
|
@ -231,13 +231,6 @@ public class ApplicationAdapter extends ClientAdapter implements ApplicationMode
|
|||
em.flush();
|
||||
}
|
||||
|
||||
public static boolean contains(String str, String[] array) {
|
||||
for (String s : array) {
|
||||
if (str.equals(s)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDefaultRoles(String[] defaultRoles) {
|
||||
Collection<RoleEntity> entities = applicationEntity.getDefaultRoles();
|
||||
|
|
|
@ -5,11 +5,14 @@ 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.IdentityProviderEntity;
|
||||
import org.keycloak.models.jpa.entities.RoleEntity;
|
||||
import org.keycloak.models.jpa.entities.ScopeMappingEntity;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.TypedQuery;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
@ -293,4 +296,60 @@ public abstract class ClientAdapter implements ClientModel {
|
|||
copy.putAll(entity.getAttributes());
|
||||
return copy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAllowedIdentityProviders(List<String> identityProviders) {
|
||||
Collection<IdentityProviderEntity> entities = entity.getAllowedIdentityProviders();
|
||||
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);
|
||||
} else {
|
||||
already.add(rel.getId());
|
||||
}
|
||||
}
|
||||
for (IdentityProviderEntity entity : remove) {
|
||||
entities.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);
|
||||
}
|
||||
}
|
||||
em.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getAllowedIdentityProviders() {
|
||||
Collection<IdentityProviderEntity> entities = entity.getAllowedIdentityProviders();
|
||||
List<String> providers = new ArrayList<String>();
|
||||
|
||||
for (IdentityProviderEntity entity : entities) {
|
||||
providers.add(entity.getId());
|
||||
}
|
||||
|
||||
return providers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasIdentityProvider(String providerId) {
|
||||
List<String> allowedIdentityProviders = getAllowedIdentityProviders();
|
||||
|
||||
if (allowedIdentityProviders.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return allowedIdentityProviders.contains(providerId);
|
||||
}
|
||||
|
||||
public static boolean contains(String str, String[] array) {
|
||||
for (String s : array) {
|
||||
if (str.equals(s)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -96,6 +96,7 @@ public class JpaUserProvider implements UserProvider {
|
|||
entity.setIdentityProvider(identity.getIdentityProvider());
|
||||
entity.setUserId(identity.getUserId());
|
||||
entity.setUserName(identity.getUserName());
|
||||
entity.setToken(identity.getToken());
|
||||
UserEntity userEntity = em.getReference(UserEntity.class, user.getId());
|
||||
entity.setUser(userEntity);
|
||||
em.persist(entity);
|
||||
|
@ -344,7 +345,7 @@ public class JpaUserProvider implements UserProvider {
|
|||
List<FederatedIdentityEntity> results = query.getResultList();
|
||||
Set<FederatedIdentityModel> set = new HashSet<FederatedIdentityModel>();
|
||||
for (FederatedIdentityEntity entity : results) {
|
||||
set.add(new FederatedIdentityModel(entity.getIdentityProvider(), entity.getUserId(), entity.getUserName()));
|
||||
set.add(new FederatedIdentityModel(entity.getIdentityProvider(), entity.getUserId(), entity.getUserName(), entity.getToken()));
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
@ -352,7 +353,7 @@ public class JpaUserProvider implements UserProvider {
|
|||
@Override
|
||||
public FederatedIdentityModel getFederatedIdentity(UserModel user, String identityProvider, RealmModel realm) {
|
||||
FederatedIdentityEntity entity = findFederatedIdentity(user, identityProvider);
|
||||
return (entity != null) ? new FederatedIdentityModel(entity.getIdentityProvider(), entity.getUserId(), entity.getUserName()) : null;
|
||||
return (entity != null) ? new FederatedIdentityModel(entity.getIdentityProvider(), entity.getUserId(), entity.getUserName(), entity.getToken()) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1120,6 +1120,7 @@ public class RealmAdapter implements RealmModel {
|
|||
identityProviderModel.setConfig(entity.getConfig());
|
||||
identityProviderModel.setEnabled(entity.isEnabled());
|
||||
identityProviderModel.setUpdateProfileFirstLogin(entity.isUpdateProfileFirstLogin());
|
||||
identityProviderModel.setStoreToken(entity.isStoreToken());
|
||||
|
||||
identityProviders.add(identityProviderModel);
|
||||
}
|
||||
|
@ -1174,6 +1175,7 @@ public class RealmAdapter implements RealmModel {
|
|||
entity.setName(identityProvider.getName());
|
||||
entity.setEnabled(identityProvider.isEnabled());
|
||||
entity.setUpdateProfileFirstLogin(identityProvider.isUpdateProfileFirstLogin());
|
||||
entity.setStoreToken(identityProvider.isStoreToken());
|
||||
entity.setConfig(identityProvider.getConfig());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,10 +9,14 @@ import javax.persistence.Id;
|
|||
import javax.persistence.Inheritance;
|
||||
import javax.persistence.InheritanceType;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.JoinTable;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.MapKeyColumn;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
|
@ -68,6 +72,10 @@ 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>();
|
||||
|
||||
public RealmEntity getRealm() {
|
||||
return realm;
|
||||
}
|
||||
|
@ -179,4 +187,12 @@ public abstract class ClientEntity {
|
|||
public void setFrontchannelLogout(boolean frontchannelLogout) {
|
||||
this.frontchannelLogout = frontchannelLogout;
|
||||
}
|
||||
|
||||
public Collection<IdentityProviderEntity> getAllowedIdentityProviders() {
|
||||
return this.allowedIdentityProviders;
|
||||
}
|
||||
|
||||
public void setAllowedIdentityProviders(Collection<IdentityProviderEntity> allowedIdentityProviders) {
|
||||
this.allowedIdentityProviders = allowedIdentityProviders;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,6 +45,9 @@ public class FederatedIdentityEntity {
|
|||
@Column(name = "FEDERATED_USERNAME")
|
||||
protected String userName;
|
||||
|
||||
@Column(name = "TOKEN")
|
||||
protected String token;
|
||||
|
||||
public UserEntity getUser() {
|
||||
return user;
|
||||
}
|
||||
|
@ -85,6 +88,14 @@ public class FederatedIdentityEntity {
|
|||
this.realmId = realmId;
|
||||
}
|
||||
|
||||
public void setToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
public static class Key implements Serializable {
|
||||
|
||||
protected UserEntity user;
|
||||
|
|
|
@ -9,6 +9,8 @@ import javax.persistence.Id;
|
|||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.MapKeyColumn;
|
||||
import javax.persistence.NamedQueries;
|
||||
import javax.persistence.NamedQuery;
|
||||
import javax.persistence.Table;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -17,6 +19,9 @@ import java.util.Map;
|
|||
*/
|
||||
@Entity
|
||||
@Table(name="IDENTITY_PROVIDER")
|
||||
@NamedQueries({
|
||||
@NamedQuery(name="findIdentityProviderById", query="select identityProvider from IdentityProviderEntity identityProvider where identityProvider.id = :id")
|
||||
})
|
||||
public class IdentityProviderEntity {
|
||||
|
||||
@Id
|
||||
|
@ -42,6 +47,9 @@ public class IdentityProviderEntity {
|
|||
@Column(name="UPDATE_PROFILE_FIRST_LOGIN")
|
||||
private boolean updateProfileFirstLogin;
|
||||
|
||||
@Column(name="STORE_TOKEN")
|
||||
private boolean storeToken;
|
||||
|
||||
@ElementCollection
|
||||
@MapKeyColumn(name="name")
|
||||
@Column(name="value", columnDefinition = "TEXT")
|
||||
|
@ -104,6 +112,14 @@ public class IdentityProviderEntity {
|
|||
this.updateProfileFirstLogin = updateProfileFirstLogin;
|
||||
}
|
||||
|
||||
public boolean isStoreToken() {
|
||||
return this.storeToken;
|
||||
}
|
||||
|
||||
public void setStoreToken(boolean storeToken) {
|
||||
this.storeToken = storeToken;
|
||||
}
|
||||
|
||||
public Map<String, String> getConfig() {
|
||||
return this.config;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ import org.keycloak.models.RoleModel;
|
|||
import org.keycloak.models.mongo.keycloak.entities.MongoApplicationEntity;
|
||||
import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity;
|
||||
import org.keycloak.models.mongo.utils.MongoModelUtils;
|
||||
import org.keycloak.util.Time;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
|
|
@ -291,5 +291,30 @@ public abstract class ClientAdapter<T extends MongoIdentifiableEntity> extends A
|
|||
return copy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAllowedIdentityProviders(List<String> identityProviders) {
|
||||
List<String> providerIds = new ArrayList<String>();
|
||||
for (String providerId : identityProviders) {
|
||||
providerIds.add(providerId);
|
||||
}
|
||||
|
||||
getMongoEntityAsClient().setAllowedIdentityProviders(identityProviders);
|
||||
updateMongoEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getAllowedIdentityProviders() {
|
||||
return getMongoEntityAsClient().getAllowedIdentityProviders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasIdentityProvider(String providerId) {
|
||||
List<String> allowedIdentityProviders = getMongoEntityAsClient().getAllowedIdentityProviders();
|
||||
|
||||
if (allowedIdentityProviders.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return allowedIdentityProviders.contains(providerId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,10 +6,10 @@ import com.mongodb.QueryBuilder;
|
|||
import org.keycloak.connections.mongo.api.MongoStore;
|
||||
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||
import org.keycloak.models.ApplicationModel;
|
||||
import org.keycloak.models.FederatedIdentityModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.FederatedIdentityModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
|
@ -225,7 +225,7 @@ public class MongoUserProvider implements UserProvider {
|
|||
Set<FederatedIdentityModel> result = new HashSet<FederatedIdentityModel>();
|
||||
for (FederatedIdentityEntity federatedIdentityEntity : linkEntities) {
|
||||
FederatedIdentityModel model = new FederatedIdentityModel(federatedIdentityEntity.getIdentityProvider(),
|
||||
federatedIdentityEntity.getUserId(), federatedIdentityEntity.getUserName());
|
||||
federatedIdentityEntity.getUserId(), federatedIdentityEntity.getUserName(), federatedIdentityEntity.getToken());
|
||||
result.add(model);
|
||||
}
|
||||
return result;
|
||||
|
@ -296,13 +296,14 @@ public class MongoUserProvider implements UserProvider {
|
|||
|
||||
|
||||
@Override
|
||||
public void addFederatedIdentity(RealmModel realm, UserModel user, FederatedIdentityModel socialLink) {
|
||||
public void addFederatedIdentity(RealmModel realm, UserModel user, FederatedIdentityModel identity) {
|
||||
user = getUserById(user.getId(), realm);
|
||||
MongoUserEntity userEntity = ((UserAdapter) user).getUser();
|
||||
FederatedIdentityEntity federatedIdentityEntity = new FederatedIdentityEntity();
|
||||
federatedIdentityEntity.setIdentityProvider(socialLink.getIdentityProvider());
|
||||
federatedIdentityEntity.setUserId(socialLink.getUserId());
|
||||
federatedIdentityEntity.setUserName(socialLink.getUserName());
|
||||
federatedIdentityEntity.setIdentityProvider(identity.getIdentityProvider());
|
||||
federatedIdentityEntity.setUserId(identity.getUserId());
|
||||
federatedIdentityEntity.setUserName(identity.getUserName());
|
||||
federatedIdentityEntity.setToken(identity.getToken());
|
||||
|
||||
getMongoStore().pushItemToList(userEntity, "federatedIdentities", federatedIdentityEntity, true, invocationContext);
|
||||
}
|
||||
|
|
|
@ -796,6 +796,7 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
|||
identityProviderModel.setConfig(entity.getConfig());
|
||||
identityProviderModel.setEnabled(entity.isEnabled());
|
||||
identityProviderModel.setUpdateProfileFirstLogin(entity.isUpdateProfileFirstLogin());
|
||||
identityProviderModel.setStoreToken(entity.isStoreToken());
|
||||
|
||||
identityProviders.add(identityProviderModel);
|
||||
}
|
||||
|
@ -850,6 +851,7 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
|||
entity.setName(identityProvider.getName());
|
||||
entity.setEnabled(identityProvider.isEnabled());
|
||||
entity.setUpdateProfileFirstLogin(identityProvider.isUpdateProfileFirstLogin());
|
||||
entity.setStoreToken(identityProvider.isStoreToken());
|
||||
entity.setConfig(identityProvider.getConfig());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,28 +28,35 @@ import org.keycloak.events.Details;
|
|||
import org.keycloak.events.Errors;
|
||||
import org.keycloak.events.EventBuilder;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.FederatedIdentityModel;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.OAuthClientModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.protocol.oidc.TokenManager;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.services.managers.AppAuthManager;
|
||||
import org.keycloak.services.managers.AuthenticationManager;
|
||||
import org.keycloak.services.managers.AuthenticationManager.AuthResult;
|
||||
import org.keycloak.services.managers.ClientSessionCode;
|
||||
import org.keycloak.services.managers.EventsManager;
|
||||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.services.resources.flows.Flows;
|
||||
import org.keycloak.social.SocialIdentityProvider;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
@ -100,6 +107,13 @@ public class AuthenticationBrokerResource {
|
|||
|
||||
try {
|
||||
ClientSessionModel clientSession = clientCode.getClientSession();
|
||||
ClientModel clientModel = clientSession.getClient();
|
||||
Response response = checkClientPermissions(clientModel, providerId);
|
||||
|
||||
if (response != null) {
|
||||
return response;
|
||||
}
|
||||
|
||||
IdentityProvider identityProvider = getIdentityProvider(realm, providerId);
|
||||
|
||||
if (identityProvider == null) {
|
||||
|
@ -109,7 +123,8 @@ public class AuthenticationBrokerResource {
|
|||
|
||||
AuthenticationResponse authenticationResponse = identityProvider.handleRequest(createAuthenticationRequest(providerId, code, realm,
|
||||
clientSession));
|
||||
Response response = authenticationResponse.getResponse();
|
||||
|
||||
response = authenticationResponse.getResponse();
|
||||
|
||||
if (response != null) {
|
||||
event.success();
|
||||
|
@ -142,6 +157,67 @@ public class AuthenticationBrokerResource {
|
|||
return handleResponse(realmName, providerId);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{realm}/{provider_id}/token")
|
||||
public Response retrieveToken(@PathParam("realm") final String realmName, @PathParam("provider_id") String providerId) {
|
||||
return getToken(realmName, providerId, false);
|
||||
}
|
||||
|
||||
private Response getToken(String realmName, String providerId, boolean forceRetrieval) {
|
||||
RealmManager realmManager = new RealmManager(session);
|
||||
RealmModel realm = realmManager.getRealmByName(realmName);
|
||||
AppAuthManager authManager = new AppAuthManager();
|
||||
AuthResult authResult = authManager.authenticateBearerToken(session, realm, uriInfo, clientConnection, request.getHttpHeaders());
|
||||
|
||||
if (authResult != null) {
|
||||
String audience = authResult.getToken().getAudience();
|
||||
ClientModel clientModel = realm.findClient(audience);
|
||||
|
||||
if (clientModel == null) {
|
||||
return Flows.errors().error("Invalid client.", Response.Status.FORBIDDEN);
|
||||
}
|
||||
|
||||
if (!clientModel.hasIdentityProvider(providerId)) {
|
||||
return Flows.errors().error("Client [" + audience + "] not authorized.", Response.Status.FORBIDDEN);
|
||||
}
|
||||
|
||||
if (OAuthClientModel.class.isInstance(clientModel) && !forceRetrieval) {
|
||||
return Flows.forms(session, realm, clientModel, uriInfo)
|
||||
.setClientSessionCode(authManager.extractAuthorizationHeaderToken(request.getHttpHeaders()))
|
||||
.setAccessRequest("Your information from " + providerId + " identity provider.")
|
||||
.setClient(clientModel)
|
||||
.setUriInfo(this.uriInfo)
|
||||
.setActionUri(this.uriInfo.getRequestUri())
|
||||
.createOAuthGrant();
|
||||
}
|
||||
|
||||
IdentityProvider identityProvider = getIdentityProvider(realm, providerId);
|
||||
IdentityProviderModel identityProviderConfig = getIdentityProviderConfig(realm, providerId);
|
||||
|
||||
if (identityProviderConfig.isStoreToken()) {
|
||||
FederatedIdentityModel identity = this.session.users().getFederatedIdentity(authResult.getUser(), providerId, realm);
|
||||
|
||||
return identityProvider.retrieveToken(identity);
|
||||
}
|
||||
|
||||
return Flows.errors().error("Identity Provider [" + providerId + "] does not support this operation.", Response.Status.FORBIDDEN);
|
||||
}
|
||||
|
||||
return Flows.errors().error("Invalid code.", Response.Status.FORBIDDEN);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{realm}/{provider_id}/token")
|
||||
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
||||
public Response consentTokenRetrieval(@PathParam("realm") final String realmName, @PathParam("provider_id") String providerId,
|
||||
final MultivaluedMap<String, String> formData) {
|
||||
if (formData.containsKey("cancel")) {
|
||||
return Flows.errors().error("Permission not approved.", Response.Status.FORBIDDEN);
|
||||
}
|
||||
|
||||
return getToken(realmName, providerId, true);
|
||||
}
|
||||
|
||||
private Response handleResponse(String realmName, String providerId) {
|
||||
RealmManager realmManager = new RealmManager(session);
|
||||
RealmModel realm = realmManager.getRealmByName(realmName);
|
||||
|
@ -166,17 +242,24 @@ public class AuthenticationBrokerResource {
|
|||
}
|
||||
|
||||
ClientSessionModel clientSession = clientCode.getClientSession();
|
||||
|
||||
AuthenticationResponse authenticationResponse = provider.handleResponse(createAuthenticationRequest(providerId, null, realm, clientSession));
|
||||
Response response = authenticationResponse.getResponse();
|
||||
ClientModel clientModel = clientSession.getClient();
|
||||
Response response = checkClientPermissions(clientModel, providerId);
|
||||
|
||||
if (response != null) {
|
||||
return response;
|
||||
}
|
||||
|
||||
FederatedIdentity socialUser = authenticationResponse.getUser();
|
||||
AuthenticationResponse authenticationResponse = provider.handleResponse(createAuthenticationRequest(providerId, null, realm, clientSession));
|
||||
|
||||
return performLocalAuthentication(realm, providerId, socialUser, clientCode);
|
||||
response = authenticationResponse.getResponse();
|
||||
|
||||
if (response != null) {
|
||||
return response;
|
||||
}
|
||||
|
||||
FederatedIdentity identity = authenticationResponse.getUser();
|
||||
|
||||
return performLocalAuthentication(realm, providerId, identity, clientCode);
|
||||
} catch (Exception e) {
|
||||
if (session.getTransaction().isActive()) {
|
||||
session.getTransaction().rollback();
|
||||
|
@ -192,14 +275,14 @@ public class AuthenticationBrokerResource {
|
|||
}
|
||||
}
|
||||
|
||||
private Response performLocalAuthentication(RealmModel realm, String providerId, FederatedIdentity socialUser, ClientSessionCode clientCode) {
|
||||
private Response performLocalAuthentication(RealmModel realm, String providerId, FederatedIdentity identity, ClientSessionCode clientCode) {
|
||||
ClientSessionModel clientSession = clientCode.getClientSession();
|
||||
FederatedIdentityModel socialLink = new FederatedIdentityModel(providerId, socialUser.getId(),
|
||||
socialUser.getUsername());
|
||||
UserModel federatedUser = session.users().getUserByFederatedIdentity(socialLink, realm);
|
||||
FederatedIdentityModel identityModel = new FederatedIdentityModel(providerId, identity.getId(),
|
||||
identity.getUsername(), identity.getToken());
|
||||
UserModel federatedUser = session.users().getUserByFederatedIdentity(identityModel, realm);
|
||||
IdentityProviderModel identityProviderConfig = getIdentityProviderConfig(realm, providerId);
|
||||
|
||||
String authMethod = socialLink.getUserId() + "@" + identityProviderConfig.getId();
|
||||
String authMethod = identityModel.getUserId() + "@" + identityProviderConfig.getId();
|
||||
EventBuilder event = new EventsManager(realm, session, clientConnection).createEventBuilder()
|
||||
.event(EventType.LOGIN)
|
||||
.client(clientSession.getClient())
|
||||
|
@ -228,29 +311,29 @@ public class AuthenticationBrokerResource {
|
|||
return redirectToErrorPage(realm, "Insufficient permissions to link identity");
|
||||
}
|
||||
|
||||
session.users().addFederatedIdentity(realm, authenticatedUser, socialLink);
|
||||
session.users().addFederatedIdentity(realm, authenticatedUser, identityModel);
|
||||
|
||||
event.success();
|
||||
|
||||
return Response.status(302).location(UriBuilder.fromUri(clientSession.getRedirectUri()).build()).build();
|
||||
}
|
||||
|
||||
UserModel user = session.users().getUserByEmail(socialUser.getEmail(), realm);
|
||||
UserModel user = session.users().getUserByEmail(identity.getEmail(), realm);
|
||||
String errorMessage = "federatedIdentityEmailExists";
|
||||
|
||||
if (user == null) {
|
||||
user = session.users().getUserByUsername(socialUser.getUsername(), realm);
|
||||
user = session.users().getUserByUsername(identity.getUsername(), realm);
|
||||
errorMessage = "federatedIdentityUsernameExists";
|
||||
}
|
||||
|
||||
if (user == null) {
|
||||
federatedUser = session.users().addUser(realm, socialUser.getUsername());
|
||||
federatedUser = session.users().addUser(realm, identity.getUsername());
|
||||
federatedUser.setEnabled(true);
|
||||
federatedUser.setFirstName(socialUser.getFirstName());
|
||||
federatedUser.setLastName(socialUser.getLastName());
|
||||
federatedUser.setEmail(socialUser.getEmail());
|
||||
federatedUser.setFirstName(identity.getFirstName());
|
||||
federatedUser.setLastName(identity.getLastName());
|
||||
federatedUser.setEmail(identity.getEmail());
|
||||
|
||||
session.users().addFederatedIdentity(realm, federatedUser, socialLink);
|
||||
session.users().addFederatedIdentity(realm, federatedUser, identityModel);
|
||||
|
||||
event.clone().user(federatedUser).event(EventType.REGISTER)
|
||||
.detail(Details.REGISTER_METHOD, authMethod)
|
||||
|
@ -272,7 +355,7 @@ public class AuthenticationBrokerResource {
|
|||
|
||||
event.user(federatedUser);
|
||||
|
||||
String username = socialLink.getUserId() + "@" + identityProviderConfig.getName();
|
||||
String username = identityModel.getUserId() + "@" + identityProviderConfig.getName();
|
||||
|
||||
UserSessionModel userSession = session.sessions()
|
||||
.createUserSession(realm, federatedUser, username, clientConnection.getRemoteAddr(), "broker", false);
|
||||
|
@ -353,4 +436,16 @@ public class AuthenticationBrokerResource {
|
|||
return null;
|
||||
}
|
||||
|
||||
private Response checkClientPermissions(ClientModel clientModel, String providerId) {
|
||||
if (clientModel == null) {
|
||||
return Flows.errors().error("Invalid client.", Response.Status.FORBIDDEN);
|
||||
}
|
||||
|
||||
if (!clientModel.hasIdentityProvider(providerId)) {
|
||||
return Flows.errors().error("Client [" + clientModel.getClientId() + "] not authorized.", Response.Status.FORBIDDEN);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import org.jboss.resteasy.plugins.providers.multipart.InputPart;
|
|||
import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput;
|
||||
import org.keycloak.broker.provider.IdentityProvider;
|
||||
import org.keycloak.broker.provider.IdentityProviderFactory;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
|
@ -83,6 +84,7 @@ public class IdentityProviderResource {
|
|||
String providerId = formDataMap.get("providerId").get(0).getBodyAsString();
|
||||
String enabled = formDataMap.get("enabled").get(0).getBodyAsString();
|
||||
String updateProfileFirstLogin = formDataMap.get("updateProfileFirstLogin").get(0).getBodyAsString();
|
||||
String storeToken = formDataMap.get("storeToken").get(0).getBodyAsString();
|
||||
InputPart file = formDataMap.get("file").get(0);
|
||||
InputStream inputStream = file.getBody(InputStream.class, null);
|
||||
IdentityProviderFactory providerFactory = getProviderFactorytById(providerId);
|
||||
|
@ -94,6 +96,7 @@ public class IdentityProviderResource {
|
|||
providerModel.setProviderId(providerId);
|
||||
providerModel.setEnabled(Boolean.valueOf(enabled));
|
||||
providerModel.setUpdateProfileFirstLogin(Boolean.valueOf(updateProfileFirstLogin));
|
||||
providerModel.setStoreToken(Boolean.valueOf(storeToken));
|
||||
providerModel.setConfig(config);
|
||||
|
||||
return create(uriInfo, providerModel);
|
||||
|
@ -117,10 +120,32 @@ public class IdentityProviderResource {
|
|||
@DELETE
|
||||
@NoCache
|
||||
public Response delete(@PathParam("id") String providerId) {
|
||||
for (ClientModel applicationModel : getClientModels()) {
|
||||
List<String> allowedIdentityProviders = applicationModel.getAllowedIdentityProviders();
|
||||
|
||||
for (String appProvider : new ArrayList<String>(allowedIdentityProviders)) {
|
||||
if (appProvider.equals(providerId)) {
|
||||
allowedIdentityProviders.remove(appProvider);
|
||||
}
|
||||
}
|
||||
|
||||
applicationModel.updateAllowedIdentityProviders(allowedIdentityProviders);
|
||||
}
|
||||
|
||||
this.realm.removeIdentityProviderById(providerId);
|
||||
|
||||
return Response.noContent().build();
|
||||
}
|
||||
|
||||
private List<ClientModel> getClientModels() {
|
||||
List<ClientModel> clients = new ArrayList<ClientModel>();
|
||||
|
||||
clients.addAll(this.realm.getOAuthClients());
|
||||
clients.addAll(this.realm.getApplications());
|
||||
|
||||
return clients;
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Consumes("application/json")
|
||||
public Response update(IdentityProviderModel model) {
|
||||
|
|
|
@ -24,8 +24,7 @@ public class FacebookIdentityProvider extends AbstractOAuth2IdentityProvider imp
|
|||
config.setUserInfoUrl(PROFILE_URL);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FederatedIdentity getFederatedIdentity(String accessToken) {
|
||||
protected FederatedIdentity doGetFederatedIdentity(String accessToken) {
|
||||
try {
|
||||
JsonNode profile = SimpleHttp.doGet(PROFILE_URL).header("Authorization", "Bearer " + accessToken).asJson();
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ public class GitHubIdentityProvider extends AbstractOAuth2IdentityProvider imple
|
|||
}
|
||||
|
||||
@Override
|
||||
protected FederatedIdentity getFederatedIdentity(String accessToken) {
|
||||
protected FederatedIdentity doGetFederatedIdentity(String accessToken) {
|
||||
try {
|
||||
JsonNode profile = SimpleHttp.doGet(PROFILE_URL).header("Authorization", "Bearer " + accessToken).asJson();
|
||||
|
||||
|
|
|
@ -27,12 +27,14 @@ import org.keycloak.broker.provider.AuthenticationRequest;
|
|||
import org.keycloak.broker.provider.AuthenticationResponse;
|
||||
import org.keycloak.broker.provider.FederatedIdentity;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.FederatedIdentityModel;
|
||||
import org.keycloak.social.SocialIdentityProvider;
|
||||
import twitter4j.Twitter;
|
||||
import twitter4j.TwitterFactory;
|
||||
import twitter4j.auth.RequestToken;
|
||||
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import java.net.URI;
|
||||
|
||||
|
@ -109,4 +111,9 @@ public class TwitterIdentityProvider extends AbstractIdentityProvider<OAuth2Iden
|
|||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response retrieveToken(FederatedIdentityModel identity) {
|
||||
return Response.noContent().build();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,6 @@ import org.junit.After;
|
|||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.events.Details;
|
||||
|
@ -220,7 +219,7 @@ public class AccountTest {
|
|||
|
||||
changePasswordPage.logout();
|
||||
|
||||
events.expectLogout(sessionId).detail(Details.REDIRECT_URI, AccountPasswordPage.PATH).assertEvent();
|
||||
events.expectLogout(sessionId).detail(Details.REDIRECT_URI, changePasswordPage.getPath()).assertEvent();
|
||||
|
||||
loginPage.open();
|
||||
loginPage.login("test-user@localhost", "password");
|
||||
|
|
|
@ -23,28 +23,43 @@ import org.junit.Before;
|
|||
import org.junit.ClassRule;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.models.FederatedIdentityModel;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.representations.IDToken;
|
||||
import org.keycloak.testsuite.OAuthClient;
|
||||
import org.keycloak.testsuite.OAuthClient.AccessTokenResponse;
|
||||
import org.keycloak.testsuite.broker.util.UserSessionStatusServlet.UserSessionStatus;
|
||||
import org.keycloak.testsuite.pages.AccountPasswordPage;
|
||||
import org.keycloak.testsuite.pages.LoginPage;
|
||||
import org.keycloak.testsuite.pages.LoginUpdateProfilePage;
|
||||
import org.keycloak.testsuite.pages.OAuthGrantPage;
|
||||
import org.keycloak.testsuite.rule.WebResource;
|
||||
import org.keycloak.testsuite.rule.WebRule;
|
||||
import org.keycloak.testsuite.rule.WebRule.HtmlUnitDriver;
|
||||
import org.openqa.selenium.By;
|
||||
import org.openqa.selenium.NoSuchElementException;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
import org.openqa.selenium.WebElement;
|
||||
|
||||
import javax.ws.rs.client.Client;
|
||||
import javax.ws.rs.client.ClientBuilder;
|
||||
import javax.ws.rs.client.ClientRequestContext;
|
||||
import javax.ws.rs.client.ClientRequestFilter;
|
||||
import javax.ws.rs.client.WebTarget;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.thoughtworks.selenium.SeleneseTestBase.fail;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
|
@ -68,6 +83,15 @@ public abstract class AbstractIdentityProviderTest {
|
|||
@WebResource
|
||||
private LoginUpdateProfilePage updateProfilePage;
|
||||
|
||||
@WebResource
|
||||
protected OAuthClient oauth;
|
||||
|
||||
@WebResource
|
||||
protected OAuthGrantPage grantPage;
|
||||
|
||||
@WebResource
|
||||
protected AccountPasswordPage changePasswordPage;
|
||||
|
||||
private KeycloakSession session;
|
||||
|
||||
@Before
|
||||
|
@ -76,6 +100,7 @@ public abstract class AbstractIdentityProviderTest {
|
|||
removeTestUsers();
|
||||
brokerServerRule.stopSession(this.session, true);
|
||||
this.session = brokerServerRule.startSession();
|
||||
assertNotNull(getIdentityProviderModel());
|
||||
}
|
||||
|
||||
@After
|
||||
|
@ -87,8 +112,6 @@ public abstract class AbstractIdentityProviderTest {
|
|||
public void testSuccessfulAuthentication() {
|
||||
IdentityProviderModel identityProviderModel = getIdentityProviderModel();
|
||||
|
||||
identityProviderModel.setUpdateProfileFirstLogin(true);
|
||||
|
||||
assertSuccessfulAuthentication(identityProviderModel);
|
||||
}
|
||||
|
||||
|
@ -96,8 +119,6 @@ public abstract class AbstractIdentityProviderTest {
|
|||
public void testSuccessfulAuthenticationWithoutUpdateProfile() {
|
||||
IdentityProviderModel identityProviderModel = getIdentityProviderModel();
|
||||
|
||||
identityProviderModel.setUpdateProfileFirstLogin(false);
|
||||
|
||||
assertSuccessfulAuthentication(identityProviderModel);
|
||||
}
|
||||
|
||||
|
@ -121,10 +142,6 @@ public abstract class AbstractIdentityProviderTest {
|
|||
|
||||
@Test
|
||||
public void testUserAlreadyExistsWhenUpdatingProfile() {
|
||||
IdentityProviderModel identityProviderModel = getIdentityProviderModel();
|
||||
|
||||
identityProviderModel.setUpdateProfileFirstLogin(true);
|
||||
|
||||
this.driver.navigate().to("http://localhost:8081/test-app/");
|
||||
|
||||
assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/login"));
|
||||
|
@ -185,7 +202,141 @@ public abstract class AbstractIdentityProviderTest {
|
|||
assertEquals("User with email already exists. Please login to account management to link the account.", element.getText());
|
||||
}
|
||||
|
||||
@Test(expected = NoSuchElementException.class)
|
||||
public void testIdentityProviderNotAllowed() {
|
||||
this.driver.navigate().to("http://localhost:8081/test-app/");
|
||||
|
||||
assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/login"));
|
||||
|
||||
driver.findElement(By.className("model-oidc-idp"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTokenStorageAndRetrievalByApplication() {
|
||||
IdentityProviderModel identityProviderModel = getIdentityProviderModel();
|
||||
|
||||
identityProviderModel.setStoreToken(true);
|
||||
|
||||
authenticateWithIdentityProvider(identityProviderModel);
|
||||
|
||||
UserModel federatedUser = getFederatedUser();
|
||||
RealmModel realm = getRealm();
|
||||
Set<FederatedIdentityModel> federatedIdentities = this.session.users().getFederatedIdentities(federatedUser, realm);
|
||||
|
||||
assertFalse(federatedIdentities.isEmpty());
|
||||
assertEquals(1, federatedIdentities.size());
|
||||
|
||||
FederatedIdentityModel identityModel = federatedIdentities.iterator().next();
|
||||
|
||||
assertNotNull(identityModel.getToken());
|
||||
|
||||
UserSessionStatus userSessionStatus = retrieveSessionStatus();
|
||||
String accessToken = userSessionStatus.getAccessTokenString();
|
||||
String tokenEndpointUrl = "http://localhost:8081/auth/broker/realm-with-broker/" + getProviderId() + "/token";
|
||||
final String authHeader = "Bearer " + accessToken;
|
||||
ClientRequestFilter authFilter = new ClientRequestFilter() {
|
||||
@Override
|
||||
public void filter(ClientRequestContext requestContext) throws IOException {
|
||||
requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, authHeader);
|
||||
}
|
||||
};
|
||||
Client client = ClientBuilder.newBuilder().register(authFilter).build();
|
||||
UriBuilder authBase = UriBuilder.fromUri(tokenEndpointUrl);
|
||||
WebTarget tokenEndpoint = client.target(authBase);
|
||||
Response response = tokenEndpoint.request().get();
|
||||
|
||||
assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
|
||||
assertNotNull(response.readEntity(String.class));
|
||||
|
||||
driver.navigate().to("http://localhost:8081/test-app/logout");
|
||||
driver.navigate().to("http://localhost:8081/test-app");
|
||||
|
||||
assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/login"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTokenStorageAndRetrievalByOAuthClient() {
|
||||
IdentityProviderModel identityProviderModel = getIdentityProviderModel();
|
||||
|
||||
identityProviderModel.setStoreToken(true);
|
||||
identityProviderModel.setUpdateProfileFirstLogin(false);
|
||||
|
||||
driver.navigate().to("http://localhost:8081/test-app");
|
||||
|
||||
// choose the identity provider
|
||||
this.loginPage.clickSocial(getProviderId());
|
||||
|
||||
assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8082/auth/"));
|
||||
|
||||
// log in to identity provider
|
||||
this.loginPage.login("test-user", "password");
|
||||
|
||||
doAfterProviderAuthentication();
|
||||
|
||||
changePasswordPage.realm("realm-with-broker");
|
||||
changePasswordPage.open();
|
||||
changePasswordPage.changePassword("password", "password");
|
||||
|
||||
driver.navigate().to("http://localhost:8081/test-app/logout");
|
||||
|
||||
oauth.realm("realm-with-broker");
|
||||
oauth.redirectUri("http://localhost:8081/third-party");
|
||||
oauth.clientId("third-party");
|
||||
oauth.doLoginGrant("test-user@localhost", "password");
|
||||
|
||||
grantPage.assertCurrent();
|
||||
grantPage.accept();
|
||||
|
||||
assertTrue(oauth.getCurrentQuery().containsKey(OAuth2Constants.CODE));
|
||||
AccessTokenResponse accessToken = oauth.doAccessTokenRequest(oauth.getCurrentQuery().get(OAuth2Constants.CODE), "password");
|
||||
String tokenEndpointUrl = "http://localhost:8081/auth/broker/realm-with-broker/" + getProviderId() + "/token";
|
||||
String authHeader = "Bearer " + accessToken.getAccessToken();
|
||||
HtmlUnitDriver htmlUnitDriver = (WebRule.HtmlUnitDriver) this.driver;
|
||||
|
||||
htmlUnitDriver.getWebClient().addRequestHeader(HttpHeaders.AUTHORIZATION, authHeader);
|
||||
|
||||
htmlUnitDriver.navigate().to(tokenEndpointUrl);
|
||||
|
||||
grantPage.assertCurrent();
|
||||
grantPage.accept();
|
||||
|
||||
assertNotNull(driver.getPageSource());
|
||||
|
||||
doAssertTokenRetrieval(driver.getPageSource());
|
||||
}
|
||||
|
||||
protected abstract void doAssertTokenRetrieval(String pageSource);
|
||||
|
||||
private void assertSuccessfulAuthentication(IdentityProviderModel identityProviderModel) {
|
||||
authenticateWithIdentityProvider(identityProviderModel);
|
||||
|
||||
// authenticated and redirected to app
|
||||
assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/test-app"));
|
||||
|
||||
UserModel federatedUser = getFederatedUser();
|
||||
|
||||
assertNotNull(federatedUser);
|
||||
|
||||
doAssertFederatedUser(federatedUser, identityProviderModel);
|
||||
|
||||
RealmModel realm = getRealm();
|
||||
|
||||
Set<FederatedIdentityModel> federatedIdentities = this.session.users().getFederatedIdentities(federatedUser, realm);
|
||||
|
||||
assertEquals(1, federatedIdentities.size());
|
||||
|
||||
FederatedIdentityModel federatedIdentityModel = federatedIdentities.iterator().next();
|
||||
|
||||
assertEquals(getProviderId(), federatedIdentityModel.getIdentityProvider());
|
||||
assertEquals(federatedUser.getUsername(), federatedIdentityModel.getUserName());
|
||||
|
||||
driver.navigate().to("http://localhost:8081/test-app/logout");
|
||||
driver.navigate().to("http://localhost:8081/test-app");
|
||||
|
||||
assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/login"));
|
||||
}
|
||||
|
||||
private void authenticateWithIdentityProvider(IdentityProviderModel identityProviderModel) {
|
||||
driver.navigate().to("http://localhost:8081/test-app");
|
||||
|
||||
assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/login"));
|
||||
|
@ -209,31 +360,6 @@ public abstract class AbstractIdentityProviderTest {
|
|||
this.updateProfilePage.assertCurrent();
|
||||
this.updateProfilePage.update(userFirstName, userLastName, userEmail);
|
||||
}
|
||||
|
||||
// authenticated and redirected to app
|
||||
assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/test-app"));
|
||||
|
||||
UserModel federatedUser = getFederatedUser();
|
||||
|
||||
assertNotNull(federatedUser);
|
||||
|
||||
doAssertFederatedUser(federatedUser);
|
||||
|
||||
RealmModel realm = getRealm();
|
||||
|
||||
Set<FederatedIdentityModel> federatedIdentities = this.session.users().getFederatedIdentities(federatedUser, realm);
|
||||
|
||||
assertEquals(1, federatedIdentities.size());
|
||||
|
||||
FederatedIdentityModel federatedIdentityModel = federatedIdentities.iterator().next();
|
||||
|
||||
assertEquals(getProviderId(), federatedIdentityModel.getIdentityProvider());
|
||||
assertEquals(federatedUser.getUsername(), federatedIdentityModel.getUserName());
|
||||
|
||||
driver.navigate().to("http://localhost:8081/test-app/logout");
|
||||
driver.navigate().to("http://localhost:8081/test-app");
|
||||
|
||||
assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/login"));
|
||||
}
|
||||
|
||||
protected UserModel getFederatedUser() {
|
||||
|
@ -256,6 +382,9 @@ public abstract class AbstractIdentityProviderTest {
|
|||
|
||||
assertNotNull(identityProviderModel);
|
||||
|
||||
identityProviderModel.setUpdateProfileFirstLogin(true);
|
||||
identityProviderModel.setEnabled(true);
|
||||
|
||||
return identityProviderModel;
|
||||
}
|
||||
|
||||
|
@ -263,9 +392,7 @@ public abstract class AbstractIdentityProviderTest {
|
|||
return this.session.realms().getRealm("realm-with-broker");
|
||||
}
|
||||
|
||||
protected void doAssertFederatedUser(UserModel federatedUser) {
|
||||
IdentityProviderModel identityProviderModel = getIdentityProviderModel();
|
||||
|
||||
protected void doAssertFederatedUser(UserModel federatedUser, IdentityProviderModel identityProviderModel) {
|
||||
if (identityProviderModel.isUpdateProfileFirstLogin()) {
|
||||
String userEmail = "new@email.com";
|
||||
String userFirstName = "New first";
|
||||
|
|
|
@ -35,6 +35,7 @@ public class BrokerKeyCloakRule extends AbstractKeycloakRule {
|
|||
server.importRealm(getClass().getResourceAsStream("/broker-test/test-realm-with-broker.json"));
|
||||
URL url = getClass().getResource("/broker-test/test-app-keycloak.json");
|
||||
deployApplication("test-app", "/test-app", UserSessionStatusServlet.class, url.getPath(), "manager");
|
||||
deployApplication("test-app-allowed-providers", "/test-app-allowed-providers", UserSessionStatusServlet.class, url.getPath(), "manager");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -76,6 +76,7 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
|
|||
identityProviderModel.getConfig().put("config-added", "value-added");
|
||||
identityProviderModel.setEnabled(false);
|
||||
identityProviderModel.setUpdateProfileFirstLogin(false);
|
||||
identityProviderModel.setStoreToken(true);
|
||||
|
||||
realm.updateIdentityProvider(identityProviderModel);
|
||||
|
||||
|
@ -87,8 +88,9 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
|
|||
|
||||
assertEquals("Changed Name", identityProviderModel.getName());
|
||||
assertEquals("value-added", identityProviderModel.getConfig().get("config-added"));
|
||||
assertEquals(false, identityProviderModel.isEnabled());
|
||||
assertEquals(false, identityProviderModel.isUpdateProfileFirstLogin());
|
||||
assertFalse(identityProviderModel.isEnabled());
|
||||
assertFalse(identityProviderModel.isUpdateProfileFirstLogin());
|
||||
assertTrue(identityProviderModel.isStoreToken());
|
||||
|
||||
identityProviderModel.setName("Changed Name Again");
|
||||
identityProviderModel.getConfig().remove("config-added");
|
||||
|
|
|
@ -3,11 +3,18 @@ package org.keycloak.testsuite.broker;
|
|||
import org.junit.ClassRule;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.representations.AccessTokenResponse;
|
||||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.testsuite.pages.OAuthGrantPage;
|
||||
import org.keycloak.testsuite.rule.AbstractKeycloakRule;
|
||||
import org.keycloak.testsuite.rule.WebResource;
|
||||
import org.keycloak.testutils.KeycloakServer;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* @author pedroigor
|
||||
|
@ -38,6 +45,18 @@ public class OIDCKeyCloakServerBrokerBasicTest extends AbstractIdentityProviderT
|
|||
grantPage.accept();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doAssertTokenRetrieval(String pageSource) {
|
||||
try {
|
||||
AccessTokenResponse accessTokenResponse = JsonSerialization.readValue(pageSource, AccessTokenResponse.class);
|
||||
|
||||
assertNotNull(accessTokenResponse.getToken());
|
||||
assertNotNull(accessTokenResponse.getIdToken());
|
||||
} catch (IOException e) {
|
||||
fail("Could not parse token.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getProviderId() {
|
||||
return "kc-oidc-idp";
|
||||
|
|
|
@ -8,9 +8,17 @@ import org.keycloak.models.UserModel;
|
|||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.testsuite.rule.AbstractKeycloakRule;
|
||||
import org.keycloak.testutils.KeycloakServer;
|
||||
import org.picketlink.identity.federation.api.saml.v2.request.SAML2Request;
|
||||
import org.picketlink.identity.federation.saml.v2.protocol.ResponseType;
|
||||
import org.picketlink.identity.federation.web.util.PostBindingUtil;
|
||||
|
||||
import java.net.URLDecoder;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* @author pedroigor
|
||||
|
@ -37,15 +45,27 @@ public class SAMLKeyCloakServerBrokerBasicTest extends AbstractIdentityProviderT
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void doAssertFederatedUser(UserModel federatedUser) {
|
||||
IdentityProviderModel identityProviderModel = getIdentityProviderModel();
|
||||
|
||||
protected void doAssertFederatedUser(UserModel federatedUser, IdentityProviderModel identityProviderModel) {
|
||||
if (identityProviderModel.isUpdateProfileFirstLogin()) {
|
||||
super.doAssertFederatedUser(federatedUser);
|
||||
super.doAssertFederatedUser(federatedUser, identityProviderModel);
|
||||
} else {
|
||||
assertEquals("test-user@localhost", federatedUser.getEmail());
|
||||
assertNull(federatedUser.getFirstName());
|
||||
assertNull(federatedUser.getLastName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doAssertTokenRetrieval(String pageSource) {
|
||||
try {
|
||||
SAML2Request saml2Request = new SAML2Request();
|
||||
ResponseType responseType = (ResponseType) saml2Request
|
||||
.getSAML2ObjectFromStream(PostBindingUtil.base64DecodeAsStream(URLDecoder.decode(pageSource, "UTF-8")));
|
||||
|
||||
assertNotNull(responseType);
|
||||
assertFalse(responseType.getAssertions().isEmpty());
|
||||
} catch (Exception e) {
|
||||
fail("Could not parse token.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,9 +8,17 @@ import org.keycloak.models.UserModel;
|
|||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.testsuite.rule.AbstractKeycloakRule;
|
||||
import org.keycloak.testutils.KeycloakServer;
|
||||
import org.picketlink.identity.federation.api.saml.v2.request.SAML2Request;
|
||||
import org.picketlink.identity.federation.saml.v2.protocol.ResponseType;
|
||||
import org.picketlink.identity.federation.web.util.PostBindingUtil;
|
||||
|
||||
import java.net.URLDecoder;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* @author pedroigor
|
||||
|
@ -37,15 +45,27 @@ public class SAMLKeyCloakServerBrokerWithSignatureTest extends AbstractIdentityP
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void doAssertFederatedUser(UserModel federatedUser) {
|
||||
IdentityProviderModel identityProviderModel = getIdentityProviderModel();
|
||||
|
||||
protected void doAssertFederatedUser(UserModel federatedUser, IdentityProviderModel identityProviderModel) {
|
||||
if (identityProviderModel.isUpdateProfileFirstLogin()) {
|
||||
super.doAssertFederatedUser(federatedUser);
|
||||
super.doAssertFederatedUser(federatedUser, identityProviderModel);
|
||||
} else {
|
||||
assertEquals("test-user@localhost", federatedUser.getEmail());
|
||||
assertNull(federatedUser.getFirstName());
|
||||
assertNull(federatedUser.getLastName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doAssertTokenRetrieval(String pageSource) {
|
||||
try {
|
||||
SAML2Request saml2Request = new SAML2Request();
|
||||
ResponseType responseType = (ResponseType) saml2Request
|
||||
.getSAML2ObjectFromStream(PostBindingUtil.base64DecodeAsStream(URLDecoder.decode(pageSource, "UTF-8")));
|
||||
|
||||
assertNotNull(responseType);
|
||||
assertFalse(responseType.getAssertions().isEmpty());
|
||||
} catch (Exception e) {
|
||||
fail("Could not parse token.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,8 +20,11 @@ package org.keycloak.testsuite.broker.provider;
|
|||
import org.keycloak.broker.provider.AbstractIdentityProvider;
|
||||
import org.keycloak.broker.provider.AuthenticationRequest;
|
||||
import org.keycloak.broker.provider.AuthenticationResponse;
|
||||
import org.keycloak.models.FederatedIdentityModel;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
/**
|
||||
* @author pedroigor
|
||||
*/
|
||||
|
@ -45,4 +48,9 @@ public class CustomIdentityProvider extends AbstractIdentityProvider<IdentityPro
|
|||
public AuthenticationResponse handleResponse(AuthenticationRequest request) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response retrieveToken(FederatedIdentityModel identity) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,9 +20,12 @@ package org.keycloak.testsuite.broker.provider.social;
|
|||
import org.keycloak.broker.provider.AbstractIdentityProvider;
|
||||
import org.keycloak.broker.provider.AuthenticationRequest;
|
||||
import org.keycloak.broker.provider.AuthenticationResponse;
|
||||
import org.keycloak.models.FederatedIdentityModel;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.social.SocialIdentityProvider;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
/**
|
||||
* @author pedroigor
|
||||
*/
|
||||
|
@ -46,4 +49,9 @@ public class CustomSocialProvider extends AbstractIdentityProvider<IdentityProvi
|
|||
public AuthenticationResponse handleResponse(AuthenticationRequest request) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response retrieveToken(FederatedIdentityModel identity) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ package org.keycloak.testsuite.broker.util;
|
|||
import org.codehaus.jackson.JsonNode;
|
||||
import org.codehaus.jackson.map.ObjectMapper;
|
||||
import org.keycloak.KeycloakSecurityContext;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.IDToken;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
|
@ -49,7 +50,8 @@ public class UserSessionStatusServlet extends HttpServlet {
|
|||
private void writeSessionStatus(HttpServletRequest req, HttpServletResponse resp) throws IOException {
|
||||
KeycloakSecurityContext context = (KeycloakSecurityContext)req.getAttribute(KeycloakSecurityContext.class.getName());
|
||||
IDToken idToken = context.getIdToken();
|
||||
JsonNode jsonNode = new ObjectMapper().valueToTree(new UserSessionStatus(idToken));
|
||||
AccessToken accessToken = context.getToken();
|
||||
JsonNode jsonNode = new ObjectMapper().valueToTree(new UserSessionStatus(idToken, accessToken, context.getTokenString()));
|
||||
PrintWriter writer = resp.getWriter();
|
||||
|
||||
writer.println(jsonNode.toString());
|
||||
|
@ -59,14 +61,18 @@ public class UserSessionStatusServlet extends HttpServlet {
|
|||
|
||||
public static class UserSessionStatus implements Serializable {
|
||||
|
||||
private String accessTokenString;
|
||||
private AccessToken accessToken;
|
||||
private IDToken idToken;
|
||||
|
||||
public UserSessionStatus() {
|
||||
|
||||
}
|
||||
|
||||
public UserSessionStatus(IDToken idToken) {
|
||||
public UserSessionStatus(IDToken idToken, AccessToken accessToken, String tokenString) {
|
||||
this.idToken = idToken;
|
||||
this.accessToken = accessToken;
|
||||
this.accessTokenString = tokenString;
|
||||
}
|
||||
|
||||
public IDToken getIdToken() {
|
||||
|
@ -76,5 +82,17 @@ public class UserSessionStatusServlet extends HttpServlet {
|
|||
public void setIdToken(IDToken idToken) {
|
||||
this.idToken = idToken;
|
||||
}
|
||||
|
||||
public AccessToken getAccessToken() {
|
||||
return this.accessToken;
|
||||
}
|
||||
|
||||
public void setAccessToken(AccessToken accessToken) {
|
||||
this.accessToken = accessToken;
|
||||
}
|
||||
|
||||
public String getAccessTokenString() {
|
||||
return this.accessTokenString;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,8 +33,6 @@ import javax.ws.rs.core.UriBuilder;
|
|||
*/
|
||||
public class AccountPasswordPage extends AbstractAccountPage {
|
||||
|
||||
public static String PATH = AccountService.passwordUrl(UriBuilder.fromUri(Constants.AUTH_SERVER_ROOT)).build("test").toString();
|
||||
|
||||
@FindBy(id = "password")
|
||||
private WebElement passwordInput;
|
||||
|
||||
|
@ -47,6 +45,8 @@ public class AccountPasswordPage extends AbstractAccountPage {
|
|||
@FindBy(className = "btn-primary")
|
||||
private WebElement submitButton;
|
||||
|
||||
private String realmName = "test";
|
||||
|
||||
public void changePassword(String password, String newPassword, String passwordConfirm) {
|
||||
passwordInput.sendKeys(password);
|
||||
newPasswordInput.sendKeys(newPassword);
|
||||
|
@ -55,12 +55,26 @@ public class AccountPasswordPage extends AbstractAccountPage {
|
|||
submitButton.click();
|
||||
}
|
||||
|
||||
public void changePassword(String newPassword, String passwordConfirm) {
|
||||
newPasswordInput.sendKeys(newPassword);
|
||||
passwordConfirmInput.sendKeys(passwordConfirm);
|
||||
|
||||
submitButton.click();
|
||||
}
|
||||
|
||||
public boolean isCurrent() {
|
||||
return driver.getTitle().contains("Account Management") && driver.getCurrentUrl().endsWith("/account/password");
|
||||
}
|
||||
|
||||
public void open() {
|
||||
driver.navigate().to(PATH);
|
||||
driver.navigate().to(getPath());
|
||||
}
|
||||
|
||||
public void realm(String realmName) {
|
||||
this.realmName = realmName;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return AccountService.passwordUrl(UriBuilder.fromUri(Constants.AUTH_SERVER_ROOT)).build(this.realmName).toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
"realm": "realm-with-broker",
|
||||
"enabled": true,
|
||||
"requiredCredentials": [ "password" ],
|
||||
"resetPasswordAllowed": true,
|
||||
"defaultRoles": [ "manager" ],
|
||||
"privateKey": "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCCPyvTTb14vSMkpe/pds2P5Cqxk7bkeFnQiNMS1vyZ+HS2O79fxzp1eAguHnBTs4XTRT7SZJhIT/6utgqZjmDigKV5N7X5ptq8BM/W1qa1cYBRip261pc+tWf3IywJYQ9yFI9mUQarmIEl0D7GH16NSZklheaWfbodRVarvX+ML0amNtGYVDft/RftYmgbKKrK218qQp9R4GZFtf/Q/RmboNXN7weMINU8GWVkTRrccKBIXSunT6zXGfuj3Wp1YpVq20BWwY2OMM/P+yDAc7LKEO1LJqPBdT4r9BRn2lXiaga3AL24gTKZPKU/tu7uqfFciF+i4Rr58SMDNOzQcnklAgMBAAECggEAc0eibJYEO5d8QXW1kPgcHV2gBChv2mxDYnWYDLbIQSdNdfYP/qABt/MTmm5KkWr16fcCEYoD1w0mqFBrtVn1msSusUmEAYGTXJMNumOmjjX1kzaTQMmqeFBrwqwYz/xehWR5P+A7fSmwNV3KEeW19GvN5w5K96w0TLAQdFV3TQVPSytusDunwuR1yltMe1voaEDZ9z0Pi08YiEk2f6xhj5CMkoiw3mNImzfruphHullxU4FD05fH6tDeJ381527ILpAzDsgYZh4aFLKjUHem96bX4EL7FIzBJ6okgN78AZnUC/EaVfgFTw0qfhoWvZV4ruVXXiMhCg4CMMRDq/k9iQKBgQDBNWsJMT84OnnWmQoJmZogkFV+tsGrSK6Re+aJxLWpishh7dwAnT2OcagZvVdUb0FwNWu1D0B9/SKDDMRnnHBhOGDpH57m/eQdRU0oX1BD27xvffk0lLcfD4BTxnR5e9jss8K4twc9jf0P1rxC/loGJ2NtCH0BrPHgz54Ea+96ewKBgQCsk3JDaaPnFwzVYm2BXlhxOxLPsF4wvD2rIRAswZV4C5xebjand8nwiMmVpNd0PRLkEnkI+waURGv2EY/P3JsssoiY8Xqe8f/1G+SQKre7lbqOas8rFoALepC0BYDiZDFy0Z9ZnRAFzRI5sgIt7jpoMRD4xDNlmiV8X+yBxc3Y3wKBgQChDQsU1YUyNKQ8+sLAL9anEEkD4Ald4q8JPHN2IY+gLLxNzT0XEfsu0pTiJ8805axxgUYv3e/PVYNAJBNPnrqaf6lgiegl+jr9Hzhqz9CTUAYqFaL2boSakoxQyNtsLI0s+cb1vDN/3uy0GDZDzcty18BsMagqDmRtFgNNAj/UIwKBgQCahbeFBv0cOPZjxisY8Bou4N8aGehsqNBq/0LVYExuXa8YmoTTdJ3bgw9Er4G/ccQNdUDsuqAMeCtW/CiRzQ0ge4d1sprB4Rv3I4+HSsiS7SFKzfZLtWzXWlpg5qCdlWr1TR7qhYjIOPO9t1beO3YOvwhcRoliyyAPenBxTmTfbwKBgDtm2WJ5VlQgNpIdOs1CCiqd0DFmWOmvBPspPC1kySiy+Ndr9jNohRZkR7pEjgqA5E8rdzc88LirUN7bY5HFHRWN9KXrs5/o3O1K3GFCp64N6nvnPEYZ2zSJalcMC2fjSsJg26z8Dg1H+gfTIDUMoGiEAAnJXuqk+WayPU+fZMLn",
|
||||
"publicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgj8r0029eL0jJKXv6XbNj+QqsZO25HhZ0IjTEtb8mfh0tju/X8c6dXgILh5wU7OF00U+0mSYSE/+rrYKmY5g4oCleTe1+abavATP1tamtXGAUYqdutaXPrVn9yMsCWEPchSPZlEGq5iBJdA+xh9ejUmZJYXmln26HUVWq71/jC9GpjbRmFQ37f0X7WJoGyiqyttfKkKfUeBmRbX/0P0Zm6DVze8HjCDVPBllZE0a3HCgSF0rp0+s1xn7o91qdWKVattAVsGNjjDPz/sgwHOyyhDtSyajwXU+K/QUZ9pV4moGtwC9uIEymTylP7bu7qnxXIhfouEa+fEjAzTs0HJ5JQIDAQAB",
|
||||
|
@ -163,7 +164,20 @@
|
|||
"redirectUris": [
|
||||
"/test-app/*"
|
||||
],
|
||||
"webOrigins": []
|
||||
"webOrigins": [],
|
||||
"allowedIdentityProviders" : [
|
||||
"model-oidc-idp"
|
||||
]
|
||||
}
|
||||
],
|
||||
"oauthClients" : [
|
||||
{
|
||||
"name" : "third-party",
|
||||
"enabled": true,
|
||||
"redirectUris": [
|
||||
"http://localhost:8081/third-party/*"
|
||||
],
|
||||
"secret": "password"
|
||||
}
|
||||
],
|
||||
"roles" : {
|
||||
|
|
Loading…
Reference in a new issue