keycloak-aplcache
Changes
services/src/main/java/org/keycloak/services/resources/admin/ServerInfoAdminResource.java 16(+16 -0)
spi/authentication-model/src/main/java/org/keycloak/spi/authentication/model/ExternalModelAuthenticationProvider.java 7(+7 -0)
spi/authentication-model/src/main/java/org/keycloak/spi/authentication/model/ModelAuthenticationProvider.java 9(+7 -2)
spi/authentication-picketlink/src/main/java/org/keycloak/spi/authentication/picketlink/PicketlinkAuthenticationProvider.java 7(+7 -0)
Details
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/js/app.js b/admin-ui/src/main/resources/META-INF/resources/admin/js/app.js
index 6fa9473..6e0c028 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/js/app.js
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/js/app.js
@@ -140,6 +140,39 @@ module.config([ '$routeProvider', function($routeProvider) {
},
controller : 'RealmAuditCtrl'
})
+ .when('/realms/:realm/auth-settings', {
+ templateUrl : 'partials/realm-auth-list.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ }
+ },
+ controller : 'RealmAuthSettingsCtrl'
+ })
+ .when('/realms/:realm/auth-settings/create', {
+ templateUrl : 'partials/realm-auth-detail.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ serverInfo : function(ServerInfoLoader) {
+ return ServerInfoLoader();
+ }
+ },
+ controller : 'RealmAuthSettingsDetailCtrl'
+ })
+ .when('/realms/:realm/auth-settings/:index', {
+ templateUrl : 'partials/realm-auth-detail.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ serverInfo : function(ServerInfoLoader) {
+ return ServerInfoLoader();
+ }
+ },
+ controller : 'RealmAuthSettingsDetailCtrl'
+ })
.when('/create/user/:realm', {
templateUrl : 'partials/user-detail.html',
resolve : {
@@ -1018,6 +1051,18 @@ module.filter('remove', function() {
module.filter('capitalize', function() {
return function(input) {
- return input.substring(0, 1).toUpperCase() + input.substring(1);
- }
+ if (!input) {
+ return;
+ }
+ var result = input.substring(0, 1).toUpperCase();
+ var s = input.substring(1);
+ for (var i=0; i<s.length ; i++) {
+ var c = s[i];
+ if (c.match(/[A-Z]/)) {
+ result = result.concat(" ")
+ };
+ result = result.concat(c);
+ };
+ return result;
+ };
});
\ No newline at end of file
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/realm.js b/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/realm.js
index c2e78cd..a3dd0ec 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/realm.js
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/realm.js
@@ -895,7 +895,7 @@ module.controller('RealmSMTPSettingsCtrl', function($scope, Current, Realm, real
}
});
-module.controller('RealmLdapSettingsCtrl', function($scope, Realm, realm, $location, Notifications) {
+module.controller('RealmLdapSettingsCtrl', function($scope, $location, Notifications, Realm, realm) {
console.log('RealmLdapSettingsCtrl');
$scope.realm = realm;
@@ -924,6 +924,101 @@ module.controller('RealmLdapSettingsCtrl', function($scope, Realm, realm, $locat
};
});
+module.controller('RealmAuthSettingsCtrl', function($scope, realm) {
+ console.log('RealmAuthSettingsCtrl');
+
+ $scope.realm = realm;
+ $scope.authenticationProviders = realm.authenticationProviders;
+});
+
+module.controller('RealmAuthSettingsDetailCtrl', function($scope, $routeParams, $location, Notifications, Dialog, Realm, realm, serverInfo) {
+ console.log('RealmAuthSettingsDetailCtrl');
+
+ $scope.realm = realm;
+ $scope.availableProviders = serverInfo.authProviders;
+ $scope.availableProviderNames = Object.keys(serverInfo.authProviders);
+
+ $scope.create = !$routeParams.index;
+ $scope.changed = false;
+
+ if ($scope.create) {
+ $scope.authProvider = {
+ passwordUpdateSupported: true,
+ config: {}
+ };
+
+ $scope.authProviderOptionNames = [];
+ } else {
+ $scope.authProvider = realm.authenticationProviders[ $routeParams.index ];
+ if (!$scope.authProvider.config) {
+ $scope.authProvider.config = {};
+ }
+
+ $scope.authProviderOptionNames = serverInfo.authProviders[ $scope.authProvider.providerName ];
+ $scope.authProviderIndex = $routeParams.index;
+ }
+
+ var oldCopy = angular.copy($scope.authProvider);
+ $scope.$watch('authProvider', function() {
+ if (!angular.equals($scope.authProvider, oldCopy)) {
+ $scope.changed = true;
+ }
+ }, true);
+
+ $scope.changeAuthProvider = function() {
+ console.log('RealmAuthSettingsDetailCtrl: provider changed to ' + $scope.authProvider.providerName);
+ $scope.authProviderOptionNames = serverInfo.authProviders[ $scope.authProvider.providerName ];
+ }
+
+ $scope.cancel = function() {
+ $location.url("/realms/" + realm.realm + "/auth-settings");
+ }
+
+ $scope.reset = function() {
+ $scope.authProvider = angular.copy(oldCopy);
+ $scope.changed = false;
+ }
+
+ $scope.save = function() {
+ if (!$scope.authProvider.providerName) {
+ console.log('RealmAuthSettingsDetailCtrl: no provider selected. Skip creation');
+ return;
+ }
+
+ console.log('RealmAuthSettingsDetailCtrl: creating provider ' + $scope.authProvider.providerName);
+ var realmCopy = angular.copy($scope.realm);
+ if (!realmCopy.authenticationProviders) {
+ realmCopy.authenticationProviders = [];
+ }
+
+ if ($scope.create) {
+ realmCopy.authenticationProviders.push($scope.authProvider);
+ } else {
+ realmCopy.authenticationProviders[ $scope.authProviderIndex ] = $scope.authProvider;
+ }
+
+ $scope.changed = false;
+ Realm.update(realmCopy, function () {
+ $location.url("/realms/" + realm.realm + "/auth-settings");
+ Notifications.success("Authentication provider has been saved.");
+ });
+ };
+
+ $scope.remove = function() {
+ Dialog.confirmDelete($scope.realm.authenticationProviders.providerName, 'authentication Provider', function() {
+ console.log('RealmAuthSettingsDetailCtrl: deleting provider ' + $scope.authProvider.providerName);
+
+ var realmCopy = angular.copy($scope.realm);
+ realmCopy.authenticationProviders.splice($scope.authProviderIndex, 1);
+
+ $scope.changed = false;
+ Realm.update(realmCopy, function () {
+ $location.url("/realms/" + realm.realm + "/auth-settings");
+ Notifications.success("Authentication provider has been deleted.");
+ });
+ });
+ };
+});
module.controller('RealmAuditCtrl', function($scope, RealmAudit, realm) {
$scope.realm = realm;
@@ -967,4 +1062,4 @@ module.controller('RealmAuditCtrl', function($scope, RealmAudit, realm) {
}
$scope.update();
-});
\ No newline at end of file
+});
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-auth-detail.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-auth-detail.html
new file mode 100644
index 0000000..cc3981b
--- /dev/null
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-auth-detail.html
@@ -0,0 +1,74 @@
+<div class="bs-sidebar col-sm-3 " data-ng-include data-src="'partials/realm-menu.html'"></div>
+<div id="content-area" class="col-md-9" role="main">
+ <data-kc-navigation data-kc-current="auth-settings" data-kc-realm="realm.realm" data-kc-social="realm.social"></data-kc-navigation>
+
+ <div id="content">
+ <ol class="breadcrumb" data-ng-show="create">
+ <li><a href="#/realms/{{realm.realm}}">{{realm.realm}}</a></li>
+ <li><a href="#/realms/{{realm.realm}}">Settings</a></li>
+ <li><a href="#/realms/{{realm.realm}}/auth-settings">Authentication</a></li>
+ <li class="active">Add</li>
+ </ol>
+ <h2 data-ng-show="create">Add Authentication provider</h2>
+ <ol class="breadcrumb" data-ng-hide="create">
+ <li><a href="#/realms/{{realm.realm}}">{{realm.realm}}</a></li>
+ <li><a href="#/realms/{{realm.realm}}">Settings</a></li>
+ <li><a href="#/realms/{{realm.realm}}/auth-settings">Authentication</a></li>
+ <li class="active">{{authProviderIndex}}</li>
+ </ol>
+ <h2 data-ng-hide="create"><span>{{authProvider.providerName|humanFriendlyFormat}}'s</span> Attributes</h2>
+
+ <form class="form-horizontal" name="userForm" novalidate kc-read-only="!access.manageRealm">
+ <fieldset class="border-top">
+ <div class="form-group input-select">
+ <label class="col-sm-2 control-label" for="authProviders">Provider Name</label>
+ <div class="col-sm-4">
+ <div class="input-group">
+ <div class="select-kc">
+ <select id="authProviders" name="authProviders"
+ data-ng-change="changeAuthProvider()"
+ data-ng-model="authProvider.providerName"
+ data-ng-options="(p|humanFriendlyFormat) for p in availableProviderNames"
+ data-ng-disabled="!create">
+ <option value="" selected> Select Authentication Provider...</option>
+ </select>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div class="form-group clearfix block">
+ <label class="col-sm-2 control-label" for="passwordUpdateSupported">Password Update Supported</label>
+ <div class="col-sm-4">
+ <input ng-model="authProvider.passwordUpdateSupported" name="passwordUpdateSupported" id="passwordUpdateSupported" onoffswitch />
+ </div>
+ </div>
+
+ </fieldset>
+
+ <fieldset>
+ <legend data-ng-show="authProvider.providerName"><span class="text">{{authProvider.providerName|humanFriendlyFormat}}'s provider options</span></legend>
+ <div data-ng-repeat="option in authProviderOptionNames" class="form-group">
+ <label class="col-sm-2 control-label">{{option|humanFriendlyFormat}} </label>
+
+ <div class="col-sm-4">
+ <input class="form-control" type="text" data-ng-model="authProvider.config[ option ]" >
+ </div>
+ </div>
+
+ </fieldset>
+
+ <div class="pull-right form-actions" data-ng-show="create && access.manageRealm">
+ <button kc-cancel data-ng-click="cancel()">Cancel</button>
+ <button kc-save data-ng-show="changed && authProvider.providerName">Save</button>
+ </div>
+
+ <div class="pull-right form-actions" data-ng-show="!create && access.manageRealm">
+ <button kc-reset data-ng-show="changed">Clear changes</button>
+ <button kc-save data-ng-show="changed">Save</button>
+ <button kc-delete data-ng-click="remove()" data-ng-hide="changed">Delete</button>
+ </div>
+
+ </form>
+ </div>
+</div>
\ No newline at end of file
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-auth-list.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-auth-list.html
new file mode 100644
index 0000000..d2aebfd
--- /dev/null
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-auth-list.html
@@ -0,0 +1,41 @@
+<div class="bs-sidebar col-sm-3 " data-ng-include data-src="'partials/realm-menu.html'"></div>
+<div id="content-area" class="col-sm-9" role="main">
+ <data-kc-navigation data-kc-current="auth-settings" data-kc-realm="realm.realm" data-kc-social="realm.social"></data-kc-navigation>
+ <div id="content">
+ <ol class="breadcrumb">
+ <li><a href="#/realms/{{realm.realm}}">{{realm.realm}}</a></li>
+ <li><a href="#/realms/{{realm.realm}}">Settings</a></li>
+ <li class="active">Authentication</li>
+ </ol>
+ <h2><span>{{realm.realm}}</span> Authentication Providers</h2>
+
+ <div class="panel">
+ <table class="table">
+ <thead>
+ <tr>
+ <th class="kc-table-actions" colspan="3">
+ <div class="pull-right" data-ng-show="access.manageRealm">
+ <a class="btn btn-primary" href="#/realms/{{realm.realm}}/auth-settings/create">Add Provider</a>
+ </div>
+ </th>
+ </tr>
+ <tr data-ng-show="authenticationProviders && authenticationProviders.length > 0">
+ <th>Provider Name</th>
+ <th>Password Update Supported</th>
+ <th>Configuration</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="authProvider in authenticationProviders">
+ <td><a href="#/realms/{{realm.realm}}/auth-settings/{{$index}}">{{authProvider.providerName|humanFriendlyFormat}}</a></td>
+ <td>{{authProvider.passwordUpdateSupported}}</td>
+ <td>{{authProvider.config}}</td>
+ </tr>
+ <tr data-ng-show="!authenticationProviders || authenticationProviders.length == 0">
+ <td>No authentication providers available</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+</div>
\ No newline at end of file
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-menu.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-menu.html
index e2d0a2b..5d929c4 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-menu.html
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-menu.html
@@ -1,7 +1,9 @@
<ul data-ng-hide="createRealm">
<li data-ng-show="access.viewRealm" data-ng-class="((!path[2] || path[1] == 'role' || path[2] == 'roles' || path[2] == 'token-settings' ||
path[2] == 'social-settings' || path[2] == 'required-credentials' || path[2] == 'default-roles' || path[2] == 'registration-settings' ||
- path[2] == 'keys-settings' || path[2] == 'smtp-settings' || path[2] == 'ldap-settings') && path[3] != 'applications') && 'active'"><a href="#/realms/{{realm.realm}}">Settings</a></li>
+ path[2] == 'keys-settings' || path[2] == 'smtp-settings' || path[2] == 'ldap-settings' || path[2] == 'auth-settings') && path[3] != 'applications') && 'active'">
+ <a href="#/realms/{{realm.realm}}">Settings</a>
+ </li>
<li data-ng-show="access.viewUsers" data-ng-class="(path[2] == 'users' || path[1] == 'user') && 'active'"><a href="#/realms/{{realm.realm}}/users">Users</a>
</li>
<li data-ng-show="access.viewApplications" data-ng-class="(path[2] == 'applications' || path[1] == 'application' || path[3] == 'applications') && 'active'"><a href="#/realms/{{realm.realm}}/applications">Applications</a></li>
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/templates/kc-navigation.html b/admin-ui/src/main/resources/META-INF/resources/admin/templates/kc-navigation.html
index 95dac11..5abcf08 100644
--- a/admin-ui/src/main/resources/META-INF/resources/admin/templates/kc-navigation.html
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/templates/kc-navigation.html
@@ -8,4 +8,5 @@
<li ng-class="{active: path[2] == 'keys-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/keys-settings">Keys</a></li>
<li ng-class="{active: path[2] == 'smtp-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/smtp-settings">Email</a></li>
<li ng-class="{active: path[2] == 'ldap-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/ldap-settings">Ldap</a></li>
+ <li ng-class="{active: path[2] == 'auth-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/auth-settings">Authentication</a></li>
</ul>
\ No newline at end of file
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ServerInfoAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ServerInfoAdminResource.java
index 04cda21..46449f3 100644
--- a/services/src/main/java/org/keycloak/services/resources/admin/ServerInfoAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ServerInfoAdminResource.java
@@ -3,6 +3,8 @@ package org.keycloak.services.resources.admin;
import org.keycloak.freemarker.Theme;
import org.keycloak.freemarker.ThemeProvider;
import org.keycloak.social.SocialProvider;
+import org.keycloak.spi.authentication.AuthenticationProvider;
+import org.keycloak.spi.authentication.AuthenticationProviderManager;
import org.keycloak.util.ProviderLoader;
import javax.ws.rs.GET;
@@ -22,6 +24,7 @@ public class ServerInfoAdminResource {
ServerInfoRepresentation info = new ServerInfoRepresentation();
setSocialProviders(info);
setThemes(info);
+ setAuthProviders(info);
return info;
}
@@ -46,12 +49,22 @@ public class ServerInfoAdminResource {
Collections.sort(info.socialProviders);
}
+ private void setAuthProviders(ServerInfoRepresentation info) {
+ info.authProviders = new HashMap<String, List<String>>();
+ Iterable<AuthenticationProvider> authProviders = AuthenticationProviderManager.load();
+ for (AuthenticationProvider authProvider : authProviders) {
+ info.authProviders.put(authProvider.getName(), authProvider.getAvailableOptions());
+ }
+ }
+
public static class ServerInfoRepresentation {
private Map<String, List<String>> themes;
private List<String> socialProviders;
+ private Map<String, List<String>> authProviders;
+
public ServerInfoRepresentation() {
}
@@ -63,6 +76,9 @@ public class ServerInfoAdminResource {
return socialProviders;
}
+ public Map<String, List<String>> getAuthProviders() {
+ return authProviders;
+ }
}
}
diff --git a/spi/authentication-model/src/main/java/org/keycloak/spi/authentication/model/ExternalModelAuthenticationProvider.java b/spi/authentication-model/src/main/java/org/keycloak/spi/authentication/model/ExternalModelAuthenticationProvider.java
index a4d129b..a1dfa10 100644
--- a/spi/authentication-model/src/main/java/org/keycloak/spi/authentication/model/ExternalModelAuthenticationProvider.java
+++ b/spi/authentication-model/src/main/java/org/keycloak/spi/authentication/model/ExternalModelAuthenticationProvider.java
@@ -1,5 +1,7 @@
package org.keycloak.spi.authentication.model;
+import java.util.Arrays;
+import java.util.List;
import java.util.Map;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
@@ -23,6 +25,11 @@ public class ExternalModelAuthenticationProvider extends AbstractModelAuthentica
}
@Override
+ public List<String> getAvailableOptions() {
+ return Arrays.asList(AuthProviderConstants.EXTERNAL_REALM_ID);
+ }
+
+ @Override
public RealmModel getRealm(RealmModel currentRealm, Map<String, String> configuration) throws AuthenticationProviderException {
String realmId = configuration.get(AuthProviderConstants.EXTERNAL_REALM_ID);
if (realmId == null) {
diff --git a/spi/authentication-model/src/main/java/org/keycloak/spi/authentication/model/ModelAuthenticationProvider.java b/spi/authentication-model/src/main/java/org/keycloak/spi/authentication/model/ModelAuthenticationProvider.java
index 29ab43a..cd28aba 100644
--- a/spi/authentication-model/src/main/java/org/keycloak/spi/authentication/model/ModelAuthenticationProvider.java
+++ b/spi/authentication-model/src/main/java/org/keycloak/spi/authentication/model/ModelAuthenticationProvider.java
@@ -1,11 +1,11 @@
package org.keycloak.spi.authentication.model;
+import java.util.Collections;
+import java.util.List;
import java.util.Map;
import org.keycloak.models.RealmModel;
-import org.keycloak.models.UserModel;
import org.keycloak.spi.authentication.AuthProviderConstants;
-import org.keycloak.spi.authentication.AuthUser;
/**
* AbstractModelAuthenticationProvider, which uses current realm to call operations on
@@ -20,6 +20,11 @@ public class ModelAuthenticationProvider extends AbstractModelAuthenticationProv
}
@Override
+ public List<String> getAvailableOptions() {
+ return Collections.EMPTY_LIST;
+ }
+
+ @Override
protected RealmModel getRealm(RealmModel currentRealm, Map<String, String> config) {
return currentRealm;
}
diff --git a/spi/authentication-picketlink/src/main/java/org/keycloak/spi/authentication/picketlink/PicketlinkAuthenticationProvider.java b/spi/authentication-picketlink/src/main/java/org/keycloak/spi/authentication/picketlink/PicketlinkAuthenticationProvider.java
index ffc1ab6..4111806 100755
--- a/spi/authentication-picketlink/src/main/java/org/keycloak/spi/authentication/picketlink/PicketlinkAuthenticationProvider.java
+++ b/spi/authentication-picketlink/src/main/java/org/keycloak/spi/authentication/picketlink/PicketlinkAuthenticationProvider.java
@@ -1,5 +1,7 @@
package org.keycloak.spi.authentication.picketlink;
+import java.util.Collections;
+import java.util.List;
import java.util.Map;
import org.jboss.logging.Logger;
@@ -35,6 +37,11 @@ public class PicketlinkAuthenticationProvider implements AuthenticationProvider
}
@Override
+ public List<String> getAvailableOptions() {
+ return Collections.EMPTY_LIST;
+ }
+
+ @Override
public AuthUser getUser(RealmModel realm, Map<String, String> configuration, String username) throws AuthenticationProviderException {
IdentityManager identityManager = getIdentityManager(realm);
User picketlinkUser = BasicModel.getUser(identityManager, username);
diff --git a/spi/authentication-spi/src/main/java/org/keycloak/spi/authentication/AuthenticationProvider.java b/spi/authentication-spi/src/main/java/org/keycloak/spi/authentication/AuthenticationProvider.java
index 014d432..550da90 100644
--- a/spi/authentication-spi/src/main/java/org/keycloak/spi/authentication/AuthenticationProvider.java
+++ b/spi/authentication-spi/src/main/java/org/keycloak/spi/authentication/AuthenticationProvider.java
@@ -1,5 +1,6 @@
package org.keycloak.spi.authentication;
+import java.util.List;
import java.util.Map;
import org.keycloak.models.RealmModel;
@@ -12,6 +13,13 @@ public interface AuthenticationProvider {
String getName();
/**
+ * Get names of all available configuration options of current provider
+ *
+ * @return options or empty list if no options available
+ */
+ List<String> getAvailableOptions();
+
+ /**
* Get user by given username or email. Return user instance or null if user doesn't exists in this authentication provider
*
* @param realm
diff --git a/spi/authentication-spi/src/main/java/org/keycloak/spi/authentication/AuthenticationProviderManager.java b/spi/authentication-spi/src/main/java/org/keycloak/spi/authentication/AuthenticationProviderManager.java
index c6a59a1..3319cff 100644
--- a/spi/authentication-spi/src/main/java/org/keycloak/spi/authentication/AuthenticationProviderManager.java
+++ b/spi/authentication-spi/src/main/java/org/keycloak/spi/authentication/AuthenticationProviderManager.java
@@ -38,7 +38,7 @@ public class AuthenticationProviderManager {
return new AuthenticationProviderManager(realm, providersMap);
}
- private static Iterable<AuthenticationProvider> load() {
+ public static Iterable<AuthenticationProvider> load() {
return ProviderLoader.load(AuthenticationProvider.class);
}