keycloak-aplcache
Changes
forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js 12(+11 -1)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-oidc.html 45(+37 -8)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-saml.html 12(+6 -6)
Details
diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java
index 3e05272..6da5e59 100755
--- a/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java
+++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java
@@ -103,7 +103,7 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
@Override
public Response keycloakInitiatedBrowserLogout(UserSessionModel userSession, UriInfo uriInfo, RealmModel realm) {
- if (getConfig().getLogoutUrl() == null) return null;
+ if (getConfig().getLogoutUrl() == null || getConfig().getLogoutUrl().trim().equals("")) return null;
UriBuilder logoutUri = UriBuilder.fromUri(getConfig().getLogoutUrl())
.queryParam("state", userSession.getId());
String idToken = userSession.getNote(FEDERATED_ID_TOKEN);
diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderConfig.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderConfig.java
index 06d18f5..0bcdfa3 100755
--- a/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderConfig.java
+++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderConfig.java
@@ -31,12 +31,21 @@ public class OIDCIdentityProviderConfig extends OAuth2IdentityProviderConfig {
public String getPrompt() {
return getConfig().get("prompt");
}
+ public void setPrompt(String prompt) {
+ getConfig().put("prompt", prompt);
+ }
public String getIssuer() {
return getConfig().get("issuer");
}
+ public void setIssuer(String issuer) {
+ getConfig().put("issuer", issuer);
+ }
public String getLogoutUrl() {
return getConfig().get("logoutUrl");
}
+ public void setLogoutUrl(String url) {
+ getConfig().put("logoutUrl", url);
+ }
}
diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderFactory.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderFactory.java
old mode 100644
new mode 100755
index 65bfedd..7a21eae
--- a/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderFactory.java
+++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderFactory.java
@@ -19,6 +19,13 @@ package org.keycloak.broker.oidc;
import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
import org.keycloak.models.IdentityProviderModel;
+import org.keycloak.protocol.oidc.representations.OIDCConfigurationRepresentation;
+import org.keycloak.util.JsonSerialization;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
/**
* @author Pedro Igor
@@ -41,4 +48,22 @@ public class OIDCIdentityProviderFactory extends AbstractIdentityProviderFactory
public String getId() {
return PROVIDER_ID;
}
+
+ @Override
+ public Map<String, String> parseConfig(InputStream inputStream) {
+ OIDCConfigurationRepresentation rep = null;
+ try {
+ rep = JsonSerialization.readValue(inputStream, OIDCConfigurationRepresentation.class);
+ } catch (IOException e) {
+ throw new RuntimeException("failed to load openid connect metadata", e);
+ }
+ OIDCIdentityProviderConfig config = new OIDCIdentityProviderConfig(new IdentityProviderModel());
+ config.setIssuer(rep.getIssuer());
+ config.setLogoutUrl(rep.getLogoutEndpoint());
+ config.setAuthorizationUrl(rep.getAuthorizationEndpoint());
+ config.setTokenUrl(rep.getTokenEndpoint());
+ config.setUserInfoUrl(rep.getUserinfoEndpoint());
+ return config.getConfig();
+
+ }
}
diff --git a/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java
index 4ac8177..214b155 100755
--- a/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java
+++ b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java
@@ -118,7 +118,7 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
@Override
public Response keycloakInitiatedBrowserLogout(UserSessionModel userSession, UriInfo uriInfo, RealmModel realm) {
- if (getConfig().getSingleLogoutServiceUrl() == null) return null;
+ if (getConfig().getSingleLogoutServiceUrl() == null || getConfig().getSingleLogoutServiceUrl().trim().equals("")) return null;
SAML2LogoutRequestBuilder logoutBuilder = new SAML2LogoutRequestBuilder()
.issuer(getEntityId(uriInfo, realm))
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js
index d39c8e5..fc0ac6d 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js
@@ -1299,6 +1299,53 @@ module.directive('onoffswitchmodel', function() {
}
});
+/**
+ * Directive for presenting an ON-OFF switch for checkbox.
+ * This directive provides some additional capabilities to the default onoffswitch such as:
+ *
+ * - Specific scope to specify the value. Instead of just true or false.
+ *
+ * Usage: <input ng-model="mmm" name="nnn" id="iii" onoffswitchvalue [on-text="ooo" off-text="fff"] />
+ */
+module.directive('onoffswitchvalue', 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='{{name}}' id='{{id}}'><label for='{{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) {
+ /*
+ We don't want to propagate basic attributes to the root element of directive. Id should be passed to the
+ input element only to achieve proper label binding (and validity).
+ */
+ element.removeAttr('name');
+ element.removeAttr('id');
+
+ if (!attrs.onText) { attrs.onText = "ON"; }
+ if (!attrs.offText) { attrs.offText = "OFF"; }
+
+ element.bind('keydown', 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,
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
index 55b74f0..b030948 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
@@ -643,6 +643,15 @@ module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload
$scope.realm = angular.copy(realm);
+ $scope.initProvider = function() {
+ if (instance && instance.alias) {
+
+ } else {
+ $scope.identityProvider.updateProfileFirstLogin = false;
+ }
+
+ };
+
$scope.initSamlProvider = function() {
$scope.nameIdFormats = [
{
@@ -684,6 +693,7 @@ module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload
} else {
$scope.identityProvider.config.nameIDPolicyFormat = $scope.nameIdFormats[0].format;
+ $scope.identityProvider.updateProfileFirstLogin = false;
}
}
@@ -698,7 +708,7 @@ module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload
$scope.identityProvider.alias = providerFactory.name;
$scope.identityProvider.providerId = providerFactory.id;
$scope.identityProvider.enabled = true;
- $scope.identityProvider.updateProfileFirstLogin = true;
+ $scope.identityProvider.updateProfileFirstLogin = false;
$scope.identityProvider.authenticateByDefault = false;
$scope.newIdentityProvider = true;
}
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-oidc.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-oidc.html
index df99908..e89569c 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-oidc.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-oidc.html
@@ -1,23 +1,42 @@
<div class="bs-sidebar col-sm-3 " data-ng-include data-src="resourceUrl + '/partials/realm-menu.html'"></div>
- <div id="content-area" class="col-sm-9" role="main">
+ <div id="content-area" class="col-sm-9" role="main" data-ng-init="initProvider()">
<data-kc-navigation data-kc-current="social" data-kc-realm="realm.realm" data-kc-social="realm.social"></data-kc-navigation>
<h2></h2>
<div id="content">
<ol class="breadcrumb">
<li><a href="#/realms/{{realm.realm}}/identity-provider-settings">Identity Providers</a></li>
- <li class="active">{{identityProvider.name}} Provider Settings</li>
+ <li class="active">{{identityProvider.alias}} Provider Settings</li>
</ol>
- <h2 class="pull-left">{{identityProvider.name}} Provider Settings</h2>
+ <h2 class="pull-left">{{identityProvider.alias}} Provider Settings</h2>
<p class="subtitle"><span class="required">*</span> Required fields</p>
<form class="form-horizontal" name="realmForm" novalidate>
<fieldset>
<div class="form-group clearfix">
<label class="col-sm-2 control-label" for="identifier">Alias <span class="required">*</span></label>
<div class="col-sm-4">
- <input class="form-control" id="identifier" type="text" ng-model="identityProvider.id" data-ng-readonly="!newIdentityProvider" required>
+ <input class="form-control" id="identifier" type="text" ng-model="identityProvider.alias" data-ng-readonly="!newIdentityProvider" required>
</div>
<span tooltip-placement="right" tooltip="The alias unique identifies an identity provider and it is also used to build the redirect uri." class="fa fa-info-circle"></span>
</div>
+ <div class="form-group" data-ng-show="newIdentityProvider && !importFile">
+ <label class="col-sm-2 control-label" for="fromUrl">Import From Url</label>
+ <div class="col-sm-4">
+ <input class="form-control" id="fromUrl" type="text" ng-model="fromUrl">
+ </div>
+ <span tooltip-placement="right" tooltip="Import metadata from a remote IDP SAML entity descriptor." class="fa fa-info-circle"></span>
+ </div>
+ <div class="form-group" data-ng-show="newIdentityProvider && !importUrl">
+ <label class="col-sm-2 control-label">Import From File</label>
+ <div class="col-sm-4">
+ <div class="controls kc-button-input-file" data-ng-show="!files || files.length == 0">
+ <a href="#" class="btn btn-default"><span class="kc-icon-upload">Icon: Upload</span>Choose a File...</a>
+ <input id="import-file" type="file" class="transparent" ng-file-select="onFileSelect($files)">
+ </div>
+ <span class="kc-uploaded-file" data-ng-show="files.length > 0">
+ {{files[0].name}}
+ </span>
+ </div>
+ </div>
<div class="form-group clearfix">
<label class="col-sm-2 control-label" for="authorizationUrl">Authorization Url <span class="required">*</span></label>
<div class="col-sm-4">
@@ -33,11 +52,18 @@
<span tooltip-placement="right" tooltip="The Token Url." class="fa fa-info-circle"></span>
</div>
<div class="form-group clearfix">
- <label class="col-sm-2 control-label" for="userInfoUrl">User Info Url <span class="required">*</span></label>
+ <label class="col-sm-2 control-label" for="userInfoUrl">Logout Url</label>
+ <div class="col-sm-4">
+ <input class="form-control" id="logoutUrl" type="text" ng-model="identityProvider.config.logoutUrl">
+ </div>
+ <span tooltip-placement="right" tooltip="End session endpoint to use to logout user from external IDP." class="fa fa-info-circle"></span>
+ </div>
+ <div class="form-group clearfix">
+ <label class="col-sm-2 control-label" for="userInfoUrl">User Info Url</label>
<div class="col-sm-4">
- <input class="form-control" id="userInfoUrl" type="text" ng-model="identityProvider.config.userInfoUrl" required>
+ <input class="form-control" id="userInfoUrl" type="text" ng-model="identityProvider.config.userInfoUrl">
</div>
- <span tooltip-placement="right" tooltip="The User Info Url." class="fa fa-info-circle"></span>
+ <span tooltip-placement="right" tooltip="The User Info Url. This is optional." class="fa fa-info-circle"></span>
</div>
<div class="form-group clearfix">
<label class="col-sm-2 control-label" for="clientId">Client ID <span class="required">*</span></label>
@@ -116,7 +142,10 @@
</fieldset>
<div class="pull-right form-actions">
- <button kc-save>Save</button>
+ <button kc-save data-ng-show="changed">Save</button>
+ <button type="submit" data-ng-click="cancel()" data-ng-show="changed" class="btn btn-lg btn-default">Cancel</button>
+ <button type="submit" data-ng-click="uploadFile()" data-ng-show="importFile" class="btn btn-lg btn-primary">Import</button>
+ <button type="submit" data-ng-click="importFrom()" data-ng-show="importUrl" class="btn btn-lg btn-primary">Import</button>
<button kc-delete data-ng-click="remove()" data-ng-show="!newIdentityProvider">Delete</button>
</div>
</form>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-saml.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-saml.html
index 4e1d88c..aa0364b 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-saml.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-saml.html
@@ -47,7 +47,7 @@
<div class="form-group clearfix" data-ng-show="!importFile && !importUrl">
<label class="col-sm-2 control-label" for="singleSignOnServiceUrl">Single Logout Service Url</label>
<div class="col-sm-4">
- <input class="form-control" id="singleLogoutServiceUrl" type="text" ng-model="identityProvider.config.singleLogoutServiceUrl" required>
+ <input class="form-control" id="singleLogoutServiceUrl" type="text" ng-model="identityProvider.config.singleLogoutServiceUrl">
</div>
<span tooltip-placement="right" tooltip="The Url that must be used to send logout requests." class="fa fa-info-circle"></span>
</div>
@@ -71,35 +71,35 @@
<div class="form-group" data-ng-show="!importFile && !importUrl">
<label class="col-sm-2 control-label" for="wantAuthnRequestsSigned">Want AuthnRequests Signed</label>
<div class="col-sm-4">
- <input ng-model="identityProvider.config.wantAuthnRequestsSigned" id="wantAuthnRequestsSigned" value="'true'" onoffswitchmodel />
+ <input ng-model="identityProvider.config.wantAuthnRequestsSigned" id="wantAuthnRequestsSigned" name="wantAuthnRequestsSigned" value="'true'" onoffswitchvalue />
</div>
<span tooltip-placement="right" tooltip=" Indicates whether the identity provider expects signed a AuthnRequest." class="fa fa-info-circle"></span>
</div>
<div class="form-group" data-ng-show="!importFile && !importUrl">
<label class="col-sm-2 control-label" for="forceAuthn">Force Authentication</label>
<div class="col-sm-4">
- <input ng-model="identityProvider.config.forceAuthn" id="forceAuthn" value="'true'" onoffswitchmodel />
+ <input ng-model="identityProvider.config.forceAuthn" id="forceAuthn" name="forceAuthn" value="'true'" onoffswitchvalue />
</div>
<span tooltip-placement="right" tooltip=" Indicates whether the identity provider must authenticate the presenter directly rather than rely on a previous security context." class="fa fa-info-circle"></span>
</div>
<div class="form-group" data-ng-show="!importFile && !importUrl">
<label class="col-sm-2 control-label" for="validateSignature">Validate Signature</label>
<div class="col-sm-4">
- <input ng-model="identityProvider.config.validateSignature" id="validateSignature" value="'true'" onoffswitchmodel />
+ <input ng-model="identityProvider.config.validateSignature" id="validateSignature" value="'true'" onoffswitchvalue />
</div>
<span tooltip-placement="right" tooltip="Enable/disable signature validation of SAML responses." class="fa fa-info-circle"></span>
</div>
<div class="form-group" data-ng-show="!importFile && !importUrl">
<label class="col-sm-2 control-label" for="postBindingResponse">HTTP-POST Binding Response</label>
<div class="col-sm-4">
- <input ng-model="identityProvider.config.postBindingResponse" id="postBindingResponse" value="'true'" onoffswitchmodel />
+ <input ng-model="identityProvider.config.postBindingResponse" id="postBindingResponse" value="'true'" onoffswitchvalue />
</div>
<span tooltip-placement="right" tooltip="Indicates whether the identity provider must respond to the AuthnRequest using HTTP-POST binding. If false, HTTP-REDIRECT binding will be used." class="fa fa-info-circle"></span>
</div>
<div class="form-group" data-ng-show="!importFile && !importUrl">
<label class="col-sm-2 control-label" for="postBindingAuthnRequest">HTTP-POST Binding for AuthnRequest</label>
<div class="col-sm-4">
- <input ng-model="identityProvider.config.postBindingAuthnRequest" id="postBindingAuthnRequest" value="'true'" onoffswitchmodel />
+ <input ng-model="identityProvider.config.postBindingAuthnRequest" id="postBindingAuthnRequest" value="'true'" onoffswitchvalue />
</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>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-social.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-social.html
index d37e6a3..aa6a5eb 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-social.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-social.html
@@ -1,5 +1,5 @@
<div class="bs-sidebar col-sm-3 " data-ng-include data-src="resourceUrl + '/partials/realm-menu.html'"></div>
- <div id="content-area" class="col-sm-9" role="main">
+ <div id="content-area" class="col-sm-9" role="main" data-ng-init="initProvider()">
<data-kc-navigation data-kc-current="social" data-kc-realm="realm.realm" data-kc-social="realm.social"></data-kc-navigation>
<h2></h2>
<div id="content">
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java
index 07b9d00..a61c811 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java
@@ -121,7 +121,7 @@ public class LogoutEndpoint {
AuthenticationManager.AuthResult authResult = authManager.authenticateIdentityCookie(session, realm, uriInfo, clientConnection, headers, false);
if (authResult != null) {
userSession = userSession != null ? userSession : authResult.getSession();
- if (redirectUri != null) userSession.setNote(OIDCLoginProtocol.LOGOUT_REDIRECT_URI, redirect);
+ if (redirect != null) userSession.setNote(OIDCLoginProtocol.LOGOUT_REDIRECT_URI, redirect);
if (state != null) userSession.setNote(OIDCLoginProtocol.LOGOUT_STATE_PARAM, state);
userSession.setNote(AuthenticationManager.KEYCLOAK_LOGOUT_PROTOCOL, OIDCLoginProtocol.LOGIN_PROTOCOL);
return AuthenticationManager.browserLogout(session, realm, authResult.getSession(), uriInfo, clientConnection, headers);
@@ -131,7 +131,7 @@ public class LogoutEndpoint {
event.user(userSession.getUser()).session(userSession).success();
}
- if (redirectUri != null) {
+ if (redirect != null) {
UriBuilder uriBuilder = UriBuilder.fromUri(redirect);
if (state != null) uriBuilder.queryParam(OIDCLoginProtocol.STATE_PARAM, state);
return Response.status(302).location(uriBuilder.build()).build();
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/representations/OIDCConfigurationRepresentation.java b/services/src/main/java/org/keycloak/protocol/oidc/representations/OIDCConfigurationRepresentation.java
index 0e3d4f3..9245e58 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/representations/OIDCConfigurationRepresentation.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/representations/OIDCConfigurationRepresentation.java
@@ -1,8 +1,12 @@
package org.keycloak.protocol.oidc.representations;
+import org.codehaus.jackson.annotate.JsonAnyGetter;
+import org.codehaus.jackson.annotate.JsonAnySetter;
import org.codehaus.jackson.annotate.JsonProperty;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Set;
/**
@@ -44,6 +48,8 @@ public class OIDCConfigurationRepresentation {
@JsonProperty("response_modes_supported")
private List<String> responseModesSupported;
+ protected Map<String, Object> otherClaims = new HashMap<String, Object>();
+
public String getIssuer() {
return issuer;
}
@@ -131,4 +137,15 @@ public class OIDCConfigurationRepresentation {
public void setResponseModesSupported(List<String> responseModesSupported) {
this.responseModesSupported = responseModesSupported;
}
+
+ @JsonAnyGetter
+ public Map<String, Object> getOtherClaims() {
+ return otherClaims;
+ }
+
+ @JsonAnySetter
+ public void setOtherClaims(String name, Object value) {
+ otherClaims.put(name, value);
+ }
+
}