required creds

This commit is contained in:
Bill Burke 2013-08-10 08:13:39 -04:00
parent 3480cb5646
commit 993fc5c301
81 changed files with 5771 additions and 400 deletions

View file

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

View file

@ -5,9 +5,13 @@ package org.keycloak.representations.idm;
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class CredentialRepresentation { 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 type;
protected String value; protected String value;
protected boolean hashed; protected String device;
public String getType() { public String getType() {
return type; return type;
@ -25,11 +29,11 @@ public class CredentialRepresentation {
this.value = value; this.value = value;
} }
public boolean isHashed() { public String getDevice() {
return hashed; return device;
} }
public void setHashed(boolean hashed) { public void setDevice(String device) {
this.hashed = hashed; this.device = device;
} }
} }

View file

@ -20,9 +20,9 @@ public class RealmRepresentation {
protected String privateKey; protected String privateKey;
protected String publicKey; protected String publicKey;
protected List<RoleRepresentation> roles; protected List<RoleRepresentation> roles;
protected List<RequiredCredentialRepresentation> requiredCredentials; protected List<String> requiredCredentials;
protected List<RequiredCredentialRepresentation> requiredResourceCredentials; protected List<String> requiredResourceCredentials;
protected List<RequiredCredentialRepresentation> requiredOAuthClientCredentials; protected List<String> requiredOAuthClientCredentials;
protected List<UserRepresentation> users; protected List<UserRepresentation> users;
protected List<RoleMappingRepresentation> roleMappings; protected List<RoleMappingRepresentation> roleMappings;
protected List<ScopeMappingRepresentation> scopeMappings; protected List<ScopeMappingRepresentation> scopeMappings;
@ -141,27 +141,27 @@ public class RealmRepresentation {
return mapping; return mapping;
} }
public List<RequiredCredentialRepresentation> getRequiredCredentials() { public List<String> getRequiredCredentials() {
return requiredCredentials; return requiredCredentials;
} }
public void setRequiredCredentials(List<RequiredCredentialRepresentation> requiredCredentials) { public void setRequiredCredentials(List<String> requiredCredentials) {
this.requiredCredentials = requiredCredentials; this.requiredCredentials = requiredCredentials;
} }
public List<RequiredCredentialRepresentation> getRequiredResourceCredentials() { public List<String> getRequiredResourceCredentials() {
return requiredResourceCredentials; return requiredResourceCredentials;
} }
public void setRequiredResourceCredentials(List<RequiredCredentialRepresentation> requiredResourceCredentials) { public void setRequiredResourceCredentials(List<String> requiredResourceCredentials) {
this.requiredResourceCredentials = requiredResourceCredentials; this.requiredResourceCredentials = requiredResourceCredentials;
} }
public List<RequiredCredentialRepresentation> getRequiredOAuthClientCredentials() { public List<String> getRequiredOAuthClientCredentials() {
return requiredOAuthClientCredentials; return requiredOAuthClientCredentials;
} }
public void setRequiredOAuthClientCredentials(List<RequiredCredentialRepresentation> requiredOAuthClientCredentials) { public void setRequiredOAuthClientCredentials(List<String> requiredOAuthClientCredentials) {
this.requiredOAuthClientCredentials = requiredOAuthClientCredentials; this.requiredOAuthClientCredentials = requiredOAuthClientCredentials;
} }

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

@ -123,12 +123,11 @@ public class ResourceRepresentation {
this.credentials = credentials; this.credentials = credentials;
} }
public ResourceRepresentation credential(String type, String value, boolean hashed) { public ResourceRepresentation credential(String type, String value) {
if (this.credentials == null) credentials = new ArrayList<CredentialRepresentation>(); if (this.credentials == null) credentials = new ArrayList<CredentialRepresentation>();
CredentialRepresentation cred = new CredentialRepresentation(); CredentialRepresentation cred = new CredentialRepresentation();
cred.setType(type); cred.setType(type);
cred.setValue(value); cred.setValue(value);
cred.setHashed(hashed);
credentials.add(cred); credentials.add(cred);
return this; return this;
} }

View file

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

View file

