Merge pull request #27 from patriot1burke/master

realm required creds
This commit is contained in:
Bill Burke 2013-08-10 13:01:44 -07:00
commit ac7f4833d8
104 changed files with 6176 additions and 704 deletions

View file

@ -111,7 +111,7 @@ public class AbstractOAuthClient {
.param("grant_type", "authorization_code")
.param("code", code)
.param("client_id", clientId)
.param("Password", password)
.param("password", password)
.param("redirect_uri", redirectUri);
Response res = client.target(codeUrl).request().header(HttpHeaders.AUTHORIZATION, authHeader).post(Entity.form(codeForm));
try {

View file

@ -1,15 +1,13 @@
package org.keycloak.representations.idm;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class ResourceRepresentation {
public class ApplicationRepresentation {
protected String self; // link
protected String id;
protected String name;
@ -70,14 +68,14 @@ public class ResourceRepresentation {
this.roles = roles;
}
public ResourceRepresentation role(RoleRepresentation role) {
public ApplicationRepresentation role(RoleRepresentation role) {
if (this.roles == null) this.roles = new ArrayList<RoleRepresentation>();
this.roles.add(role);
return this;
}
public ResourceRepresentation role(String role, String description) {
public ApplicationRepresentation role(String role, String description) {
if (this.roles == null) this.roles = new ArrayList<RoleRepresentation>();
this.roles.add(new RoleRepresentation(role, description));
return this;
@ -123,12 +121,11 @@ public class ResourceRepresentation {
this.credentials = credentials;
}
public ResourceRepresentation credential(String type, String value, boolean hashed) {
public ApplicationRepresentation credential(String type, String value) {
if (this.credentials == null) credentials = new ArrayList<CredentialRepresentation>();
CredentialRepresentation cred = new CredentialRepresentation();
cred.setType(type);
cred.setValue(value);
cred.setHashed(hashed);
credentials.add(cred);
return this;
}

View file

@ -5,9 +5,13 @@ package org.keycloak.representations.idm;
* @version $Revision: 1 $
*/
public class CredentialRepresentation {
public static final String PASSWORD = "password";
public static final String TOTP = "totp";
public static final String CLIENT_CERT = "cert";
protected String type;
protected String value;
protected boolean hashed;
protected String device;
public String getType() {
return type;
@ -25,11 +29,11 @@ public class CredentialRepresentation {
this.value = value;
}
public boolean isHashed() {
return hashed;
public String getDevice() {
return device;
}
public void setHashed(boolean hashed) {
this.hashed = hashed;
public void setDevice(String device) {
this.device = device;
}
}

View file

@ -17,16 +17,18 @@ public class RealmRepresentation {
protected boolean enabled;
protected boolean sslNotRequired;
protected boolean cookieLoginAllowed;
protected boolean registrationAllowed;
protected boolean social;
protected String privateKey;
protected String publicKey;
protected List<RoleRepresentation> roles;
protected List<RequiredCredentialRepresentation> requiredCredentials;
protected List<RequiredCredentialRepresentation> requiredResourceCredentials;
protected List<RequiredCredentialRepresentation> requiredOAuthClientCredentials;
protected Set<String> requiredCredentials;
protected Set<String> requiredApplicationCredentials;
protected Set<String> requiredOAuthClientCredentials;
protected List<UserRepresentation> users;
protected List<RoleMappingRepresentation> roleMappings;
protected List<ScopeMappingRepresentation> scopeMappings;
protected List<ResourceRepresentation> resources;
protected List<ApplicationRepresentation> applications;
public String getSelf() {
@ -57,14 +59,14 @@ public class RealmRepresentation {
return users;
}
public List<ResourceRepresentation> getResources() {
return resources;
public List<ApplicationRepresentation> getApplications() {
return applications;
}
public ResourceRepresentation resource(String name) {
ResourceRepresentation resource = new ResourceRepresentation();
if (resources == null) resources = new ArrayList<ResourceRepresentation>();
resources.add(resource);
public ApplicationRepresentation resource(String name) {
ApplicationRepresentation resource = new ApplicationRepresentation();
if (applications == null) applications = new ArrayList<ApplicationRepresentation>();
applications.add(resource);
resource.setName(name);
return resource;
}
@ -81,8 +83,8 @@ public class RealmRepresentation {
return user;
}
public void setResources(List<ResourceRepresentation> resources) {
this.resources = resources;
public void setApplications(List<ApplicationRepresentation> applications) {
this.applications = applications;
}
public boolean isEnabled() {
@ -141,27 +143,27 @@ public class RealmRepresentation {
return mapping;
}
public List<RequiredCredentialRepresentation> getRequiredCredentials() {
public Set<String> getRequiredCredentials() {
return requiredCredentials;
}
public void setRequiredCredentials(List<RequiredCredentialRepresentation> requiredCredentials) {
public void setRequiredCredentials(Set<String> requiredCredentials) {
this.requiredCredentials = requiredCredentials;
}
public List<RequiredCredentialRepresentation> getRequiredResourceCredentials() {
return requiredResourceCredentials;
public Set<String> getRequiredApplicationCredentials() {
return requiredApplicationCredentials;
}
public void setRequiredResourceCredentials(List<RequiredCredentialRepresentation> requiredResourceCredentials) {
this.requiredResourceCredentials = requiredResourceCredentials;
public void setRequiredApplicationCredentials(Set<String> requiredApplicationCredentials) {
this.requiredApplicationCredentials = requiredApplicationCredentials;
}
public List<RequiredCredentialRepresentation> getRequiredOAuthClientCredentials() {
public Set<String> getRequiredOAuthClientCredentials() {
return requiredOAuthClientCredentials;
}
public void setRequiredOAuthClientCredentials(List<RequiredCredentialRepresentation> requiredOAuthClientCredentials) {
public void setRequiredOAuthClientCredentials(Set<String> requiredOAuthClientCredentials) {
this.requiredOAuthClientCredentials = requiredOAuthClientCredentials;
}
@ -196,4 +198,20 @@ public class RealmRepresentation {
public void setPublicKey(String publicKey) {
this.publicKey = publicKey;
}
public boolean isRegistrationAllowed() {
return registrationAllowed;
}
public void setRegistrationAllowed(boolean registrationAllowed) {
this.registrationAllowed = registrationAllowed;
}
public boolean isSocial() {
return social;
}
public void setSocial(boolean social) {
this.social = social;
}
}

View file

@ -1,39 +0,0 @@
package org.keycloak.representations.idm;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class RequiredCredentialRepresentation {
public static final String PASSWORD = "Password";
public static final String TOTP = "TOTP";
public static final String CLIENT_CERT = "CLIENT_CERT";
public static final String CALLER_PRINCIPAL = "CALLER_PRINCIPAL";
protected String type;
protected boolean input;
protected boolean secret;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public boolean isInput() {
return input;
}
public void setInput(boolean input) {
this.input = input;
}
public boolean isSecret() {
return secret;
}
public void setSecret(boolean secret) {
this.secret = secret;
}
}

View file

@ -14,6 +14,9 @@ public class UserRepresentation {
protected String self; // link
protected String username;
protected boolean enabled;
protected String firstName;
protected String lastName;
protected String email;
protected Map<String, String> attributes;
protected List<CredentialRepresentation> credentials;
@ -25,6 +28,30 @@ public class UserRepresentation {
this.self = self;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getUsername() {
return username;
}
@ -55,12 +82,11 @@ public class UserRepresentation {
this.credentials = credentials;
}
public UserRepresentation credential(String type, String value, boolean hashed) {
public UserRepresentation credential(String type, String value) {
if (this.credentials == null) credentials = new ArrayList<CredentialRepresentation>();
CredentialRepresentation cred = new CredentialRepresentation();
cred.setType(type);
cred.setValue(value);
cred.setHashed(hashed);
credentials.add(cred);
return this;
}

View file

@ -1,6 +1,7 @@
package org.keycloak.example.demo;
import org.jboss.resteasy.jwt.JsonSerialization;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.models.KeycloakSession;
@ -40,7 +41,7 @@ public class DemoApplication extends KeycloakApplication {
defaultRealm.setCookieLoginAllowed(true);
defaultRealm.setRegistrationAllowed(true);
manager.generateRealmKeys(defaultRealm);
defaultRealm.addRequiredCredential(RequiredCredentialModel.PASSWORD);
defaultRealm.addRequiredCredential(CredentialRepresentation.PASSWORD);
defaultRealm.addRole(SaasService.REALM_CREATOR_ROLE);
RealmRepresentation rep = loadJson("META-INF/testrealm.json");

View file

@ -19,7 +19,7 @@
<class>org.picketlink.idm.jpa.model.sample.simple.OTPCredentialTypeEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.AttributeTypeEntity</class>
<class>org.keycloak.services.models.picketlink.mappings.RealmEntity</class>
<class>org.keycloak.services.models.picketlink.mappings.ResourceEntity</class>
<class>org.keycloak.services.models.picketlink.mappings.ApplicationEntity</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>

View file

@ -1,33 +1,15 @@
{
"realm": "demo",
"enabled": true,
"tokenLifespan" : 10,
"tokenLifespan": 300,
"accessCodeLifespan": 10,
"sslNotRequired": true,
"cookieLoginAllowed": true,
"privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"requiredCredentials" : [
{
"type" : "Password",
"input" : true,
"secret" : true
}
],
"requiredResourceCredentials" : [
{
"type" : "Password",
"input" : true,
"secret" : true
}
],
"requiredOAuthClientCredentials" : [
{
"type" : "Password",
"input" : true,
"secret" : true
}
],
"requiredCredentials": [ "password" ],
"requiredApplicationCredentials": [ "password" ],
"requiredOAuthClientCredentials": [ "password" ],
"users" : [
{
"username" : "bburke@redhat.com",
@ -36,7 +18,7 @@
"email" : "bburke@redhat.com"
},
"credentials" : [
{ "type" : "Password",
{ "type" : "password",
"value" : "password" }
]
},
@ -44,14 +26,20 @@
"username" : "third-party",
"enabled" : true,
"credentials" : [
{ "type" : "Password",
{ "type" : "password",
"value" : "password" }
]
}
],
"roles": [
{ "name" : "user", "description" : "Have User privileges" },
{ "name" : "admin", "description" : "Have Administrator privileges" }
{
"name": "user",
"description": "Have User privileges"
},
{
"name": "admin",
"description": "Have Administrator privileges"
}
],
"roleMappings": [
{
@ -69,15 +57,17 @@
"roles": ["user"]
}
],
"resources" : [
"applications": [
{
"name": "customer-portal",
"enabled": true,
"adminUrl": "http://localhost:8080/customer-portal/j_admin_request",
"useRealmMappings": true,
"credentials": [
{ "type" : "Password",
"value" : "password" }
{
"type": "password",
"value": "password"
}
]
},
{
@ -86,8 +76,10 @@
"adminUrl": "http://localhost:8080/product-portal/j_admin_request",
"useRealmMappings": true,
"credentials": [
{ "type" : "Password",
"value" : "password" }
{
"type": "password",
"value": "password"
}
]
}
]

View file

@ -15,13 +15,20 @@
<link href="lib/bootstrap/css/bootstrap-responsive.css" rel="stylesheet">
<link href="css/admin.css" rel="stylesheet">
<link href="css/admin-responsive.css" rel="stylesheet">
<link href="lib/select2-3.4.1/select2.css" rel="stylesheet">
<link href="css/styles.css" rel="stylesheet">
<script src="lib/jquery/jquery-1.10.2.js" type="text/javascript"></script>
<script src="lib/select2-3.4.1/select2.js" type="text/javascript"></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.idletimer.js" type="text/javascript"></script>
<script src="lib/jquery/jquery.idletimeout.js" type="text/javascript"></script>
<script src="lib/angular/select2.js" type="text/javascript"></script>
<script src="js/app.js"></script>
<script src="js/controllers.js"></script>
<script src="js/loaders.js"></script>
@ -57,9 +64,6 @@
</div>
</div>
<script src="lib/jquery/jquery-1.10.2.js" type="text/javascript"></script>
<script src="lib/jquery/jquery.idletimer.js" type="text/javascript"></script>
<script src="lib/jquery/jquery.idletimeout.js" type="text/javascript"></script>
<script type="text/javascript">
$.idleTimeout('#idletimeout', '#idletimeout a', {
idleAfter: 300,

View file

@ -1,6 +1,6 @@
'use strict';
var module = angular.module('keycloak', [ 'keycloak.services', 'keycloak.loaders', 'keycloak.controllers', 'ui.bootstrap' ]);
var module = angular.module('keycloak', [ 'keycloak.services', 'keycloak.loaders', 'keycloak.controllers', 'ui.bootstrap', 'ui.select2' ]);
var resourceRequests = 0;
module.config([ '$routeProvider', function($routeProvider) {
@ -197,13 +197,12 @@ module.factory('spinnerInterceptor', function($q, $window, $rootScope, $location
};
});
/*
module.directive('kcInput', function() {
var d = {
scope : true,
replace : false,
link : function(scope, element, attrs) {
var form = element.closest('form');
var form = element.children('form');
var label = element.children('label');
var input = element.children('input');
@ -242,7 +241,7 @@ module.directive('kcEnter', function() {
});
};
});
*/
module.filter('remove', function() {
return function(input, remove, attribute) {

View file

@ -27,25 +27,9 @@ module.controller('GlobalCtrl', function($scope, $http, Auth, Current, $location
$http.get('/auth-server/rest/saas/admin/realms').success(function(data) {
Current.realms = data;
var count = 0;
var showrealm = false;
var id = null;
for (var key in data) {
if (count > 0) {
showrealm = false;
break;
}
id = key;
showrealm = true;
count++;
}
if (showrealm) {
console.log('default redirect to realm: ' + id);
Current.realm = Current.realms[id];
$location.url("/realms/" + id);
} else {
//console.log('not redirecting');
if (data.length > 0) {
Current.realm = data[0];
$location.url("/realms/" + Current.realm.id);
}
});
});
@ -58,137 +42,115 @@ module.controller('RealmListCtrl', function($scope, Realm, Current) {
module.controller('RealmDropdownCtrl', function($scope, Realm, Current, Auth, $location) {
// Current.realms = Realm.get();
$scope.current = Current;
if (Current.realms.length > 0) {
console.log('[0]: ' + current.realms[0].realm);
}
$scope.changeRealm = function() {
for (var id in Current.realms) {
var val = Current.realms[id];
if (val == Current.realm) {
$location.url("/realms/" + id);
break;
}
}
$location.url("/realms/" + $scope.current.realm.id);
};
$scope.showNav = function() {
var show = false;
for (var key in Current.realms) {
if (typeof Current.realms[key] != "function") {
if (Current.realms[key] == Current.realm) {
$scope.currentRealmId = key;
}
show = true;
}
}
var show = Current.realms.length > 0;
console.log('Show dropdown? ' + show);
return Auth.loggedIn && show;
}
});
module.controller('RealmDetailCtrl', function($scope, Current, Realm, realm, $location, Dialog, Notifications) {
$scope.realm = angular.copy(realm);
module.controller('RealmDetailCtrl', function($scope, Current, Realm, realm, $http, $location, Dialog, Notifications) {
$scope.createRealm = !realm.id;
console.log('RealmDetailCtrl');
if ($scope.createRealm) {
$scope.realm.enabled = true;
$scope.realm.requireSsl = true;
$scope.realm.cookieLoginAllowed = true;
$scope.realm.tokenLifespan = 300;
$scope.realm.tokenLifespanUnit = 'SECONDS';
$scope.realm.accessCodeLifespan = 300;
$scope.realm.accessCodeLifespanUnit = 'SECONDS';
$scope.realm = {
enabled: true,
requireSsl: true,
cookieLoginAllowed: true,
tokenLifespan: 300,
tokenLifespanUnit: 'SECONDS',
accessCodeLifespan: 300,
accessCodeLifespanUnit: 'SECONDS',
requiredCredentials: ['password'],
requiredOAuthClientCredentials: ['password'],
requiredApplicationCredentials: ['password']
};
} else {
$scope.realm.name = realm.realm;
$scope.realm.requireSsl = !$scope.realm.sslNotRequired;
$scope.realm.tokenLifespanUnit = 'SECONDS';
$scope.realm.acessCodeLifespanUnit = 'SECONDS';
if (Current.realm == null || Current.realm.id != realm.id) {
for (var i = 0; i < Current.realms.length; i++) {
if (realm.id == Current.realms[i].id) {
Current.realm = Current.realms[i];
break;
}
}
}
if (Current.realm == null || Current.realm.id != realm.id) {
console.log('should be unreachable');
console.log('Why? ' + Current.realms.length + ' ' + Current.realm);
return;
}
$scope.realm = angular.copy(realm);
$scope.realm.requireSsl = !realm.sslNotRequired;
$scope.realm.tokenLifespanUnit = 'SECONDS';
$scope.realm.accessCodeLifespanUnit = 'SECONDS';
}
var oldCopy = angular.copy($scope.realm);
$scope.userCredentialOptions = {
'multiple' : true,
'simple_tags' : true,
'tags' : ['password', 'totp', 'cert']
};
$scope.changed = $scope.create;
$scope.$watch('realm', function() {
if (!angular.equals($scope.realm, realm)) {
if (!angular.equals($scope.realm, oldCopy)) {
$scope.changed = true;
}
}, true);
$scope.addRole = function() {
if ($scope.newRole) {
if ($scope.realm.roles) {
for ( var i = 0; i < $scope.realm.roles.length; i++) {
if ($scope.realm.roles[i] == $scope.newRole) {
Notifications.warn("Role already exists");
$scope.newRole = null;
return;
}
}
}
if (!$scope.realm.roles) {
$scope.realm.roles = [];
}
$scope.realm.roles.push($scope.newRole);
$scope.newRole = null;
}
}
$scope.removeRole = function(role) {
Dialog.confirmDelete(role, 'role', function() {
var i = $scope.realm.roles.indexOf(role);
if (i > -1) {
$scope.realm.roles.splice(i, 1);
}
if ($scope.realm.initialRoles) {
$scope.removeInitialRole(role);
}
});
};
$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(role) {
var i = $scope.realm.initialRoles.indexOf(role);
if (i > -1) {
$scope.realm.initialRoles.splice(i, 1);
}
};
$scope.save = function() {
if ($scope.realmForm.$valid) {
var realmCopy = {
realm: $scope.realm.name,
enabled: $scope.realm.enabled,
cookieLoginAllowed: $scope.realm.cookieLoginAllowed,
sslNotRequired: !$scope.realm.requireSsl,
tokenLifespan: $scope.realm.tokenLifespan,
accessCodeLifespan: $scope.realm.accessCodeLifespan
};
var realmCopy = angular.copy($scope.realm);
realmCopy.sslNotRequired = !realmCopy.requireSsl;
delete realmCopy["requireSsl"];
delete realmCopy["tokenLifespanUnit"];
delete realmCopy["accessCodeLifespanUnit"];
if ($scope.createRealm) {
Realm.save(realmCopy, function(data, headers) {
console.log('creating new realm');
var l = headers().location;
var id = l.substring(l.lastIndexOf("/") + 1);
var data = Realm.get(function() {
var data = Realm.query(function() {
Current.realms = data;
Current.realm = Current.realms[id];
for (var i = 0; i < Current.realms.length; i++) {
if (Current.realms[i].id == id) {
Current.realm = Current.realms[i];
}
}
});
$location.url("/realms/" + id);
Notifications.success("Created realm");
});
} else {
Realm.update(realmCopy, function() {
Current.realms = Realm.get();
console.log('updating realm...');
$scope.changed = false;
realm = angular.copy($scope.realm);
Realm.update(realmCopy, function() {
var id = realmCopy.id;
var data = Realm.query(function() {
Current.realms = data;
for (var i = 0; i < Current.realms.length; i++) {
if (Current.realms[i].id == id) {
Current.realm = Current.realms[i];
oldCopy = angular.copy($scope.realm);
}
}
});
$location.url("/realms/" + id);
Notifications.success("Saved changes to realm");
});
}
@ -198,7 +160,7 @@ module.controller('RealmDetailCtrl', function($scope, Current, Realm, realm, $lo
};
$scope.reset = function() {
$scope.realm = angular.copy(realm);
$scope.realm = angular.copy(oldCopy);
$scope.changed = false;
$scope.realmForm.showErrors = false;
};
@ -385,7 +347,7 @@ module.controller('ApplicationDetailCtrl', function($scope, realm, applications,
application : null
};
selection.applications = angular.copy(applications);
selection.applications = applications;
for (var i=0;i < selection.applications.length; i++) {
if (selection.applications[i].name == application.name) {
@ -396,10 +358,11 @@ module.controller('ApplicationDetailCtrl', function($scope, realm, applications,
}
$scope.selection = selection;
$scope.application = angular.copy(application);
if (!$scope.create) {
$scope.application= selection.application;
} else {
$scope.application = {};
}
$scope.changeApplication = function() {
console.log('ApplicationDetailCtrl.changeApplication() - ' + $scope.selection.application.name);
@ -407,7 +370,7 @@ module.controller('ApplicationDetailCtrl', function($scope, realm, applications,
};
$scope.$watch('application', function() {
if (!angular.equals($scope.application, application)) {
if (!angular.equals($scope.selection.application, application)) {
$scope.changed = true;
}
}, true);

View file

@ -132,7 +132,7 @@ module.factory('Role', function($resource) {
});
module.factory('Application', function($resource) {
return $resource('/auth-server/rest/saas/admin/realms/:realm/resources/:id', {
return $resource('/auth-server/rest/saas/admin/realms/:realm/applications/:id', {
realm : '@realm',
id : '@id'
}, {

View file

@ -22574,7 +22574,7 @@ var ngSubmitDirective = ngDirective(function(scope, element, attrs) {
* @description
* Fetches, compiles and includes an external HTML fragment.
*
* Keep in mind that Same Origin Policy applies to included resources
* Keep in mind that Same Origin Policy applies to included applications
* (e.g. ngInclude won't work for cross-domain requests on all browsers and for
* file:// access on some browsers).
*

View file

@ -13168,7 +13168,7 @@ var ngSubmitDirective = ngDirective(function(scope, element, attrs) {
* @description
* Fetches, compiles and includes an external HTML fragment.
*
* Keep in mind that Same Origin Policy applies to included resources
* Keep in mind that Same Origin Policy applies to included applications
* (e.g. ngInclude won't work for cross-domain requests on all browsers and for
* file:// access on some browsers).
*

View file

@ -0,0 +1,195 @@
/**
* Enhanced Select2 Dropmenus
*
* @AJAX Mode - When in this mode, your value will be an object (or array of objects) of the data used by Select2
* This change is so that you do not have to do an additional query yourself on top of Select2's own query
* @params [options] {object} The configuration options passed to $.fn.select2(). Refer to the documentation
*/
angular.module('ui.select2', []).value('uiSelect2Config', {}).directive('uiSelect2', ['uiSelect2Config', '$timeout', function (uiSelect2Config, $timeout) {
var options = {};
if (uiSelect2Config) {
angular.extend(options, uiSelect2Config);
}
return {
require: 'ngModel',
compile: function (tElm, tAttrs) {
var watch,
repeatOption,
repeatAttr,
isSelect = tElm.is('select'),
isMultiple = (tAttrs.multiple !== undefined);
// Enable watching of the options dataset if in use
if (tElm.is('select')) {
repeatOption = tElm.find('option[ng-repeat], option[data-ng-repeat]');
if (repeatOption.length) {
repeatAttr = repeatOption.attr('ng-repeat') || repeatOption.attr('data-ng-repeat');
watch = jQuery.trim(repeatAttr.split('|')[0]).split(' ').pop();
}
}
return function (scope, elm, attrs, controller) {
// instance-specific options
var opts = angular.extend({}, options, scope.$eval(attrs.uiSelect2));
/*
Convert from Select2 view-model to Angular view-model.
*/
var convertToAngularModel = function(select2_data) {
var model;
if (opts.simple_tags) {
model = []
angular.forEach(select2_data, function(value, index) {
model.push(value.id)
})
} else {
model = select2_data
}
return model
}
/*
Convert from Angular view-model to Select2 view-model.
*/
var convertToSelect2Model = function(angular_data) {
var model = []
if (!angular_data) {
return model;
}
if (opts.simple_tags) {
model = [];
angular.forEach(
angular_data,
function(value, index) {
model.push({'id': value, 'text': value});
})
} else {
model = angular_data;
}
return model
}
if (isSelect) {
// Use <select multiple> instead
delete opts.multiple;
delete opts.initSelection;
} else if (isMultiple) {
opts.multiple = true;
}
if (controller) {
// Watch the model for programmatic changes
scope.$watch(tAttrs.ngModel, function(current, old) {
if (!current) {
return
}
if (current == old) {
return
}
controller.$render()
}, true)
controller.$render = function () {
if (isSelect) {
elm.select2('val', controller.$viewValue);
} else {
if (opts.multiple) {
elm.select2(
'data', convertToSelect2Model(controller.$viewValue));
} else {
if (angular.isObject(controller.$viewValue)) {
elm.select2('data', controller.$viewValue);
} else if (!controller.$viewValue) {
elm.select2('data', null);
} else {
elm.select2('val', controller.$viewValue);
}
}
}
};
// Watch the options dataset for changes
if (watch) {
scope.$watch(watch, function (newVal, oldVal, scope) {
if (!newVal) return;
// Delayed so that the options have time to be rendered
$timeout(function () {
elm.select2('val', controller.$viewValue);
// Refresh angular to remove the superfluous option
elm.trigger('change');
});
});
}
// Update valid and dirty statuses
controller.$parsers.push(function (value) {
var div = elm.prev()
div
.toggleClass('ng-invalid', !controller.$valid)
.toggleClass('ng-valid', controller.$valid)
.toggleClass('ng-invalid-required', !controller.$valid)
.toggleClass('ng-valid-required', controller.$valid)
.toggleClass('ng-dirty', controller.$dirty)
.toggleClass('ng-pristine', controller.$pristine);
return value;
});
if (!isSelect) {
// Set the view and model value and update the angular template manually for the ajax/multiple select2.
elm.bind("change", function () {
if (scope.$$phase) return;
scope.$apply(function () {
controller.$setViewValue(
convertToAngularModel(elm.select2('data')));
});
});
if (opts.initSelection) {
var initSelection = opts.initSelection;
opts.initSelection = function (element, callback) {
initSelection(element, function (value) {
controller.$setViewValue(convertToAngularModel(value));
callback(value);
});
};
}
}
}
elm.bind("$destroy", function() {
elm.select2("destroy");
});
attrs.$observe('disabled', function (value) {
elm.select2('enable', !value);
});
attrs.$observe('readonly', function (value) {
elm.select2('readonly', !!value);
});
if (attrs.ngMultiple) {
scope.$watch(attrs.ngMultiple, function(newVal) {
elm.select2(opts);
});
}
// Initialize the plugin late so that the injected DOM does not disrupt the template compiler
$timeout(function () {
elm.select2(opts);
// Set initial value - I'm not sure about this but it seems to need to be there
elm.val(controller.$viewValue);
// important!
controller.$render();
// Not sure if I should just check for !isSelect OR if I should check for 'tags' key
if (!opts.initSelection && !isSelect)
controller.$setViewValue(
convertToAngularModel(elm.select2('data')));
});
};
}
};
}]);

View file

@ -0,0 +1,18 @@
Copyright 2012 Igor Vaynberg
Version: @@ver@@ Timestamp: @@timestamp@@
This software is licensed under the Apache License, Version 2.0 (the "Apache License") or the GNU
General Public License version 2 (the "GPL License"). You may choose either license to govern your
use of this software only upon the condition that you accept all of the terms of either the Apache
License or the GPL License.
You may obtain a copy of the Apache License and the GPL License at:
http://www.apache.org/licenses/LICENSE-2.0
http://www.gnu.org/licenses/gpl-2.0.html
Unless required by applicable law or agreed to in writing, software distributed under the Apache License
or the GPL Licesnse is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
either express or implied. See the Apache License and the GPL License for the specific language governing
permissions and limitations under the Apache License and the GPL License.

View file

@ -0,0 +1,83 @@
Select2
=======
Select2 is a jQuery-based replacement for select boxes. It supports searching, remote data sets, and infinite scrolling of results.
To get started, checkout examples and documentation at http://ivaynberg.github.com/select2
Use cases
---------
* Enhancing native selects with search.
* Enhancing native selects with a better multi-select interface.
* Loading data from JavaScript: easily load items via ajax and have them searchable.
* Nesting optgroups: native selects only support one level of nested. Select2 does not have this restriction.
* Tagging: ability to add new items on the fly.
* Working with large, remote datasets: ability to partially load a dataset based on the search term.
* Paging of large datasets: easy support for loading more pages when the results are scrolled to the end.
* Templating: support for custom rendering of results and selections.
Browser compatibility
---------------------
* IE 8+
* Chrome 8+
* Firefox 10+
* Safari 3+
* Opera 10.6+
Integrations
------------
* [Wicket-Select2](https://github.com/ivaynberg/wicket-select2) (Java / [Apache Wicket](http://wicket.apache.org))
* [select2-rails](https://github.com/argerim/select2-rails) (Ruby on Rails)
* [AngularUI](http://angular-ui.github.com/#directives-select2) ([AngularJS](angularjs.org))
* [Django](https://github.com/applegrew/django-select2)
* [Symfony](https://github.com/19Gerhard85/sfSelect2WidgetsPlugin)
* [Bootstrap](https://github.com/t0m/select2-bootstrap-css) (CSS skin)
* [Yii](https://github.com/tonybolzan/yii-select2)
Internationalization (i18n)
---------------------------
Select2 supports multiple languages by simply including the right
language JS file (`select2_locale_it.js`, `select2_locale_nl.js`, etc.).
Missing a language? Just copy `select2_locale_en.js.template`, translate
it, and make a pull request back to Select2 here on GitHub.
Bug tracker
-----------
Have a bug? Please create an issue here on GitHub!
https://github.com/ivaynberg/select2/issues
Mailing list
------------
Have a question? Ask on our mailing list!
select2@googlegroups.com
https://groups.google.com/d/forum/select2
Copyright and license
---------------------
Copyright 2012 Igor Vaynberg
This software is licensed under the Apache License, Version 2.0 (the "Apache License") or the GNU
General Public License version 2 (the "GPL License"). You may choose either license to govern your
use of this software only upon the condition that you accept all of the terms of either the Apache
License or the GPL License.
You may obtain a copy of the Apache License and the GPL License in the LICENSE file, or at:
http://www.apache.org/licenses/LICENSE-2.0
http://www.gnu.org/licenses/gpl-2.0.html
Unless required by applicable law or agreed to in writing, software distributed under the Apache License
or the GPL License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
either express or implied. See the Apache License and the GPL License for the specific language governing
permissions and limitations under the Apache License and the GPL License.

View file

@ -0,0 +1,8 @@
{
"name": "select2",
"version": "3.4.1",
"main": ["select2.js", "select2.css", "select2.png", "select2x2.png", "select2-spinner.gif"],
"dependencies": {
"jquery": ">= 1.7.1"
}
}

View file

@ -0,0 +1,69 @@
#!/bin/bash
set -e
echo -n "Enter the version for this release: "
read ver
if [ ! $ver ]; then
echo "Invalid version."
exit
fi
name="select2"
js="$name.js"
mini="$name.min.js"
css="$name.css"
release="$name-$ver"
tag="$ver"
branch="build-$ver"
curbranch=`git branch | grep "*" | sed "s/* //"`
timestamp=$(date)
tokens="s/@@ver@@/$ver/g;s/\@@timestamp@@/$timestamp/g"
remote="github"
echo "Updating Version Identifiers"
sed -E -e "s/\"version\": \"([0-9\.]+)\",/\"version\": \"$ver\",/g" -i "" component.json select2.jquery.json
git add component.json
git add select2.jquery.json
git commit -m "modified version identifiers in descriptors for release $ver"
git push
git branch "$branch"
git checkout "$branch"
echo "Tokenizing..."
find . -name "$js" | xargs -I{} sed -e "$tokens" -i "" {}
find . -name "$css" | xargs -I{} sed -e "$tokens" -i "" {}
sed -e "s/latest/$ver/g" -i "" component.json
git add "$js"
git add "$css"
echo "Minifying..."
echo "/*" > "$mini"
cat LICENSE | sed "$tokens" >> "$mini"
echo "*/" >> "$mini"
curl -s \
--data-urlencode "js_code@$js" \
http://marijnhaverbeke.nl/uglifyjs \
>> "$mini"
git add "$mini"
git commit -m "release $ver"
echo "Tagging..."
git tag -a "$tag" -m "tagged version $ver"
git push "$remote" --tags
echo "Cleaning Up..."
git checkout "$curbranch"
git branch -D "$branch"
echo "Done"

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -0,0 +1,680 @@
/*
Version: 3.4.1 Timestamp: Thu Jun 27 18:02:10 PDT 2013
*/
.select2-container {
margin: 0;
position: relative;
display: inline-block;
/* inline-block for ie7 */
zoom: 1;
*display: inline;
vertical-align: middle;
}
.select2-container,
.select2-drop,
.select2-search,
.select2-search input{
/*
Force border-box so that % widths fit the parent
container without overlap because of margin/padding.
More Info : http://www.quirksmode.org/css/box.html
*/
-webkit-box-sizing: border-box; /* webkit */
-khtml-box-sizing: border-box; /* konqueror */
-moz-box-sizing: border-box; /* firefox */
-ms-box-sizing: border-box; /* ie */
box-sizing: border-box; /* css3 */
}
.select2-container .select2-choice {
display: block;
height: 26px;
padding: 0 0 0 8px;
overflow: hidden;
position: relative;
border: 1px solid #aaa;
white-space: nowrap;
line-height: 26px;
color: #444;
text-decoration: none;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
-webkit-background-clip: padding-box;
-moz-background-clip: padding;
background-clip: padding-box;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
background-color: #fff;
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eeeeee), color-stop(0.5, white));
background-image: -webkit-linear-gradient(center bottom, #eeeeee 0%, white 50%);
background-image: -moz-linear-gradient(center bottom, #eeeeee 0%, white 50%);
background-image: -o-linear-gradient(bottom, #eeeeee 0%, #ffffff 50%);
background-image: -ms-linear-gradient(top, #ffffff 0%, #eeeeee 50%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr = '#ffffff', endColorstr = '#eeeeee', GradientType = 0);
background-image: linear-gradient(top, #ffffff 0%, #eeeeee 50%);
}
.select2-container.select2-drop-above .select2-choice {
border-bottom-color: #aaa;
-webkit-border-radius:0 0 4px 4px;
-moz-border-radius:0 0 4px 4px;
border-radius:0 0 4px 4px;
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eeeeee), color-stop(0.9, white));
background-image: -webkit-linear-gradient(center bottom, #eeeeee 0%, white 90%);
background-image: -moz-linear-gradient(center bottom, #eeeeee 0%, white 90%);
background-image: -o-linear-gradient(bottom, #eeeeee 0%, white 90%);
background-image: -ms-linear-gradient(top, #eeeeee 0%,#ffffff 90%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#eeeeee',GradientType=0 );
background-image: linear-gradient(top, #eeeeee 0%,#ffffff 90%);
}
.select2-container.select2-allowclear .select2-choice .select2-chosen {
margin-right: 42px;
}
.select2-container .select2-choice > .select2-chosen {
margin-right: 26px;
display: block;
overflow: hidden;
white-space: nowrap;
-ms-text-overflow: ellipsis;
-o-text-overflow: ellipsis;
text-overflow: ellipsis;
}
.select2-container .select2-choice abbr {
display: none;
width: 12px;
height: 12px;
position: absolute;
right: 24px;
top: 8px;
font-size: 1px;
text-decoration: none;
border: 0;
background: url('select2.png') right top no-repeat;
cursor: pointer;
outline: 0;
}
.select2-container.select2-allowclear .select2-choice abbr {
display: inline-block;
}
.select2-container .select2-choice abbr:hover {
background-position: right -11px;
cursor: pointer;
}
.select2-drop-undermask {
border: 0;
margin: 0;
padding: 0;
position: absolute;
left: 0;
top: 0;
z-index: 9998;
background-color: transparent;
filter: alpha(opacity=0);
}
.select2-drop-mask {
border: 0;
margin: 0;
padding: 0;
position: absolute;
left: 0;
top: 0;
z-index: 9998;
/* styles required for IE to work */
background-color: #fff;
opacity: 0;
filter: alpha(opacity=0);
}
.select2-drop {
width: 100%;
margin-top: -1px;
position: absolute;
z-index: 9999;
top: 100%;
background: #fff;
color: #000;
border: 1px solid #aaa;
border-top: 0;
-webkit-border-radius: 0 0 4px 4px;
-moz-border-radius: 0 0 4px 4px;
border-radius: 0 0 4px 4px;
-webkit-box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
-moz-box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
}
.select2-drop-auto-width {
border-top: 1px solid #aaa;
width: auto;
}
.select2-drop-auto-width .select2-search {
padding-top: 4px;
}
.select2-drop.select2-drop-above {
margin-top: 1px;
border-top: 1px solid #aaa;
border-bottom: 0;
-webkit-border-radius: 4px 4px 0 0;
-moz-border-radius: 4px 4px 0 0;
border-radius: 4px 4px 0 0;
-webkit-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);
-moz-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);
box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);
}
.select2-drop-active {
border: 1px solid #5897fb;
border-top: none;
}
.select2-drop.select2-drop-above.select2-drop-active {
border-top: 1px solid #5897fb;
}
.select2-container .select2-choice .select2-arrow {
display: inline-block;
width: 18px;
height: 100%;
position: absolute;
right: 0;
top: 0;
border-left: 1px solid #aaa;
-webkit-border-radius: 0 4px 4px 0;
-moz-border-radius: 0 4px 4px 0;
border-radius: 0 4px 4px 0;
-webkit-background-clip: padding-box;
-moz-background-clip: padding;
background-clip: padding-box;
background: #ccc;
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #ccc), color-stop(0.6, #eee));
background-image: -webkit-linear-gradient(center bottom, #ccc 0%, #eee 60%);
background-image: -moz-linear-gradient(center bottom, #ccc 0%, #eee 60%);
background-image: -o-linear-gradient(bottom, #ccc 0%, #eee 60%);
background-image: -ms-linear-gradient(top, #cccccc 0%, #eeeeee 60%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr = '#eeeeee', endColorstr = '#cccccc', GradientType = 0);
background-image: linear-gradient(top, #cccccc 0%, #eeeeee 60%);
}
.select2-container .select2-choice .select2-arrow b {
display: block;
width: 100%;
height: 100%;
background: url('select2.png') no-repeat 0 1px;
}
.select2-search {
display: inline-block;
width: 100%;
min-height: 26px;
margin: 0;
padding-left: 4px;
padding-right: 4px;
position: relative;
z-index: 10000;
white-space: nowrap;
}
.select2-search input {
width: 100%;
height: auto !important;
min-height: 26px;
padding: 4px 20px 4px 5px;
margin: 0;
outline: 0;
font-family: sans-serif;
font-size: 1em;
border: 1px solid #aaa;
-webkit-border-radius: 0;
-moz-border-radius: 0;
border-radius: 0;
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
background: #fff url('select2.png') no-repeat 100% -22px;
background: url('select2.png') no-repeat 100% -22px, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee));
background: url('select2.png') no-repeat 100% -22px, -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%);
background: url('select2.png') no-repeat 100% -22px, -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%);
background: url('select2.png') no-repeat 100% -22px, -o-linear-gradient(bottom, white 85%, #eeeeee 99%);
background: url('select2.png') no-repeat 100% -22px, -ms-linear-gradient(top, #ffffff 85%, #eeeeee 99%);
background: url('select2.png') no-repeat 100% -22px, linear-gradient(top, #ffffff 85%, #eeeeee 99%);
}
.select2-drop.select2-drop-above .select2-search input {
margin-top: 4px;
}
.select2-search input.select2-active {
background: #fff url('select2-spinner.gif') no-repeat 100%;
background: url('select2-spinner.gif') no-repeat 100%, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee));
background: url('select2-spinner.gif') no-repeat 100%, -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%);
background: url('select2-spinner.gif') no-repeat 100%, -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%);
background: url('select2-spinner.gif') no-repeat 100%, -o-linear-gradient(bottom, white 85%, #eeeeee 99%);
background: url('select2-spinner.gif') no-repeat 100%, -ms-linear-gradient(top, #ffffff 85%, #eeeeee 99%);
background: url('select2-spinner.gif') no-repeat 100%, linear-gradient(top, #ffffff 85%, #eeeeee 99%);
}
.select2-container-active .select2-choice,
.select2-container-active .select2-choices {
border: 1px solid #5897fb;
outline: none;
-webkit-box-shadow: 0 0 5px rgba(0,0,0,.3);
-moz-box-shadow: 0 0 5px rgba(0,0,0,.3);
box-shadow: 0 0 5px rgba(0,0,0,.3);
}
.select2-dropdown-open .select2-choice {
border-bottom-color: transparent;
-webkit-box-shadow: 0 1px 0 #fff inset;
-moz-box-shadow: 0 1px 0 #fff inset;
box-shadow: 0 1px 0 #fff inset;
-webkit-border-bottom-left-radius: 0;
-moz-border-radius-bottomleft: 0;
border-bottom-left-radius: 0;
-webkit-border-bottom-right-radius: 0;
-moz-border-radius-bottomright: 0;
border-bottom-right-radius: 0;
background-color: #eee;
background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, white), color-stop(0.5, #eeeeee));
background-image: -webkit-linear-gradient(center bottom, white 0%, #eeeeee 50%);
background-image: -moz-linear-gradient(center bottom, white 0%, #eeeeee 50%);
background-image: -o-linear-gradient(bottom, white 0%, #eeeeee 50%);
background-image: -ms-linear-gradient(top, #ffffff 0%,#eeeeee 50%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#eeeeee', endColorstr='#ffffff',GradientType=0 );
background-image: linear-gradient(top, #ffffff 0%,#eeeeee 50%);
}
.select2-dropdown-open.select2-drop-above .select2-choice,
.select2-dropdown-open.select2-drop-above .select2-choices {
border: 1px solid #5897fb;
border-top-color: transparent;
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, white), color-stop(0.5, #eeeeee));
background-image: -webkit-linear-gradient(center top, white 0%, #eeeeee 50%);
background-image: -moz-linear-gradient(center top, white 0%, #eeeeee 50%);
background-image: -o-linear-gradient(top, white 0%, #eeeeee 50%);
background-image: -ms-linear-gradient(bottom, #ffffff 0%,#eeeeee 50%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#eeeeee', endColorstr='#ffffff',GradientType=0 );
background-image: linear-gradient(bottom, #ffffff 0%,#eeeeee 50%);
}
.select2-dropdown-open .select2-choice .select2-arrow {
background: transparent;
border-left: none;
filter: none;
}
.select2-dropdown-open .select2-choice .select2-arrow b {
background-position: -18px 1px;
}
/* results */
.select2-results {
max-height: 200px;
padding: 0 0 0 4px;
margin: 4px 4px 4px 0;
position: relative;
overflow-x: hidden;
overflow-y: auto;
-webkit-tap-highlight-color: rgba(0,0,0,0);
}
.select2-results ul.select2-result-sub {
margin: 0;
padding-left: 0;
}
.select2-results ul.select2-result-sub > li .select2-result-label { padding-left: 20px }
.select2-results ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 40px }
.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 60px }
.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 80px }
.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 100px }
.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 110px }
.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 120px }
.select2-results li {
list-style: none;
display: list-item;
background-image: none;
}
.select2-results li.select2-result-with-children > .select2-result-label {
font-weight: bold;
}
.select2-results .select2-result-label {
padding: 3px 7px 4px;
margin: 0;
cursor: pointer;
min-height: 1em;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.select2-results .select2-highlighted {
background: #3875d7;
color: #fff;
}
.select2-results li em {
background: #feffde;
font-style: normal;
}
.select2-results .select2-highlighted em {
background: transparent;
}
.select2-results .select2-highlighted ul {
background: white;
color: #000;
}
.select2-results .select2-no-results,
.select2-results .select2-searching,
.select2-results .select2-selection-limit {
background: #f4f4f4;
display: list-item;
}
/*
disabled look for disabled choices in the results dropdown
*/
.select2-results .select2-disabled.select2-highlighted {
color: #666;
background: #f4f4f4;
display: list-item;
cursor: default;
}
.select2-results .select2-disabled {
background: #f4f4f4;
display: list-item;
cursor: default;
}
.select2-results .select2-selected {
display: none;
}
.select2-more-results.select2-active {
background: #f4f4f4 url('select2-spinner.gif') no-repeat 100%;
}
.select2-more-results {
background: #f4f4f4;
display: list-item;
}
/* disabled styles */
.select2-container.select2-container-disabled .select2-choice {
background-color: #f4f4f4;
background-image: none;
border: 1px solid #ddd;
cursor: default;
}
.select2-container.select2-container-disabled .select2-choice .select2-arrow {
background-color: #f4f4f4;
background-image: none;
border-left: 0;
}
.select2-container.select2-container-disabled .select2-choice abbr {
display: none;
}
/* multiselect */
.select2-container-multi .select2-choices {
height: auto !important;
height: 1%;
margin: 0;
padding: 0;
position: relative;
border: 1px solid #aaa;
cursor: text;
overflow: hidden;
background-color: #fff;
background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff));
background-image: -webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
background-image: -moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
background-image: -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
background-image: -ms-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
background-image: linear-gradient(top, #eeeeee 1%, #ffffff 15%);
}
.select2-locked {
padding: 3px 5px 3px 5px !important;
}
.select2-container-multi .select2-choices {
min-height: 26px;
}
.select2-container-multi.select2-container-active .select2-choices {
border: 1px solid #5897fb;
outline: none;
-webkit-box-shadow: 0 0 5px rgba(0,0,0,.3);
-moz-box-shadow: 0 0 5px rgba(0,0,0,.3);
box-shadow: 0 0 5px rgba(0,0,0,.3);
}
.select2-container-multi .select2-choices li {
float: left;
list-style: none;
}
.select2-container-multi .select2-choices .select2-search-field {
margin: 0;
padding: 0;
white-space: nowrap;
}
.select2-container-multi .select2-choices .select2-search-field input {
padding: 5px;
margin: 1px 0;
font-family: sans-serif;
font-size: 100%;
color: #666;
outline: 0;
border: 0;
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
background: transparent !important;
}
.select2-container-multi .select2-choices .select2-search-field input.select2-active {
background: #fff url('select2-spinner.gif') no-repeat 100% !important;
}
.select2-default {
color: #999 !important;
}
.select2-container-multi .select2-choices .select2-search-choice {
padding: 3px 5px 3px 18px;
margin: 3px 0 3px 5px;
position: relative;
line-height: 13px;
color: #333;
cursor: default;
border: 1px solid #aaaaaa;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
-webkit-box-shadow: 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05);
-moz-box-shadow: 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05);
box-shadow: 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05);
-webkit-background-clip: padding-box;
-moz-background-clip: padding;
background-clip: padding-box;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
background-color: #e4e4e4;
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#eeeeee', endColorstr='#f4f4f4', GradientType=0 );
background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eeeeee));
background-image: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
background-image: -moz-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
background-image: -o-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
background-image: -ms-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
background-image: linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
}
.select2-container-multi .select2-choices .select2-search-choice .select2-chosen {
cursor: default;
}
.select2-container-multi .select2-choices .select2-search-choice-focus {
background: #d4d4d4;
}
.select2-search-choice-close {
display: block;
width: 12px;
height: 13px;
position: absolute;
right: 3px;
top: 4px;
font-size: 1px;
outline: none;
background: url('select2.png') right top no-repeat;
}
.select2-container-multi .select2-search-choice-close {
left: 3px;
}
.select2-container-multi .select2-choices .select2-search-choice .select2-search-choice-close:hover {
background-position: right -11px;
}
.select2-container-multi .select2-choices .select2-search-choice-focus .select2-search-choice-close {
background-position: right -11px;
}
/* disabled styles */
.select2-container-multi.select2-container-disabled .select2-choices{
background-color: #f4f4f4;
background-image: none;
border: 1px solid #ddd;
cursor: default;
}
.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice {
padding: 3px 5px 3px 5px;
border: 1px solid #ddd;
background-image: none;
background-color: #f4f4f4;
}
.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice .select2-search-choice-close { display: none;
background:none;
}
/* end multiselect */
.select2-result-selectable .select2-match,
.select2-result-unselectable .select2-match {
text-decoration: underline;
}
.select2-offscreen, .select2-offscreen:focus {
clip: rect(0 0 0 0);
width: 1px;
height: 1px;
border: 0;
margin: 0;
padding: 0;
overflow: hidden;
position: absolute;
outline: 0;
left: 0px;
}
.select2-display-none {
display: none;
}
.select2-measure-scrollbar {
position: absolute;
top: -10000px;
left: -10000px;
width: 100px;
height: 100px;
overflow: scroll;
}
/* Retina-ize icons */
@media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-resolution: 144dpi) {
.select2-search input, .select2-search-choice-close, .select2-container .select2-choice abbr, .select2-container .select2-choice .select2-arrow b {
background-image: url('select2x2.png') !important;
background-repeat: no-repeat !important;
background-size: 60px 40px !important;
}
.select2-search input {
background-position: 100% -21px !important;
}
}

View file

@ -0,0 +1,36 @@
{
"name": "select2",
"title": "Select2",
"description": "Select2 is a jQuery based replacement for select boxes. It supports searching, remote data sets, and infinite scrolling of results.",
"keywords": [
"select",
"autocomplete",
"typeahead",
"dropdown",
"multiselect",
"tag",
"tagging"
],
"version": "3.4.1",
"author": {
"name": "Igor Vaynberg",
"url": "https://github.com/ivaynberg"
},
"licenses": [
{
"type": "Apache",
"url": "http://www.apache.org/licenses/LICENSE-2.0"
},
{
"type": "GPL v2",
"url": "http://www.gnu.org/licenses/gpl-2.0.html"
}
],
"bugs": "https://github.com/ivaynberg/select2/issues",
"homepage": "http://ivaynberg.github.com/select2",
"docs": "http://ivaynberg.github.com/select2/",
"download": "https://github.com/ivaynberg/select2/tags",
"dependencies": {
"jquery": ">=1.7.1"
}
}

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 613 B

View file

@ -0,0 +1,17 @@
/**
* Select2 Arabic translation.
*
* Author: Your Name <amedhat3@gmail.com>
*/
(function ($) {
"use strict";
$.extend($.fn.select2.defaults, {
formatNoMatches: function () { return "لا توجد نتائج"; },
formatInputTooShort: function (input, min) { var n = min - input.length; return "من فضلك أدخل " + n + " حروف أكثر"; },
formatInputTooLong: function (input, max) { var n = input.length - max; return "من فضلك أحذف " + n + " حروف"; },
formatSelectionTooBig: function (limit) { return "يمكنك ان تختار " + limit + " أختيارات فقط"; },
formatLoadMore: function (pageNumber) { return "تحمل المذيد من النتائج ..."; },
formatSearching: function () { return "جاري البحث ..."; }
});
})(jQuery);

View file

@ -0,0 +1,17 @@
/**
* Select2 Catalan translation.
*
* Author: David Planella <david.planella@gmail.com>
*/
(function ($) {
"use strict";
$.extend($.fn.select2.defaults, {
formatNoMatches: function () { return "No s'ha trobat cap coincidència"; },
formatInputTooShort: function (input, min) { var n = min - input.length; return "Introduïu " + n + " caràcter" + (n == 1 ? "" : "s") + " més"; },
formatInputTooLong: function (input, max) { var n = input.length - max; return "Introduïu " + n + " caràcter" + (n == 1? "" : "s") + "menys"; },
formatSelectionTooBig: function (limit) { return "Només podeu seleccionar " + limit + " element" + (limit == 1 ? "" : "s"); },
formatLoadMore: function (pageNumber) { return "S'estan carregant més resultats..."; },
formatSearching: function () { return "S'està cercant..."; }
});
})(jQuery);

View file

@ -0,0 +1,49 @@
/**
* Select2 Czech translation.
*
* Author: Michal Marek <ahoj@michal-marek.cz>
* Author - sklonovani: David Vallner <david@vallner.net>
*/
(function ($) {
"use strict";
// use text for the numbers 2 through 4
var smallNumbers = {
2: function(masc) { return (masc ? "dva" : "dvě"); },
3: function() { return "tři"; },
4: function() { return "čtyři"; }
}
$.extend($.fn.select2.defaults, {
formatNoMatches: function () { return "Nenalezeny žádné položky"; },
formatInputTooShort: function (input, min) {
var n = min - input.length;
if (n == 1) {
return "Prosím zadejte ještě jeden znak";
} else if (n <= 4) {
return "Prosím zadejte ještě další "+smallNumbers[n](true)+" znaky";
} else {
return "Prosím zadejte ještě dalších "+n+" znaků";
}
},
formatInputTooLong: function (input, max) {
var n = input.length - max;
if (n == 1) {
return "Prosím zadejte o jeden znak méně";
} else if (n <= 4) {
return "Prosím zadejte o "+smallNumbers[n](true)+" znaky méně";
} else {
return "Prosím zadejte o "+n+" znaků méně";
}
},
formatSelectionTooBig: function (limit) {
if (limit == 1) {
return "Můžete zvolit jen jednu položku";
} else if (limit <= 4) {
return "Můžete zvolit maximálně "+smallNumbers[limit](false)+" položky";
} else {
return "Můžete zvolit maximálně "+limit+" položek";
}
},
formatLoadMore: function (pageNumber) { return "Načítají se další výsledky..."; },
formatSearching: function () { return "Vyhledávání..."; }
});
})(jQuery);

View file

@ -0,0 +1,17 @@
/**
* Select2 Danish translation.
*
* Author: Anders Jenbo <anders@jenbo.dk>
*/
(function ($) {
"use strict";
$.extend($.fn.select2.defaults, {
formatNoMatches: function () { return "Ingen resultater fundet"; },
formatInputTooShort: function (input, min) { var n = min - input.length; return "Angiv venligst " + n + " tegn mere"; },
formatInputTooLong: function (input, max) { var n = input.length - max; return "Angiv venligst " + n + " tegn mindre"; },
formatSelectionTooBig: function (limit) { return "Du kan kun vælge " + limit + " emne" + (limit === 1 ? "" : "r"); },
formatLoadMore: function (pageNumber) { return "Indlæser flere resultater…"; },
formatSearching: function () { return "Søger…"; }
});
})(jQuery);

View file

@ -0,0 +1,15 @@
/**
* Select2 German translation
*/
(function ($) {
"use strict";
$.extend($.fn.select2.defaults, {
formatNoMatches: function () { return "Keine Übereinstimmungen gefunden"; },
formatInputTooShort: function (input, min) { var n = min - input.length; return "Bitte " + n + " Zeichen mehr eingeben"; },
formatInputTooLong: function (input, max) { var n = input.length - max; return "Bitte " + n + " Zeichen weniger eingeben"; },
formatSelectionTooBig: function (limit) { return "Sie können nur " + limit + " Eintr" + (limit === 1 ? "ag" : "äge") + " auswählen"; },
formatLoadMore: function (pageNumber) { return "Lade mehr Ergebnisse..."; },
formatSearching: function () { return "Suche..."; }
});
})(jQuery);

View file

@ -0,0 +1,17 @@
/**
* Select2 <Language> translation.
*
* Author: Your Name <your@email>
*/
(function ($) {
"use strict";
$.extend($.fn.select2.defaults, {
formatNoMatches: function () { return "Δεν βρέθηκαν αποτελέσματα"; },
formatInputTooShort: function (input, min) { var n = min - input.length; return "Παρακαλούμε εισάγετε " + n + " περισσότερους χαρακτήρες" + (n == 1 ? "" : "s"); },
formatInputTooLong: function (input, max) { var n = input.length - max; return "Παρακαλούμε διαγράψτε " + n + " χαρακτήρες" + (n == 1 ? "" : "s"); },
formatSelectionTooBig: function (limit) { return "Μπορείτε να επιλέξετε μόνο " + limit + " αντικείμενο" + (limit == 1 ? "" : "s"); },
formatLoadMore: function (pageNumber) { return "Φόρτωση περισσότερων..."; },
formatSearching: function () { return "Αναζήτηση..."; }
});
})(jQuery);

View file

@ -0,0 +1,17 @@
/**
* Select2 <Language> translation.
*
* Author: Your Name <your@email>
*/
(function ($) {
"use strict";
$.extend($.fn.select2.defaults, {
formatNoMatches: function () { return "No matches found"; },
formatInputTooShort: function (input, min) { var n = min - input.length; return "Please enter " + n + " more character" + (n == 1 ? "" : "s"); },
formatInputTooLong: function (input, max) { var n = input.length - max; return "Please delete " + n + " character" + (n == 1 ? "" : "s"); },
formatSelectionTooBig: function (limit) { return "You can only select " + limit + " item" + (limit == 1 ? "" : "s"); },
formatLoadMore: function (pageNumber) { return "Loading more results..."; },
formatSearching: function () { return "Searching..."; }
});
})(jQuery);

View file

@ -0,0 +1,15 @@
/**
* Select2 Spanish translation
*/
(function ($) {
"use strict";
$.extend($.fn.select2.defaults, {
formatNoMatches: function () { return "No se encontraron resultados"; },
formatInputTooShort: function (input, min) { var n = min - input.length; return "Por favor adicione " + n + " caracter" + (n == 1? "" : "es"); },
formatInputTooLong: function (input, max) { var n = input.length - max; return "Por favor elimine " + n + " caracter" + (n == 1? "" : "es"); },
formatSelectionTooBig: function (limit) { return "Solo puede seleccionar " + limit + " elemento" + (limit == 1 ? "" : "s"); },
formatLoadMore: function (pageNumber) { return "Cargando más resultados..."; },
formatSearching: function () { return "Buscando..."; }
});
})(jQuery);

View file

@ -0,0 +1,17 @@
/**
* Select2 Estonian translation.
*
* Author: Kuldar Kalvik <kuldar@kalvik.ee>
*/
(function ($) {
"use strict";
$.extend($.fn.select2.defaults, {
formatNoMatches: function () { return "Tulemused puuduvad"; },
formatInputTooShort: function (input, min) { var n = min - input.length; return "Sisesta " + n + " täht" + (n == 1 ? "" : "e") + " rohkem"; },
formatInputTooLong: function (input, max) { var n = input.length - max; return "Sisesta " + n + " täht" + (n == 1? "" : "e") + " vähem"; },
formatSelectionTooBig: function (limit) { return "Saad vaid " + limit + " tulemus" + (limit == 1 ? "e" : "t") + " valida"; },
formatLoadMore: function (pageNumber) { return "Laen tulemusi.."; },
formatSearching: function () { return "Otsin.."; }
});
})(jQuery);

View file

@ -0,0 +1,43 @@
/**
* Select2 Basque translation.
*
* Author: Julen Ruiz Aizpuru <julenx at gmail dot com>
*/
(function ($) {
"use strict";
$.extend($.fn.select2.defaults, {
formatNoMatches: function () {
return "Ez da bat datorrenik aurkitu";
},
formatInputTooShort: function (input, min) {
var n = min - input.length;
if (n === 1) {
return "Idatzi karaktere bat gehiago";
} else {
return "Idatzi " + n + " karaktere gehiago";
}
},
formatInputTooLong: function (input, max) {
var n = input.length - max;
if (n === 1) {
return "Idatzi karaktere bat gutxiago";
} else {
return "Idatzi " + n + " karaktere gutxiago";
}
},
formatSelectionTooBig: function (limit) {
if (limit === 1 ) {
return "Elementu bakarra hauta dezakezu";
} else {
return limit + " elementu hauta ditzakezu soilik";
}
},
formatLoadMore: function (pageNumber) {
return "Emaitza gehiago kargatzen...";
},
formatSearching: function () {
return "Bilatzen...";
}
});
})(jQuery);

View file

@ -0,0 +1,28 @@
/**
* Select2 Finnish translation
*/
(function ($) {
"use strict";
$.extend($.fn.select2.defaults, {
formatNoMatches: function () {
return "Ei tuloksia";
},
formatInputTooShort: function (input, min) {
var n = min - input.length;
return "Ole hyvä ja anna " + n + " merkkiä lisää.";
},
formatInputTooLong: function (input, max) {
var n = input.length - max;
return "Ole hyvä ja annar " + n + " merkkiä vähemmän.";
},
formatSelectionTooBig: function (limit) {
return "Voit valita ainoastaan " + limit + " kpl";
},
formatLoadMore: function (pageNumber) {
return "Ladataan lisää tuloksia...";
},
formatSearching: function () {
return "Etsitään...";
}
});
})(jQuery);

View file

@ -0,0 +1,15 @@
/**
* Select2 French translation
*/
(function ($) {
"use strict";
$.extend($.fn.select2.defaults, {
formatNoMatches: function () { return "Aucun résultat trouvé"; },
formatInputTooShort: function (input, min) { var n = min - input.length; return "Merci de saisir " + n + " caractère" + (n == 1? "" : "s") + " de plus"; },
formatInputTooLong: function (input, max) { var n = input.length - max; return "Merci de supprimer " + n + " caractère" + (n == 1? "" : "s"); },
formatSelectionTooBig: function (limit) { return "Vous pouvez seulement sélectionner " + limit + " élément" + (limit == 1 ? "" : "s"); },
formatLoadMore: function (pageNumber) { return "Chargement de résultats supplémentaires..."; },
formatSearching: function () { return "Recherche en cours..."; }
});
})(jQuery);

View file

@ -0,0 +1,43 @@
/**
* Select2 Galician translation
*
* Author: Leandro Regueiro <leandro.regueiro@gmail.com>
*/
(function ($) {
"use strict";
$.extend($.fn.select2.defaults, {
formatNoMatches: function () {
return "Non se atoparon resultados";
},
formatInputTooShort: function (input, min) {
var n = min - input.length;
if (n === 1) {
return "Engada un carácter";
} else {
return "Engada " + n + " caracteres";
}
},
formatInputTooLong: function (input, max) {
var n = input.length - max;
if (n === 1) {
return "Elimine un carácter";
} else {
return "Elimine " + n + " caracteres";
}
},
formatSelectionTooBig: function (limit) {
if (limit === 1 ) {
return "Só pode seleccionar un elemento";
} else {
return "Só pode seleccionar " + limit + " elementos";
}
},
formatLoadMore: function (pageNumber) {
return "Cargando máis resultados...";
},
formatSearching: function () {
return "Buscando...";
}
});
})(jQuery);

View file

@ -0,0 +1,17 @@
/**
* Select2 Hebrew translation.
*
* Author: Yakir Sitbon <http://www.yakirs.net/>
*/
(function ($) {
"use strict";
$.extend($.fn.select2.defaults, {
formatNoMatches: function () { return "לא נמצאו התאמות"; },
formatInputTooShort: function (input, min) { var n = min - input.length; return "נא להזין עוד " + n + " תווים נוספים"; },
formatInputTooLong: function (input, max) { var n = input.length - max; return "נא להזין פחות " + n + " תווים"; },
formatSelectionTooBig: function (limit) { return "ניתן לבחור " + limit + " פריטים"; },
formatLoadMore: function (pageNumber) { return "טוען תוצאות נוספות..."; },
formatSearching: function () { return "מחפש..."; }
});
})(jQuery);

View file

@ -0,0 +1,42 @@
/**
* Select2 Croatian translation.
*
* Author: Edi Modrić <edi.modric@gmail.com>
*/
(function ($) {
"use strict";
var specialNumbers = {
1: function(n) { return (n % 100 != 11 ? "znak" : "znakova"); },
2: function(n) { return (n % 100 != 12 ? "znaka" : "znakova"); },
3: function(n) { return (n % 100 != 13 ? "znaka" : "znakova"); },
4: function(n) { return (n % 100 != 14 ? "znaka" : "znakova"); }
};
$.extend($.fn.select2.defaults, {
formatNoMatches: function () { return "Nema rezultata"; },
formatInputTooShort: function (input, min) {
var n = min - input.length;
var nMod10 = n % 10;
if (nMod10 > 0 && nMod10 < 5) {
return "Unesite još " + n + " " + specialNumbers[nMod10](n);
}
return "Unesite još " + n + " znakova";
},
formatInputTooLong: function (input, max) {
var n = input.length - max;
var nMod10 = n % 10;
if (nMod10 > 0 && nMod10 < 5) {
return "Unesite " + n + " " + specialNumbers[nMod10](n) + " manje";
}
return "Unesite " + n + " znakova manje";
},
formatSelectionTooBig: function (limit) { return "Maksimalan broj odabranih stavki je " + limit; },
formatLoadMore: function (pageNumber) { return "Učitavanje rezultata..."; },
formatSearching: function () { return "Pretraga..."; }
});
})(jQuery);

View file

@ -0,0 +1,15 @@
/**
* Select2 Hungarian translation
*/
(function ($) {
"use strict";
$.extend($.fn.select2.defaults, {
formatNoMatches: function () { return "Nincs találat."; },
formatInputTooShort: function (input, min) { var n = min - input.length; return "Túl rövid. Még " + n + " karakter hiányzik."; },
formatInputTooLong: function (input, max) { var n = input.length - max; return "Túl hosszú. " + n + " kerekterrel több mint kellene."; },
formatSelectionTooBig: function (limit) { return "Csak " + limit + " elemet lehet kiválasztani."; },
formatLoadMore: function (pageNumber) { return "Töltés..."; },
formatSearching: function () { return "Keresés..."; }
});
})(jQuery);

View file

@ -0,0 +1,17 @@
/**
* Select2 Indonesian translation.
*
* Author: Ibrahim Yusuf <ibrahim7usuf@gmail.com>
*/
(function ($) {
"use strict";
$.extend($.fn.select2.defaults, {
formatNoMatches: function () { return "Tidak ada data yang sesuai"; },
formatInputTooShort: function (input, min) { var n = min - input.length; return "Masukkan " + n + " huruf lagi" + (n == 1 ? "" : "s"); },
formatInputTooLong: function (input, max) { var n = input.length - max; return "Hapus " + n + " huruf" + (n == 1 ? "" : "s"); },
formatSelectionTooBig: function (limit) { return "Anda hanya dapat memilih " + limit + " pilihan" + (limit == 1 ? "" : "s"); },
formatLoadMore: function (pageNumber) { return "Mengambil data..."; },
formatSearching: function () { return "Mencari..."; }
});
})(jQuery);

View file

@ -0,0 +1,16 @@
/**
* Select2 Icelandic translation.
*
*/
(function ($) {
"use strict";
$.extend($.fn.select2.defaults, {
formatNoMatches: function () { return "Ekkert fannst"; },
formatInputTooShort: function (input, min) { var n = min - input.length; return "Vinsamlegast skrifið " + n + " staf" + (n == 1 ? "" : "i") + " í viðbót"; },
formatInputTooLong: function (input, max) { var n = input.length - max; return "Vinsamlegast styttið texta um " + n + " staf" + (n == 1 ? "" : "i"); },
formatSelectionTooBig: function (limit) { return "Þú getur aðeins valið " + limit + " atriði"; },
formatLoadMore: function (pageNumber) { return "Sæki fleiri niðurstöður..."; },
formatSearching: function () { return "Leita..."; }
});
})(jQuery);

View file

@ -0,0 +1,15 @@
/**
* Select2 Italian translation
*/
(function ($) {
"use strict";
$.extend($.fn.select2.defaults, {
formatNoMatches: function () { return "Nessuna corrispondenza trovata"; },
formatInputTooShort: function (input, min) { var n = min - input.length; return "Inserisci ancora " + n + " caratter" + (n == 1? "e" : "i"); },
formatInputTooLong: function (input, max) { var n = input.length - max; return "Inserisci " + n + " caratter" + (n == 1? "e" : "i") + " in meno"; },
formatSelectionTooBig: function (limit) { return "Puoi selezionare solo " + limit + " element" + (limit == 1 ? "o" : "i"); },
formatLoadMore: function (pageNumber) { return "Caricamento in corso..."; },
formatSearching: function () { return "Ricerca..."; }
});
})(jQuery);

View file

@ -0,0 +1,15 @@
/**
* Select2 Japanese translation.
*/
(function ($) {
"use strict";
$.extend($.fn.select2.defaults, {
formatNoMatches: function () { return "該当なし"; },
formatInputTooShort: function (input, min) { var n = min - input.length; return "後" + n + "文字入れてください"; },
formatInputTooLong: function (input, max) { var n = input.length - max; return "検索文字列が" + n + "文字長すぎます"; },
formatSelectionTooBig: function (limit) { return "最多で" + limit + "項目までしか選択できません"; },
formatLoadMore: function (pageNumber) { return "読込中・・・"; },
formatSearching: function () { return "検索中・・・"; }
});
})(jQuery);

View file

@ -0,0 +1,17 @@
/**
* Select2 <Language> translation.
*
* Author: Swen Mun <longfinfunnel@gmail.com>
*/
(function ($) {
"use strict";
$.extend($.fn.select2.defaults, {
formatNoMatches: function () { return "결과 없음"; },
formatInputTooShort: function (input, min) { var n = min - input.length; return "너무 짧습니다. "+n+"글자 더 입력해주세요."; },
formatInputTooLong: function (input, max) { var n = input.length - max; return "너무 깁니다. "+n+"글자 지워주세요."; },
formatSelectionTooBig: function (limit) { return "최대 "+limit+"개까지만 선택하실 수 있습니다."; },
formatLoadMore: function (pageNumber) { return "불러오는 중…"; },
formatSearching: function () { return "검색 중…"; }
});
})(jQuery);

View file

@ -0,0 +1,29 @@
/**
* Select2 lithuanian translation.
*
* Author: CRONUS Karmalakas <cronus dot karmalakas at gmail dot com>
*/
(function ($) {
"use strict";
$.extend($.fn.select2.defaults, {
formatNoMatches: function () { return "Atitikmenų nerasta"; },
formatInputTooShort: function (input, min) {
var n = min - input.length,
suffix = (n % 10 == 1) && (n % 100 != 11) ? 'į' : (((n % 10 >= 2) && ((n % 100 < 10) || (n % 100 >= 20))) ? 'ius' : 'ių');
return "Įrašykite dar " + n + " simbol" + suffix;
},
formatInputTooLong: function (input, max) {
var n = input.length - max,
suffix = (n % 10 == 1) && (n % 100 != 11) ? 'į' : (((n % 10 >= 2) && ((n % 100 < 10) || (n % 100 >= 20))) ? 'ius' : 'ių');
return "Pašalinkite " + n + " simbol" + suffix;
},
formatSelectionTooBig: function (limit) {
var n = limit,
suffix = (n % 10 == 1) && (n % 100 != 11) ? 'ą' : (((n % 10 >= 2) && ((n % 100 < 10) || (n % 100 >= 20))) ? 'us' : 'ų');
return "Jūs galite pasirinkti tik " + limit + " element" + suffix;
},
formatLoadMore: function (pageNumber) { return "Kraunama daugiau rezultatų..."; },
formatSearching: function () { return "Ieškoma..."; }
});
})(jQuery);

View file

@ -0,0 +1,16 @@
/**
* Select2 Latvian translation
*/
(function ($) {
"use strict";
$.extend($.fn.select2.defaults, {
formatNoMatches: function () { return "Sakritību nav"; },
formatInputTooShort: function (input, min) { var n = min - input.length; return "Lūdzu ievadiet vēl " + n + " simbol" + (n == 11 ? "us" : (/^\d*[1]$/im.test(n)? "u" : "us")); },
formatInputTooLong: function (input, max) { var n = input.length - max; return "Lūdzu ievadiet par " + n + " simbol" + (n == 11 ? "iem" : (/^\d*[1]$/im.test(n)? "u" : "iem")) + " mazāk"; },
formatSelectionTooBig: function (limit) { return "Jūs varat izvēlēties ne vairāk kā " + limit + " element" + (limit == 11 ? "us" : (/^\d*[1]$/im.test(limit)? "u" : "us")); },
formatLoadMore: function (pageNumber) { return "Datu ielāde..."; },
formatSearching: function () { return "Meklēšana..."; }
});
})(jQuery);

View file

@ -0,0 +1,17 @@
/**
* Select2 Macedonian translation.
*
* Author: Marko Aleksic <psybaron@gmail.com>
*/
(function ($) {
"use strict";
$.extend($.fn.select2.defaults, {
formatNoMatches: function () { return "Нема пронајдено совпаѓања"; },
formatInputTooShort: function (input, min) { var n = min - input.length; return "Ве молиме внесете уште " + n + " карактер" + (n == 1 ? "" : "и"); },
formatInputTooLong: function (input, max) { var n = input.length - max; return "Ве молиме внесете " + n + " помалку карактер" + (n == 1? "" : "и"); },
formatSelectionTooBig: function (limit) { return "Можете да изберете само " + limit + " ставк" + (limit == 1 ? "а" : "и"); },
formatLoadMore: function (pageNumber) { return "Вчитување резултати..."; },
formatSearching: function () { return "Пребарување..."; }
});
})(jQuery);

View file

@ -0,0 +1,15 @@
/**
* Select2 Dutch translation
*/
(function ($) {
"use strict";
$.extend($.fn.select2.defaults, {
formatNoMatches: function () { return "Geen resultaten gevonden"; },
formatInputTooShort: function (input, min) { var n = min - input.length; return "Vul " + n + " karakter" + (n == 1? "" : "s") + " meer in"; },
formatInputTooLong: function (input, max) { var n = input.length - max; return "Vul " + n + " karakter" + (n == 1? "" : "s") + " minder in"; },
formatSelectionTooBig: function (limit) { return "Maximaal " + limit + " item" + (limit == 1 ? "" : "s") + " toegestaan"; },
formatLoadMore: function (pageNumber) { return "Meer resultaten laden..."; },
formatSearching: function () { return "Zoeken..."; },
});
})(jQuery);

View file

@ -0,0 +1,18 @@
/**
* Select2 Norwegian translation.
*
* Author: Torgeir Veimo <torgeir.veimo@gmail.com>
*/
(function ($) {
"use strict";
$.extend($.fn.select2.defaults, {
formatNoMatches: function () { return "Ingen treff"; },
formatInputTooShort: function (input, min) { var n = min - input.length; return "Vennligst skriv inn " + n + (n>1 ? " flere tegn" : " tegn til"); },
formatInputTooLong: function (input, max) { var n = input.length - max; return "Vennligst fjern " + n + " tegn"; },
formatSelectionTooBig: function (limit) { return "Du kan velge maks " + limit + " elementer"; },
formatLoadMore: function (pageNumber) { return "Laster flere resultater..."; },
formatSearching: function () { return "Søker..."; }
});
})(jQuery);

View file

@ -0,0 +1,37 @@
/**
* Select2 Polish translation.
*
* Author: Jan Kondratowicz <jan@kondratowicz.pl>
*/
(function ($) {
"use strict";
var pl_suffix = function(n) {
if(n == 1) return "";
if((n%100 > 1 && n%100 < 5) || (n%100 > 20 && n%10 > 1 && n%10 < 5)) return "i";
return "ów";
};
$.extend($.fn.select2.defaults, {
formatNoMatches: function () {
return "Brak wyników.";
},
formatInputTooShort: function (input, min) {
var n = min - input.length;
return "Wpisz jeszcze " + n + " znak" + pl_suffix(n) + ".";
},
formatInputTooLong: function (input, max) {
var n = input.length - max;
return "Wpisana fraza jest za długa o " + n + " znak" + pl_suffix(n) + ".";
},
formatSelectionTooBig: function (limit) {
return "Możesz zaznaczyć najwyżej " + limit + " element" + pl_suffix(limit) + ".";
},
formatLoadMore: function (pageNumber) {
return "Ładowanie wyników...";
},
formatSearching: function () {
return "Szukanie...";
}
});
})(jQuery);

View file

@ -0,0 +1,15 @@
/**
* Select2 Brazilian Portuguese translation
*/
(function ($) {
"use strict";
$.extend($.fn.select2.defaults, {
formatNoMatches: function () { return "Nenhum resultado encontrado"; },
formatInputTooShort: function (input, min) { var n = min - input.length; return "Informe " + n + " caracter" + (n == 1? "" : "es"); },
formatInputTooLong: function (input, max) { var n = input.length - max; return "Apague " + n + " caracter" + (n == 1? "" : "es"); },
formatSelectionTooBig: function (limit) { return "Só é possível selecionar " + limit + " elemento" + (limit == 1 ? "" : "s"); },
formatLoadMore: function (pageNumber) { return "Carregando mais resultados..."; },
formatSearching: function () { return "Buscando..."; }
});
})(jQuery);

View file

@ -0,0 +1,15 @@
/**
* Select2 Portuguese (Portugal) translation
*/
(function ($) {
"use strict";
$.extend($.fn.select2.defaults, {
formatNoMatches: function () { return "Nenhum resultado encontrado"; },
formatInputTooShort: function (input, min) { var n = min - input.length; return "Introduza " + n + " caracter" + (n == 1 ? "" : "es"); },
formatInputTooLong: function (input, max) { var n = input.length - max; return "Apague " + n + " caracter" + (n == 1 ? "" : "es"); },
formatSelectionTooBig: function (limit) { return "Só é possível selecionar " + limit + " elemento" + (limit == 1 ? "" : "s"); },
formatLoadMore: function (pageNumber) { return "A carregar mais resultados..."; },
formatSearching: function () { return "A pesquisar..."; }
});
})(jQuery);

View file

@ -0,0 +1,15 @@
/**
* Select2 Romanian translation.
*/
(function ($) {
"use strict";
$.extend($.fn.select2.defaults, {
formatNoMatches: function () { return "Nu a fost găsit nimic"; },
formatInputTooShort: function (input, min) { var n = min - input.length; return "Vă rugăm să introduceți incă " + n + " caracter" + (n == 1 ? "" : "e"); },
formatInputTooLong: function (input, max) { var n = input.length - max; return "Vă rugăm să introduceți mai puțin de " + n + " caracter" + (n == 1? "" : "e"); },
formatSelectionTooBig: function (limit) { return "Aveți voie să selectați cel mult " + limit + " element" + (limit == 1 ? "" : "e"); },
formatLoadMore: function (pageNumber) { return "Se încarcă..."; },
formatSearching: function () { return "Căutare..."; }
});
})(jQuery);

View file

@ -0,0 +1,15 @@
/**
* Select2 Russian translation
*/
(function ($) {
"use strict";
$.extend($.fn.select2.defaults, {
formatNoMatches: function () { return "Совпадений не найдено"; },
formatInputTooShort: function (input, min) { var n = min - input.length; return "Пожалуйста, введите еще " + n + " символ" + (n == 1 ? "" : ((n > 1)&&(n < 5) ? "а" : "ов")); },
formatInputTooLong: function (input, max) { var n = input.length - max; return "Пожалуйста, введите на " + n + " символ" + (n == 1 ? "" : ((n > 1)&&(n < 5)? "а" : "ов")) + " меньше"; },
formatSelectionTooBig: function (limit) { return "Вы можете выбрать не более " + limit + " элемент" + (limit == 1 ? "а" : "ов"); },
formatLoadMore: function (pageNumber) { return "Загрузка данных..."; },
formatSearching: function () { return "Поиск..."; }
});
})(jQuery);

View file

@ -0,0 +1,48 @@
/**
* Select2 Slovak translation.
*
* Author: David Vallner <david@vallner.net>
*/
(function ($) {
"use strict";
// use text for the numbers 2 through 4
var smallNumbers = {
2: function(masc) { return (masc ? "dva" : "dve"); },
3: function() { return "tri"; },
4: function() { return "štyri"; }
}
$.extend($.fn.select2.defaults, {
formatNoMatches: function () { return "Nenašli sa žiadne položky"; },
formatInputTooShort: function (input, min) {
var n = min - input.length;
if (n == 1) {
return "Prosím zadajte ešte jeden znak";
} else if (n <= 4) {
return "Prosím zadajte ešte ďalšie "+smallNumbers[n](true)+" znaky";
} else {
return "Prosím zadajte ešte ďalších "+n+" znakov";
}
},
formatInputTooLong: function (input, max) {
var n = input.length - max;
if (n == 1) {
return "Prosím zadajte o jeden znak menej";
} else if (n <= 4) {
return "Prosím zadajte o "+smallNumbers[n](true)+" znaky menej";
} else {
return "Prosím zadajte o "+n+" znakov menej";
}
},
formatSelectionTooBig: function (limit) {
if (limit == 1) {
return "Môžete zvoliť len jednu položku";
} else if (limit <= 4) {
return "Môžete zvoliť najviac "+smallNumbers[limit](false)+" položky";
} else {
return "Môžete zvoliť najviac "+limit+" položiek";
}
},
formatLoadMore: function (pageNumber) { return "Načítavajú sa ďalšie výsledky..."; },
formatSearching: function () { return "Vyhľadávanie..."; }
});
})(jQuery);

View file

@ -0,0 +1,17 @@
/**
* Select2 Swedish translation.
*
* Author: Jens Rantil <jens.rantil@telavox.com>
*/
(function ($) {
"use strict";
$.extend($.fn.select2.defaults, {
formatNoMatches: function () { return "Inga träffar"; },
formatInputTooShort: function (input, min) { var n = min - input.length; return "Var god skriv in " + n + (n>1 ? " till tecken" : " tecken till"); },
formatInputTooLong: function (input, max) { var n = input.length - max; return "Var god sudda ut " + n + " tecken"; },
formatSelectionTooBig: function (limit) { return "Du kan max välja " + limit + " element"; },
formatLoadMore: function (pageNumber) { return "Laddar fler resultat..."; },
formatSearching: function () { return "Söker..."; }
});
})(jQuery);

View file

@ -0,0 +1,17 @@
/**
* Select2 Turkish translation.
*
* Author: Salim KAYABAŞI <salim.kayabasi@gmail.com>
*/
(function ($) {
"use strict";
$.extend($.fn.select2.defaults, {
formatNoMatches: function () { return "Sonuç bulunamadı"; },
formatInputTooShort: function (input, min) { var n = min - input.length; return "En az " + n + " karakter daha girmelisiniz"; },
formatInputTooLong: function (input, max) { var n = input.length - max; return n + " karakter azaltmalısınız"; },
formatSelectionTooBig: function (limit) { return "Sadece " + limit + " seçim yapabilirsiniz"; },
formatLoadMore: function (pageNumber) { return "Daha fazla..."; },
formatSearching: function () { return "Aranıyor..."; }
});
})(jQuery);

View file

@ -0,0 +1,17 @@
/**
* Select2 <Language> translation.
*
* Author: bigmihail <bigmihail@bigmir.net>
*/
(function ($) {
"use strict";
$.extend($.fn.select2.defaults, {
formatNoMatches: function () { return "Нічого не знайдено"; },
formatInputTooShort: function (input, min) { var n = min - input.length, s = ["", "и", "ів"], p = [2,0,1,1,1,2]; return "Введіть буль ласка ще " + n + " символ" + s[ (n%100>4 && n%100<=20)? 2 : p[Math.min(n%10, 5)] ]; },
formatInputTooLong: function (input, max) { var n = input.length - max, s = ["", "и", "ів"], p = [2,0,1,1,1,2]; return "Введіть буль ласка на " + n + " символ" + s[ (n%100>4 && n%100<=20)? 2 : p[Math.min(n%10, 5)] ] + " менше"; },
formatSelectionTooBig: function (limit) {var s = ["", "и", "ів"], p = [2,0,1,1,1,2]; return "Ви можете вибрати лише " + limit + " елемент" + s[ (limit%100>4 && limit%100<=20)? 2 : p[Math.min(limit%10, 5)] ]; },
formatLoadMore: function (pageNumber) { return "Завантаження даних..."; },
formatSearching: function () { return "Пошук..."; }
});
})(jQuery);

View file

@ -0,0 +1,18 @@
/**
* Select2 Vietnamese translation.
*
* Author: Long Nguyen <olragon@gmail.com>
*/
(function ($) {
"use strict";
$.extend($.fn.select2.defaults, {
formatNoMatches: function () { return "Không tìm thấy kết quả"; },
formatInputTooShort: function (input, min) { var n = min - input.length; return "Vui lòng nhập nhiều hơn " + n + " ký tự" + (n == 1 ? "" : "s"); },
formatInputTooLong: function (input, max) { var n = input.length - max; return "Vui lòng nhập ít hơn " + n + " ký tự" + (n == 1? "" : "s"); },
formatSelectionTooBig: function (limit) { return "Chỉ có thể chọn được " + limit + " tùy chọn" + (limit == 1 ? "" : "s"); },
formatLoadMore: function (pageNumber) { return "Đang lấy thêm kết quả..."; },
formatSearching: function () { return "Đang tìm..."; }
});
})(jQuery);

View file

@ -0,0 +1,14 @@
/**
* Select2 Chinese translation
*/
(function ($) {
"use strict";
$.extend($.fn.select2.defaults, {
formatNoMatches: function () { return "没有找到匹配项"; },
formatInputTooShort: function (input, min) { var n = min - input.length; return "请再输入" + n + "个字符";},
formatInputTooLong: function (input, max) { var n = input.length - max; return "请删掉" + n + "个字符";},
formatSelectionTooBig: function (limit) { return "你只能选择最多" + limit + "项"; },
formatLoadMore: function (pageNumber) { return "加载结果中..."; },
formatSearching: function () { return "搜索中..."; }
});
})(jQuery);

View file

@ -0,0 +1,14 @@
/**
* Select2 Traditional Chinese translation
*/
(function ($) {
"use strict";
$.extend($.fn.select2.defaults, {
formatNoMatches: function () { return "沒有找到相符的項目"; },
formatInputTooShort: function (input, min) { var n = min - input.length; return "請再輸入" + n + "個字元";},
formatInputTooLong: function (input, max) { var n = input.length - max; return "請刪掉" + n + "個字元";},
formatSelectionTooBig: function (limit) { return "你只能選擇最多" + limit + "項"; },
formatLoadMore: function (pageNumber) { return "載入中..."; },
formatSearching: function () { return "搜尋中..."; }
});
})(jQuery);

Binary file not shown.

After

Width:  |  Height:  |  Size: 845 B

View file

@ -16,10 +16,10 @@
</ul>
<hr/>
<div>
<a href="#/realms/{{realm.id}}">Back to realm management...</a>
<a href="#/create/application/{{realm.id}}">New Application...</a>
</div>
<div>
<a href="#/create/application/{{realm.id}}">New Application...</a>
<a href="#/realms/{{realm.id}}">Back to realm management...</a>
</div>
</nav>
</div>

View file

@ -5,9 +5,9 @@
<nav id="global-nav">
<div data-ng-controller="RealmDropdownCtrl" >
<ul class="nav pull-left" data-ng-show="showNav()">
<li class="divider-vertical-right"><a href="#/realms/{{currentRealmId}}">Realm</a></li>
<li class="divider-vertical-right"><a href="#/realms/{{current.realm.id}}">Realm</a></li>
</ul>
<select class="nav pull-left" data-ng-show="showNav()" ng-change="changeRealm()" ng-model="current.realm" ng-options="name for (id, name) in current.realms">
<select class="nav pull-left" data-ng-show="showNav()" ng-change="changeRealm()" ng-model="current.realm" ng-options="r.realm for r in current.realms">
</select>
<!-- <select class="nav pull-left" ng-options="r.name for r in current.realms"></select> -->
</div>

View file

@ -17,39 +17,74 @@
<form class="form-horizontal" name="realmForm" novalidate>
<fieldset>
<legend>Settings</legend>
<div data-kc-input>
<label>Name</label>
<input class="input-xlarge" type="text" name="name" data-ng-model="realm.name" autofocus
<div class="control-group">
<label for="realmForm-name" class="control-label">Name <span class="required">*</span></label>
<div class="controls">
<input class="input-xlarge" type="text" name="name" data-ng-model="realm.realm" 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>
<table>
<tr>
<td>
<div class="control-group">
<label class="control-label">Enabled</label>
<div data-kc-input>
<label>Require SSL</label>
<input class="input-xlarge" type="checkbox" name="requireSsl" data-ng-model="realm.requireSsl">
<div class="controls">
<input class="input-xlarge" type="checkbox" name="enabled"
data-ng-model="realm.enabled">
</div>
<div data-kc-input>
<label>Cookie login allowed</label>
<input class="input-xlarge" type="checkbox" name="cookieLoginAllowed" data-ng-model="realm.cookieLoginAllowed">
</div>
</td>
<td>
<div class="control-group">
<label class="control-label">Social login</label>
<div data-kc-input>
<label>User registration</label>
<div class="controls">
<input class="input-xlarge" type="checkbox" name="social"
data-ng-model="realm.social">
</div>
</div>
</td>
</tr>
<tr>
<td>
<div class="control-group">
<label class="control-label">Require SSL</label>
<div class="controls">
<input class="input-xlarge" type="checkbox" name="requireSsl"
data-ng-model="realm.requireSsl">
</div>
</div>
</td>
<td>
<div class="control-group">
<label class="control-label">Cookie login allowed</label>
<div class="controls">
<input class="input-xlarge" type="checkbox" name="cookieLoginAllowed"
data-ng-model="realm.cookieLoginAllowed">
</div>
</div>
</td>
</tr>
<tr>
<td>
<div class="control-group">
<label class="control-label">User registration</label>
<div class="controls">
<input class="input-xlarge" type="checkbox" name="userRegistration"
data-ng-model="realm.userRegistration">
</div>
</div>
</td>
</tr>
</table>
<div class="control-group">
<label for="realmForm-tokenLifespan" class="control-label">Token lifespan</label>
@ -74,13 +109,35 @@
data-ng-model="realm.accessCodeLifespan">
<select style="width: auto;" name="accessCodeLifespanUnit"
data-ng-model="realm.accessCodeLifespanUnit">
<option value="SECONDS" data-ng-selected="!realm.accessCodeLifespanUnit">Seconds</option>
<option value="SECONDS" data-ng-selected="!realm.accessCodeLifespanUnit">Seconds
</option>
<option value="MINUTES">Minutes</option>
<option value="HOURS">Hours</option>
<option value="DAYS">Days</option>
</select>
</div>
</div>
<div class="control-group">
<label class="control-label">Required User Credentials</label>
<div class="controls">
<input style="width:250px" type="text" ui-select2="userCredentialOptions" ng-model="realm.requiredCredentials">
</div>
</div>
<div class="control-group">
<label class="control-label">Required Application Credentials</label>
<div class="controls">
<input style="width:250px" type="text" ui-select2="userCredentialOptions" ng-model="realm.requiredApplicationCredentials">
</div>
</div>
<div class="control-group">
<label class="control-label">Required OAuth Credentials</label>
<div class="controls">
<input style="width:250px" type="text" ui-select2="userCredentialOptions" ng-model="realm.requiredOAuthClientCredentials">
</div>
</div>
</fieldset>
<div class="form-actions" data-ng-show="createRealm">
<button type="submit" data-ng-click="save()" class="btn btn-primary" data-ng-show="changed">Save
@ -96,7 +153,6 @@
</button>
<button type="submit" data-ng-click="reset()" class="btn" data-ng-show="changed">Clear changes
</button>
<a href="#/realms" data-ng-hide="changed">View realms &#187;</a>
<button type="submit" data-ng-click="remove()" class="btn btn-danger" data-ng-hide="changed">
Delete
</button>

View file

@ -1,23 +1,22 @@
<div data-ng-hide="createRealm">
<nav id="local-nav">
<ul class="nav nav-list">
<li data-ng-class="!path[2] && 'active'"><a href="#/realms/{{realm.id}}">Realm Settings</a></li>
<li data-ng-class="path[2] == 'roles' && 'active'"><a href="#/realms/{{realm.id}}/roles">Realm Roles</a>
<ul class="sub-items">
<li data-ng-class="path[0] == 'create' && path[1] == 'role' && 'active'"><a
href="#/create/role/{{realm.id}}">Add Role</a></li>
</ul>
</li>
<li data-ng-class="path[2] == 'users' && 'active'"><a href="#/realms/{{realm.id}}/users">Realm Users</a>
<li data-ng-class="path[2] == 'users' && 'active'"><a href="#/realms/{{realm.id}}/users">Users</a>
<ul class="sub-items">
<li data-ng-class="path[0] == 'create' && path[1] == 'user' && 'active'"><a
href="#/create/user/{{realm.id}}">Add User</a></li>
href="#/create/user/{{realm.id}}">New User</a></li>
<li data-ng-class="path[0] == 'find' && path[1] == 'user' && 'active'"><a
href="#/find/user/{{realm.id}}">Find User</a></li>
</ul>
</li>
<li data-ng-class="path[2] == 'resources' && 'active'"><a href="#/realms/{{realm.id}}/applications">Manage Applications</a></li>
<li data-ng-class="path[2] == 'roles' && 'active'"><a href="#/realms/{{realm.id}}/roles">Roles</a>
<ul class="sub-items">
<li data-ng-class="path[0] == 'create' && path[1] == 'role' && 'active'"><a
href="#/create/role/{{realm.id}}">New Role</a></li>
</ul>
</li>
<li data-ng-class="path[2] == 'applications' && 'active'"><a href="#/realms/{{realm.id}}/applications">Manage Applications</a></li>
<li data-ng-class="!path[2] && 'active'"><a href="#/realms/{{realm.id}}">Realm Settings</a></li>
</ul>
</nav>
</div>

View file

@ -447,7 +447,7 @@ public class OAuthAuthenticationServerValve extends FormAuthenticator implements
userSessionManagement.logout(username);
request.setUserPrincipal(null);
request.setAuthType(null);
// logout user on all declared authenticated resources
// logout user on all declared authenticated applications
logoutResources(username, admin);
redirectToWelcomePage(request, response);
}

View file

@ -6,7 +6,7 @@ import org.keycloak.RealmConfiguration;
import org.keycloak.VerificationException;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.SkeletonKeyToken;
import org.keycloak.representations.idm.RequiredCredentialRepresentation;
import org.keycloak.representations.idm.CredentialRepresentation;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
@ -230,7 +230,7 @@ public class ServletOAuthLogin {
form.param("grant_type", "authorization_code")
.param("code", code)
.param("client_id", client_id)
.param(RequiredCredentialRepresentation.PASSWORD, password)
.param(CredentialRepresentation.PASSWORD, password)
.param("redirect_uri", redirectUri);
Response res = realmInfo.getCodeUrl().request()

View file

@ -153,7 +153,7 @@ public class LoginBean {
requiredCredentials = new LinkedList<RequiredCredential>();
for (RequiredCredentialModel m : realm.getRequiredCredentials()) {
if (m.isInput()) {
requiredCredentials.add(new RequiredCredential(m.getType(), m.isSecret()));
requiredCredentials.add(new RequiredCredential(m.getType(), m.isSecret(), m.getFormLabel()));
}
}
}
@ -193,10 +193,12 @@ public class LoginBean {
public class RequiredCredential {
private String type;
private boolean secret;
private String formLabel;
public RequiredCredential(String type, boolean secure) {
public RequiredCredential(String type, boolean secure, String formLabel) {
this.type = type;
this.secret = secure;
this.formLabel = formLabel;
}
public String getName() {
@ -204,7 +206,7 @@ public class LoginBean {
}
public String getLabel() {
return type;
return formLabel;
}
public String getInputType() {

View file

@ -8,7 +8,7 @@ import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.RSATokenVerifier;
import org.keycloak.VerificationException;
import org.keycloak.representations.SkeletonKeyToken;
import org.keycloak.representations.idm.RequiredCredentialRepresentation;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.services.models.RealmModel;
import org.keycloak.services.models.RequiredCredentialModel;
import org.keycloak.services.models.UserModel;
@ -206,9 +206,9 @@ public class AuthenticationManager {
List<RequiredCredentialModel> requiredCredentials = null;
if (realm.hasRole(user, RealmManager.RESOURCE_ROLE)) {
requiredCredentials = realm.getResourceRequiredCredentials();
requiredCredentials = realm.getRequiredApplicationCredentials();
} else if (realm.hasRole(user, RealmManager.IDENTITY_REQUESTER_ROLE)) {
requiredCredentials = realm.getOAuthClientRequiredCredentials();
requiredCredentials = realm.getRequiredOAuthClientCredentials();
} else {
requiredCredentials = realm.getRequiredCredentials();
}
@ -216,21 +216,23 @@ public class AuthenticationManager {
types.add(credential.getType());
}
if (types.contains(RequiredCredentialRepresentation.PASSWORD)) {
String password = formData.getFirst(RequiredCredentialRepresentation.PASSWORD);
if (types.contains(CredentialRepresentation.PASSWORD)) {
String password = formData.getFirst(CredentialRepresentation.PASSWORD);
if (password == null) {
logger.warn("Password not provided");
return false;
}
if (types.contains(RequiredCredentialRepresentation.TOTP)) {
String token = formData.getFirst(RequiredCredentialRepresentation.TOTP);
if (types.contains(CredentialRepresentation.TOTP)) {
String token = formData.getFirst(CredentialRepresentation.TOTP);
if (token == null) {
logger.warn("TOTP token not provided");
return false;
}
logger.info("validating TOTP");
return realm.validateTOTP(user, password, token);
} else {
logger.info("validating password for user: " + user.getLoginName());
return realm.validatePassword(user, password);
}
} else {

View file

@ -1,28 +1,14 @@
package org.keycloak.services.managers;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.RequiredCredentialRepresentation;
import org.keycloak.representations.idm.ResourceRepresentation;
import org.keycloak.representations.idm.RoleMappingRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.representations.idm.ScopeMappingRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.services.models.KeycloakSession;
import org.keycloak.services.models.RealmModel;
import org.keycloak.services.models.RequiredCredentialModel;
import org.keycloak.services.models.ResourceModel;
import org.keycloak.services.models.RoleModel;
import org.keycloak.services.models.UserCredentialModel;
import org.keycloak.services.models.UserModel;
import org.jboss.resteasy.logging.Logger;
import org.keycloak.representations.idm.*;
import org.keycloak.representations.idm.ApplicationRepresentation;
import org.keycloak.services.models.*;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
/**
@ -32,6 +18,7 @@ import java.util.concurrent.atomic.AtomicLong;
* @version $Revision: 1 $
*/
public class RealmManager {
protected static final Logger logger = Logger.getLogger(RealmManager.class);
private static AtomicLong counter = new AtomicLong(1);
public static final String RESOURCE_ROLE = "KEYCLOAK_RESOURCE";
public static final String IDENTITY_REQUESTER_ROLE = "KEYCLOAK_IDENTITY_REQUESTER";
@ -79,6 +66,26 @@ public class RealmManager {
realm.setPublicKey(keyPair.getPublic());
}
public void updateRealm(RealmRepresentation rep, RealmModel realm) {
if (rep.getRealm() != null) realm.setName(rep.getRealm());
realm.setEnabled(rep.isEnabled());
realm.setSocial(rep.isSocial());
realm.setCookieLoginAllowed(rep.isCookieLoginAllowed());
realm.setRegistrationAllowed(rep.isRegistrationAllowed());
realm.setSslNotRequired((rep.isSslNotRequired()));
realm.setAccessCodeLifespan(rep.getAccessCodeLifespan());
realm.setTokenLifespan(rep.getTokenLifespan());
if (rep.getRequiredOAuthClientCredentials() != null) {
realm.updateRequiredOAuthClientCredentials(rep.getRequiredOAuthClientCredentials());
}
if (rep.getRequiredCredentials() != null) {
realm.updateRequiredCredentials(rep.getRequiredCredentials());
}
if (rep.getRequiredApplicationCredentials() != null) {
realm.updateRequiredApplicationCredentials(rep.getRequiredApplicationCredentials());
}
}
public RealmModel importRealm(RealmRepresentation rep, UserModel realmCreator) {
//verifyRealmRepresentation(rep);
RealmModel realm = createRealm(rep.getRealm());
@ -91,6 +98,7 @@ public class RealmManager {
public void importRealm(RealmRepresentation rep, RealmModel newRealm) {
newRealm.setName(rep.getRealm());
newRealm.setEnabled(rep.isEnabled());
newRealm.setSocial(rep.isSocial());
newRealm.setTokenLifespan(rep.getTokenLifespan());
newRealm.setAccessCodeLifespan(rep.getAccessCodeLifespan());
newRealm.setSslNotRequired(rep.isSslNotRequired());
@ -105,19 +113,19 @@ public class RealmManager {
Map<String, UserModel> userMap = new HashMap<String, UserModel>();
if (rep.getRequiredCredentials() != null) {
for (RequiredCredentialRepresentation requiredCred : rep.getRequiredCredentials()) {
for (String requiredCred : rep.getRequiredCredentials()) {
addRequiredCredential(newRealm, requiredCred);
}
}
if (rep.getRequiredResourceCredentials() != null) {
for (RequiredCredentialRepresentation requiredCred : rep.getRequiredCredentials()) {
if (rep.getRequiredApplicationCredentials() != null) {
for (String requiredCred : rep.getRequiredCredentials()) {
addResourceRequiredCredential(newRealm, requiredCred);
}
}
if (rep.getRequiredOAuthClientCredentials() != null) {
for (RequiredCredentialRepresentation requiredCred : rep.getRequiredCredentials()) {
for (String requiredCred : rep.getRequiredCredentials()) {
addOAuthClientRequiredCredential(newRealm, requiredCred);
}
}
@ -137,7 +145,7 @@ public class RealmManager {
}
}
if (rep.getResources() != null) {
if (rep.getApplications() != null) {
createResources(rep, newRealm);
}
@ -193,33 +201,22 @@ public class RealmManager {
return user;
}
public void addRequiredCredential(RealmModel newRealm, RequiredCredentialRepresentation requiredCred) {
RequiredCredentialModel credential = initializeCred(requiredCred);
newRealm.addRequiredCredential(credential);
public void addRequiredCredential(RealmModel newRealm, String requiredCred) {
newRealm.addRequiredCredential(requiredCred);
}
public void addResourceRequiredCredential(RealmModel newRealm, RequiredCredentialRepresentation requiredCred) {
RequiredCredentialModel credential = initializeCred(requiredCred);
newRealm.addResourceRequiredCredential(credential);
public void addResourceRequiredCredential(RealmModel newRealm, String requiredCred) {
newRealm.addRequiredResourceCredential(requiredCred);
}
public void addOAuthClientRequiredCredential(RealmModel newRealm, RequiredCredentialRepresentation requiredCred) {
RequiredCredentialModel credential = initializeCred(requiredCred);
newRealm.addOAuthClientRequiredCredential(credential);
public void addOAuthClientRequiredCredential(RealmModel newRealm, String requiredCred) {
newRealm.addRequiredOAuthClientCredential(requiredCred);
}
private RequiredCredentialModel initializeCred(RequiredCredentialRepresentation requiredCred) {
RequiredCredentialModel credential = new RequiredCredentialModel();
credential.setType(requiredCred.getType());
credential.setInput(requiredCred.isInput());
credential.setSecret(requiredCred.isSecret());
return credential;
}
protected void createResources(RealmRepresentation rep, RealmModel realm) {
RoleModel loginRole = realm.getRole(RealmManager.RESOURCE_ROLE);
ResourceManager manager = new ResourceManager(this);
for (ResourceRepresentation resourceRep : rep.getResources()) {
for (ApplicationRepresentation resourceRep : rep.getApplications()) {
manager.createResource(realm, loginRole, resourceRep);
}
}
@ -232,4 +229,40 @@ public class RealmManager {
return rep;
}
public RealmRepresentation toRepresentation(RealmModel realm) {
RealmRepresentation rep = new RealmRepresentation();
rep.setId(realm.getId());
rep.setRealm(realm.getName());
rep.setEnabled(realm.isEnabled());
rep.setSocial(realm.isSocial());
rep.setSslNotRequired(realm.isSslNotRequired());
rep.setCookieLoginAllowed(realm.isCookieLoginAllowed());
rep.setPublicKey(realm.getPublicKeyPem());
rep.setTokenLifespan(realm.getTokenLifespan());
rep.setAccessCodeLifespan(realm.getAccessCodeLifespan());
List<RequiredCredentialModel> requiredCredentialModels = realm.getRequiredCredentials();
if (requiredCredentialModels.size() > 0) {
rep.setRequiredCredentials(new HashSet<String>());
for (RequiredCredentialModel cred : requiredCredentialModels) {
rep.getRequiredCredentials().add(cred.getType());
}
}
List<RequiredCredentialModel> requiredResourceCredentialModels = realm.getRequiredApplicationCredentials();
if (requiredResourceCredentialModels.size() > 0) {
rep.setRequiredApplicationCredentials(new HashSet<String>());
for (RequiredCredentialModel cred : requiredResourceCredentialModels) {
rep.getRequiredApplicationCredentials().add(cred.getType());
}
}
List<RequiredCredentialModel> requiredOAuthCredentialModels = realm.getRequiredOAuthClientCredentials();
if (requiredOAuthCredentialModels.size() > 0) {
rep.setRequiredOAuthClientCredentials(new HashSet<String>());
for (RequiredCredentialModel cred : requiredOAuthCredentialModels) {
rep.getRequiredOAuthClientCredentials().add(cred.getType());
}
}
return rep;
}
}

View file

@ -6,7 +6,7 @@ import org.jboss.resteasy.logging.Logger;
import org.keycloak.TokenIdGenerator;
import org.keycloak.representations.idm.admin.LogoutAction;
import org.keycloak.services.models.RealmModel;
import org.keycloak.services.models.ResourceModel;
import org.keycloak.services.models.ApplicationModel;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Form;
@ -29,14 +29,14 @@ public class ResourceAdminManager {
.disableTrustManager() // todo fix this, should have a trust manager or a good default
.build();
List<ResourceModel> resources = realm.getResources();
List<ApplicationModel> resources = realm.getApplications();
logger.info("logging out " + resources.size() + " resoures.");
for (ResourceModel resource : resources) {
for (ApplicationModel resource : resources) {
logoutResource(realm, resource, user, client);
}
}
protected boolean logoutResource(RealmModel realm, ResourceModel resource, String user, ResteasyClient client) {
protected boolean logoutResource(RealmModel realm, ApplicationModel resource, String user, ResteasyClient client) {
LogoutAction adminAction = new LogoutAction(TokenIdGenerator.generateId(), System.currentTimeMillis() / 1000 + 30, resource.getName(), user);
String token = new TokenManager().encodeToken(realm, adminAction);
Form form = new Form();

View file

@ -1,18 +1,13 @@
package org.keycloak.services.managers;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.ResourceRepresentation;
import org.keycloak.representations.idm.RoleMappingRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.representations.idm.ScopeMappingRepresentation;
import org.keycloak.representations.idm.*;
import org.keycloak.representations.idm.ApplicationRepresentation;
import org.keycloak.services.models.RealmModel;
import org.keycloak.services.models.ResourceModel;
import org.keycloak.services.models.ApplicationModel;
import org.keycloak.services.models.RoleModel;
import org.keycloak.services.models.UserCredentialModel;
import org.keycloak.services.models.UserModel;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
@ -25,8 +20,8 @@ public class ResourceManager {
this.realmManager = realmManager;
}
public ResourceModel createResource(RealmModel realm, RoleModel loginRole, ResourceRepresentation resourceRep) {
ResourceModel resource = realm.addResource(resourceRep.getName());
public ApplicationModel createResource(RealmModel realm, RoleModel loginRole, ApplicationRepresentation resourceRep) {
ApplicationModel resource = realm.addApplication(resourceRep.getName());
resource.setEnabled(resourceRep.isEnabled());
resource.setManagementUrl(resourceRep.getAdminUrl());
resource.setSurrogateAuthRequired(resourceRep.isSurrogateAuthRequired());
@ -78,12 +73,12 @@ public class ResourceManager {
return resource;
}
public ResourceModel createResource(RealmModel realm, ResourceRepresentation resourceRep) {
public ApplicationModel createResource(RealmModel realm, ApplicationRepresentation resourceRep) {
RoleModel loginRole = realm.getRole(RealmManager.RESOURCE_ROLE);
return createResource(realm, loginRole, resourceRep);
}
public void updateResource(ResourceRepresentation rep, ResourceModel resource) {
public void updateResource(ApplicationRepresentation rep, ApplicationModel resource) {
resource.setName(rep.getName());
resource.setEnabled(rep.isEnabled());
resource.setManagementUrl(rep.getAdminUrl());
@ -92,13 +87,13 @@ public class ResourceManager {
}
public ResourceRepresentation toRepresentation(ResourceModel resourceModel) {
ResourceRepresentation rep = new ResourceRepresentation();
rep.setId(resourceModel.getId());
rep.setName(resourceModel.getName());
rep.setEnabled(resourceModel.isEnabled());
rep.setAdminUrl(resourceModel.getManagementUrl());
rep.setSurrogateAuthRequired(resourceModel.isSurrogateAuthRequired());
public ApplicationRepresentation toRepresentation(ApplicationModel applicationModel) {
ApplicationRepresentation rep = new ApplicationRepresentation();
rep.setId(applicationModel.getId());
rep.setName(applicationModel.getName());
rep.setEnabled(applicationModel.isEnabled());
rep.setAdminUrl(applicationModel.getManagementUrl());
rep.setSurrogateAuthRequired(applicationModel.isSurrogateAuthRequired());
return rep;
}

View file

@ -6,7 +6,7 @@ import org.jboss.resteasy.jwt.JsonSerialization;
import org.keycloak.representations.SkeletonKeyScope;
import org.keycloak.representations.SkeletonKeyToken;
import org.keycloak.services.models.RealmModel;
import org.keycloak.services.models.ResourceModel;
import org.keycloak.services.models.ApplicationModel;
import org.keycloak.services.models.RoleModel;
import org.keycloak.services.models.UserModel;
@ -66,7 +66,7 @@ public class TokenManager {
}
}
}
for (ResourceModel resource : realm.getResources()) {
for (ApplicationModel resource : realm.getApplications()) {
Set<String> mapping = resource.getRoleMappings(user);
if (mapping != null && mapping.size() > 0 && (scopeMap == null || scopeMap.containsKey(resource.getName()))) {
Set<String> scope = resource.getScope(client);
@ -131,9 +131,9 @@ public class TokenManager {
}
if (accessCodeEntry.getResourceRolesRequested().size() > 0) {
Map<String, ResourceModel> resourceMap = realm.getResourceNameMap();
Map<String, ApplicationModel> resourceMap = realm.getResourceNameMap();
for (String resourceName : accessCodeEntry.getResourceRolesRequested().keySet()) {
ResourceModel resource = resourceMap.get(resourceName);
ApplicationModel resource = resourceMap.get(resourceName);
SkeletonKeyToken.Access access = token.addAccess(resourceName).verifyCaller(resource.isSurrogateAuthRequired());
for (RoleModel role : accessCodeEntry.getResourceRolesRequested().get(resourceName)) {
access.addRole(role.getName());
@ -166,7 +166,7 @@ public class TokenManager {
public SkeletonKeyToken createAccessToken(RealmModel realm, UserModel user) {
List<ResourceModel> resources = realm.getResources();
List<ApplicationModel> resources = realm.getApplications();
SkeletonKeyToken token = new SkeletonKeyToken();
token.id(RealmManager.generateId());
token.issuedNow();
@ -186,7 +186,7 @@ public class TokenManager {
token.setRealmAccess(access);
}
if (resources != null) {
for (ResourceModel resource : resources) {
for (ApplicationModel resource : resources) {
Set<String> mapping = resource.getRoleMappings(user);
if (mapping == null) continue;
SkeletonKeyToken.Access access = token.addAccess(resource.getName())

View file

@ -7,7 +7,7 @@ import java.util.Set;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface ResourceModel {
public interface ApplicationModel {
void updateResource();
UserModel getResourceUser();

View file

@ -61,7 +61,7 @@ public interface RealmModel {
List<RequiredCredentialModel> getRequiredCredentials();
void addRequiredCredential(RequiredCredentialModel cred);
void addRequiredCredential(String cred);
boolean validatePassword(UserModel user, String password);
@ -79,11 +79,11 @@ public interface RealmModel {
List<RoleModel> getRoles();
Map<String, ResourceModel> getResourceNameMap();
Map<String, ApplicationModel> getResourceNameMap();
List<ResourceModel> getResources();
List<ApplicationModel> getApplications();
ResourceModel addResource(String name);
ApplicationModel addApplication(String name);
boolean hasRole(UserModel user, RoleModel role);
@ -101,15 +101,27 @@ public interface RealmModel {
RoleModel getRoleById(String id);
void addResourceRequiredCredential(RequiredCredentialModel cred);
List<RequiredCredentialModel> getResourceRequiredCredentials();
List<RequiredCredentialModel> getRequiredApplicationCredentials();
void addOAuthClientRequiredCredential(RequiredCredentialModel cred);
List<RequiredCredentialModel> getOAuthClientRequiredCredentials();
List<RequiredCredentialModel> getRequiredOAuthClientCredentials();
boolean hasRole(UserModel user, String role);
ResourceModel getResourceById(String id);
ApplicationModel getApplicationById(String id);
void addRequiredOAuthClientCredential(String type);
void addRequiredResourceCredential(String type);
void updateRequiredCredentials(Set<String> creds);
void updateRequiredOAuthClientCredentials(Set<String> creds);
void updateRequiredApplicationCredentials(Set<String> creds);
boolean isSocial();
void setSocial(boolean social);
}

View file

@ -1,6 +1,10 @@
package org.keycloak.services.models;
import org.keycloak.representations.idm.RequiredCredentialRepresentation;
import org.keycloak.representations.idm.CredentialRepresentation;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -10,16 +14,11 @@ public class RequiredCredentialModel {
protected String type;
protected boolean input;
protected boolean secret;
protected String formLabel;
public RequiredCredentialModel() {
}
public RequiredCredentialModel(String type, boolean input, boolean secret) {
this.type = type;
this.input = input;
this.secret = secret;
}
public String getType() {
return type;
}
@ -44,5 +43,39 @@ public class RequiredCredentialModel {
this.secret = secret;
}
public static final RequiredCredentialModel PASSWORD = new RequiredCredentialModel(RequiredCredentialRepresentation.PASSWORD, true, true);
public String getFormLabel() {
return formLabel;
}
public void setFormLabel(String formLabel) {
this.formLabel = formLabel;
}
public static final Map<String, RequiredCredentialModel> BUILT_IN;
public static final RequiredCredentialModel PASSWORD;
public static final RequiredCredentialModel TOTP;
public static final RequiredCredentialModel CLIENT_CERT;
static {
Map<String, RequiredCredentialModel> map = new HashMap<String, RequiredCredentialModel>();
PASSWORD = new RequiredCredentialModel();
PASSWORD.setType(CredentialRepresentation.PASSWORD);
PASSWORD.setInput(true);
PASSWORD.setSecret(true);
PASSWORD.setFormLabel("Password");
map.put(PASSWORD.getType(), PASSWORD);
TOTP = new RequiredCredentialModel();
TOTP.setType(CredentialRepresentation.TOTP);
TOTP.setInput(true);
TOTP.setSecret(false);
TOTP.setFormLabel("Authenticator Code");
map.put(TOTP.getType(), TOTP);
CLIENT_CERT = new RequiredCredentialModel();
CLIENT_CERT.setType(CredentialRepresentation.CLIENT_CERT);
CLIENT_CERT.setInput(false);
CLIENT_CERT.setSecret(false);
CLIENT_CERT.setFormLabel("Client Certificate");
map.put(CLIENT_CERT.getType(), CLIENT_CERT);
BUILT_IN = Collections.unmodifiableMap(map);
}
}

View file

@ -8,6 +8,7 @@ public class UserCredentialModel {
protected String type;
protected String value;
protected String device;
public String getType() {
return type;
@ -24,4 +25,12 @@ public class UserCredentialModel {
public void setValue(String value) {
this.value = value;
}
public String getDevice() {
return device;
}
public void setDevice(String device) {
this.device = device;
}
}

View file

@ -20,4 +20,16 @@ public interface UserModel {
String getAttribute(String name);
Map<String, String> getAttributes();
String getFirstName();
void setFirstName(String firstName);
String getLastName();
void setLastName(String lastName);
String getEmail();
void setEmail(String email);
}

View file

@ -1,10 +1,9 @@
package org.keycloak.services.models.picketlink;
import org.keycloak.services.models.ResourceModel;
import org.keycloak.services.models.ApplicationModel;
import org.keycloak.services.models.RoleModel;
import org.keycloak.services.models.UserModel;
import org.keycloak.services.models.picketlink.mappings.ResourceData;
import org.keycloak.services.models.picketlink.relationships.ResourceRelationship;
import org.keycloak.services.models.picketlink.mappings.ApplicationData;
import org.keycloak.services.models.picketlink.relationships.ScopeRelationship;
import org.picketlink.idm.IdentityManager;
import org.picketlink.idm.PartitionManager;
@ -24,14 +23,14 @@ import java.util.Set;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class ResourceAdapter implements ResourceModel {
protected ResourceData resource;
public class ApplicationAdapter implements ApplicationModel {
protected ApplicationData resource;
protected RealmAdapter realm;
protected IdentityManager idm;
protected PartitionManager partitionManager;
protected RelationshipManager relationshipManager;
public ResourceAdapter(ResourceData resource, RealmAdapter realm, PartitionManager partitionManager) {
public ApplicationAdapter(ApplicationData resource, RealmAdapter realm, PartitionManager partitionManager) {
this.resource = resource;
this.realm = realm;
this.partitionManager = partitionManager;

View file

@ -1,24 +1,21 @@
package org.keycloak.services.models.picketlink;
import org.bouncycastle.openssl.PEMWriter;
import org.jboss.resteasy.logging.Logger;
import org.jboss.resteasy.security.PemUtils;
import org.keycloak.representations.idm.RequiredCredentialRepresentation;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.models.KeycloakSession;
import org.keycloak.services.models.RealmModel;
import org.keycloak.services.models.RequiredCredentialModel;
import org.keycloak.services.models.ResourceModel;
import org.keycloak.services.models.ApplicationModel;
import org.keycloak.services.models.RoleModel;
import org.keycloak.services.models.UserCredentialModel;
import org.keycloak.services.models.UserModel;
import org.keycloak.services.models.picketlink.mappings.RealmData;
import org.keycloak.services.models.picketlink.mappings.ResourceData;
import org.keycloak.services.models.picketlink.relationships.OAuthClientRequiredCredentialRelationship;
import org.keycloak.services.models.picketlink.relationships.RealmAdminRelationship;
import org.keycloak.services.models.picketlink.relationships.RequiredCredentialRelationship;
import org.keycloak.services.models.picketlink.relationships.ResourceRelationship;
import org.keycloak.services.models.picketlink.relationships.ResourceRequiredCredentialRelationship;
import org.keycloak.services.models.picketlink.relationships.ScopeRelationship;
import org.keycloak.services.models.picketlink.mappings.ApplicationData;
import org.keycloak.services.models.picketlink.relationships.*;
import org.keycloak.services.models.picketlink.relationships.RequiredApplicationCredentialRelationship;
import org.picketlink.idm.IdentityManager;
import org.picketlink.idm.PartitionManager;
import org.picketlink.idm.RelationshipManager;
@ -29,6 +26,7 @@ import org.picketlink.idm.credential.TOTPCredentials;
import org.picketlink.idm.credential.UsernamePasswordCredentials;
import org.picketlink.idm.credential.X509CertificateCredentials;
import org.picketlink.idm.model.IdentityType;
import org.picketlink.idm.model.annotation.AttributeProperty;
import org.picketlink.idm.model.sample.Grant;
import org.picketlink.idm.model.sample.Role;
import org.picketlink.idm.model.sample.SampleModel;
@ -55,6 +53,7 @@ import java.util.Set;
* @version $Revision: 1 $
*/
public class RealmAdapter implements RealmModel {
protected static final Logger logger = Logger.getLogger(RealmManager.class);
protected RealmData realm;
protected volatile transient PublicKey publicKey;
@ -112,6 +111,16 @@ public class RealmAdapter implements RealmModel {
updateRealm();
}
@Override
public boolean isSocial() {
return realm.isSocial();
}
@Override
public void setSocial(boolean social) {
realm.setSocial(social);
}
@Override
public boolean isSslNotRequired() {
return realm.isSslNotRequired();
@ -251,44 +260,51 @@ public class RealmAdapter implements RealmModel {
@Override
public List<RequiredCredentialModel> getRequiredCredentials() {
RelationshipQuery<RequiredCredentialRelationship> query = getRelationshipManager().createRelationshipQuery(RequiredCredentialRelationship.class);
query.setParameter(RequiredCredentialRelationship.REALM, realm.getName());
List<RequiredCredentialRelationship> results = query.getResultList();
List<RequiredCredentialRelationship> results = getRequiredCredentialRelationships();
return getRequiredCredentialModels(results);
}
protected List<RequiredCredentialRelationship> getRequiredCredentialRelationships() {
RelationshipQuery<RequiredCredentialRelationship> query = getRelationshipManager().createRelationshipQuery(RequiredCredentialRelationship.class);
query.setParameter(RequiredCredentialRelationship.REALM, realm.getName());
return query.getResultList();
}
@Override
public void addResourceRequiredCredential(RequiredCredentialModel cred) {
ResourceRequiredCredentialRelationship relationship = new ResourceRequiredCredentialRelationship();
public void addRequiredApplicationCredential(RequiredCredentialModel cred) {
RequiredApplicationCredentialRelationship relationship = new RequiredApplicationCredentialRelationship();
addRequiredCredential(cred, relationship);
}
@Override
public List<RequiredCredentialModel> getResourceRequiredCredentials() {
RelationshipQuery<ResourceRequiredCredentialRelationship> query = getRelationshipManager().createRelationshipQuery(ResourceRequiredCredentialRelationship.class);
query.setParameter(ResourceRequiredCredentialRelationship.REALM, realm.getName());
List<ResourceRequiredCredentialRelationship> results = query.getResultList();
public List<RequiredCredentialModel> getRequiredApplicationCredentials() {
List<RequiredApplicationCredentialRelationship> results = getResourceRequiredCredentialRelationships();
return getRequiredCredentialModels(results);
}
@Override
public void addOAuthClientRequiredCredential(RequiredCredentialModel cred) {
protected List<RequiredApplicationCredentialRelationship> getResourceRequiredCredentialRelationships() {
RelationshipQuery<RequiredApplicationCredentialRelationship> query = getRelationshipManager().createRelationshipQuery(RequiredApplicationCredentialRelationship.class);
query.setParameter(RequiredApplicationCredentialRelationship.REALM, realm.getName());
return query.getResultList();
}
public void addRequiredOAuthClientCredential(RequiredCredentialModel cred) {
OAuthClientRequiredCredentialRelationship relationship = new OAuthClientRequiredCredentialRelationship();
addRequiredCredential(cred, relationship);
}
@Override
public List<RequiredCredentialModel> getOAuthClientRequiredCredentials() {
RelationshipQuery<OAuthClientRequiredCredentialRelationship> query = getRelationshipManager().createRelationshipQuery(OAuthClientRequiredCredentialRelationship.class);
query.setParameter(ResourceRequiredCredentialRelationship.REALM, realm.getName());
List<OAuthClientRequiredCredentialRelationship> results = query.getResultList();
public List<RequiredCredentialModel> getRequiredOAuthClientCredentials() {
List<OAuthClientRequiredCredentialRelationship> results = getOAuthClientRequiredCredentialRelationships();
return getRequiredCredentialModels(results);
}
protected List<OAuthClientRequiredCredentialRelationship> getOAuthClientRequiredCredentialRelationships() {
RelationshipQuery<OAuthClientRequiredCredentialRelationship> query = getRelationshipManager().createRelationshipQuery(OAuthClientRequiredCredentialRelationship.class);
query.setParameter(RequiredApplicationCredentialRelationship.REALM, realm.getName());
return query.getResultList();
}
@Override
public void addRequiredCredential(RequiredCredentialModel cred) {
RequiredCredentialRelationship relationship = new RequiredCredentialRelationship();
addRequiredCredential(cred, relationship);
@ -302,6 +318,7 @@ public class RealmAdapter implements RealmModel {
model.setInput(relationship.isInput());
model.setSecret(relationship.isSecret());
model.setType(relationship.getCredentialType());
model.setFormLabel(relationship.getFormLabel());
rtn.add(model);
}
return rtn;
@ -311,9 +328,94 @@ public class RealmAdapter implements RealmModel {
relationship.setInput(cred.isInput());
relationship.setSecret(cred.isSecret());
relationship.setRealm(realm.getName());
relationship.setFormLabel(cred.getFormLabel());
getRelationshipManager().add(relationship);
}
@Override
public void updateRequiredCredentials(Set<String> creds) {
List<RequiredCredentialRelationship> relationships = getRequiredCredentialRelationships();
RelationshipManager rm = getRelationshipManager();
Set<String> already = new HashSet<String>();
for (RequiredCredentialRelationship rel : relationships) {
if (!creds.contains(rel.getCredentialType())) {
rm.remove(rel);
} else {
already.add(rel.getCredentialType());
}
}
for (String cred : creds) {
logger.info("updating cred: " + cred);
if (!already.contains(cred)) {
addRequiredCredential(cred);
}
}
}
@Override
public void updateRequiredOAuthClientCredentials(Set<String> creds) {
List<OAuthClientRequiredCredentialRelationship> relationships = getOAuthClientRequiredCredentialRelationships();
RelationshipManager rm = getRelationshipManager();
Set<String> already = new HashSet<String>();
for (RequiredCredentialRelationship rel : relationships) {
if (!creds.contains(rel.getCredentialType())) {
rm.remove(rel);
} else {
already.add(rel.getCredentialType());
}
}
for (String cred : creds) {
if (!already.contains(cred)) {
addRequiredOAuthClientCredential(cred);
}
}
}
@Override
public void updateRequiredApplicationCredentials(Set<String> creds) {
List<RequiredApplicationCredentialRelationship> relationships = getResourceRequiredCredentialRelationships();
RelationshipManager rm = getRelationshipManager();
Set<String> already = new HashSet<String>();
for (RequiredCredentialRelationship rel : relationships) {
if (!creds.contains(rel.getCredentialType())) {
rm.remove(rel);
} else {
already.add(rel.getCredentialType());
}
}
for (String cred : creds) {
if (!already.contains(cred)) {
addRequiredResourceCredential(cred);
}
}
}
@Override
public void addRequiredCredential(String type) {
RequiredCredentialModel model = initRequiredCredentialModel(type);
addRequiredCredential(model);
}
@Override
public void addRequiredOAuthClientCredential(String type) {
RequiredCredentialModel model = initRequiredCredentialModel(type);
addRequiredOAuthClientCredential(model);
}
@Override
public void addRequiredResourceCredential(String type) {
RequiredCredentialModel model = initRequiredCredentialModel(type);
addRequiredApplicationCredential(model);
}
protected RequiredCredentialModel initRequiredCredentialModel(String type) {
RequiredCredentialModel model = RequiredCredentialModel.BUILT_IN.get(type);
if (model == null) {
throw new RuntimeException("Unknown credential type " + type);
}
return model;
}
@Override
public boolean validatePassword(UserModel user, String password) {
@ -335,13 +437,14 @@ public class RealmAdapter implements RealmModel {
@Override
public void updateCredential(UserModel user, UserCredentialModel cred) {
IdentityManager idm = getIdm();
if (cred.getType().equals(RequiredCredentialRepresentation.PASSWORD)) {
if (cred.getType().equals(CredentialRepresentation.PASSWORD)) {
Password password = new Password(cred.getValue());
idm.updateCredential(((UserAdapter)user).getUser(), password);
} else if (cred.getType().equals(RequiredCredentialRepresentation.TOTP)) {
} else if (cred.getType().equals(CredentialRepresentation.TOTP)) {
TOTPCredential totp = new TOTPCredential(cred.getValue());
totp.setDevice(cred.getDevice());
idm.updateCredential(((UserAdapter)user).getUser(), totp);
} else if (cred.getType().equals(RequiredCredentialRepresentation.CLIENT_CERT)) {
} else if (cred.getType().equals(CredentialRepresentation.CLIENT_CERT)) {
X509Certificate cert = null;
try {
cert = org.keycloak.PemUtils.decodeCertificate(cred.getValue());
@ -416,9 +519,9 @@ public class RealmAdapter implements RealmModel {
* @return
*/
@Override
public Map<String, ResourceModel> getResourceNameMap() {
Map<String, ResourceModel> resourceMap = new HashMap<String, ResourceModel>();
for (ResourceModel resource : getResources()) {
public Map<String, ApplicationModel> getResourceNameMap() {
Map<String, ApplicationModel> resourceMap = new HashMap<String, ApplicationModel>();
for (ApplicationModel resource : getApplications()) {
resourceMap.put(resource.getName(), resource);
}
return resourceMap;
@ -430,27 +533,27 @@ public class RealmAdapter implements RealmModel {
* @return
*/
@Override
public ResourceModel getResourceById(String id) {
public ApplicationModel getApplicationById(String id) {
RelationshipQuery<ResourceRelationship> query = getRelationshipManager().createRelationshipQuery(ResourceRelationship.class);
query.setParameter(ResourceRelationship.REALM, realm.getName());
query.setParameter(ResourceRelationship.RESOURCE, id);
List<ResourceRelationship> results = query.getResultList();
if (results.size() == 0) return null;
ResourceData resource = partitionManager.getPartition(ResourceData.class, id);
ResourceModel model = new ResourceAdapter(resource, this, partitionManager);
ApplicationData resource = partitionManager.getPartition(ApplicationData.class, id);
ApplicationModel model = new ApplicationAdapter(resource, this, partitionManager);
return model;
}
@Override
public List<ResourceModel> getResources() {
public List<ApplicationModel> getApplications() {
RelationshipQuery<ResourceRelationship> query = getRelationshipManager().createRelationshipQuery(ResourceRelationship.class);
query.setParameter(ResourceRelationship.REALM, realm.getName());
List<ResourceRelationship> results = query.getResultList();
List<ResourceModel> resources = new ArrayList<ResourceModel>();
List<ApplicationModel> resources = new ArrayList<ApplicationModel>();
for (ResourceRelationship relationship : results) {
ResourceData resource = partitionManager.getPartition(ResourceData.class, relationship.getResource());
ResourceModel model = new ResourceAdapter(resource, this, partitionManager);
ApplicationData resource = partitionManager.getPartition(ApplicationData.class, relationship.getResource());
ApplicationModel model = new ApplicationAdapter(resource, this, partitionManager);
resources.add(model);
}
@ -458,19 +561,19 @@ public class RealmAdapter implements RealmModel {
}
@Override
public ResourceModel addResource(String name) {
ResourceData resourceData = new ResourceData(RealmManager.generateId());
public ApplicationModel addApplication(String name) {
ApplicationData applicationData = new ApplicationData(RealmManager.generateId());
User resourceUser = new User(name);
idm.add(resourceUser);
resourceData.setResourceUser(resourceUser);
resourceData.setResourceName(name);
resourceData.setResourceUser(resourceUser);
partitionManager.add(resourceData);
applicationData.setResourceUser(resourceUser);
applicationData.setResourceName(name);
applicationData.setResourceUser(resourceUser);
partitionManager.add(applicationData);
ResourceRelationship resourceRelationship = new ResourceRelationship();
resourceRelationship.setRealm(realm.getName());
resourceRelationship.setResource(resourceData.getName());
resourceRelationship.setResource(applicationData.getName());
getRelationshipManager().add(resourceRelationship);
ResourceModel resource = new ResourceAdapter(resourceData, this, partitionManager);
ApplicationModel resource = new ApplicationAdapter(applicationData, this, partitionManager);
resource.addRole("*");
resource.addScope(new UserAdapter(resourceUser, idm), "*");
return resource;

View file

@ -3,6 +3,7 @@ package org.keycloak.services.models.picketlink;
import org.keycloak.services.models.UserModel;
import org.picketlink.idm.IdentityManager;
import org.picketlink.idm.model.Attribute;
import org.picketlink.idm.model.annotation.AttributeProperty;
import org.picketlink.idm.model.sample.User;
import java.util.HashMap;
@ -41,6 +42,39 @@ public class UserAdapter implements UserModel {
idm.update(user);
}
@Override
public String getFirstName() {
return user.getFirstName();
}
@Override
public void setFirstName(String firstName) {
user.setFirstName(firstName);
idm.update(user);
}
@Override
public String getLastName() {
return user.getLastName();
}
@Override
public void setLastName(String lastName) {
user.setLastName(lastName);
idm.update(user);
}
@Override
public String getEmail() {
return user.getEmail();
}
@Override
public void setEmail(String email) {
user.setEmail(email);
idm.update(user);
}
@Override
public void setAttribute(String name, String value) {
user.setAttribute(new Attribute<String>(name, value));

View file

@ -9,17 +9,17 @@ import org.picketlink.idm.model.sample.User;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class ResourceData extends AbstractPartition {
public class ApplicationData extends AbstractPartition {
private String resourceName;
private boolean enabled;
private boolean surrogateAuthRequired;
private String managementUrl;
private User resourceUser;
public ResourceData() {
public ApplicationData() {
super(null);
}
public ResourceData(String name) {
public ApplicationData(String name) {
super(name);
}

View file

@ -15,9 +15,9 @@ import java.io.Serializable;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@IdentityManaged(ResourceData.class)
@IdentityManaged(ApplicationData.class)
@Entity
public class ResourceEntity implements Serializable {
public class ApplicationEntity implements Serializable {
@OneToOne
@Id
@OwnerReference

View file

@ -14,6 +14,7 @@ public class RealmData extends AbstractPartition {
private boolean sslNotRequired;
private boolean cookieLoginAllowed;
private boolean registrationAllowed;
private boolean social;
private int tokenLifespan;
private int accessCodeLifespan;
private String publicKeyPem;
@ -44,6 +45,15 @@ public class RealmData extends AbstractPartition {
this.enabled = enabled;
}
@AttributeProperty
public boolean isSocial() {
return social;
}
public void setSocial(boolean social) {
this.social = social;
}
@AttributeProperty
public boolean isSslNotRequired() {
return sslNotRequired;

View file

@ -35,6 +35,8 @@ public class RealmEntity implements Serializable {
@AttributeValue
private boolean registrationAllowed;
@AttributeValue
private boolean social;
@AttributeValue
private int tokenLifespan;
@AttributeValue
private int accessCodeLifespan;
@ -94,6 +96,14 @@ public class RealmEntity implements Serializable {
this.registrationAllowed = registrationAllowed;
}
public boolean isSocial() {
return social;
}
public void setSocial(boolean social) {
this.social = social;
}
public int getTokenLifespan() {
return tokenLifespan;
}

View file

@ -4,5 +4,5 @@ package org.keycloak.services.models.picketlink.relationships;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class ResourceRequiredCredentialRelationship extends RequiredCredentialRelationship {
public class RequiredApplicationCredentialRelationship extends RequiredCredentialRelationship {
}

View file

@ -68,4 +68,14 @@ public class RequiredCredentialRelationship extends AbstractAttributedType imple
public void setSecret(boolean secret) {
setAttribute(new Attribute<Boolean>("secret", secret));
}
@AttributeProperty
public String getFormLabel() {
return (String)getAttribute("formLabel").getValue();
}
public void setFormLabel(String label) {
setAttribute(new Attribute<String>("formLabel", label));
}
}

View file

@ -6,8 +6,8 @@ import org.keycloak.services.managers.TokenManager;
import org.keycloak.services.models.KeycloakSessionFactory;
import org.keycloak.services.models.picketlink.PicketlinkKeycloakSession;
import org.keycloak.services.models.picketlink.PicketlinkKeycloakSessionFactory;
import org.keycloak.services.models.picketlink.mappings.ApplicationEntity;
import org.keycloak.services.models.picketlink.mappings.RealmEntity;
import org.keycloak.services.models.picketlink.mappings.ResourceEntity;
import org.keycloak.social.SocialRequestManager;
import org.picketlink.idm.PartitionManager;
import org.picketlink.idm.config.IdentityConfigurationBuilder;
@ -98,7 +98,7 @@ public class KeycloakApplication extends Application {
OTPCredentialTypeEntity.class,
AttributeTypeEntity.class,
RealmEntity.class,
ResourceEntity.class
ApplicationEntity.class
)
.supportGlobalRelationship(org.picketlink.idm.model.Relationship.class)
.addContextInitializer(new JPAContextInitializer(null) {

View file

@ -6,7 +6,6 @@ import org.jboss.resteasy.spi.HttpRequest;
import org.jboss.resteasy.spi.HttpResponse;
import org.jboss.resteasy.spi.NotImplementedYetException;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.RequiredCredentialRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.RealmManager;
@ -19,6 +18,7 @@ import org.keycloak.services.resources.admin.RealmsAdminResource;
import javax.ws.rs.*;
import javax.ws.rs.core.*;
import java.net.URI;
import java.util.StringTokenizer;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -109,7 +109,7 @@ public class SaasService {
if (user == null) {
return Response.status(401).build();
}
return Response.ok(new WhoAmI(user.getLoginName(), user.getLoginName())).build();
return Response.ok(new WhoAmI(user.getLoginName(), user.getFirstName() + " " + user.getLastName())).build();
}
}.call();
}
@ -295,7 +295,7 @@ public class SaasService {
@Path("registrations")
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response processRegister(final @FormParam("name") String name,
public Response processRegister(final @FormParam("name") String fullname,
final @FormParam("email") String email,
final @FormParam("username") String username,
final @FormParam("password") String password,
@ -312,7 +312,29 @@ public class SaasService {
RealmModel defaultRealm = realmManager.defaultRealm();
UserRepresentation newUser = new UserRepresentation();
newUser.setUsername(username);
newUser.credential(RequiredCredentialRepresentation.PASSWORD, password, false);
newUser.setEmail(email);
if (fullname != null) {
StringTokenizer tokenizer = new StringTokenizer(fullname, " ");
StringBuffer first = null;
String last = "";
while (tokenizer.hasMoreTokens()) {
String token = tokenizer.nextToken();
if (tokenizer.hasMoreTokens()) {
if (first == null) {
first = new StringBuffer();
} else {
first.append(" ");
}
first.append(token);
} else {
last = token;
}
}
if (first == null) first = new StringBuffer();
newUser.setFirstName(first.toString());
newUser.setLastName(last);
}
newUser.credential(CredentialRepresentation.PASSWORD, password);
UserModel user = registerMe(defaultRealm, newUser);
if (user == null) {
request.setAttribute("KEYCLOAK_LOGIN_ERROR_MESSAGE", "Username already exists.");
@ -340,6 +362,9 @@ public class SaasService {
}
user = defaultRealm.addUser(newUser.getUsername());
user.setFirstName(newUser.getFirstName());
user.setLastName(newUser.getLastName());
user.setEmail(newUser.getEmail());
for (CredentialRepresentation cred : newUser.getCredentials()) {
UserCredentialModel credModel = new UserCredentialModel();
credModel.setType(cred.getType());

View file

@ -2,53 +2,45 @@ package org.keycloak.services.resources.admin;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.jboss.resteasy.logging.Logger;
import org.keycloak.representations.idm.ResourceRepresentation;
import org.keycloak.representations.idm.ApplicationRepresentation;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.managers.ResourceManager;
import org.keycloak.services.models.RealmModel;
import org.keycloak.services.models.ResourceModel;
import org.keycloak.services.models.ApplicationModel;
import org.keycloak.services.models.UserModel;
import org.keycloak.services.resources.Transaction;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import java.util.ArrayList;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class RealmResourceResource {
public class ApplicationResource {
protected static final Logger logger = Logger.getLogger(RealmAdminResource.class);
protected UserModel admin;
protected RealmModel realm;
protected ResourceModel resourceModel;
protected ApplicationModel applicationModel;
public RealmResourceResource(UserModel admin, RealmModel realm, ResourceModel resourceModel) {
public ApplicationResource(UserModel admin, RealmModel realm, ApplicationModel applicationModel) {
this.admin = admin;
this.realm = realm;
this.resourceModel = resourceModel;
this.applicationModel = applicationModel;
}
@PUT
@Consumes(MediaType.APPLICATION_JSON)
public void update(final ResourceRepresentation rep) {
public void update(final ApplicationRepresentation rep) {
new Transaction() {
@Override
protected void runImpl() {
ResourceManager resourceManager = new ResourceManager(new RealmManager(session));
resourceManager.updateResource(rep, resourceModel);
resourceManager.updateResource(rep, applicationModel);
}
}.run();
}
@ -57,12 +49,12 @@ public class RealmResourceResource {
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public ResourceRepresentation getResource(final @PathParam("id") String id) {
public ApplicationRepresentation getResource(final @PathParam("id") String id) {
return new Transaction() {
@Override
protected ResourceRepresentation callImpl() {
protected ApplicationRepresentation callImpl() {
ResourceManager resourceManager = new ResourceManager(new RealmManager(session));
return resourceManager.toRepresentation(resourceModel);
return resourceManager.toRepresentation(applicationModel);
}
}.call();
}

View file

@ -2,11 +2,11 @@ package org.keycloak.services.resources.admin;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.jboss.resteasy.logging.Logger;
import org.keycloak.representations.idm.ResourceRepresentation;
import org.keycloak.representations.idm.ApplicationRepresentation;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.managers.ResourceManager;
import org.keycloak.services.models.RealmModel;
import org.keycloak.services.models.ResourceModel;
import org.keycloak.services.models.ApplicationModel;
import org.keycloak.services.models.UserModel;
import org.keycloak.services.resources.Transaction;
@ -14,7 +14,6 @@ import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
@ -29,12 +28,12 @@ import java.util.List;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class RealmResourcesResource {
public class ApplicationsResource {
protected static final Logger logger = Logger.getLogger(RealmAdminResource.class);
protected UserModel admin;
protected RealmModel realm;
public RealmResourcesResource(UserModel admin, RealmModel realm) {
public ApplicationsResource(UserModel admin, RealmModel realm) {
this.admin = admin;
this.realm = realm;
}
@ -42,15 +41,15 @@ public class RealmResourcesResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public List<ResourceRepresentation> getResources() {
public List<ApplicationRepresentation> getResources() {
return new Transaction() {
@Override
protected List<ResourceRepresentation> callImpl() {
List<ResourceRepresentation> rep = new ArrayList<ResourceRepresentation>();
List<ResourceModel> resourceModels = realm.getResources();
protected List<ApplicationRepresentation> callImpl() {
List<ApplicationRepresentation> rep = new ArrayList<ApplicationRepresentation>();
List<ApplicationModel> applicationModels = realm.getApplications();
ResourceManager resourceManager = new ResourceManager(new RealmManager(session));
for (ResourceModel resourceModel : resourceModels) {
rep.add(resourceManager.toRepresentation(resourceModel));
for (ApplicationModel applicationModel : applicationModels) {
rep.add(resourceManager.toRepresentation(applicationModel));
}
return rep;
}
@ -59,27 +58,27 @@ public class RealmResourcesResource {
@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response createResource(final @Context UriInfo uriInfo, final ResourceRepresentation rep) {
public Response createResource(final @Context UriInfo uriInfo, final ApplicationRepresentation rep) {
return new Transaction() {
@Override
protected Response callImpl() {
ResourceManager resourceManager = new ResourceManager(new RealmManager(session));
ResourceModel resourceModel = resourceManager.createResource(realm, rep);
return Response.created(uriInfo.getAbsolutePathBuilder().path(resourceModel.getId()).build()).build();
ApplicationModel applicationModel = resourceManager.createResource(realm, rep);
return Response.created(uriInfo.getAbsolutePathBuilder().path(applicationModel.getId()).build()).build();
}
}.call();
}
@Path("{id}")
public RealmResourceResource getResource(final @PathParam("id") String id) {
public ApplicationResource getResource(final @PathParam("id") String id) {
return new Transaction(false) {
@Override
protected RealmResourceResource callImpl() {
ResourceModel resourceModel = realm.getResourceById(id);
if (resourceModel == null) {
protected ApplicationResource callImpl() {
ApplicationModel applicationModel = realm.getApplicationById(id);
if (applicationModel == null) {
throw new NotFoundException();
}
return new RealmResourceResource(admin, realm, resourceModel);
return new ApplicationResource(admin, realm, applicationModel);
}
}.call();

View file

@ -9,14 +9,11 @@ import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.models.RealmModel;
import org.keycloak.services.models.RoleModel;
import org.keycloak.services.models.UserModel;
import org.keycloak.services.resources.PublicRealmResource;
import org.keycloak.services.resources.Transaction;
import javax.ws.rs.Consumes;
import javax.ws.rs.ForbiddenException;
import javax.ws.rs.GET;
import javax.ws.rs.InternalServerErrorException;
import javax.ws.rs.NotAuthorizedException;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
@ -24,15 +21,10 @@ import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -48,9 +40,9 @@ public class RealmAdminResource {
this.realm = realm;
}
@Path("resources")
public RealmResourcesResource getResources() {
return new RealmResourcesResource(admin, realm);
@Path("applications")
public ApplicationsResource getResources() {
return new ApplicationsResource(admin, realm);
}
@GET
@ -60,21 +52,12 @@ public class RealmAdminResource {
return new Transaction() {
@Override
protected RealmRepresentation callImpl() {
RealmRepresentation rep = new RealmRepresentation();
rep.setId(realm.getId());
rep.setRealm(realm.getName());
rep.setEnabled(realm.isEnabled());
rep.setSslNotRequired(realm.isSslNotRequired());
rep.setCookieLoginAllowed(realm.isCookieLoginAllowed());
rep.setPublicKey(realm.getPublicKeyPem());
rep.setTokenLifespan(realm.getTokenLifespan());
rep.setAccessCodeLifespan(realm.getAccessCodeLifespan());
return rep;
return new RealmManager(session).toRepresentation(realm);
}
}.call();
}
@Path("roles")
@GET
@NoCache
@ -94,6 +77,19 @@ public class RealmAdminResource {
}.call();
}
@PUT
@Consumes("application/json")
public void updateRealm(final RealmRepresentation rep) {
new Transaction() {
@Override
protected void runImpl() {
logger.info("updating realm: " + rep.getRealm());
new RealmManager(session).updateRealm(rep, realm);
}
}.run();
}
@Path("roles/{id}")
@GET
@NoCache

View file

@ -28,6 +28,7 @@ import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -52,18 +53,18 @@ public class RealmsAdminResource {
@GET
@NoCache
@Produces("application/json")
public Response getRealms() {
public List<RealmRepresentation> getRealms() {
return new Transaction() {
@Override
protected Response callImpl() {
protected List<RealmRepresentation> callImpl() {
logger.info(("getRealms()"));
RealmManager realmManager = new RealmManager(session);
List<RealmModel> realms = session.getRealms(admin);
Map<String, String> map = new HashMap<String, String>();
List<RealmRepresentation> reps = new ArrayList<RealmRepresentation>();
for (RealmModel realm : realms) {
map.put(realm.getId(), realm.getName());
reps.add(realmManager.toRepresentation(realm));
}
return Response.ok(new GenericEntity<Map<String, String>>(map){})
.cacheControl(noCache).build();
return reps;
}
}.call();
}

View file

@ -6,7 +6,7 @@ import org.junit.Before;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
import org.keycloak.representations.idm.RequiredCredentialRepresentation;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.models.KeycloakSession;
import org.keycloak.services.models.KeycloakSessionFactory;
@ -18,7 +18,10 @@ import org.keycloak.services.models.UserCredentialModel;
import org.keycloak.services.resources.KeycloakApplication;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -52,6 +55,16 @@ public class AdapterTest {
}
@Test
public void testMe() {
String hello = "Bill Burke";
StringTokenizer tokenizer = new StringTokenizer(hello, " ");
while (tokenizer.hasMoreTokens()) {
System.out.println("token: " + tokenizer.nextToken());
}
}
@Test
public void test1CreateRealm() throws Exception {
realmModel = adapter.createRealm("JUGGLER");
@ -77,26 +90,29 @@ public class AdapterTest {
@Test
public void test2RequiredCredential() throws Exception {
test1CreateRealm();
RequiredCredentialModel creds = new RequiredCredentialModel();
creds.setSecret(true);
creds.setType(RequiredCredentialRepresentation.PASSWORD);
creds.setInput(true);
realmModel.addRequiredCredential(creds);
creds = new RequiredCredentialModel();
creds.setSecret(true);
creds.setType(RequiredCredentialRepresentation.TOTP);
creds.setInput(true);
realmModel.addRequiredCredential(creds);
realmModel.addRequiredCredential(CredentialRepresentation.PASSWORD);
List<RequiredCredentialModel> storedCreds = realmModel.getRequiredCredentials();
Assert.assertEquals(1, storedCreds.size());
Set<String> creds = new HashSet<String>();
creds.add(CredentialRepresentation.PASSWORD);
creds.add(CredentialRepresentation.TOTP);
realmModel.updateRequiredCredentials(creds);
storedCreds = realmModel.getRequiredCredentials();
Assert.assertEquals(2, storedCreds.size());
boolean totp = false;
boolean password = false;
for (RequiredCredentialModel cred : storedCreds) {
if (cred.getType().equals(RequiredCredentialRepresentation.PASSWORD)) password = true;
else if (cred.getType().equals(RequiredCredentialRepresentation.TOTP)) totp = true;
Assert.assertTrue(cred.isInput());
if (cred.getType().equals(CredentialRepresentation.PASSWORD)) {
password = true;
Assert.assertTrue(cred.isSecret());
}
else if (cred.getType().equals(CredentialRepresentation.TOTP)) {
totp = true;
Assert.assertFalse(cred.isSecret());
}
}
Assert.assertTrue(totp);
Assert.assertTrue(password);
}
@ -106,7 +122,7 @@ public class AdapterTest {
test1CreateRealm();
UserModel user = realmModel.addUser("bburke");
UserCredentialModel cred = new UserCredentialModel();
cred.setType(RequiredCredentialRepresentation.PASSWORD);
cred.setType(CredentialRepresentation.PASSWORD);
cred.setValue("geheim");
realmModel.updateCredential(user, cred);
Assert.assertTrue(realmModel.validatePassword(user, "geheim"));

View file

@ -6,18 +6,18 @@ import org.junit.Before;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.models.KeycloakSession;
import org.keycloak.services.models.KeycloakSessionFactory;
import org.keycloak.services.models.RealmModel;
import org.keycloak.services.models.RequiredCredentialModel;
import org.keycloak.services.models.ResourceModel;
import org.keycloak.services.models.ApplicationModel;
import org.keycloak.services.models.RoleModel;
import org.keycloak.services.models.UserModel;
import org.keycloak.services.resources.KeycloakApplication;
import org.keycloak.services.resources.SaasService;
import org.keycloak.services.resources.SaasService;
import java.util.List;
import java.util.Set;
@ -59,7 +59,7 @@ public class ImportTest {
defaultRealm.setCookieLoginAllowed(true);
defaultRealm.setRegistrationAllowed(true);
manager.generateRealmKeys(defaultRealm);
defaultRealm.addRequiredCredential(RequiredCredentialModel.PASSWORD);
defaultRealm.addRequiredCredential(CredentialRepresentation.PASSWORD);
RoleModel role = defaultRealm.addRole(SaasService.REALM_CREATOR_ROLE);
UserModel admin = defaultRealm.addUser("admin");
defaultRealm.grantRole(admin, role);
@ -70,19 +70,44 @@ public class ImportTest {
realm.addRealmAdmin(admin);
List<RequiredCredentialModel> creds = realm.getRequiredCredentials();
Assert.assertEquals(1, creds.size());
RequiredCredentialModel cred = creds.get(0);
Assert.assertEquals("Password", cred.getFormLabel());
UserModel user = realm.getUser("loginclient");
Assert.assertNotNull(user);
Set<String> scopes = realm.getScope(user);
System.out.println("Scopes size: " + scopes.size());
Assert.assertTrue(scopes.contains("*"));
List<ResourceModel> resources = realm.getResources();
List<ApplicationModel> resources = realm.getApplications();
Assert.assertEquals(2, resources.size());
List<RealmModel> realms = identitySession.getRealms(admin);
Assert.assertEquals(1, realms.size());
}
@Test
public void install2() throws Exception {
RealmModel defaultRealm = manager.createRealm(RealmModel.DEFAULT_REALM, RealmModel.DEFAULT_REALM);
defaultRealm.setName(RealmModel.DEFAULT_REALM);
defaultRealm.setEnabled(true);
defaultRealm.setTokenLifespan(300);
defaultRealm.setAccessCodeLifespan(60);
defaultRealm.setSslNotRequired(false);
defaultRealm.setCookieLoginAllowed(true);
defaultRealm.setRegistrationAllowed(true);
manager.generateRealmKeys(defaultRealm);
defaultRealm.addRequiredCredential(CredentialRepresentation.PASSWORD);
RoleModel role = defaultRealm.addRole(SaasService.REALM_CREATOR_ROLE);
UserModel admin = defaultRealm.addUser("admin");
defaultRealm.grantRole(admin, role);
RealmRepresentation rep = KeycloakTestBase.loadJson("testrealm-demo.json");
RealmModel realm = manager.createRealm("demo", rep.getRealm());
manager.importRealm(rep, realm);
realm.addRealmAdmin(admin);
}

View file

@ -1,5 +1,6 @@
package org.keycloak.test;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.models.RealmModel;
import org.keycloak.services.models.RequiredCredentialModel;
@ -21,7 +22,7 @@ public class InstallationManager {
defaultRealm.setCookieLoginAllowed(true);
defaultRealm.setRegistrationAllowed(true);
manager.generateRealmKeys(defaultRealm);
defaultRealm.addRequiredCredential(RequiredCredentialModel.PASSWORD);
defaultRealm.addRequiredCredential(CredentialRepresentation.PASSWORD);
defaultRealm.addRole(SaasService.REALM_CREATOR_ROLE);
}

Some files were not shown because too many files have changed in this diff Show more