Merge branch 'ldap' of https://github.com/mposolda/keycloak into mposolda-ldap

Conflicts:
	admin-ui/src/main/resources/META-INF/resources/admin/js/app.js
	admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/realm.js
This commit is contained in:
Stian Thorgersen 2014-04-04 15:29:03 +01:00
commit 216e24864a
12 changed files with 309 additions and 8 deletions

View file

@ -140,6 +140,39 @@ module.config([ '$routeProvider', function($routeProvider) {
}, },
controller : 'RealmAuditCtrl' 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', { .when('/create/user/:realm', {
templateUrl : 'partials/user-detail.html', templateUrl : 'partials/user-detail.html',
resolve : { resolve : {
@ -1018,6 +1051,18 @@ module.filter('remove', function() {
module.filter('capitalize', function() { module.filter('capitalize', function() {
return function(input) { 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;
};
}); });

View file

@ -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'); console.log('RealmLdapSettingsCtrl');
$scope.realm = realm; $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) { module.controller('RealmAuditCtrl', function($scope, RealmAudit, realm) {
$scope.realm = realm; $scope.realm = realm;
@ -967,4 +1062,4 @@ module.controller('RealmAuditCtrl', function($scope, RealmAudit, realm) {
} }
$scope.update(); $scope.update();
}); });

View file

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

View file

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

View file

@ -1,7 +1,9 @@
<ul data-ng-hide="createRealm"> <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' || <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] == '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 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>
<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> <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>

View file

@ -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] == '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] == '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] == '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> </ul>

View file

@ -3,6 +3,8 @@ package org.keycloak.services.resources.admin;
import org.keycloak.freemarker.Theme; import org.keycloak.freemarker.Theme;
import org.keycloak.freemarker.ThemeProvider; import org.keycloak.freemarker.ThemeProvider;
import org.keycloak.social.SocialProvider; import org.keycloak.social.SocialProvider;
import org.keycloak.spi.authentication.AuthenticationProvider;
import org.keycloak.spi.authentication.AuthenticationProviderManager;
import org.keycloak.util.ProviderLoader; import org.keycloak.util.ProviderLoader;
import javax.ws.rs.GET; import javax.ws.rs.GET;
@ -22,6 +24,7 @@ public class ServerInfoAdminResource {
ServerInfoRepresentation info = new ServerInfoRepresentation(); ServerInfoRepresentation info = new ServerInfoRepresentation();
setSocialProviders(info); setSocialProviders(info);
setThemes(info); setThemes(info);
setAuthProviders(info);
return info; return info;
} }
@ -46,12 +49,22 @@ public class ServerInfoAdminResource {
Collections.sort(info.socialProviders); 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 { public static class ServerInfoRepresentation {
private Map<String, List<String>> themes; private Map<String, List<String>> themes;
private List<String> socialProviders; private List<String> socialProviders;
private Map<String, List<String>> authProviders;
public ServerInfoRepresentation() { public ServerInfoRepresentation() {
} }
@ -63,6 +76,9 @@ public class ServerInfoAdminResource {
return socialProviders; return socialProviders;
} }
public Map<String, List<String>> getAuthProviders() {
return authProviders;
}
} }
} }

View file

@ -1,5 +1,7 @@
package org.keycloak.spi.authentication.model; package org.keycloak.spi.authentication.model;
import java.util.Arrays;
import java.util.List;
import java.util.Map; import java.util.Map;
import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.jboss.resteasy.spi.ResteasyProviderFactory;
@ -22,6 +24,11 @@ public class ExternalModelAuthenticationProvider extends AbstractModelAuthentica
return AuthProviderConstants.PROVIDER_NAME_EXTERNAL_MODEL; return AuthProviderConstants.PROVIDER_NAME_EXTERNAL_MODEL;
} }
@Override
public List<String> getAvailableOptions() {
return Arrays.asList(AuthProviderConstants.EXTERNAL_REALM_ID);
}
@Override @Override
public RealmModel getRealm(RealmModel currentRealm, Map<String, String> configuration) throws AuthenticationProviderException { public RealmModel getRealm(RealmModel currentRealm, Map<String, String> configuration) throws AuthenticationProviderException {
String realmId = configuration.get(AuthProviderConstants.EXTERNAL_REALM_ID); String realmId = configuration.get(AuthProviderConstants.EXTERNAL_REALM_ID);

View file

@ -1,11 +1,11 @@
package org.keycloak.spi.authentication.model; package org.keycloak.spi.authentication.model;
import java.util.Collections;
import java.util.List;
import java.util.Map; import java.util.Map;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.spi.authentication.AuthProviderConstants; import org.keycloak.spi.authentication.AuthProviderConstants;
import org.keycloak.spi.authentication.AuthUser;
/** /**
* AbstractModelAuthenticationProvider, which uses current realm to call operations on * AbstractModelAuthenticationProvider, which uses current realm to call operations on
@ -19,6 +19,11 @@ public class ModelAuthenticationProvider extends AbstractModelAuthenticationProv
return AuthProviderConstants.PROVIDER_NAME_MODEL; return AuthProviderConstants.PROVIDER_NAME_MODEL;
} }
@Override
public List<String> getAvailableOptions() {
return Collections.EMPTY_LIST;
}
@Override @Override
protected RealmModel getRealm(RealmModel currentRealm, Map<String, String> config) { protected RealmModel getRealm(RealmModel currentRealm, Map<String, String> config) {
return currentRealm; return currentRealm;

View file

@ -1,5 +1,7 @@
package org.keycloak.spi.authentication.picketlink; package org.keycloak.spi.authentication.picketlink;
import java.util.Collections;
import java.util.List;
import java.util.Map; import java.util.Map;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
@ -34,6 +36,11 @@ public class PicketlinkAuthenticationProvider implements AuthenticationProvider
return AuthProviderConstants.PROVIDER_NAME_PICKETLINK; return AuthProviderConstants.PROVIDER_NAME_PICKETLINK;
} }
@Override
public List<String> getAvailableOptions() {
return Collections.EMPTY_LIST;
}
@Override @Override
public AuthUser getUser(RealmModel realm, Map<String, String> configuration, String username) throws AuthenticationProviderException { public AuthUser getUser(RealmModel realm, Map<String, String> configuration, String username) throws AuthenticationProviderException {
IdentityManager identityManager = getIdentityManager(realm); IdentityManager identityManager = getIdentityManager(realm);

View file

@ -1,5 +1,6 @@
package org.keycloak.spi.authentication; package org.keycloak.spi.authentication;
import java.util.List;
import java.util.Map; import java.util.Map;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
@ -11,6 +12,13 @@ public interface AuthenticationProvider {
String getName(); 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 * Get user by given username or email. Return user instance or null if user doesn't exists in this authentication provider
* *

View file

@ -38,7 +38,7 @@ public class AuthenticationProviderManager {
return new AuthenticationProviderManager(realm, providersMap); return new AuthenticationProviderManager(realm, providersMap);
} }
private static Iterable<AuthenticationProvider> load() { public static Iterable<AuthenticationProvider> load() {
return ProviderLoader.load(AuthenticationProvider.class); return ProviderLoader.load(AuthenticationProvider.class);
} }