@ -1,94 +1,86 @@
{ {
"realm" : "demo", "realm": "demo",
"enabled" : true, "enabled": true,
"tokenLifespan" : 10, "tokenLifespan": 300,
"accessCodeLifespan" : 10, "accessCodeLifespan": 10,
"sslNotRequired" : true, "sslNotRequired": true,
"cookieLoginAllowed" : true, "cookieLoginAllowed": true,
"privateKey" : "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=", "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", "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"requiredCredentials" : [ "requiredCredentials": [ "password" ],
"requiredResourceCredentials": [ "password" ],
"requiredOAuthClientCredentials": [ "password" ],
"users" : [
{ {
"type" : "Password",
"input" : true,
"secret" : true
}
],
"requiredResourceCredentials" : [
{
"type" : "Password",
"input" : true,
"secret" : true
}
],
"requiredOAuthClientCredentials" : [
{
"type" : "Password",
"input" : true,
"secret" : true
}
],
"users" : [
{
"username" : "bburke@redhat.com", "username" : "bburke@redhat.com",
"enabled" : true, "enabled" : true,
"attributes" : { "attributes" : {
"email" : "bburke@redhat.com" "email" : "bburke@redhat.com"
}, },
"credentials" : [ "credentials" : [
{ "type" : "Password", { "type" : "password",
"value" : "password" } "value" : "password" }
] ]
}, },
{ {
"username" : "third-party", "username" : "third-party",
"enabled" : true, "enabled" : true,
"credentials" : [ "credentials" : [
{ "type" : "Password", { "type" : "password",
"value" : "password" } "value" : "password" }
] ]
} }
], ],
"roles" : [ "roles": [
{ "name" : "user", "description" : "Have User privileges" }, {
{ "name" : "admin", "description" : "Have Administrator privileges" } "name": "user",
], "description": "Have User privileges"
"roleMappings" : [ },
{ {
"username" : "bburke@redhat.com", "name": "admin",
"roles" : ["user"] "description": "Have Administrator privileges"
}, }
{ ],
"username" : "third-party", "roleMappings": [
"roles" : ["KEYCLOAK_IDENTITY_REQUESTER"] {
} "username": "bburke@redhat.com",
], "roles": ["user"]
"scopeMappings" : [ },
{ {
"username" : "third-party", "username": "third-party",
"roles" : ["user"] "roles": ["KEYCLOAK_IDENTITY_REQUESTER"]
} }
], ],
"resources" : [ "scopeMappings": [
{ {
"name" : "customer-portal", "username": "third-party",
"enabled" : true, "roles": ["user"]
"adminUrl" : "http://localhost:8080/customer-portal/j_admin_request", }
"useRealmMappings" : true, ],
"credentials" : [ "resources": [
{ "type" : "Password", {
"value" : "password" } "name": "customer-portal",
] "enabled": true,
}, "adminUrl": "http://localhost:8080/customer-portal/j_admin_request",
{ "useRealmMappings": true,
"name" : "product-portal", "credentials": [
"enabled" : true, {
"adminUrl" : "http://localhost:8080/product-portal/j_admin_request", "type": "password",
"useRealmMappings" : true, "value": "password"
"credentials" : [ }
{ "type" : "Password", ]
"value" : "password" } },
] {
} "name": "product-portal",
] "enabled": true,
"adminUrl": "http://localhost:8080/product-portal/j_admin_request",
"useRealmMappings": true,
"credentials": [
{
"type": "password",
"value": "password"
}
]
}
]
} }

View file

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

View file

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

View file

