Added support for role mapping to applications and realms
This commit is contained in:
parent
23922d6ab7
commit
8b8dc45e34
14 changed files with 507 additions and 446 deletions
|
@ -1,12 +1,11 @@
|
|||
package org.keycloak.ui.example;
|
||||
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.ws.rs.ApplicationPath;
|
||||
|
@ -18,8 +17,10 @@ import javax.ws.rs.PUT;
|
|||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
|
||||
@ApplicationPath("ui/api")
|
||||
@Path("")
|
||||
|
@ -29,7 +30,27 @@ public class Admin extends javax.ws.rs.core.Application {
|
|||
|
||||
private static Map<String, Realm> realms = new HashMap<String, Realm>();
|
||||
|
||||
private static Map<UserId, User> users = new HashMap<UserId, User>();
|
||||
private static Map<String, List<User>> users = new HashMap<String, List<User>>();
|
||||
|
||||
private static Map<Id, List<String>> roles = new HashMap<Id, List<String>>();
|
||||
|
||||
static {
|
||||
Realm realm = new Realm();
|
||||
realm.setId(UUID.randomUUID().toString());
|
||||
realm.setName("Test realm");
|
||||
realm.setEnabled(true);
|
||||
realm.setRoles(new String[] { "admin", "user" });
|
||||
realms.put(realm.getId(), realm);
|
||||
|
||||
User user = new User();
|
||||
user.setUserId("user");
|
||||
user.setPassword("password");
|
||||
|
||||
List<User> l = new LinkedList<User>();
|
||||
l.add(user);
|
||||
|
||||
users.put(realm.getId(), l);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("applications/{id}")
|
||||
|
@ -41,7 +62,6 @@ public class Admin extends javax.ws.rs.core.Application {
|
|||
@Path("realms/{id}")
|
||||
public void deleteRealm(@PathParam("id") String id) {
|
||||
realms.remove(id);
|
||||
users.remove(id);
|
||||
}
|
||||
|
||||
@GET
|
||||
|
@ -65,7 +85,6 @@ public class Admin extends javax.ws.rs.core.Application {
|
|||
return realms.get(id);
|
||||
}
|
||||
|
||||
|
||||
@GET
|
||||
@Path("realms")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
|
@ -97,6 +116,11 @@ public class Admin extends javax.ws.rs.core.Application {
|
|||
@Path("applications/{id}")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
public void save(@PathParam("id") String id, Application application) {
|
||||
try {
|
||||
deleteApplication(id);
|
||||
} catch (WebApplicationException e) {
|
||||
}
|
||||
|
||||
applications.put(id, application);
|
||||
}
|
||||
|
||||
|
@ -104,6 +128,11 @@ public class Admin extends javax.ws.rs.core.Application {
|
|||
@Path("realms/{id}")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
public void save(@PathParam("id") String id, Realm realm) {
|
||||
try {
|
||||
deleteRealm(id);
|
||||
} catch (WebApplicationException e) {
|
||||
}
|
||||
|
||||
realms.put(id, realm);
|
||||
}
|
||||
|
||||
|
@ -111,33 +140,91 @@ public class Admin extends javax.ws.rs.core.Application {
|
|||
@Path("realms/{realm}/users/{id}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public User getUser(@PathParam("realm") String realm, @PathParam("id") String id) {
|
||||
return users.get(new UserId(realm, id));
|
||||
for (User u : getUsers(realm)) {
|
||||
if (u.getUserId().equals(id)) {
|
||||
return u;
|
||||
}
|
||||
}
|
||||
throw new WebApplicationException(Status.NOT_FOUND);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("realms/{realm}/users")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public List<User> getUsers(@PathParam("realm") String realm) {
|
||||
LinkedList<User> list = new LinkedList<User>();
|
||||
for (Entry<UserId, User> e : users.entrySet()) {
|
||||
if (e.getKey().getRealm().equals(realm)) {
|
||||
list.add(e.getValue());
|
||||
}
|
||||
List<User> l = users.get(realm);
|
||||
if (l == null) {
|
||||
l = new LinkedList<User>();
|
||||
users.put(realm, l);
|
||||
}
|
||||
return list;
|
||||
return l;
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("realms/{realm}/users/{id}")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
public void save(@PathParam("realm") String realm, @PathParam("id") String id, User user) {
|
||||
users.put(new UserId(realm, id), user);
|
||||
try {
|
||||
deleteUser(realm, id);
|
||||
} catch (WebApplicationException e) {
|
||||
}
|
||||
|
||||
getUsers(realm).add(user);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("realms/{realm}/users/{id}")
|
||||
public void deleteUser(@PathParam("realm") String realm, @PathParam("id") String id) {
|
||||
users.remove(new UserId(realm, id));
|
||||
getUsers(realm).remove(getUser(realm, id));
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("roles/{realm}/{role}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public List<User> getRoleMapping(@PathParam("realm") String realm, @PathParam("role") String role) {
|
||||
List<String> ids = getRoleMapping(new Id(realm, role));
|
||||
|
||||
List<User> users = new LinkedList<User>();
|
||||
List<User> realmUsers = getUsers(realm);
|
||||
for (String id : ids) {
|
||||
for (User u : realmUsers) {
|
||||
if (u.getUserId().equals(id)) {
|
||||
users.add(u);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return users;
|
||||
}
|
||||
|
||||
private List<String> getRoleMapping(Id id) {
|
||||
List<String> l = roles.get(id);
|
||||
if (l == null) {
|
||||
l = new LinkedList<String>();
|
||||
roles.put(id, l);
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("roles/{realm}/{role}/{user}")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
public void addRoleMapping(@PathParam("realm") String realm, @PathParam("role") String role,
|
||||
@PathParam("user") String user, User u) {
|
||||
getRoleMapping(new Id(realm, role)).add(user);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("roles/{realm}/{role}/{user}")
|
||||
public void deleteRoleMapping(@PathParam("realm") String realm, @PathParam("role") String role,
|
||||
@PathParam("user") String user) {
|
||||
Iterator<String> itr = getRoleMapping(new Id(realm, role)).iterator();
|
||||
while (itr.hasNext()) {
|
||||
if (itr.next().equals(user)) {
|
||||
itr.remove();
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new WebApplicationException(Status.NOT_FOUND);
|
||||
}
|
||||
}
|
43
ui/src/main/java/org/keycloak/ui/example/Id.java
Normal file
43
ui/src/main/java/org/keycloak/ui/example/Id.java
Normal file
|
@ -0,0 +1,43 @@
|
|||
package org.keycloak.ui.example;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class Id {
|
||||
|
||||
private String[] id;
|
||||
|
||||
public Id(String... id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String[] getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getId(int i) {
|
||||
return id[i];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + Arrays.hashCode(id);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
Id other = (Id) obj;
|
||||
if (!Arrays.equals(id, other.id))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
package org.keycloak.ui.example;
|
||||
|
||||
public class UserId {
|
||||
|
||||
private String realm;
|
||||
private String user;
|
||||
|
||||
public UserId(String realm, String user) {
|
||||
this.realm = realm;
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public String getRealm() {
|
||||
return realm;
|
||||
}
|
||||
|
||||
public String getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((realm == null) ? 0 : realm.hashCode());
|
||||
result = prime * result + ((user == null) ? 0 : user.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
UserId other = (UserId) obj;
|
||||
if (realm == null) {
|
||||
if (other.realm != null)
|
||||
return false;
|
||||
} else if (!realm.equals(other.realm))
|
||||
return false;
|
||||
if (user == null) {
|
||||
if (other.user != null)
|
||||
return false;
|
||||
} else if (!user.equals(other.user))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
<script src="js/app.js"></script>
|
||||
<script src="js/controllers.js"></script>
|
||||
<script src="js/loaders.js"></script>
|
||||
<script src="js/services.js"></script>
|
||||
</head>
|
||||
|
||||
|
|
|
@ -1,40 +1,29 @@
|
|||
'use strict';
|
||||
|
||||
var module = angular.module('keycloak', [ 'keycloak.services', 'keycloak.controllers', 'ui.bootstrap' ]);
|
||||
var module = angular.module('keycloak', [ 'keycloak.services', 'keycloak.loaders', 'keycloak.controllers', 'ui.bootstrap' ]);
|
||||
var resourceRequests = 0;
|
||||
|
||||
module.config([ '$routeProvider', function($routeProvider) {
|
||||
|
||||
$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) {
|
||||
return ApplicationListLoader();
|
||||
},
|
||||
application : function(ApplicationLoader) {
|
||||
return ApplicationLoader();
|
||||
},
|
||||
realms : function(RealmListLoader) {
|
||||
return RealmListLoader();
|
||||
},
|
||||
providers : function(ProviderListLoader) {
|
||||
return ProviderListLoader();
|
||||
}
|
||||
},
|
||||
controller : 'ApplicationDetailCtrl'
|
||||
|
@ -46,16 +35,36 @@ module.config([ '$routeProvider', function($routeProvider) {
|
|||
}
|
||||
},
|
||||
controller : 'ApplicationListCtrl'
|
||||
}).when('/create/user/:realm', {
|
||||
})
|
||||
|
||||
.when('/create/realm', {
|
||||
templateUrl : 'partials/realm-detail.html',
|
||||
resolve : {
|
||||
realm : function(RealmLoader) {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
controller : 'RealmDetailCtrl'
|
||||
}).when('/realms/:realm', {
|
||||
templateUrl : 'partials/realm-detail.html',
|
||||
resolve : {
|
||||
realm : function(RealmLoader) {
|
||||
return RealmLoader();
|
||||
}
|
||||
},
|
||||
controller : 'RealmDetailCtrl'
|
||||
}).when('/realms', {
|
||||
templateUrl : 'partials/realm-list.html',
|
||||
controller : 'RealmListCtrl'
|
||||
})
|
||||
|
||||
.when('/create/user/:realm', {
|
||||
templateUrl : 'partials/user-detail.html',
|
||||
resolve : {
|
||||
realms : function(RealmListLoader) {
|
||||
return RealmListLoader();
|
||||
},
|
||||
realm : function(RealmLoader) {
|
||||
return RealmLoader();
|
||||
},
|
||||
user : function(UserLoader) {
|
||||
user : function() {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
|
@ -63,9 +72,6 @@ module.config([ '$routeProvider', function($routeProvider) {
|
|||
}).when('/realms/:realm/users/:user', {
|
||||
templateUrl : 'partials/user-detail.html',
|
||||
resolve : {
|
||||
realms : function(RealmListLoader) {
|
||||
return RealmListLoader();
|
||||
},
|
||||
realm : function(RealmLoader) {
|
||||
return RealmLoader();
|
||||
},
|
||||
|
@ -77,9 +83,6 @@ module.config([ '$routeProvider', function($routeProvider) {
|
|||
}).when('/realms/:realm/users', {
|
||||
templateUrl : 'partials/user-list.html',
|
||||
resolve : {
|
||||
realms : function(RealmListLoader) {
|
||||
return RealmListLoader();
|
||||
},
|
||||
realm : function(RealmLoader) {
|
||||
return RealmLoader();
|
||||
},
|
||||
|
@ -88,39 +91,19 @@ module.config([ '$routeProvider', function($routeProvider) {
|
|||
}
|
||||
},
|
||||
controller : 'UserListCtrl'
|
||||
}).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) {
|
||||
return RealmListLoader();
|
||||
},
|
||||
realm : function(RealmLoader) {
|
||||
return RealmLoader();
|
||||
}
|
||||
},
|
||||
controller : 'RealmDetailCtrl'
|
||||
}).when('/realms/:realm/roles', {
|
||||
})
|
||||
|
||||
.when('/realms/:realm/roles', {
|
||||
templateUrl : 'partials/role-mapping.html',
|
||||
resolve : {
|
||||
realms : function(RealmListLoader) {
|
||||
return RealmListLoader();
|
||||
},
|
||||
realm : function(RealmLoader) {
|
||||
return RealmLoader();
|
||||
},
|
||||
users : function(UserListLoader) {
|
||||
return UserListLoader();
|
||||
application : function() {
|
||||
return null;
|
||||
},
|
||||
users : function() {
|
||||
return null;
|
||||
},
|
||||
role : function() {
|
||||
return null;
|
||||
|
@ -130,29 +113,53 @@ module.config([ '$routeProvider', function($routeProvider) {
|
|||
}).when('/realms/:realm/roles/:role', {
|
||||
templateUrl : 'partials/role-mapping.html',
|
||||
resolve : {
|
||||
realms : function(RealmListLoader) {
|
||||
return RealmListLoader();
|
||||
},
|
||||
realm : function(RealmLoader) {
|
||||
return RealmLoader();
|
||||
},
|
||||
users : function(UserListLoader) {
|
||||
return UserListLoader();
|
||||
application : function() {
|
||||
return null;
|
||||
},
|
||||
role : function($route) {
|
||||
return $route.current.params.role;
|
||||
},
|
||||
users : function(RoleMappingLoader) {
|
||||
return RoleMappingLoader();
|
||||
}
|
||||
},
|
||||
controller : 'RoleMappingCtrl'
|
||||
}).when('/realms', {
|
||||
templateUrl : 'partials/realm-list.html',
|
||||
})
|
||||
|
||||
.when('/applications/:application/roles', {
|
||||
templateUrl : 'partials/role-mapping.html',
|
||||
resolve : {
|
||||
realms : function(RealmListLoader) {
|
||||
return RealmListLoader();
|
||||
realm : function(ApplicationLoader) {
|
||||
return ApplicationLoader();
|
||||
},
|
||||
users : function() {
|
||||
return null;
|
||||
},
|
||||
role : function() {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
controller : 'RealmListCtrl'
|
||||
}).otherwise({
|
||||
controller : 'RoleMappingCtrl'
|
||||
}).when('/applications/:application/roles/:role', {
|
||||
templateUrl : 'partials/role-mapping.html',
|
||||
resolve : {
|
||||
realm : function(ApplicationLoader) {
|
||||
return ApplicationLoader();
|
||||
},
|
||||
role : function($route) {
|
||||
return $route.current.params.role;
|
||||
},
|
||||
users : function(RoleMappingLoader) {
|
||||
return RoleMappingLoader();
|
||||
}
|
||||
},
|
||||
controller : 'RoleMappingCtrl'
|
||||
})
|
||||
|
||||
.otherwise({
|
||||
templateUrl : 'partials/home.html'
|
||||
});
|
||||
} ]);
|
||||
|
@ -250,17 +257,34 @@ module.directive('kcEnter', function() {
|
|||
});
|
||||
|
||||
module.filter('remove', function() {
|
||||
return function(input, remove) {
|
||||
return function(input, remove, attribute) {
|
||||
if (!input || !remove) {
|
||||
return input;
|
||||
}
|
||||
|
||||
|
||||
var out = [];
|
||||
for (var i = 0; i < input.length; i++) {
|
||||
if (remove.indexOf(input[i]) == -1) {
|
||||
out.push(input[i]);
|
||||
for ( var i = 0; i < input.length; i++) {
|
||||
var e = input[i];
|
||||
|
||||
for (var j = 0; j < remove.length; j++) {
|
||||
if (attribute) {
|
||||
if (remove[j][attribute] == e[attribute]) {
|
||||
e = null;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (remove[j] == e) {
|
||||
e = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (e != null) {
|
||||
out.push(e);
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
};
|
||||
});
|
|
@ -16,21 +16,16 @@ module.controller('GlobalCtrl', function($scope, Auth, $location, Notifications)
|
|||
});
|
||||
});
|
||||
|
||||
module.controller('ApplicationListCtrl', function($scope, applications) {
|
||||
$scope.applications = applications;
|
||||
module.controller('ApplicationListCtrl', function($scope, Application) {
|
||||
$scope.applications = Application.query();
|
||||
});
|
||||
|
||||
module.controller('ApplicationDetailCtrl', function($scope, applications, application, Application, realms, providers, $location, $window, Dialog,
|
||||
module.controller('ApplicationDetailCtrl', function($scope, application, Application, realms, $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() {
|
||||
|
@ -66,7 +61,10 @@ module.controller('ApplicationDetailCtrl', function($scope, applications, applic
|
|||
if (i > -1) {
|
||||
$scope.application.roles.splice(i, 1);
|
||||
}
|
||||
$scope.removeInitialRole(role);
|
||||
|
||||
if ($scope.application.initialRoles) {
|
||||
$scope.removeInitialRole(role);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -127,137 +125,14 @@ module.controller('ApplicationDetailCtrl', function($scope, applications, applic
|
|||
});
|
||||
});
|
||||
};
|
||||
|
||||
$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;
|
||||
|
||||
module.controller('RealmListCtrl', function($scope, Realm) {
|
||||
$scope.realms = Realm.query();
|
||||
});
|
||||
|
||||
module.controller('UserListCtrl', function($scope, realms, realm, 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({
|
||||
realm : realm.id,
|
||||
id : $scope.user.userId
|
||||
}, $scope.user, function() {
|
||||
$scope.changed = false;
|
||||
user = angular.copy($scope.user);
|
||||
|
||||
if ($scope.create) {
|
||||
$location.url("/realms/" + realm.id + "/users/" + $scope.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.id + "/users");
|
||||
};
|
||||
|
||||
$scope.remove = function() {
|
||||
Dialog.confirmDelete($scope.user.userId, 'user', function() {
|
||||
$scope.user.$remove({
|
||||
realm : realm.id,
|
||||
id : $scope.user.userId
|
||||
}, function() {
|
||||
$location.url("/realms/" + realm.id + "/users");
|
||||
Notifications.success("Deleted user");
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
module.controller('RealmDetailCtrl', function($scope, Realm, realms, realm, $location, Dialog, Notifications) {
|
||||
$scope.realms = realms;
|
||||
module.controller('RealmDetailCtrl', function($scope, Realm, realm, $location, Dialog, Notifications) {
|
||||
$scope.realm = angular.copy(realm);
|
||||
$scope.create = !realm.name;
|
||||
|
||||
|
@ -296,7 +171,10 @@ module.controller('RealmDetailCtrl', function($scope, Realm, realms, realm, $loc
|
|||
if (i > -1) {
|
||||
$scope.realm.roles.splice(i, 1);
|
||||
}
|
||||
$scope.removeInitialRole(role);
|
||||
|
||||
if ($scope.realm.initialRoles) {
|
||||
$scope.removeInitialRole(role);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -359,36 +237,111 @@ module.controller('RealmDetailCtrl', function($scope, Realm, realms, realm, $loc
|
|||
};
|
||||
});
|
||||
|
||||
module.controller('RoleMappingCtrl', function($scope, realms, realm, users, role, Notifications) {
|
||||
$scope.realms = realms;
|
||||
|
||||
module.controller('UserListCtrl', function($scope, realm, users) {
|
||||
$scope.realm = realm;
|
||||
$scope.allUsers = users;
|
||||
$scope.users = [];
|
||||
$scope.name = realm.name;
|
||||
$scope.users = users;
|
||||
});
|
||||
|
||||
module.controller('UserDetailCtrl', function($scope, realm, user, User, $location, Dialog, Notifications) {
|
||||
$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({
|
||||
realm : realm.id,
|
||||
}, $scope.user, function() {
|
||||
$scope.changed = false;
|
||||
user = angular.copy($scope.user);
|
||||
|
||||
if ($scope.create) {
|
||||
$location.url("/realms/" + realm.id + "/users/" + $scope.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.id + "/users");
|
||||
};
|
||||
|
||||
$scope.remove = function() {
|
||||
Dialog.confirmDelete($scope.user.userId, 'user', function() {
|
||||
$scope.user.$remove({
|
||||
realm : realm.id,
|
||||
userId : $scope.user.userId
|
||||
}, function() {
|
||||
$location.url("/realms/" + realm.id + "/users");
|
||||
Notifications.success("Deleted user");
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
module.controller('RoleMappingCtrl', function($scope, realm, User, users, role, RoleMapping, Notifications) {
|
||||
$scope.realm = realm;
|
||||
$scope.realmId = realm.realm || realm.id;
|
||||
$scope.allUsers = User.query({ realm : $scope.realmId });
|
||||
$scope.users = users;
|
||||
$scope.role = role;
|
||||
|
||||
console.debug("role: " + role)
|
||||
|
||||
|
||||
$scope.addUser = function() {
|
||||
for (var i = 0; i < $scope.allUsers.length; i++) {
|
||||
if ($scope.allUsers[i].userId == $scope.newUser) {
|
||||
console.debug("add user " + $scope.allUsers[i]);
|
||||
$scope.users.push($scope.allUsers[i]);
|
||||
$scope.newUser = null;
|
||||
|
||||
// Send notification when rest call is success
|
||||
Notifications.success("Saved role mapping for user");
|
||||
var user = $scope.newUser;
|
||||
$scope.newUser = null;
|
||||
|
||||
for ( var i = 0; i < $scope.allUsers.length; i++) {
|
||||
if ($scope.allUsers[i].userId == user) {
|
||||
user = $scope.allUsers[i];
|
||||
RoleMapping.save({
|
||||
realm : $scope.realmId,
|
||||
role : role
|
||||
}, user, function() {
|
||||
$scope.users = RoleMapping.query({
|
||||
realm : $scope.realmId,
|
||||
role : role
|
||||
});
|
||||
Notifications.success("Added role mapping for user");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$scope.removeUser = function(id) {
|
||||
|
||||
$scope.removeUser = function(userId) {
|
||||
for (var i = 0; i < $scope.users.length; i++) {
|
||||
if ($scope.users[i].userId == id) {
|
||||
$scope.users.splice(i, 1);
|
||||
|
||||
// Send notification when rest call is success
|
||||
Notifications.success("Removed role mapping for user");
|
||||
var user = $scope.users[i];
|
||||
if ($scope.users[i].userId == userId) {
|
||||
RoleMapping.delete({
|
||||
realm : $scope.realmId,
|
||||
role : role
|
||||
}, user, function() {
|
||||
$scope.users = RoleMapping.query({
|
||||
realm : $scope.realmId,
|
||||
role : role
|
||||
});
|
||||
|
||||
Notifications.success("Removed role mapping for user");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
84
ui/src/main/resources/META-INF/resources/ui/js/loaders.js
Normal file
84
ui/src/main/resources/META-INF/resources/ui/js/loaders.js
Normal file
|
@ -0,0 +1,84 @@
|
|||
'use strict';
|
||||
|
||||
var module = angular.module('keycloak.loaders', [ 'keycloak.services', 'ngResource' ]);
|
||||
|
||||
module.factory('Loader', function($q) {
|
||||
var loader = {};
|
||||
loader.get = function(service, id) {
|
||||
return function() {
|
||||
var i = id && id();
|
||||
var delay = $q.defer();
|
||||
service.get(i, function(entry) {
|
||||
delay.resolve(entry);
|
||||
}, function() {
|
||||
delay.reject('Unable to fetch ' + i);
|
||||
});
|
||||
return delay.promise;
|
||||
};
|
||||
}
|
||||
loader.query = function(service, id) {
|
||||
return function() {
|
||||
var i = id && id();
|
||||
var delay = $q.defer();
|
||||
service.query(i, function(entry) {
|
||||
delay.resolve(entry);
|
||||
}, function() {
|
||||
delay.reject('Unable to fetch ' + i);
|
||||
});
|
||||
return delay.promise;
|
||||
};
|
||||
}
|
||||
return loader;
|
||||
});
|
||||
|
||||
module.factory('ApplicationListLoader', function(Loader, Application, $q) {
|
||||
return Loader.query(Application);
|
||||
});
|
||||
|
||||
module.factory('ApplicationLoader', function(Loader, Application, $route, $q) {
|
||||
return Loader.get(Application, function() {
|
||||
return {
|
||||
id : $route.current.params.application
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
module.factory('RealmListLoader', function(Loader, Realm, $q) {
|
||||
return Loader.query(Realm);
|
||||
});
|
||||
|
||||
module.factory('RealmLoader', function(Loader, Realm, $route, $q) {
|
||||
return Loader.get(Realm, function() {
|
||||
return {
|
||||
id : $route.current.params.realm
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
module.factory('UserListLoader', function(Loader, User, $route, $q) {
|
||||
return Loader.query(User, function() {
|
||||
return {
|
||||
realm : $route.current.params.realm
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
module.factory('UserLoader', function(Loader, User, $route, $q) {
|
||||
return Loader.get(User, function() {
|
||||
return {
|
||||
realm : $route.current.params.realm,
|
||||
userId : $route.current.params.user
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
module.factory('RoleMappingLoader', function(Loader, RoleMapping, $route, $q) {
|
||||
var realm = $route.current.params.realm || $route.current.params.application;
|
||||
|
||||
return Loader.query(RoleMapping, function() {
|
||||
return {
|
||||
realm : realm,
|
||||
role : $route.current.params.role
|
||||
}
|
||||
});
|
||||
});
|
|
@ -2,6 +2,40 @@
|
|||
|
||||
var module = angular.module('keycloak.services', [ 'ngResource' ]);
|
||||
|
||||
module.service('Auth', function($resource, $http, $location, $routeParams) {
|
||||
var auth = {
|
||||
loggedIn : true
|
||||
};
|
||||
auth.user = {
|
||||
userId : 'test',
|
||||
displayName : 'Test User'
|
||||
};
|
||||
return auth;
|
||||
});
|
||||
|
||||
module.service('Dialog', function($dialog) {
|
||||
var dialog = {};
|
||||
dialog.confirmDelete = function(name, type, success) {
|
||||
var title = 'Delete ' + name;
|
||||
var msg = 'Are you sure you want to permanently delete this ' + type + '?';
|
||||
var btns = [ {
|
||||
result : 'cancel',
|
||||
label : 'Cancel'
|
||||
}, {
|
||||
result : 'ok',
|
||||
label : 'Delete this ' + type,
|
||||
cssClass : 'btn-primary'
|
||||
} ];
|
||||
|
||||
$dialog.messageBox(title, msg, btns).open().then(function(result) {
|
||||
if (result == "ok") {
|
||||
success();
|
||||
}
|
||||
});
|
||||
}
|
||||
return dialog
|
||||
});
|
||||
|
||||
module.factory('Notifications', function($rootScope, $timeout) {
|
||||
var notifications = {};
|
||||
|
||||
|
@ -59,49 +93,10 @@ module.factory('Application', function($resource) {
|
|||
});
|
||||
});
|
||||
|
||||
module.factory('ApplicationListLoader', function(Application, $q) {
|
||||
return function() {
|
||||
var delay = $q.defer();
|
||||
Application.query(function(applications) {
|
||||
delay.resolve(applications);
|
||||
}, function() {
|
||||
delay.reject('Unable to fetch applications');
|
||||
});
|
||||
return delay.promise;
|
||||
};
|
||||
});
|
||||
|
||||
module.factory('ApplicationLoader', function(Application, $route, $q) {
|
||||
return function() {
|
||||
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;
|
||||
};
|
||||
});
|
||||
|
||||
module.factory('Provider', function($resource) {
|
||||
return $resource('/ejs-identity/api/admin/providers');
|
||||
});
|
||||
|
||||
module.factory('ProviderListLoader', function(Provider, $q) {
|
||||
return function() {
|
||||
var delay = $q.defer();
|
||||
Provider.query(function(providers) {
|
||||
delay.resolve(providers);
|
||||
}, function() {
|
||||
delay.reject('Unable to fetch providers');
|
||||
});
|
||||
return delay.promise;
|
||||
};
|
||||
});
|
||||
|
||||
module.factory('Realm', function($resource) {
|
||||
return $resource('/keycloak-server/ui/api/realms/:id', {
|
||||
id : '@id'
|
||||
|
@ -112,37 +107,11 @@ module.factory('Realm', function($resource) {
|
|||
});
|
||||
});
|
||||
|
||||
module.factory('RealmListLoader', function(Realm, $q) {
|
||||
return function() {
|
||||
var delay = $q.defer();
|
||||
Realm.query(function(realms) {
|
||||
delay.resolve(realms);
|
||||
}, function() {
|
||||
delay.reject('Unable to fetch realms');
|
||||
});
|
||||
return delay.promise;
|
||||
};
|
||||
});
|
||||
|
||||
module.factory('RealmLoader', function(Realm, $route, $q) {
|
||||
return function() {
|
||||
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('/keycloak-server/ui/api/realms/:realm/users/:id', {
|
||||
module.factory('RoleMapping', function($resource) {
|
||||
return $resource('/keycloak-server/ui/api/roles/:realm/:role/:userId', {
|
||||
realm : '@realm',
|
||||
id : '@id'
|
||||
role : '@role',
|
||||
userId : '@userId'
|
||||
}, {
|
||||
save : {
|
||||
method : 'PUT'
|
||||
|
@ -150,66 +119,13 @@ module.factory('User', function($resource) {
|
|||
});
|
||||
});
|
||||
|
||||
module.factory('UserListLoader', function(User, $route, $q) {
|
||||
return function() {
|
||||
var delay = $q.defer();
|
||||
User.query({
|
||||
realm : $route.current.params.realm
|
||||
}, function(users) {
|
||||
delay.resolve(users);
|
||||
}, function() {
|
||||
delay.reject('Unable to fetch users');
|
||||
});
|
||||
return delay.promise;
|
||||
};
|
||||
});
|
||||
|
||||
module.factory('UserLoader', function(User, $route, $q) {
|
||||
return function() {
|
||||
var id = $route.current.params.user;
|
||||
var delay = $q.defer();
|
||||
User.get({
|
||||
realm : $route.current.params.realm,
|
||||
id : id
|
||||
}, function(user) {
|
||||
delay.resolve(user);
|
||||
}, function() {
|
||||
delay.reject('Unable to fetch user ' + name);
|
||||
});
|
||||
return delay.promise;
|
||||
};
|
||||
});
|
||||
|
||||
module.service('Dialog', function($dialog) {
|
||||
var dialog = {};
|
||||
dialog.confirmDelete = function(name, type, success) {
|
||||
var title = 'Delete ' + name;
|
||||
var msg = 'Are you sure you want to permanently delete this ' + type + '?';
|
||||
var btns = [ {
|
||||
result : 'cancel',
|
||||
label : 'Cancel'
|
||||
}, {
|
||||
result : 'ok',
|
||||
label : 'Delete this ' + type,
|
||||
cssClass : 'btn-primary'
|
||||
} ];
|
||||
|
||||
$dialog.messageBox(title, msg, btns).open().then(function(result) {
|
||||
if (result == "ok") {
|
||||
success();
|
||||
}
|
||||
});
|
||||
}
|
||||
return dialog
|
||||
});
|
||||
|
||||
module.service('Auth', function($resource, $http, $location, $routeParams) {
|
||||
var auth = {
|
||||
loggedIn : true
|
||||
};
|
||||
auth.user = {
|
||||
userId : 'test',
|
||||
displayName : 'Test User'
|
||||
};
|
||||
return auth;
|
||||
module.factory('User', function($resource) {
|
||||
return $resource('/keycloak-server/ui/api/realms/:realm/users/:userId', {
|
||||
realm : '@realm',
|
||||
userId : '@userId'
|
||||
}, {
|
||||
save : {
|
||||
method : 'PUT'
|
||||
}
|
||||
});
|
||||
});
|
|
@ -4,9 +4,9 @@
|
|||
<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> configuration
|
||||
<h1 data-ng-show="create"><span class="gray">New Application</span></h1>
|
||||
<h1 data-ng-hide="create">
|
||||
<span class="gray">{{application.name}}</span> configuration
|
||||
</h1>
|
||||
|
||||
<div data-ng-show="applicationForm.showErrors && applicationForm.$error.required" class="alert alert-error">Please fill in all required fields</div>
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
<nav id="local-nav">
|
||||
<nav id="local-nav" data-ng-controller="ApplicationListCtrl">
|
||||
<ul class="nav nav-list">
|
||||
<li>
|
||||
<div>
|
||||
<span class="toggle">Applications</span>
|
||||
</div>
|
||||
<ul>
|
||||
<li data-ng-repeat="application in applications" data-ng-class="path[1] == application.id && 'active'">
|
||||
<a href="#/applications/{{application.id}}">{{application.name}}</a>
|
||||
<li data-ng-repeat="a in applications" data-ng-class="path[1] == a.id && 'active'">
|
||||
<a href="#/applications/{{a.id}}">{{a.name}}</a>
|
||||
<ul class="sub-items" data-ng-show="path[1] == a.id">
|
||||
<li data-ng-class="!path[2] && 'active'"><a href="#/applications/{{a.id}}">Configuration</a></li>
|
||||
<li data-ng-class="path[2] == 'roles' && 'active'"><a href="#/applications/{{a.id}}/roles">Role mapping</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
<div id="actions-bg"></div>
|
||||
|
||||
<div id="container-right" class="span9">
|
||||
<h1>
|
||||
<span class="gray" data-ng-show="create">New Realm</span>
|
||||
<span class="gray" data-ng-hide="create">{{realm.name}}</span> configuration
|
||||
<h1 data-ng-show="create"><span class="gray">New Realm</span></h1>
|
||||
<h1 data-ng-hide="create">
|
||||
<span class="gray">{{realm.name}}</span> configuration
|
||||
</h1>
|
||||
|
||||
<div data-ng-show="realmForm.showErrors && realmForm.$error.required" class="alert alert-error">Please fill in all required fields</div>
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
<nav id="local-nav">
|
||||
<nav id="local-nav" data-ng-controller="RealmListCtrl">
|
||||
<ul class="nav nav-list">
|
||||
<li>
|
||||
<div>
|
||||
<span class="toggle">Realms</span>
|
||||
</div>
|
||||
<ul>
|
||||
<li data-ng-repeat="r in realms" data-ng-class="realm.id == r.id && 'active'">
|
||||
<li data-ng-repeat="r in realms" data-ng-class="path[1] == r.id && 'active'">
|
||||
<a href=#/realms/{{r.id}}>{{r.name}}</a>
|
||||
<ul class="sub-items" data-ng-show="realm.id == r.id">
|
||||
<ul class="sub-items" data-ng-show="path[1] == 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>
|
||||
<li data-ng-class="path[2] == 'roles' && 'active'"><a href="#/realms/{{r.id}}/roles">Role mapping</a></li>
|
||||
|
|
|
@ -1,20 +1,21 @@
|
|||
<div id="wrapper" class="container">
|
||||
<div class="row">
|
||||
<aside class="span3" data-ng-include data-src="'partials/realm-menu.html'"></aside>
|
||||
<aside class="span3" data-ng-include data-src="'partials/' + path[0].slice(0, -1) + '-menu.html'"></aside>
|
||||
|
||||
<div id="actions-bg"></div>
|
||||
|
||||
<div id="container-right" class="span9">
|
||||
<h1>
|
||||
<span class="gray" data-ng-hide="create">{{name}}</span> role mapping
|
||||
<span class="gray" data-ng-hide="create">{{realm.name}}</span> role mapping
|
||||
</h1>
|
||||
|
||||
<ul class="nav nav-tabs">
|
||||
<li data-ng-class="path[3] == r && 'active'" data-ng-repeat="r in (realm.roles|orderBy:'toString()')"><a href="#/realms/{{realm.id}}/roles/{{r}}">{{r}}</a></li>
|
||||
<li data-ng-class="path[3] == r && 'active'" data-ng-repeat="r in (realm.roles|orderBy:'toString()')"><a href="#/{{path[0]}}/{{realm.id}}/roles/{{r}}">{{r}}</a></li>
|
||||
</ul>
|
||||
|
||||
<div data-ng-show="role">
|
||||
<select id="realm" name="realm" data-ng-model="newUser" data-ng-click="addUser(u)">
|
||||
<option data-ng-repeat="u in (allUsers|remove:users)" value="{{u.userId}}">{{u.userId}}</option>
|
||||
<select style="width: auto;" id="realm" name="realm" data-ng-model="newUser" data-ng-click="addUser(u)">
|
||||
<option data-ng-repeat="u in (allUsers|remove:users:'userId')" value="{{u.userId}}">{{u.userId}}</option>
|
||||
</select>
|
||||
|
||||
<table class="table table-striped table-bordered">
|
||||
|
@ -32,7 +33,7 @@
|
|||
<td>{{user.firstName}}</td>
|
||||
<td>{{user.lastName}}</td>
|
||||
<td>{{user.email}}</td>
|
||||
<td><button ng-click="removeUser(user.userId)">
|
||||
<td><button data-ng-click="removeUser(user.userId)">
|
||||
<i class="icon-remove"></i>
|
||||
</button></td>
|
||||
</tr>
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
<div id="actions-bg"></div>
|
||||
|
||||
<div id="container-right" class="span9">
|
||||
<h1>
|
||||
<span class="gray" data-ng-show="create">New User</span>
|
||||
<span class="gray" data-ng-hide="create">{{user.userId}}</span>
|
||||
<h1 data-ng-show="create"><span class="gray">New User</span></h1>
|
||||
<h1 data-ng-hide="create">
|
||||
<span class="gray">{{user.userId}}</span> configuration
|
||||
</h1>
|
||||
|
||||
<div data-ng-show="userForm.showErrors && userForm.$error.required" class="alert alert-error">Please fill in all required fields</div>
|
||||
|
|
Loading…
Reference in a new issue