Details
diff --git a/ui/src/main/java/org/keycloak/ui/example/Admin.java b/ui/src/main/java/org/keycloak/ui/example/Admin.java
index ce064bb..2a70f78 100644
--- a/ui/src/main/java/org/keycloak/ui/example/Admin.java
+++ b/ui/src/main/java/org/keycloak/ui/example/Admin.java
@@ -24,27 +24,27 @@ import javax.ws.rs.core.Response;
@Path("")
public class Admin extends javax.ws.rs.core.Application {
- private static Map<String, Realm> realms = new HashMap<String, Realm>();
-
private static Map<String, Application> applications = new HashMap<String, Application>();
+ private static Map<String, Realm> realms = new HashMap<String, Realm>();
+
@DELETE
- @Path("/applications/{key}")
- public void delete(@PathParam("key") String applicationKey) {
- applications.remove(applicationKey);
+ @Path("/applications/{id}")
+ public void delete(@PathParam("id") String id) {
+ applications.remove(id);
}
@DELETE
- @Path("/realms/{key}")
- public void deleteRealm(@PathParam("key") String key) {
- realms.remove(key);
+ @Path("/realms/{id}")
+ public void deleteRealm(@PathParam("id") String id) {
+ realms.remove(id);
}
@GET
- @Path("/applications/{key}")
+ @Path("/applications/{id}")
@Produces(MediaType.APPLICATION_JSON)
- public Application getApplication(@PathParam("key") String applicationKey) {
- return applications.get(applicationKey);
+ public Application getApplication(@PathParam("id") String id) {
+ return applications.get(id);
}
@GET
@@ -55,10 +55,10 @@ public class Admin extends javax.ws.rs.core.Application {
}
@GET
- @Path("/realms/{key}")
+ @Path("/realms/{id}")
@Produces(MediaType.APPLICATION_JSON)
- public Realm getRealm(@PathParam("key") String key) {
- return realms.get(key);
+ public Realm getRealm(@PathParam("id") String id) {
+ return realms.get(id);
}
@@ -73,33 +73,33 @@ public class Admin extends javax.ws.rs.core.Application {
@Path("/applications")
@Consumes(MediaType.APPLICATION_JSON)
public Response save(Application application) {
- String key = UUID.randomUUID().toString();
- application.setKey(key);
- applications.put(key, application);
- return Response.created(URI.create("/applications/" + application.getKey())).build();
+ String id = UUID.randomUUID().toString();
+ application.setId(id);
+ applications.put(id, application);
+ return Response.created(URI.create("/applications/" + id)).build();
}
@POST
@Path("/realms")
@Consumes(MediaType.APPLICATION_JSON)
public Response save(Realm realm) {
- String key = UUID.randomUUID().toString();
- realm.setKey(key);
- realms.put(key, realm);
- return Response.created(URI.create("/realms/" + realm.getKey())).build();
+ String id = UUID.randomUUID().toString();
+ realm.setId(id);
+ realms.put(id, realm);
+ return Response.created(URI.create("/realms/" + id)).build();
}
@PUT
- @Path("/applications/{key}")
+ @Path("/applications/{id}")
@Consumes(MediaType.APPLICATION_JSON)
- public void save(@PathParam("key") String applicationKey, Application application) {
- applications.put(applicationKey, application);
+ public void save(@PathParam("id") String id, Application application) {
+ applications.put(id, application);
}
@PUT
- @Path("/realms/{key}")
+ @Path("/realms/{id}")
@Consumes(MediaType.APPLICATION_JSON)
- public void save(@PathParam("key") String key, Realm realm) {
- realms.put(key, realm);
+ public void save(@PathParam("id") String id, Realm realm) {
+ realms.put(id, realm);
}
}
\ No newline at end of file
diff --git a/ui/src/main/java/org/keycloak/ui/example/Application.java b/ui/src/main/java/org/keycloak/ui/example/Application.java
index c110036..c977223 100644
--- a/ui/src/main/java/org/keycloak/ui/example/Application.java
+++ b/ui/src/main/java/org/keycloak/ui/example/Application.java
@@ -21,8 +21,6 @@
*/
package org.keycloak.ui.example;
-import java.util.List;
-
import javax.xml.bind.annotation.XmlRootElement;
/**
@@ -31,84 +29,74 @@ import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class Application {
- private String callbackUrl;
-
- private String key;
+ private String[] callbackUrl;
- private String name;
+ private boolean enabled;
- private String owner;
+ private String id;
- private String javaScriptOrigin;
+ private String[] initialRoles;
- private List<IdentityProviderConfig> providers;
-
- private String secret;
+ private String name;
private String realm;
- public String getCallbackUrl() {
+ private String[] roles;
+
+ public String[] getCallbackUrl() {
return callbackUrl;
}
- public String getJavaScriptOrigin() {
- return javaScriptOrigin;
+ public String getId() {
+ return id;
}
- public String getKey() {
- return key;
+ public String[] getInitialRoles() {
+ return initialRoles;
}
public String getName() {
return name;
}
- public String getOwner() {
- return owner;
- }
-
- public List<IdentityProviderConfig> getProviders() {
- return providers;
- }
-
- public String getSecret() {
- return secret;
- }
-
public String getRealm() {
return realm;
}
- public void setCallbackUrl(String callbackUrl) {
- this.callbackUrl = callbackUrl;
+ public String[] getRoles() {
+ return roles;
}
- public void setJavaScriptOrigin(String javaScriptOrigin) {
- this.javaScriptOrigin = javaScriptOrigin;
+ public boolean isEnabled() {
+ return enabled;
}
- public void setKey(String key) {
- this.key = key;
+ public void setCallbackUrl(String[] callbackUrl) {
+ this.callbackUrl = callbackUrl;
}
- public void setName(String name) {
- this.name = name;
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
}
- public void setOwner(String owner) {
- this.owner = owner;
+ public void setId(String id) {
+ this.id = id;
}
- public void setProviders(List<IdentityProviderConfig> providers) {
- this.providers = providers;
+ public void setInitialRoles(String[] initialRoles) {
+ this.initialRoles = initialRoles;
}
- public void setSecret(String secret) {
- this.secret = secret;
+ public void setName(String name) {
+ this.name = name;
}
public void setRealm(String realm) {
this.realm = realm;
}
+ public void setRoles(String[] roles) {
+ this.roles = roles;
+ }
+
}
diff --git a/ui/src/main/java/org/keycloak/ui/example/Realm.java b/ui/src/main/java/org/keycloak/ui/example/Realm.java
index 0153521..2c2a758 100644
--- a/ui/src/main/java/org/keycloak/ui/example/Realm.java
+++ b/ui/src/main/java/org/keycloak/ui/example/Realm.java
@@ -21,40 +21,101 @@
*/
package org.keycloak.ui.example;
+import java.util.concurrent.TimeUnit;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class Realm {
- private String key;
+ private boolean enabled;
+
+ private String[] initialRoles;
+
+ private String id;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
private String name;
- private String owner;
+ private String[] roles;
+
+ private boolean social;
+
+ private long tokenExpiration;
- public String getKey() {
- return key;
+ private TimeUnit tokenExpirationUnit;
+
+ private boolean userRegistration;
+
+ public String[] getInitialRoles() {
+ return initialRoles;
}
public String getName() {
return name;
}
- public String getOwner() {
- return owner;
+ public String[] getRoles() {
+ return roles;
+ }
+
+ public long getTokenExpiration() {
+ return tokenExpiration;
+ }
+
+ public TimeUnit getTokenExpirationUnit() {
+ return tokenExpirationUnit;
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public boolean isSocial() {
+ return social;
+ }
+
+ public boolean isUserRegistration() {
+ return userRegistration;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
}
- public void setKey(String key) {
- this.key = key;
+ public void setInitialRoles(String[] initialRoles) {
+ this.initialRoles = initialRoles;
}
public void setName(String name) {
this.name = name;
}
- public void setOwner(String owner) {
- this.owner = owner;
+ public void setRoles(String[] roles) {
+ this.roles = roles;
+ }
+
+ public void setSocial(boolean social) {
+ this.social = social;
+ }
+
+ public void setTokenExpiration(long tokenExpiration) {
+ this.tokenExpiration = tokenExpiration;
+ }
+
+ public void setTokenExpirationUnit(TimeUnit tokenExpirationUnit) {
+ this.tokenExpirationUnit = tokenExpirationUnit;
+ }
+
+ public void setUserRegistration(boolean userRegistration) {
+ this.userRegistration = userRegistration;
}
}
diff --git a/ui/src/main/resources/META-INF/resources/ui/index.html b/ui/src/main/resources/META-INF/resources/ui/index.html
index df90d48..49a3ce9 100644
--- a/ui/src/main/resources/META-INF/resources/ui/index.html
+++ b/ui/src/main/resources/META-INF/resources/ui/index.html
@@ -12,12 +12,12 @@
<link href="css/styles.css" rel="stylesheet">
+<script src="lib/jquery/jquery-1.10.2.js"></script>
+
<script src="lib/angular/angular.js"></script>
<script src="lib/angular/angular-resource.js"></script>
<script src="lib/angular/ui-bootstrap-tpls-0.4.0.js"></script>
-<script src="lib/jquery/jquery-1.10.2.js"></script>
-
<script src="js/app.js"></script>
<script src="js/controllers.js"></script>
<script src="js/services.js"></script>
diff --git a/ui/src/main/resources/META-INF/resources/ui/js/app.js b/ui/src/main/resources/META-INF/resources/ui/js/app.js
index 24f6d82..bc4bc53 100644
--- a/ui/src/main/resources/META-INF/resources/ui/js/app.js
+++ b/ui/src/main/resources/META-INF/resources/ui/js/app.js
@@ -4,7 +4,24 @@ var module = angular.module('keycloak', [ 'keycloak.services', 'keycloak.control
var resourceRequests = 0;
module.config([ '$routeProvider', function($routeProvider) {
- $routeProvider.when('/applications/:key', {
+ $routeProvider.when('/create/application', {
+ templateUrl : 'partials/application-detail.html',
+ resolve : {
+ applications : function(ApplicationListLoader) {
+ return ApplicationListLoader();
+ },
+ application : function(ApplicationLoader) {
+ return {};
+ },
+ realms : function(RealmListLoader) {
+ return RealmListLoader();
+ },
+ providers : function(ProviderListLoader) {
+ return ProviderListLoader();
+ }
+ },
+ controller : 'ApplicationDetailCtrl'
+ }).when('/applications/:application', {
templateUrl : 'partials/application-detail.html',
resolve : {
applications : function(ApplicationListLoader) {
@@ -29,7 +46,7 @@ module.config([ '$routeProvider', function($routeProvider) {
}
},
controller : 'ApplicationListCtrl'
- }).when('/realms/:realmKey/users/:userId', {
+ }).when('/realms/:realm/users/:user', {
templateUrl : 'partials/user-detail.html',
resolve : {
realms : function(RealmListLoader) {
@@ -43,7 +60,7 @@ module.config([ '$routeProvider', function($routeProvider) {
}
},
controller : 'UserDetailCtrl'
- }).when('/realms/:realmKey/users', {
+ }).when('/realms/:realm/users', {
templateUrl : 'partials/user-list.html',
resolve : {
realms : function(RealmListLoader) {
@@ -57,7 +74,18 @@ module.config([ '$routeProvider', function($routeProvider) {
}
},
controller : 'UserListCtrl'
- }).when('/realms/:realmKey', {
+ }).when('/create/realm', {
+ templateUrl : 'partials/realm-detail.html',
+ resolve : {
+ realms : function(RealmListLoader) {
+ return RealmListLoader();
+ },
+ realm : function(RealmLoader) {
+ return {};
+ }
+ },
+ controller : 'RealmDetailCtrl'
+ }).when('/realms/:realm', {
templateUrl : 'partials/realm-detail.html',
resolve : {
realms : function(RealmListLoader) {
@@ -126,4 +154,49 @@ module.factory('spinnerInterceptor', function($q, $window, $rootScope, $location
return $q.reject(response);
});
};
+});
+
+module.directive('kcInput', function() {
+ var d = {
+ scope : true,
+ replace: false,
+ link : function(scope, element, attrs) {
+ var form = element.closest('form');
+ var label = element.children('label');
+ var input = element.children('input');
+
+ var id = form.attr('name') + '.' + input.attr('name');
+
+ element.attr('class', 'control-group');
+
+ label.attr('class', 'control-label');
+ label.attr('for', id);
+
+ input.wrap('<div class="controls"/>');
+ input.attr('id', id);
+
+ if (!input.attr('placeHolder')) {
+ input.attr('placeHolder', label.text());
+ }
+
+ if (input.attr('required')) {
+ label.append(' <span class="required">*</span>');
+ }
+ }
+ };
+ return d;
+});
+
+module.directive('kcEnter', function() {
+ return function(scope, element, attrs) {
+ element.bind("keydown keypress", function(event) {
+ if(event.which === 13) {
+ scope.$apply(function(){
+ scope.$eval(attrs.kcEnter);
+ });
+
+ event.preventDefault();
+ }
+ });
+ };
});
\ No newline at end of file
diff --git a/ui/src/main/resources/META-INF/resources/ui/js/controllers.js b/ui/src/main/resources/META-INF/resources/ui/js/controllers.js
index 041af40..f15a05f 100644
--- a/ui/src/main/resources/META-INF/resources/ui/js/controllers.js
+++ b/ui/src/main/resources/META-INF/resources/ui/js/controllers.js
@@ -1,308 +1,361 @@
'use strict';
-var module = angular.module('keycloak.controllers', [ 'keycloak.services' ]);
+var module = angular.module('keycloak.controllers', [ 'keycloak.services' ]);
module.controller('GlobalCtrl', function($scope, Auth, $location, Notifications) {
- $scope.addMessage = function() {
- Notifications.success("test");
- };
-
- $scope.auth = Auth;
-
- $scope.$watch(function() {
- return $location.path();
- }, function() {
- $scope.path = $location.path().substring(1).split("/");
- });
-});
+ $scope.addMessage = function() {
+ Notifications.success("test");
+ };
+
+ $scope.auth = Auth;
+ $scope.$watch(function() {
+ return $location.path();
+ }, function() {
+ $scope.path = $location.path().substring(1).split("/");
+ });
+});
module.controller('ApplicationListCtrl', function($scope, applications) {
- $scope.applications = applications;
+ $scope.applications = applications;
});
-module.controller('ApplicationDetailCtrl', function($scope, applications, application, Application, realms, providers, $location, $window, $dialog, Notifications) {
- $scope.application = angular.copy(application);
- $scope.applications = applications;
- $scope.realms = realms;
- $scope.providers = providers;
-
- $scope.callbackUrl = $window.location.origin + "/ejs-identity/api/callback/" + application.key;
-
- $scope.create = !application.key;
-
- $scope.changed = $scope.create;
-
- $scope.$watch('application', function() {
- if (!angular.equals($scope.application, application)) {
- $scope.changed = true;
- }
- }, true);
-
- $scope.save = function() {
- if ($scope.applicationForm.$valid) {
- if (!$scope.application.key) {
- Application.save($scope.application, function(data, headers) {
- var l = headers().location;
- var key = l.substring(l.lastIndexOf("/") + 1);
- $location.url("/applications/" + key);
- Notifications.success("Created application");
- });
- } else {
- Application.update($scope.application, function() {
- $scope.changed = false;
- application = angular.copy($scope.application);
- if ($scope.create) {
- $location.url("/applications/" + $scope.application.key);
- }
- Notifications.success("Saved changes to the application");
- });
- }
- } else {
- $scope.applicationForm.showErrors = true;
- }
- };
-
- $scope.reset = function() {
- $scope.application = angular.copy(application);
- $scope.changed = false;
- $scope.applicationForm.showErrors = false;
- };
-
- $scope.cancel = function() {
- $location.url("/applications");
- };
-
- $scope.remove = function() {
- var title = 'Delete ' + $scope.application.name;
- var msg = 'Are you sure you want to permanently delete this application?';
- var btns = [ {
- result : 'cancel',
- label : 'Cancel'
- }, {
- result : 'ok',
- label : 'Delete this application',
- cssClass : 'btn-primary'
- } ];
-
- $dialog.messageBox(title, msg, btns).open().then(function(result) {
- if (result == "ok") {
- $scope.application.$remove(function() {
- $location.url("/applications");
- Notifications.success("Deleted application");
- });
- }
- });
- };
-
- $scope.availableProviders = [];
-
- $scope.addProvider = function() {
- if (!$scope.application.providers) {
- $scope.application.providers = [];
- }
-
- $scope.application.providers.push({
- "providerId" : $scope.newProviderId
- });
-
- $scope.newProviderId = null;
- };
-
- $scope.getProviderDescription = function(providerId) {
- for ( var i = 0; i < $scope.providers.length; i++) {
- if ($scope.providers[i].id == providerId) {
- return $scope.providers[i];
- }
- }
- };
-
- $scope.removeProvider = function(i) {
- $scope.application.providers.splice(i, 1);
- };
-
- var updateAvailableProviders = function() {
- $scope.availableProviders.splice(0, $scope.availableProviders.length);
-
- for ( var i in $scope.providers) {
- var add = true;
-
- for ( var j in $scope.application.providers) {
- if ($scope.application.providers[j].providerId == $scope.providers[i].id) {
- add = false;
- break;
- }
- }
-
- if (add) {
- $scope.availableProviders.push($scope.providers[i]);
- }
- }
- };
-
- $scope.openHelp = function(i) {
- $scope.providerHelpModal = true;
- $scope.providerHelp = {};
- $scope.providerHelp.index = i;
- $scope.providerHelp.description = $scope.getProviderDescription($scope.application.providers[i].providerId);
- };
-
- $scope.closeHelp = function() {
- $scope.providerHelpModal = false;
- $scope.providerHelp = null;
- };
-
- $scope.$watch("providers.length + application.providers.length", updateAvailableProviders);
+module.controller('ApplicationDetailCtrl', function($scope, applications, application, Application, realms, providers, $location, $window, $dialog,
+ Notifications) {
+ $scope.application = angular.copy(application);
+ $scope.applications = applications;
+ $scope.realms = realms;
+ $scope.providers = providers;
+
+ $scope.callbackUrl = $window.location.origin + "/ejs-identity/api/callback/" + application.id;
+
+ $scope.create = !application.id;
+
+ $scope.changed = $scope.create;
+
+ $scope.$watch('application', function() {
+ if (!angular.equals($scope.application, application)) {
+ $scope.changed = true;
+ }
+ }, true);
+
+
+ $scope.addRole = function() {
+ if ($scope.newRole) {
+ if (!$scope.application.roles) {
+ $scope.application.roles = [];
+ }
+
+ $scope.application.roles.push($scope.newRole);
+ $scope.newRole = null;
+ }
+ }
+
+ $scope.removeRole = function(i) {
+ $scope.application.roles.splice(i, 1);
+ };
+
+ $scope.addInitialRole = function() {
+ if ($scope.newInitialRole) {
+ if (!$scope.application.initialRoles) {
+ $scope.application.initialRoles = [];
+ }
+
+ $scope.application.initialRoles.push($scope.newInitialRole);
+ $scope.newInitialRole = null;
+ }
+ }
+
+ $scope.removeInitialRole = function(i) {
+ $scope.application.initialRoles.splice(i, 1);
+ };
+
+ $scope.save = function() {
+ if ($scope.applicationForm.$valid) {
+ if ($scope.create) {
+ Application.save($scope.application, function(data, headers) {
+ var l = headers().location;
+ var id = l.substring(l.lastIndexOf("/") + 1);
+ $location.url("/applications/" + id);
+ Notifications.success("Created application");
+ });
+ } else {
+ Application.update($scope.application, function() {
+ $scope.changed = false;
+ application = angular.copy($scope.application);
+ Notifications.success("Saved changes to the application");
+ });
+ }
+ } else {
+ $scope.applicationForm.showErrors = true;
+ }
+ };
+
+ $scope.reset = function() {
+ $scope.application = angular.copy(application);
+ $scope.changed = false;
+ $scope.applicationForm.showErrors = false;
+ };
+
+ $scope.cancel = function() {
+ $location.url("/applications");
+ };
+
+ $scope.remove = function() {
+ var title = 'Delete ' + $scope.application.name;
+ var msg = 'Are you sure you want to permanently delete this application?';
+ var btns = [ {
+ result : 'cancel',
+ label : 'Cancel'
+ }, {
+ result : 'ok',
+ label : 'Delete this application',
+ cssClass : 'btn-primary'
+ } ];
+
+ $dialog.messageBox(title, msg, btns).open().then(function(result) {
+ if (result == "ok") {
+ $scope.application.$remove(function() {
+ $location.url("/applications");
+ Notifications.success("Deleted application");
+ });
+ }
+ });
+ };
+
+ $scope.availableProviders = [];
+
+ $scope.addProvider = function() {
+ if (!$scope.application.providers) {
+ $scope.application.providers = [];
+ }
+
+ $scope.application.providers.push({
+ "providerId" : $scope.newProviderId
+ });
+
+ $scope.newProviderId = null;
+ };
+
+ $scope.getProviderDescription = function(providerId) {
+ for ( var i = 0; i < $scope.providers.length; i++) {
+ if ($scope.providers[i].id == providerId) {
+ return $scope.providers[i];
+ }
+ }
+ };
+
+ $scope.removeProvider = function(i) {
+ $scope.application.providers.splice(i, 1);
+ };
+
+ var updateAvailableProviders = function() {
+ $scope.availableProviders.splice(0, $scope.availableProviders.length);
+
+ for ( var i in $scope.providers) {
+ var add = true;
+
+ for ( var j in $scope.application.providers) {
+ if ($scope.application.providers[j].providerId == $scope.providers[i].id) {
+ add = false;
+ break;
+ }
+ }
+
+ if (add) {
+ $scope.availableProviders.push($scope.providers[i]);
+ }
+ }
+ };
+
+ $scope.openHelp = function(i) {
+ $scope.providerHelpModal = true;
+ $scope.providerHelp = {};
+ $scope.providerHelp.index = i;
+ $scope.providerHelp.description = $scope.getProviderDescription($scope.application.providers[i].providerId);
+ };
+
+ $scope.closeHelp = function() {
+ $scope.providerHelpModal = false;
+ $scope.providerHelp = null;
+ };
+
+ $scope.$watch("providers.length + application.providers.length", updateAvailableProviders);
});
module.controller('RealmListCtrl', function($scope, realms) {
- $scope.realms = realms;
+ $scope.realms = realms;
});
module.controller('UserListCtrl', function($scope, realms, realm, users) {
- $scope.realms = realms;
- $scope.realm = realm;
- $scope.users = users;
+ $scope.realms = realms;
+ $scope.realm = realm;
+ $scope.users = users;
});
module.controller('UserDetailCtrl', function($scope, realms, realm, user, User, $location, $dialog, Notifications) {
- $scope.realms = realms;
- $scope.realm = realm;
- $scope.user = angular.copy(user);
- $scope.create = !user.userId;
-
- $scope.changed = $scope.create;
-
- $scope.$watch('user', function() {
- if (!angular.equals($scope.user, user)) {
- $scope.changed = true;
- }
- }, true);
-
- $scope.save = function() {
- if ($scope.userForm.$valid) {
- User.save({
- realmKey : realm.key
- }, $scope.user, function() {
- $scope.changed = false;
- user = angular.copy($scope.user);
-
- if ($scope.create) {
- $location.url("/realms/" + realm.key + "/users/" + user.userId);
- Notifications.success("Created user");
- } else {
- Notifications.success("Saved changes to user");
- }
- });
- } else {
- $scope.userForm.showErrors = true;
- }
- };
-
- $scope.reset = function() {
- $scope.user = angular.copy(user);
- $scope.changed = false;
- $scope.userForm.showErrors = false;
- };
-
- $scope.cancel = function() {
- $location.url("/realms/" + realm.key + "/users");
- };
-
- $scope.remove = function() {
- var title = 'Delete ' + $scope.user.userId;
- var msg = 'Are you sure you want to permanently delete this user?';
- var btns = [ {
- result : 'cancel',
- label : 'Cancel'
- }, {
- result : 'ok',
- label : 'Delete this user',
- cssClass : 'btn-primary'
- } ];
-
- $dialog.messageBox(title, msg, btns).open().then(function(result) {
- if (result == "ok") {
- $scope.user.$remove({
- realmKey : realm.key,
- userId : $scope.user.userId
- }, function() {
- $location.url("/realms/" + realm.key + "/users");
- Notifications.success("Deleted user");
- });
- }
- });
- };
+ $scope.realms = realms;
+ $scope.realm = realm;
+ $scope.user = angular.copy(user);
+ $scope.create = !user.userId;
+
+ $scope.changed = $scope.create;
+
+ $scope.$watch('user', function() {
+ if (!angular.equals($scope.user, user)) {
+ $scope.changed = true;
+ }
+ }, true);
+
+ $scope.save = function() {
+ if ($scope.userForm.$valid) {
+ User.save({
+ realmKey : realm.key
+ }, $scope.user, function() {
+ $scope.changed = false;
+ user = angular.copy($scope.user);
+
+ if ($scope.create) {
+ $location.url("/realms/" + realm.key + "/users/" + user.userId);
+ Notifications.success("Created user");
+ } else {
+ Notifications.success("Saved changes to user");
+ }
+ });
+ } else {
+ $scope.userForm.showErrors = true;
+ }
+ };
+
+ $scope.reset = function() {
+ $scope.user = angular.copy(user);
+ $scope.changed = false;
+ $scope.userForm.showErrors = false;
+ };
+
+ $scope.cancel = function() {
+ $location.url("/realms/" + realm.key + "/users");
+ };
+
+ $scope.remove = function() {
+ var title = 'Delete ' + $scope.user.userId;
+ var msg = 'Are you sure you want to permanently delete this user?';
+ var btns = [ {
+ result : 'cancel',
+ label : 'Cancel'
+ }, {
+ result : 'ok',
+ label : 'Delete this user',
+ cssClass : 'btn-primary'
+ } ];
+
+ $dialog.messageBox(title, msg, btns).open().then(function(result) {
+ if (result == "ok") {
+ $scope.user.$remove({
+ realmKey : realm.key,
+ userId : $scope.user.userId
+ }, function() {
+ $location.url("/realms/" + realm.key + "/users");
+ Notifications.success("Deleted user");
+ });
+ }
+ });
+ };
});
module.controller('RealmDetailCtrl', function($scope, Realm, realms, realm, $location, $dialog, Notifications) {
- $scope.realms = realms;
- $scope.realm = angular.copy(realm);
- $scope.create = !realm.name;
-
- $scope.changed = $scope.create;
-
- $scope.$watch('realm', function() {
- if (!angular.equals($scope.realm, realm)) {
- $scope.changed = true;
- }
- }, true);
-
- $scope.save = function() {
- if ($scope.realmForm.$valid) {
- if (!$scope.realm.key) {
- Realm.save($scope.realm, function(data, headers) {
- var l = headers().location;
- var key = l.substring(l.lastIndexOf("/") + 1);
- $location.url("/realms/" + key);
- Notifications.success("Created realm");
- });
- } else {
- Realm.update($scope.realm, function() {
- $scope.changed = false;
- realm = angular.copy($scope.realm);
- if ($scope.create) {
- $location.url("/realms/" + $scope.realm.key);
- Notifications.success("Created realm");
- } else {
- Notifications.success("Saved changes to realm");
- }
- });
- }
- } else {
- $scope.realmForm.showErrors = true;
- }
- };
-
- $scope.reset = function() {
- $scope.realm = angular.copy(realm);
- $scope.changed = false;
- $scope.realmForm.showErrors = false;
- };
-
- $scope.cancel = function() {
- $location.url("/realms");
- };
-
- $scope.remove = function() {
- var title = 'Delete ' + $scope.realm.name;
- var msg = 'Are you sure you want to permanently delete this realm?';
- var btns = [ {
- result : 'cancel',
- label : 'Cancel'
- }, {
- result : 'ok',
- label : 'Delete this realm',
- cssClass : 'btn-primary'
- } ];
-
- $dialog.messageBox(title, msg, btns).open().then(function(result) {
- if (result == "ok") {
- Realm.remove($scope.realm, function() {
- $location.url("/realms");
- Notifications.success("Deleted realm");
- });
- }
- });
- };
+ $scope.realms = realms;
+ $scope.realm = angular.copy(realm);
+ $scope.create = !realm.name;
+
+ $scope.changed = $scope.create;
+
+ $scope.$watch('realm', function() {
+ if (!angular.equals($scope.realm, realm)) {
+ $scope.changed = true;
+ }
+ }, true);
+
+ $scope.addRole = function() {
+ if ($scope.newRole) {
+ if (!$scope.realm.roles) {
+ $scope.realm.roles = [];
+ }
+
+ $scope.realm.roles.push($scope.newRole);
+ $scope.newRole = null;
+ }
+ }
+
+ $scope.removeRole = function(i) {
+ $scope.realm.roles.splice(i, 1);
+ };
+
+ $scope.addInitialRole = function() {
+ if ($scope.newInitialRole) {
+ if (!$scope.realm.initialRoles) {
+ $scope.realm.initialRoles = [];
+ }
+
+ $scope.realm.initialRoles.push($scope.newInitialRole);
+ $scope.newInitialRole = null;
+ }
+ }
+
+ $scope.removeInitialRole = function(i) {
+ $scope.realm.initialRoles.splice(i, 1);
+ };
+
+ $scope.save = function() {
+ if ($scope.realmForm.$valid) {
+ if ($scope.create) {
+ Realm.save($scope.realm, function(data, headers) {
+ var l = headers().location;
+ var id = l.substring(l.lastIndexOf("/") + 1);
+ $location.url("/realms/" + id);
+ Notifications.success("Created realm");
+ });
+ } else {
+ Realm.update($scope.realm, function() {
+ $scope.changed = false;
+ realm = angular.copy($scope.realm);
+ Notifications.success("Saved changes to realm");
+ });
+ }
+ } else {
+ $scope.realmForm.showErrors = true;
+ }
+ };
+
+ $scope.reset = function() {
+ $scope.realm = angular.copy(realm);
+ $scope.changed = false;
+ $scope.realmForm.showErrors = false;
+ };
+
+ $scope.cancel = function() {
+ $location.url("/realms");
+ };
+
+ $scope.remove = function() {
+ var title = 'Delete ' + $scope.realm.name;
+ var msg = 'Are you sure you want to permanently delete this realm?';
+ var btns = [ {
+ result : 'cancel',
+ label : 'Cancel'
+ }, {
+ result : 'ok',
+ label : 'Delete this realm',
+ cssClass : 'btn-primary'
+ } ];
+
+ $dialog.messageBox(title, msg, btns).open().then(function(result) {
+ if (result == "ok") {
+ Realm.remove($scope.realm, function() {
+ $location.url("/realms");
+ Notifications.success("Deleted realm");
+ });
+ }
+ });
+ };
});
\ No newline at end of file
diff --git a/ui/src/main/resources/META-INF/resources/ui/js/services.js b/ui/src/main/resources/META-INF/resources/ui/js/services.js
index 6e58fa7..6f70643 100644
--- a/ui/src/main/resources/META-INF/resources/ui/js/services.js
+++ b/ui/src/main/resources/META-INF/resources/ui/js/services.js
@@ -34,8 +34,8 @@ module.factory('Notifications', function($rootScope, $timeout) {
});
module.factory('Application', function($resource) {
- return $resource('/keycloak-server/ui/api/applications/:key', {
- key : '@key'
+ return $resource('/keycloak-server/ui/api/applications/:id', {
+ id : '@id'
}, {
update : {
method : 'PUT'
@@ -57,20 +57,16 @@ module.factory('ApplicationListLoader', function(Application, $q) {
module.factory('ApplicationLoader', function(Application, $route, $q) {
return function() {
- var key = $route.current.params.key;
- if (key == 'new') {
- return {};
- } else {
- var delay = $q.defer();
- Application.get({
- key : key
- }, function(application) {
- delay.resolve(application);
- }, function() {
- delay.reject('Unable to fetch application ' + key);
- });
- return delay.promise;
- }
+ var id = $route.current.params.application;
+ var delay = $q.defer();
+ Application.get({
+ id : id
+ }, function(application) {
+ delay.resolve(application);
+ }, function() {
+ delay.reject('Unable to fetch application ' + id);
+ });
+ return delay.promise;
};
});
@@ -91,8 +87,8 @@ module.factory('ProviderListLoader', function(Provider, $q) {
});
module.factory('Realm', function($resource) {
- return $resource('/keycloak-server/ui/api/realms/:key', {
- key : '@key'
+ return $resource('/keycloak-server/ui/api/realms/:id', {
+ id : '@id'
}, {
update : {
method : 'PUT'
@@ -114,27 +110,23 @@ module.factory('RealmListLoader', function(Realm, $q) {
module.factory('RealmLoader', function(Realm, $route, $q) {
return function() {
- var key = $route.current.params.realmKey;
- if (key == 'new') {
- return {};
- } else {
- var delay = $q.defer();
- Realm.get({
- key : key
- }, function(realm) {
- delay.resolve(realm);
- }, function() {
- delay.reject('Unable to fetch key ' + key);
- });
- return delay.promise;
- }
+ var id = $route.current.params.realm;
+ var delay = $q.defer();
+ Realm.get({
+ id : id
+ }, function(realm) {
+ delay.resolve(realm);
+ }, function() {
+ delay.reject('Unable to fetch realm ' + name);
+ });
+ return delay.promise;
};
});
module.factory('User', function($resource) {
- return $resource('/ejs-identity/api/im/:realmKey/users/:userId', {
- realmKey : '@realmKey',
- userId : '@userId'
+ return $resource('/ejs-identity/api/im/:realm/users/:id', {
+ realm : '@realm',
+ id : '@id'
}, {
save : {
method : 'PUT'
@@ -158,18 +150,18 @@ module.factory('UserListLoader', function(User, $route, $q) {
module.factory('UserLoader', function(User, $route, $q) {
return function() {
- var userId = $route.current.params.userId;
- if (userId == 'new') {
+ var name = $route.current.params.user;
+ if (name == 'new') {
return {};
} else {
var delay = $q.defer();
User.get({
- realmKey : $route.current.params.realmKey,
- userId : userId
+ realm : $route.current.params.realm,
+ name : name
}, function(user) {
delay.resolve(user);
}, function() {
- delay.reject('Unable to fetch user ' + $route.current.params.userId);
+ delay.reject('Unable to fetch user ' + name);
});
return delay.promise;
}
@@ -182,7 +174,7 @@ module.service('Auth', function($resource, $http, $location, $routeParams) {
};
auth.user = {
userId : 'test',
- displayName: 'Test User'
+ displayName : 'Test User'
};
return auth;
});
\ No newline at end of file
diff --git a/ui/src/main/resources/META-INF/resources/ui/partials/application-detail.html b/ui/src/main/resources/META-INF/resources/ui/partials/application-detail.html
index 924ba07..f274cc2 100644
--- a/ui/src/main/resources/META-INF/resources/ui/partials/application-detail.html
+++ b/ui/src/main/resources/META-INF/resources/ui/partials/application-detail.html
@@ -2,7 +2,7 @@
<div class="row">
<aside class="span3" data-ng-include data-src="'partials/application-menu.html'"></aside>
<div id="actions-bg"></div>
-
+
<div id="container-right" class="span9">
<h1>
<span class="gray" data-ng-show="create">New Application</span> <span class="gray" data-ng-hide="create">{{application.name}}</span>
@@ -15,87 +15,55 @@
<form class="form-horizontal" name="applicationForm" novalidate>
<fieldset>
<legend>Settings</legend>
- <div>
- <div class="control-group">
- <label class="control-label" for="name">Name <span class="required">*</span></label>
- <div class="controls">
- <input type="text" class="input-xlarge" id="name" name="name" data-ng-model="application.name" autofocus required>
- </div>
- </div>
- <div class="control-group">
- <label class="control-label" for="callbackUrl">Callback URL <span class="required">*</span></label>
- <div class="controls">
- <input type="text" class="input-xxlarge" id="callbackUrl" name="callbackUrl" data-ng-model="application.callbackUrl"
- required>
- </div>
- </div>
- <div class="control-group">
- <label class="control-label" for="javaScriptOrigin">JavaScript Origin </label>
- <div class="controls">
- <input type="text" class="input-xxlarge" id="javaScriptOrigin" data-ng-model="application.javaScriptOrigin">
- </div>
- </div>
- <div class="control-group">
- <label class="control-label" for="key">Key </label>
- <div class="controls">
- <input class="input-xxlarge" type="text" id="key" data-ng-model="application.key" data-ng-readonly="!(auth.root && create)">
- </div>
- </div>
- <div class="control-group">
- <label class="control-label" for="secret">Secret </label>
- <div class="controls">
- <input class="input-xxlarge" type="text" id="secret" data-ng-model="application.secret" data-ng-readonly="!(auth.root && create)">
- </div>
- </div>
- <div class="control-group">
- <label class="control-label" for="realm">Realm <span class="required">*</span></label>
- <div class="controls">
- <select data-ng-model="application.realm" id="realm" name="realm" data-ng-required>
- <option data-ng-repeat="r in realms" value="{{r.key}}" data-ng-selected="r.key == application.realm">{{r.name}}</option>
- </select>
- </div>
- </div>
- <div class="control-group" data-ng-show="auth.root">
- <label class="control-label" for="owner">Owner </label>
- <div class="controls">
- <input class="input-xxlarge" type="text" id="owner" data-ng-model="application.owner">
- </div>
+
+ <div data-kc-input>
+ <label>Name</label>
+ <input class="input-xlarge" type="text" name="name" data-ng-model="application.name" autofocus required>
+ </div>
+
+ <div data-kc-input>
+ <label>Enabled</label>
+ <input class="input-xlarge" type="checkbox" name="enabled" data-ng-model="application.enabled">
+ </div>
+
+ <div class="control-group">
+ <label class="control-label" for="realm">Realm <span class="required">*</span></label>
+ <div class="controls">
+ <select data-ng-model="application.realm" id="realm" name="realm" data-ng-required>
+ <option data-ng-repeat="r in realms" value="{{r.id}}" data-ng-selected="r.id == application.realm">{{r.name}}</option>
+ </select>
</div>
</div>
</fieldset>
+
<fieldset>
- <legend>Identity Providers</legend>
- <div>
- <div class="input-append">
- <select data-ng-model="newProviderId">
- <option data-ng-repeat="p in availableProviders" value="{{p.id}}">{{p.name}}</option>
- </select>
- <button class="btn" data-ng-click="addProvider()" data-ng-disabled="!newProviderId">Add Provider</button>
- </div>
+ <legend>Roles</legend>
+
+ <div class="control-group">
+ <label class="control-label">Roles</label>
+ <div class="controls">
+ <span style="margin-right: 1em;" data-ng-repeat="r in application.roles">{{r}} <button data-ng-click="removeRole($index)"><i class="icon-remove"></i></button></span>
+
+ <div class="input-append">
+ <input class="input-small" type="text" data-ng-model="newRole" placeHolder="Role" data-kc-enter="addRole()" />
+ <button class="btn" type="button" data-ng-click="addRole()">Add</button>
+ </div>
+ </div>
+ </div>
- <table class="table table-striped table-bordered margin-top" data-ng-show="application.providers.length > 0">
- <thead>
- <tr>
- <th>Provider</th>
- <th>Key <span class="required">*</span></th>
- <th>Secret <span class="required">*</span></th>
- <th> </th>
- </tr>
- </thead>
- <tr data-ng-repeat="provider in application.providers">
- <td><input type="text" placeholder="Key" value="{{getProviderDescription(provider.providerId).name}}" readonly></td>
- <td>
- <input type="text" placeholder="Key" data-ng-model="provider.key" required>
- </td>
- <td>
- <input type="text" placeholder="Secret" data-ng-model="provider.secret" required>
- </td>
- <td><i class="icon-question-sign" data-ng-click="openHelp($index)"></i></td>
- <td><i class="icon-trash" data-ng-click="removeProvider($index)"></i></td>
- </tr>
- </table>
- </div>
- </fieldset>
+ <div class="control-group">
+ <label class="control-label">Initial Roles</label>
+ <div class="controls">
+ <span style="margin-right: 1em;" data-ng-repeat="r in application.initialRoles">{{r}} <button data-ng-click="removeInitialRole($index)"><i class="icon-remove"></i></button></span>
+
+ <div class="input-append">
+ <select style="width: auto;" data-ng-model="newInitialRole" data-ng-click="addInitialRole()">
+ <option data-ng-repeat="r in application.roles" value="{{r}}">{{r}}</option>
+ </select>
+ </div>
+ </div>
+ </div>
+ </fieldset>
<div class="form-actions" data-ng-show="create">
<button type="submit" data-ng-click="save()" class="btn btn-primary" data-ng-show="changed">Save</button>
@@ -113,16 +81,4 @@
<div id="container-right-bg"></div>
</div>
-</div>
-
-<div data-modal="providerHelpModal" data-close="closeHelp()" data-options="opts">
- <div class="modal-header">
- <h3>Configure {{providerHelp.description.name}}</h3>
- </div>
- <div class="modal-body">
- <div data-ng-include data-src="providerHelp && 'partials/provider/' + providerHelp.description.id + '-help.html'"></div>
- </div>
- <div class="modal-footer">
- <button class="btn" data-ng-click="closeHelp()">Close</button>
- </div>
</div>
\ No newline at end of file
diff --git a/ui/src/main/resources/META-INF/resources/ui/partials/application-list.html b/ui/src/main/resources/META-INF/resources/ui/partials/application-list.html
index 1b8c68e..fa5fe4e 100644
--- a/ui/src/main/resources/META-INF/resources/ui/partials/application-list.html
+++ b/ui/src/main/resources/META-INF/resources/ui/partials/application-list.html
@@ -4,7 +4,7 @@
<div id="actions-bg"></div>
<div id="container-right" class="span9">
- <a class="btn btn-small pull-right" href="#/applications/new">Add Application</a>
+ <a class="btn btn-small pull-right" href="#/create/application">Add Application</a>
<h1>
<span class="gray">Applications</span>
@@ -17,7 +17,7 @@
</tr>
</thead>
<tr data-ng-repeat="application in applications">
- <td><a href="#/applications/{{application.key}}">{{application.name}}</a></td>
+ <td><a href="#/applications/{{application.id}}">{{application.name}}</a></td>
</tr>
</table>
</div>
diff --git a/ui/src/main/resources/META-INF/resources/ui/partials/application-menu.html b/ui/src/main/resources/META-INF/resources/ui/partials/application-menu.html
index 7eec7a1..5197082 100644
--- a/ui/src/main/resources/META-INF/resources/ui/partials/application-menu.html
+++ b/ui/src/main/resources/META-INF/resources/ui/partials/application-menu.html
@@ -5,8 +5,8 @@
<span class="toggle">Applications</span>
</div>
<ul>
- <li data-ng-repeat="application in applications" data-ng-class="path[1] == application.key && 'active'">
- <a href="#/applications/{{application.key}}">{{application.name}}</a>
+ <li data-ng-repeat="application in applications" data-ng-class="path[1] == application.id && 'active'">
+ <a href="#/applications/{{application.id}}">{{application.name}}</a>
</li>
</ul>
</li>
diff --git a/ui/src/main/resources/META-INF/resources/ui/partials/realm-detail.html b/ui/src/main/resources/META-INF/resources/ui/partials/realm-detail.html
index db2e374..473e60b 100644
--- a/ui/src/main/resources/META-INF/resources/ui/partials/realm-detail.html
+++ b/ui/src/main/resources/META-INF/resources/ui/partials/realm-detail.html
@@ -14,25 +14,69 @@
<form class="form-horizontal" name="realmForm" novalidate>
<fieldset>
- <legend>Details</legend>
+ <legend>Settings</legend>
+
+ <div data-kc-input>
+ <label>Name</label>
+ <input class="input-xlarge" type="text" name="name" data-ng-model="realm.name" autofocus required>
+ </div>
+
+ <div data-kc-input>
+ <label>Enabled</label>
+ <input class="input-xlarge" type="checkbox" name="enabled" data-ng-model="realm.enabled">
+ </div>
+
+ <div data-kc-input>
+ <label>Social login</label>
+ <input class="input-xlarge" type="checkbox" name="social" data-ng-model="realm.social">
+ </div>
+
+ <div data-kc-input>
+ <label>User registration</label>
+ <input class="input-xlarge" type="checkbox" name="social" data-ng-model="realm.userRegistration">
+ </div>
+
<div class="control-group">
- <label class="control-label" for="name">Name <span class="required">*</span></label>
+ <label for="realmForm-tokenExpiration" class="control-label">Token expiration</label>
<div class="controls">
- <input type="text" class="input-xlarge" id="name" name="name" data-ng-model="realm.name" autofocus required>
- </div>
- </div>
- <div class="control-group">
- <label class="control-label" for="key">Key </label>
- <div class="controls">
- <input class="input-xxlarge" type="text" id="key" name="key" data-ng-model="realm.key" data-ng-readonly="!(auth.root && create)">
- </div>
- </div>
- <div class="control-group" data-ng-show="auth.root">
- <label class="control-label" for="owner">Owner </label>
- <div class="controls">
- <input class="input-xxlarge" type="text" id="owner" data-ng-model="realm.owner">
- </div>
- </div>
+ <input class="input-small" type="text" name="tokenExpiration" data-ng-model="realm.tokenExpiration">
+ <select style="width: auto;" name="tokenExpirationUnit" data-ng-model="realm.tokenExpirationUnit">
+ <option value="SECONDS" data-ng-selected="!realm.tokenExpirationUnit">Seconds</option>
+ <option value="MINUTES">Minutes</option>
+ <option value="HOURS">Hours</option>
+ <option value="DAYS">Days</option>
+ </select>
+ </div>
+ </div>
+ </fieldset>
+
+ <fieldset>
+ <legend>Roles</legend>
+
+ <div class="control-group">
+ <label class="control-label">Roles</label>
+ <div class="controls">
+ <span style="margin-right: 1em;" data-ng-repeat="r in realm.roles">{{r}} <button data-ng-click="removeRole($index)"><i class="icon-remove"></i></button></span>
+
+ <div class="input-append">
+ <input class="input-small" type="text" data-ng-model="newRole" placeHolder="Role" data-kc-enter="addRole()" />
+ <button class="btn" type="button" data-ng-click="addRole()">Add</button>
+ </div>
+ </div>
+ </div>
+
+ <div class="control-group">
+ <label class="control-label">Initial Roles</label>
+ <div class="controls">
+ <span style="margin-right: 1em;" data-ng-repeat="r in realm.initialRoles">{{r}} <button data-ng-click="removeInitialRole($index)"><i class="icon-remove"></i></button></span>
+
+ <div class="input-append">
+ <select style="width: auto;" data-ng-model="newInitialRole" data-ng-click="addInitialRole()">
+ <option data-ng-repeat="r in realm.roles" value="{{r}}">{{r}}</option>
+ </select>
+ </div>
+ </div>
+ </div>
</fieldset>
<div class="form-actions" data-ng-show="create">
diff --git a/ui/src/main/resources/META-INF/resources/ui/partials/realm-list.html b/ui/src/main/resources/META-INF/resources/ui/partials/realm-list.html
index 2618643..344838e 100644
--- a/ui/src/main/resources/META-INF/resources/ui/partials/realm-list.html
+++ b/ui/src/main/resources/META-INF/resources/ui/partials/realm-list.html
@@ -4,7 +4,7 @@
<div id="actions-bg"></div>
<div id="container-right" class="span9">
- <a class="btn btn-small pull-right" href="#/realms/new">Add Realm</a>
+ <a class="btn btn-small pull-right" href="#/create/realm">Add Realm</a>
<h1>
<span class="gray">Realms</span>
@@ -17,7 +17,7 @@
</tr>
</thead>
<tr data-ng-repeat="r in realms">
- <td><a href="#/realms/{{r.key}}">{{r.name}}</a></td>
+ <td><a href="#/realms/{{r.id}}">{{r.name}}</a></td>
</tr>
</table>
</div>
diff --git a/ui/src/main/resources/META-INF/resources/ui/partials/realm-menu.html b/ui/src/main/resources/META-INF/resources/ui/partials/realm-menu.html
index 646a79a..f568909 100644
--- a/ui/src/main/resources/META-INF/resources/ui/partials/realm-menu.html
+++ b/ui/src/main/resources/META-INF/resources/ui/partials/realm-menu.html
@@ -5,11 +5,11 @@
<span class="toggle">Realms</span>
</div>
<ul>
- <li data-ng-repeat="r in realms" data-ng-class="realm.key == r.key && 'active'">
- <a href=#/realms/{{r.key}}>{{r.name}}</a>
- <ul class="sub-items" data-ng-show="realm.key == r.key">
- <li data-ng-class="!path[2] && 'active'"><a href="#/realms/{{r.key}}">Configuration</a></li>
- <li data-ng-class="path[2] == 'users' && 'active'"><a href="#/realms/{{r.key}}/users">Users</a></li>
+ <li data-ng-repeat="r in realms" data-ng-class="realm.id == r.id && 'active'">
+ <a href=#/realms/{{r.id}}>{{r.name}}</a>
+ <ul class="sub-items" data-ng-show="realm.id == r.id">
+ <li data-ng-class="!path[2] && 'active'"><a href="#/realms/{{r.id}}">Configuration</a></li>
+ <li data-ng-class="path[2] == 'users' && 'active'"><a href="#/realms/{{r.id}}/users">Users</a></li>
</ul>
</li>
</ul>