@ -93,6 +93,7 @@ module.controller('RealmDetailCtrl', function($scope, Current, Realm, realm, $lo
$scope.realm.tokenLifespanUnit = 'SECONDS'; $scope.realm.tokenLifespanUnit = 'SECONDS';
$scope.realm.accessCodeLifespan = 300; $scope.realm.accessCodeLifespan = 300;
$scope.realm.accessCodeLifespanUnit = 'SECONDS'; $scope.realm.accessCodeLifespanUnit = 'SECONDS';
$scope.realm.requiredCredentials = ['password'];
} else { } else {
$scope.realm.name = realm.realm; $scope.realm.name = realm.realm;
$scope.realm.requireSsl = !$scope.realm.sslNotRequired; $scope.realm.requireSsl = !$scope.realm.sslNotRequired;
@ -100,6 +101,12 @@ module.controller('RealmDetailCtrl', function($scope, Current, Realm, realm, $lo
$scope.realm.acessCodeLifespanUnit = 'SECONDS'; $scope.realm.acessCodeLifespanUnit = 'SECONDS';
} }
$scope.userCredentialOptions = {
'multiple' : true,
'simple_tags' : true,
'tags' : ['password', 'totp', 'cert']
};
$scope.changed = $scope.create; $scope.changed = $scope.create;
$scope.$watch('realm', function() { $scope.$watch('realm', function() {
@ -168,7 +175,8 @@ module.controller('RealmDetailCtrl', function($scope, Current, Realm, realm, $lo
cookieLoginAllowed: $scope.realm.cookieLoginAllowed, cookieLoginAllowed: $scope.realm.cookieLoginAllowed,
sslNotRequired: !$scope.realm.requireSsl, sslNotRequired: !$scope.realm.requireSsl,
tokenLifespan: $scope.realm.tokenLifespan, tokenLifespan: $scope.realm.tokenLifespan,
accessCodeLifespan: $scope.realm.accessCodeLifespan accessCodeLifespan: $scope.realm.accessCodeLifespan,
requiredCredentials: $scope.realm.requiredCredentials
}; };
@ -385,7 +393,7 @@ module.controller('ApplicationDetailCtrl', function($scope, realm, applications,
application : null application : null
}; };
selection.applications = angular.copy(applications); selection.applications = applications;
for (var i=0;i < selection.applications.length; i++) { for (var i=0;i < selection.applications.length; i++) {
if (selection.applications[i].name == application.name) { if (selection.applications[i].name == application.name) {
@ -396,10 +404,11 @@ module.controller('ApplicationDetailCtrl', function($scope, realm, applications,
} }
$scope.selection = selection; $scope.selection = selection;
if (!$scope.create) {
$scope.application = angular.copy(application); $scope.application= selection.application;
} else {
$scope.application = {};
}
$scope.changeApplication = function() { $scope.changeApplication = function() {
console.log('ApplicationDetailCtrl.changeApplication() - ' + $scope.selection.application.name); console.log('ApplicationDetailCtrl.changeApplication() - ' + $scope.selection.application.name);
@ -407,7 +416,7 @@ module.controller('ApplicationDetailCtrl', function($scope, realm, applications,
}; };
$scope.$watch('application', function() { $scope.$watch('application', function() {
if (!angular.equals($scope.application, application)) { if (!angular.equals($scope.selection.application, application)) {
$scope.changed = true; $scope.changed = true;
} }
}, true); }, true);

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> </ul>
<hr/> <hr/>
<div> <div>
<a href="#/realms/{{realm.id}}">Back to realm management...</a> <a href="#/create/application/{{realm.id}}">New Application...</a>
</div> </div>
<div> <div>
<a href="#/create/application/{{realm.id}}">New Application...</a> <a href="#/realms/{{realm.id}}">Back to realm management...</a>
</div> </div>
</nav> </nav>
</div> </div>

View file

@ -17,39 +17,74 @@
<form class="form-horizontal" name="realmForm" novalidate> <form class="form-horizontal" name="realmForm" novalidate>
<fieldset> <fieldset>
<legend>Settings</legend>
<div data-kc-input> <div class="control-group">
<label>Name</label> <label for="realmForm-name" class="control-label">Name</label>
<input class="input-xlarge" type="text" name="name" data-ng-model="realm.name" autofocus
required> <div class="controls">
<input class="input-xlarge" type="text" name="name" data-ng-model="realm.name" autofocus
required>
</div>
</div> </div>
<div data-kc-input> <table>
<label>Enabled</label> <tr>
<input class="input-xlarge" type="checkbox" name="enabled" data-ng-model="realm.enabled"> <td>
</div> <div class="control-group">
<label class="control-label">Enabled</label>
<div data-kc-input> <div class="controls">
<label>Social login</label> <input class="input-xlarge" type="checkbox" name="enabled"
<input class="input-xlarge" type="checkbox" name="social" data-ng-model="realm.social"> data-ng-model="realm.enabled">
</div> </div>
</div>
</td>
<td>
<div class="control-group">
<label class="control-label">Social login</label>
<div data-kc-input> <div class="controls">
<label>Require SSL</label> <input class="input-xlarge" type="checkbox" name="social"
<input class="input-xlarge" type="checkbox" name="requireSsl" data-ng-model="realm.requireSsl"> data-ng-model="realm.social">
</div> </div>
</div>
</td>
</tr>
<tr>
<td>
<div class="control-group">
<label class="control-label">Require SSL</label>
<div data-kc-input> <div class="controls">
<label>Cookie login allowed</label> <input class="input-xlarge" type="checkbox" name="requireSsl"
<input class="input-xlarge" type="checkbox" name="cookieLoginAllowed" data-ng-model="realm.cookieLoginAllowed"> data-ng-model="realm.requireSsl">
</div> </div>
</div>
</td>
<td>
<div class="control-group">
<label class="control-label">Cookie login allowed</label>
<div data-kc-input> <div class="controls">
<label>User registration</label> <input class="input-xlarge" type="checkbox" name="cookieLoginAllowed"
<input class="input-xlarge" type="checkbox" name="social" data-ng-model="realm.cookieLoginAllowed">
data-ng-model="realm.userRegistration"> </div>
</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"> <div class="control-group">
<label for="realmForm-tokenLifespan" class="control-label">Token lifespan</label> <label for="realmForm-tokenLifespan" class="control-label">Token lifespan</label>
@ -74,13 +109,21 @@
data-ng-model="realm.accessCodeLifespan"> data-ng-model="realm.accessCodeLifespan">
<select style="width: auto;" name="accessCodeLifespanUnit" <select style="width: auto;" name="accessCodeLifespanUnit"
data-ng-model="realm.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="MINUTES">Minutes</option>
<option value="HOURS">Hours</option> <option value="HOURS">Hours</option>
<option value="DAYS">Days</option> <option value="DAYS">Days</option>
</select> </select>
</div> </div>
</div> </div>
<div class="control-group">
<label class="control-label">User Required Credentials</label>
<div class="controls">
<input style="width:250px" type="text" ui-select2="userCredentialOptions" ng-model="realm.requiredCredentials">
</div>
</div>
</fieldset> </fieldset>
<div class="form-actions" data-ng-show="createRealm"> <div class="form-actions" data-ng-show="createRealm">
<button type="submit" data-ng-click="save()" class="btn btn-primary" data-ng-show="changed">Save <button type="submit" data-ng-click="save()" class="btn btn-primary" data-ng-show="changed">Save

View file

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

View file

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

View file

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

View file

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

View file

@ -2,26 +2,19 @@ package org.keycloak.services.managers;
import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.RequiredCredentialRepresentation;
import org.keycloak.representations.idm.ResourceRepresentation; import org.keycloak.representations.idm.ResourceRepresentation;
import org.keycloak.representations.idm.RoleMappingRepresentation; import org.keycloak.representations.idm.RoleMappingRepresentation;
import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.representations.idm.ScopeMappingRepresentation; import org.keycloak.representations.idm.ScopeMappingRepresentation;
import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.services.models.KeycloakSession; import org.keycloak.services.models.*;
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 javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import java.security.KeyPair; import java.security.KeyPair;
import java.security.KeyPairGenerator; import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
@ -105,19 +98,19 @@ public class RealmManager {
Map<String, UserModel> userMap = new HashMap<String, UserModel>(); Map<String, UserModel> userMap = new HashMap<String, UserModel>();
if (rep.getRequiredCredentials() != null) { if (rep.getRequiredCredentials() != null) {
for (RequiredCredentialRepresentation requiredCred : rep.getRequiredCredentials()) { for (String requiredCred : rep.getRequiredCredentials()) {
addRequiredCredential(newRealm, requiredCred); addRequiredCredential(newRealm, requiredCred);
} }
} }
if (rep.getRequiredResourceCredentials() != null) { if (rep.getRequiredResourceCredentials() != null) {
for (RequiredCredentialRepresentation requiredCred : rep.getRequiredCredentials()) { for (String requiredCred : rep.getRequiredCredentials()) {
addResourceRequiredCredential(newRealm, requiredCred); addResourceRequiredCredential(newRealm, requiredCred);
} }
} }
if (rep.getRequiredOAuthClientCredentials() != null) { if (rep.getRequiredOAuthClientCredentials() != null) {
for (RequiredCredentialRepresentation requiredCred : rep.getRequiredCredentials()) { for (String requiredCred : rep.getRequiredCredentials()) {
addOAuthClientRequiredCredential(newRealm, requiredCred); addOAuthClientRequiredCredential(newRealm, requiredCred);
} }
} }
@ -193,30 +186,19 @@ public class RealmManager {
return user; return user;
} }
public void addRequiredCredential(RealmModel newRealm, RequiredCredentialRepresentation requiredCred) { public void addRequiredCredential(RealmModel newRealm, String requiredCred) {
RequiredCredentialModel credential = initializeCred(requiredCred); newRealm.addRequiredCredential(requiredCred);
newRealm.addRequiredCredential(credential);
} }
public void addResourceRequiredCredential(RealmModel newRealm, RequiredCredentialRepresentation requiredCred) { public void addResourceRequiredCredential(RealmModel newRealm, String requiredCred) {
RequiredCredentialModel credential = initializeCred(requiredCred); newRealm.addRequiredResourceCredential(requiredCred);
newRealm.addResourceRequiredCredential(credential);
} }
public void addOAuthClientRequiredCredential(RealmModel newRealm, RequiredCredentialRepresentation requiredCred) { public void addOAuthClientRequiredCredential(RealmModel newRealm, String requiredCred) {
RequiredCredentialModel credential = initializeCred(requiredCred); newRealm.addRequiredOAuthClientCredential(requiredCred);
newRealm.addOAuthClientRequiredCredential(credential);
} }
private RequiredCredentialModel initializeCred(RequiredCredentialRepresentation requiredCred) { protected void createResources(RealmRepresentation rep, RealmModel realm) {
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); RoleModel loginRole = realm.getRole(RealmManager.RESOURCE_ROLE);
ResourceManager manager = new ResourceManager(this); ResourceManager manager = new ResourceManager(this);
for (ResourceRepresentation resourceRep : rep.getResources()) { for (ResourceRepresentation resourceRep : rep.getResources()) {
@ -232,4 +214,39 @@ public class RealmManager {
return rep; return rep;
} }
public RealmRepresentation toRepresentation(RealmModel realm) {
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());
List<RequiredCredentialModel> requiredCredentialModels = realm.getRequiredCredentials();
if (requiredCredentialModels.size() > 0) {
rep.setRequiredCredentials(new ArrayList<String>());
for (RequiredCredentialModel cred : requiredCredentialModels) {
rep.getRequiredCredentials().add(cred.getType());
}
}
List<RequiredCredentialModel> requiredResourceCredentialModels = realm.getRequiredResourceCredentials();
if (requiredResourceCredentialModels.size() > 0) {
rep.setRequiredResourceCredentials(new ArrayList<String>());
for (RequiredCredentialModel cred : requiredResourceCredentialModels) {
rep.getRequiredResourceCredentials().add(cred.getType());
}
}
List<RequiredCredentialModel> requiredOAuthCredentialModels = realm.getRequiredOAuthClientCredentials();
if (requiredOAuthCredentialModels.size() > 0) {
rep.setRequiredOAuthClientCredentials(new ArrayList<String>());
for (RequiredCredentialModel cred : requiredOAuthCredentialModels) {
rep.getRequiredOAuthClientCredentials().add(cred.getType());
}
}
return rep;
}
} }

View file

@ -62,6 +62,7 @@ public interface RealmModel {
List<RequiredCredentialModel> getRequiredCredentials(); List<RequiredCredentialModel> getRequiredCredentials();
void addRequiredCredential(RequiredCredentialModel cred); void addRequiredCredential(RequiredCredentialModel cred);
void addRequiredCredential(String cred);
boolean validatePassword(UserModel user, String password); boolean validatePassword(UserModel user, String password);
@ -101,15 +102,19 @@ public interface RealmModel {
RoleModel getRoleById(String id); RoleModel getRoleById(String id);
void addResourceRequiredCredential(RequiredCredentialModel cred); void addRequiredResourceCredential(RequiredCredentialModel cred);
List<RequiredCredentialModel> getResourceRequiredCredentials(); List<RequiredCredentialModel> getRequiredResourceCredentials();
void addOAuthClientRequiredCredential(RequiredCredentialModel cred); void addRequiredOAuthClientCredential(RequiredCredentialModel cred);
List<RequiredCredentialModel> getOAuthClientRequiredCredentials(); List<RequiredCredentialModel> getRequiredOAuthClientCredentials();
boolean hasRole(UserModel user, String role); boolean hasRole(UserModel user, String role);
ResourceModel getResourceById(String id); ResourceModel getResourceById(String id);
void addRequiredOAuthClientCredential(String type);
void addRequiredResourceCredential(String type);
} }

View file

@ -1,6 +1,10 @@
package org.keycloak.services.models; 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> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -10,16 +14,11 @@ public class RequiredCredentialModel {
protected String type; protected String type;
protected boolean input; protected boolean input;
protected boolean secret; protected boolean secret;
protected String formLabel;
public RequiredCredentialModel() { public RequiredCredentialModel() {
} }
public RequiredCredentialModel(String type, boolean input, boolean secret) {
this.type = type;
this.input = input;
this.secret = secret;
}
public String getType() { public String getType() {
return type; return type;
} }
@ -44,5 +43,39 @@ public class RequiredCredentialModel {
this.secret = secret; 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 type;
protected String value; protected String value;
protected String device;
public String getType() { public String getType() {
return type; return type;
@ -24,4 +25,12 @@ public class UserCredentialModel {
public void setValue(String value) { public void setValue(String value) {
this.value = 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); String getAttribute(String name);
Map<String, String> getAttributes(); Map<String, String> getAttributes();
String getFirstName();
void setFirstName(String firstName);
String getLastName();
void setLastName(String lastName);
String getEmail();
void setEmail(String email);
} }

View file

@ -2,7 +2,7 @@ package org.keycloak.services.models.picketlink;
import org.bouncycastle.openssl.PEMWriter; import org.bouncycastle.openssl.PEMWriter;
import org.jboss.resteasy.security.PemUtils; 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.managers.RealmManager;
import org.keycloak.services.models.KeycloakSession; import org.keycloak.services.models.KeycloakSession;
import org.keycloak.services.models.RealmModel; import org.keycloak.services.models.RealmModel;
@ -259,13 +259,13 @@ public class RealmAdapter implements RealmModel {
@Override @Override
public void addResourceRequiredCredential(RequiredCredentialModel cred) { public void addRequiredResourceCredential(RequiredCredentialModel cred) {
ResourceRequiredCredentialRelationship relationship = new ResourceRequiredCredentialRelationship(); ResourceRequiredCredentialRelationship relationship = new ResourceRequiredCredentialRelationship();
addRequiredCredential(cred, relationship); addRequiredCredential(cred, relationship);
} }
@Override @Override
public List<RequiredCredentialModel> getResourceRequiredCredentials() { public List<RequiredCredentialModel> getRequiredResourceCredentials() {
RelationshipQuery<ResourceRequiredCredentialRelationship> query = getRelationshipManager().createRelationshipQuery(ResourceRequiredCredentialRelationship.class); RelationshipQuery<ResourceRequiredCredentialRelationship> query = getRelationshipManager().createRelationshipQuery(ResourceRequiredCredentialRelationship.class);
query.setParameter(ResourceRequiredCredentialRelationship.REALM, realm.getName()); query.setParameter(ResourceRequiredCredentialRelationship.REALM, realm.getName());
List<ResourceRequiredCredentialRelationship> results = query.getResultList(); List<ResourceRequiredCredentialRelationship> results = query.getResultList();
@ -273,13 +273,13 @@ public class RealmAdapter implements RealmModel {
} }
@Override @Override
public void addOAuthClientRequiredCredential(RequiredCredentialModel cred) { public void addRequiredOAuthClientCredential(RequiredCredentialModel cred) {
OAuthClientRequiredCredentialRelationship relationship = new OAuthClientRequiredCredentialRelationship(); OAuthClientRequiredCredentialRelationship relationship = new OAuthClientRequiredCredentialRelationship();
addRequiredCredential(cred, relationship); addRequiredCredential(cred, relationship);
} }
@Override @Override
public List<RequiredCredentialModel> getOAuthClientRequiredCredentials() { public List<RequiredCredentialModel> getRequiredOAuthClientCredentials() {
RelationshipQuery<OAuthClientRequiredCredentialRelationship> query = getRelationshipManager().createRelationshipQuery(OAuthClientRequiredCredentialRelationship.class); RelationshipQuery<OAuthClientRequiredCredentialRelationship> query = getRelationshipManager().createRelationshipQuery(OAuthClientRequiredCredentialRelationship.class);
query.setParameter(ResourceRequiredCredentialRelationship.REALM, realm.getName()); query.setParameter(ResourceRequiredCredentialRelationship.REALM, realm.getName());
List<OAuthClientRequiredCredentialRelationship> results = query.getResultList(); List<OAuthClientRequiredCredentialRelationship> results = query.getResultList();
@ -302,6 +302,7 @@ public class RealmAdapter implements RealmModel {
model.setInput(relationship.isInput()); model.setInput(relationship.isInput());
model.setSecret(relationship.isSecret()); model.setSecret(relationship.isSecret());
model.setType(relationship.getCredentialType()); model.setType(relationship.getCredentialType());
model.setFormLabel(relationship.getFormLabel());
rtn.add(model); rtn.add(model);
} }
return rtn; return rtn;
@ -311,9 +312,35 @@ public class RealmAdapter implements RealmModel {
relationship.setInput(cred.isInput()); relationship.setInput(cred.isInput());
relationship.setSecret(cred.isSecret()); relationship.setSecret(cred.isSecret());
relationship.setRealm(realm.getName()); relationship.setRealm(realm.getName());
relationship.setFormLabel(cred.getFormLabel());
getRelationshipManager().add(relationship); getRelationshipManager().add(relationship);
} }
@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);
addRequiredResourceCredential(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 @Override
public boolean validatePassword(UserModel user, String password) { public boolean validatePassword(UserModel user, String password) {
@ -335,13 +362,14 @@ public class RealmAdapter implements RealmModel {
@Override @Override
public void updateCredential(UserModel user, UserCredentialModel cred) { public void updateCredential(UserModel user, UserCredentialModel cred) {
IdentityManager idm = getIdm(); IdentityManager idm = getIdm();
if (cred.getType().equals(RequiredCredentialRepresentation.PASSWORD)) { if (cred.getType().equals(CredentialRepresentation.PASSWORD)) {
Password password = new Password(cred.getValue()); Password password = new Password(cred.getValue());
idm.updateCredential(((UserAdapter)user).getUser(), password); 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()); TOTPCredential totp = new TOTPCredential(cred.getValue());
totp.setDevice(cred.getDevice());
idm.updateCredential(((UserAdapter)user).getUser(), totp); 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; X509Certificate cert = null;
try { try {
cert = org.keycloak.PemUtils.decodeCertificate(cred.getValue()); cert = org.keycloak.PemUtils.decodeCertificate(cred.getValue());

View file

@ -3,6 +3,7 @@ package org.keycloak.services.models.picketlink;
import org.keycloak.services.models.UserModel; import org.keycloak.services.models.UserModel;
import org.picketlink.idm.IdentityManager; import org.picketlink.idm.IdentityManager;
import org.picketlink.idm.model.Attribute; import org.picketlink.idm.model.Attribute;
import org.picketlink.idm.model.annotation.AttributeProperty;
import org.picketlink.idm.model.sample.User; import org.picketlink.idm.model.sample.User;
import java.util.HashMap; import java.util.HashMap;
@ -41,6 +42,39 @@ public class UserAdapter implements UserModel {
idm.update(user); 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 @Override
public void setAttribute(String name, String value) { public void setAttribute(String name, String value) {
user.setAttribute(new Attribute<String>(name, value)); user.setAttribute(new Attribute<String>(name, value));

View file

@ -68,4 +68,14 @@ public class RequiredCredentialRelationship extends AbstractAttributedType imple
public void setSecret(boolean secret) { public void setSecret(boolean secret) {
setAttribute(new Attribute<Boolean>("secret", 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,7 +6,6 @@ import org.jboss.resteasy.spi.HttpRequest;
import org.jboss.resteasy.spi.HttpResponse; import org.jboss.resteasy.spi.HttpResponse;
import org.jboss.resteasy.spi.NotImplementedYetException; import org.jboss.resteasy.spi.NotImplementedYetException;
import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.RequiredCredentialRepresentation;
import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.RealmManager; 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.*;
import javax.ws.rs.core.*; import javax.ws.rs.core.*;
import java.net.URI; import java.net.URI;
import java.util.StringTokenizer;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -109,7 +109,7 @@ public class SaasService {
if (user == null) { if (user == null) {
return Response.status(401).build(); 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(); }.call();
} }
@ -295,7 +295,7 @@ public class SaasService {
@Path("registrations") @Path("registrations")
@POST @POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED) @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("email") String email,
final @FormParam("username") String username, final @FormParam("username") String username,
final @FormParam("password") String password, final @FormParam("password") String password,
@ -312,7 +312,29 @@ public class SaasService {
RealmModel defaultRealm = realmManager.defaultRealm(); RealmModel defaultRealm = realmManager.defaultRealm();
UserRepresentation newUser = new UserRepresentation(); UserRepresentation newUser = new UserRepresentation();
newUser.setUsername(username); 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); UserModel user = registerMe(defaultRealm, newUser);
if (user == null) { if (user == null) {
request.setAttribute("KEYCLOAK_LOGIN_ERROR_MESSAGE", "Username already exists."); request.setAttribute("KEYCLOAK_LOGIN_ERROR_MESSAGE", "Username already exists.");
@ -340,6 +362,9 @@ public class SaasService {
} }
user = defaultRealm.addUser(newUser.getUsername()); user = defaultRealm.addUser(newUser.getUsername());
user.setFirstName(newUser.getFirstName());
user.setLastName(newUser.getLastName());
user.setEmail(newUser.getEmail());
for (CredentialRepresentation cred : newUser.getCredentials()) { for (CredentialRepresentation cred : newUser.getCredentials()) {
UserCredentialModel credModel = new UserCredentialModel(); UserCredentialModel credModel = new UserCredentialModel();
credModel.setType(cred.getType()); credModel.setType(cred.getType());

View file

@ -7,6 +7,7 @@ import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.services.managers.RealmManager; import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.models.RealmModel; import org.keycloak.services.models.RealmModel;
import org.keycloak.services.models.RequiredCredentialModel;
import org.keycloak.services.models.RoleModel; import org.keycloak.services.models.RoleModel;
import org.keycloak.services.models.UserModel; import org.keycloak.services.models.UserModel;
import org.keycloak.services.resources.PublicRealmResource; import org.keycloak.services.resources.PublicRealmResource;
@ -60,21 +61,12 @@ public class RealmAdminResource {
return new Transaction() { return new Transaction() {
@Override @Override
protected RealmRepresentation callImpl() { protected RealmRepresentation callImpl() {
RealmRepresentation rep = new RealmRepresentation(); return new RealmManager(session).toRepresentation(realm);
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;
} }
}.call(); }.call();
} }
@Path("roles") @Path("roles")
@GET @GET
@NoCache @NoCache

View file

@ -6,7 +6,7 @@ import org.junit.Before;
import org.junit.FixMethodOrder; import org.junit.FixMethodOrder;
import org.junit.Test; import org.junit.Test;
import org.junit.runners.MethodSorters; 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.managers.RealmManager;
import org.keycloak.services.models.KeycloakSession; import org.keycloak.services.models.KeycloakSession;
import org.keycloak.services.models.KeycloakSessionFactory; import org.keycloak.services.models.KeycloakSessionFactory;
@ -19,6 +19,7 @@ import org.keycloak.services.resources.KeycloakApplication;
import java.util.List; import java.util.List;
import java.util.StringTokenizer;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -52,6 +53,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 @Test
public void test1CreateRealm() throws Exception { public void test1CreateRealm() throws Exception {
realmModel = adapter.createRealm("JUGGLER"); realmModel = adapter.createRealm("JUGGLER");
@ -77,25 +88,22 @@ public class AdapterTest {
@Test @Test
public void test2RequiredCredential() throws Exception { public void test2RequiredCredential() throws Exception {
test1CreateRealm(); test1CreateRealm();
RequiredCredentialModel creds = new RequiredCredentialModel(); realmModel.addRequiredCredential(CredentialRepresentation.PASSWORD);
creds.setSecret(true); realmModel.addRequiredCredential(CredentialRepresentation.TOTP);
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);
List<RequiredCredentialModel> storedCreds = realmModel.getRequiredCredentials(); List<RequiredCredentialModel> storedCreds = realmModel.getRequiredCredentials();
Assert.assertEquals(2, storedCreds.size()); Assert.assertEquals(2, storedCreds.size());
boolean totp = false; boolean totp = false;
boolean password = false; boolean password = false;
for (RequiredCredentialModel cred : storedCreds) { 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()); Assert.assertTrue(cred.isInput());
Assert.assertTrue(cred.isSecret()); 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(totp);
Assert.assertTrue(password); Assert.assertTrue(password);
@ -106,7 +114,7 @@ public class AdapterTest {
test1CreateRealm(); test1CreateRealm();
UserModel user = realmModel.addUser("bburke"); UserModel user = realmModel.addUser("bburke");
UserCredentialModel cred = new UserCredentialModel(); UserCredentialModel cred = new UserCredentialModel();
cred.setType(RequiredCredentialRepresentation.PASSWORD); cred.setType(CredentialRepresentation.PASSWORD);
cred.setValue("geheim"); cred.setValue("geheim");
realmModel.updateCredential(user, cred); realmModel.updateCredential(user, cred);
Assert.assertTrue(realmModel.validatePassword(user, "geheim")); Assert.assertTrue(realmModel.validatePassword(user, "geheim"));

View file

@ -70,6 +70,8 @@ public class ImportTest {
realm.addRealmAdmin(admin); realm.addRealmAdmin(admin);
List<RequiredCredentialModel> creds = realm.getRequiredCredentials(); List<RequiredCredentialModel> creds = realm.getRequiredCredentials();
Assert.assertEquals(1, creds.size()); Assert.assertEquals(1, creds.size());
RequiredCredentialModel cred = creds.get(0);
Assert.assertEquals("Password", cred.getFormLabel());
UserModel user = realm.getUser("loginclient"); UserModel user = realm.getUser("loginclient");
Assert.assertNotNull(user); Assert.assertNotNull(user);
@ -83,6 +85,29 @@ public class ImportTest {
} }
@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(RequiredCredentialModel.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

@ -8,8 +8,8 @@ import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.keycloak.SkeletonKeyContextResolver; import org.keycloak.SkeletonKeyContextResolver;
import org.keycloak.representations.AccessTokenResponse; import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.RequiredCredentialRepresentation;
import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.RealmManager; import org.keycloak.services.managers.RealmManager;
@ -61,7 +61,7 @@ public class RealmCreationTest {
public void testRegisterLoginAndCreate() throws Exception { public void testRegisterLoginAndCreate() throws Exception {
UserRepresentation user = new UserRepresentation(); UserRepresentation user = new UserRepresentation();
user.setUsername("bburke"); user.setUsername("bburke");
user.credential(RequiredCredentialRepresentation.PASSWORD, "geheim", false); user.credential(CredentialRepresentation.PASSWORD, "geheim");
WebTarget target = client.target(generateURL("/")); WebTarget target = client.target(generateURL("/"));
Response response = target.path("saas/registrations").request().post(Entity.json(user)); Response response = target.path("saas/registrations").request().post(Entity.json(user));
@ -73,14 +73,14 @@ public class RealmCreationTest {
try { try {
Form form = new Form(); Form form = new Form();
form.param(AuthenticationManager.FORM_USERNAME, "bburke"); form.param(AuthenticationManager.FORM_USERNAME, "bburke");
form.param(RequiredCredentialRepresentation.PASSWORD, "badpassword"); form.param(CredentialRepresentation.PASSWORD, "badpassword");
tokenResponse = target.path("realms").path(RealmModel.DEFAULT_REALM).path("tokens/grants/identity-token").request().post(Entity.form(form), AccessTokenResponse.class); tokenResponse = target.path("realms").path(RealmModel.DEFAULT_REALM).path("tokens/grants/identity-token").request().post(Entity.form(form), AccessTokenResponse.class);
Assert.fail(); Assert.fail();
} catch (NotAuthorizedException e) { } catch (NotAuthorizedException e) {
} }
Form form = new Form(); Form form = new Form();
form.param(AuthenticationManager.FORM_USERNAME, "bburke"); form.param(AuthenticationManager.FORM_USERNAME, "bburke");
form.param(RequiredCredentialRepresentation.PASSWORD, "geheim"); form.param(CredentialRepresentation.PASSWORD, "geheim");
tokenResponse = target.path("realms").path(RealmModel.DEFAULT_REALM).path("tokens/grants/identity-token").request().post(Entity.form(form), AccessTokenResponse.class); tokenResponse = target.path("realms").path(RealmModel.DEFAULT_REALM).path("tokens/grants/identity-token").request().post(Entity.form(form), AccessTokenResponse.class);
Assert.assertNotNull(tokenResponse); Assert.assertNotNull(tokenResponse);
System.out.println(tokenResponse.getToken()); System.out.println(tokenResponse.getToken());

View file

@ -0,0 +1,86 @@
{
"realm": "demo",
"enabled": true,
"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": [ "password" ],
"requiredResourceCredentials": [ "password" ],
"requiredOAuthClientCredentials": [ "password" ],
"users" : [
{
"username" : "bburke@redhat.com",
"enabled" : true,
"attributes" : {
"email" : "bburke@redhat.com"
},
"credentials" : [
{ "type" : "Password",
"value" : "password" }
]
},
{
"username" : "third-party",
"enabled" : true,
"credentials" : [
{ "type" : "Password",
"value" : "password" }
]
}
],
"roles": [
{
"name": "user",
"description": "Have User privileges"
},
{
"name": "admin",
"description": "Have Administrator privileges"
}
],
"roleMappings": [
{
"username": "bburke@redhat.com",
"roles": ["user"]
},
{
"username": "third-party",
"roles": ["KEYCLOAK_IDENTITY_REQUESTER"]
}
],
"scopeMappings": [
{
"username": "third-party",
"roles": ["user"]
}
],
"resources": [
{
"name": "customer-portal",
"enabled": true,
"adminUrl": "http://localhost:8080/customer-portal/j_admin_request",
"useRealmMappings": true,
"credentials": [
{
"type": "password",
"value": "password"
}
]
},
{
"name": "product-portal",
"enabled": true,
"adminUrl": "http://localhost:8080/product-portal/j_admin_request",
"useRealmMappings": true,
"credentials": [
{
"type": "password",
"value": "password"
}
]
}
]
}

View file

@ -1,123 +1,121 @@
{ {
"realm" : "test-realm", "realm": "test-realm",
"enabled" : true, "enabled": true,
"tokenLifespan" : 6000, "tokenLifespan": 6000,
"accessCodeLifespan" : 30, "accessCodeLifespan": 30,
"requiredCredentials" : [ "requiredCredentials": [ "password" ],
"requiredResourceCredentials": [ "password" ],
"requiredOAuthClientCredentials": [ "password" ],
"users": [
{ {
"type" : "Password", "username": "wburke",
"input" : true, "enabled": true,
"secret" : true "attributes": {
} "email": "bburke@redhat.com"
],
"requiredResourceCredentials" : [
{
"type" : "Password",
"input" : true,
"secret" : true
}
],
"requiredOAuthClientCredentials" : [
{
"type" : "Password",
"input" : true,
"secret" : true
}
],
"users" : [
{
"username" : "wburke",
"enabled" : true,
"attributes" : {
"email" : "bburke@redhat.com"
}, },
"credentials" : [ "credentials": [
{ "type" : "Password", {
"value" : "userpassword" } "type": "password",
"value": "userpassword"
}
] ]
}, },
{ {
"username" : "loginclient", "username": "loginclient",
"enabled" : true, "enabled": true,
"credentials" : [ "credentials": [
{ "type" : "Password", {
"value" : "clientpassword" } "type": "password",
"value": "clientpassword"
}
] ]
}, },
{ {
"username" : "admin", "username": "admin",
"enabled" : true, "enabled": true,
"credentials" : [ "credentials": [
{ "type" : "Password", {
"value" : "adminpassword" } "type": "password",
"value": "adminpassword"
}
] ]
}, },
{ {
"username" : "oauthclient", "username": "oauthclient",
"enabled" : true, "enabled": true,
"credentials" : [ "credentials": [
{ "type" : "Password", {
"value" : "clientpassword" } "type": "password",
"value": "clientpassword"
}
] ]
} }
], ],
"roleMappings" : [ "roleMappings": [
{ {
"username" : "admin", "username": "admin",
"roles" : ["admin"] "roles": ["admin"]
} }
], ],
"scopeMappings" : [ "scopeMappings": [
{ {
"username" : "loginclient", "username": "loginclient",
"roles" : ["*"] "roles": ["*"]
} }
], ],
"resources" : [ "resources": [
{ {
"name" : "Application", "name": "Application",
"enabled" : true, "enabled": true,
"roles" : [ "roles": [
{ "name" : "admin" }, {
{ "name" : "user" } "name": "admin"
], },
"roleMappings" : [ {
{ "name": "user"
"username" : "wburke", }
"roles" : ["user"] ],
}, "roleMappings": [
{ {
"username" : "admin", "username": "wburke",
"roles" : ["admin"] "roles": ["user"]
} },
], {
"scopeMappings" : [ "username": "admin",
{ "roles": ["admin"]
"username" : "oauthclient", }
"roles" : ["user"] ],
} "scopeMappings": [
] {
}, "username": "oauthclient",
{ "roles": ["user"]
"name" : "OtherApp", }
"enabled" : true, ]
"roles" : [ },
{ "name" : "admin" }, {
{ "name" : "user" } "name": "OtherApp",
], "enabled": true,
"roleMappings" : [ "roles": [
{ {
"username" : "wburke", "name": "admin"
"roles" : ["user"] },
}, {
{ "name": "user"
"username" : "admin", }
"roles" : ["admin"] ],
} "roleMappings": [
] {
} "username": "wburke",
"roles": ["user"]
},
{
"username": "admin",
"roles": ["admin"]
}
]
}
] ]
} }