This commit is contained in:
Bill Burke 2013-11-15 14:18:00 -05:00
commit 934eca73f4
120 changed files with 1173 additions and 652 deletions

View file

@ -55,17 +55,44 @@ body {
color: #fff; color: #fff;
font-weight: bold; font-weight: bold;
} }
.loading span { .loading-backdrop {
background: url(img/loader.gif) no-repeat center top;
position: fixed; position: fixed;
z-index: 1000; top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 1031;
background-color: #FFFFFF;
opacity: 0.75;
}
.loading {
position: fixed;
z-index: 1032;
top: 50%; top: 50%;
left: 50%; left: 50%;
width: 6em;
height: 6em;
margin-top: -3em;
margin-left: -3em;
text-align: center;
}
.loading img {
width: 3em;
height: 3em;
background-color: #f0f0f0;
display: inline-block;
padding: 0.3em;
border-radius: 0.4em;
}
.loading span {
background: url(img/loader.gif) no-repeat center top;
font-size: 1.2em;
color: #666;
display: inline-block;
padding-top: 0.36363636363636em;
margin-top: -2.27272727272727em; margin-top: -2.27272727272727em;
margin-left: -2.27272727272727em; margin-left: -2.27272727272727em;
padding-top: 2.90909090909091em; padding-top: 2.90909090909091em;
font-size: 1.1em;
color: #666;
} }
/* Header */ /* Header */
.header.rcue { .header.rcue {
@ -485,11 +512,11 @@ td.token-cell button {
font-family: "Open Sans", sans-serif; font-family: "Open Sans", sans-serif;
margin: 0; margin: 0;
} }
.modal .modal-body p { .modal .modal-body p span {
display: block;
font-size: 1.1em; font-size: 1.1em;
} }
.modal .modal-body p.primary { .modal .modal-body p span.primary {
font-size: 1.1em;
font-weight: bold; font-weight: bold;
margin-bottom: 0.45454545454545em; margin-bottom: 0.45454545454545em;
} }

View file

@ -74,19 +74,46 @@ body {
} }
} }
.loading-backdrop {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 1031;
background-color: #FFFFFF;
opacity: 0.75;
}
.loading { .loading {
position: fixed;
z-index: 1032;
top: 50%;
left: 50%;
width: 6em;
height: 6em;
margin-top: -3em;
margin-left: -3em;
text-align: center;
img {
width: 3em;
height: 3em;
background-color: #f0f0f0;
display: inline-block;
padding: 0.3em;
border-radius: 0.4em;
}
span { span {
background: url(img/loader.gif) no-repeat center top; background: url(img/loader.gif) no-repeat center top;
position: fixed; font-size: 1.2em;
z-index: 1000; color: #666;
top: 50%; display: inline-block;
left: 50%; padding-top: 0.36363636363636em;
margin-top: -2.27272727272727em; margin-top: -2.27272727272727em;
margin-left: -2.27272727272727em; margin-left: -2.27272727272727em;
padding-top: 2.90909090909091em; padding-top: 2.90909090909091em;
font-size: 1.1em;
color: #666;
} }
} }
@ -592,11 +619,11 @@ td.token-cell button {
.modal-body { .modal-body {
p { p span {
display: block;
font-size: 1.1em; font-size: 1.1em;
&.primary { &.primary {
font-size: 1.1em;
font-weight: bold; font-weight: bold;
margin-bottom: 0.45454545454545em; margin-bottom: 0.45454545454545em;
} }

View file

@ -73,8 +73,10 @@
</button> </button>
</div> </div>
<div id="loading"> <div id="loading" class="loading-backdrop">
<i class="icon-spinner icon-spin"></i> Loading... <div class="loading">
<span>Loading...</span>
</div>
</div> </div>
</div> </div>

View file

@ -2,6 +2,7 @@
var module = angular.module('keycloak', [ 'keycloak.services', 'keycloak.loaders', 'keycloak.controllers', 'ui.bootstrap', 'ui.select2' ]); var module = angular.module('keycloak', [ 'keycloak.services', 'keycloak.loaders', 'keycloak.controllers', 'ui.bootstrap', 'ui.select2' ]);
var resourceRequests = 0; var resourceRequests = 0;
var loadingTimer = -1;
module.config([ '$routeProvider', function($routeProvider) { module.config([ '$routeProvider', function($routeProvider) {
@ -46,6 +47,21 @@ module.config([ '$routeProvider', function($routeProvider) {
}, },
controller : 'RealmSocialCtrl' controller : 'RealmSocialCtrl'
}) })
.when('/realms/:realm/registration-settings', {
templateUrl : 'partials/realm-registration.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
applications : function(ApplicationListLoader) {
return ApplicationListLoader();
},
roles : function(RoleListLoader) {
return RoleListLoader();
}
},
controller : 'RealmRegistrationCtrl'
})
.when('/realms/:realm/required-credentials', { .when('/realms/:realm/required-credentials', {
templateUrl : 'partials/realm-credentials.html', templateUrl : 'partials/realm-credentials.html',
resolve : { resolve : {
@ -281,7 +297,10 @@ module.config(function($httpProvider) {
var spinnerFunction = function(data, headersGetter) { var spinnerFunction = function(data, headersGetter) {
if (resourceRequests == 0) { if (resourceRequests == 0) {
$('#loading').show(); loadingTimer = window.setTimeout(function() {
$('#loading').show();
loadingTimer = -1;
}, 500);
} }
resourceRequests++; resourceRequests++;
return data; return data;
@ -301,7 +320,7 @@ module.factory('errorInterceptor', function($q, $window, $rootScope, $location,
if (response.status == 401) { if (response.status == 401) {
console.log('session timeout?'); console.log('session timeout?');
Auth.loggedIn = false; Auth.loggedIn = false;
$location.url('/'); window.location = '/auth-server/rest/saas/login?path=' + $location.path();
} else { } else {
$rootScope.httpProviderError = response.status; $rootScope.httpProviderError = response.status;
} }
@ -315,12 +334,20 @@ module.factory('spinnerInterceptor', function($q, $window, $rootScope, $location
return promise.then(function(response) { return promise.then(function(response) {
resourceRequests--; resourceRequests--;
if (resourceRequests == 0) { if (resourceRequests == 0) {
if(loadingTimer != -1) {
window.clearTimeout(loadingTimer);
loadingTimer = -1;
}
$('#loading').hide(); $('#loading').hide();
} }
return response; return response;
}, function(response) { }, function(response) {
resourceRequests--; resourceRequests--;
if (resourceRequests == 0) { if (resourceRequests == 0) {
if(loadingTimer != -1) {
window.clearTimeout(loadingTimer);
loadingTimer = -1;
}
$('#loading').hide(); $('#loading').hide();
} }
@ -368,10 +395,42 @@ module.directive('collapsed', function() {
} }
}); });
/**
* Directive for presenting an ON-OFF switch for checkbox.
* Usage: <input ng-model="mmm" name="nnn" id="iii" onoffswitch [on-text="ooo" off-text="fff"] />
*/
module.directive('onoffswitch', function() {
return {
restrict: "EA",
require: 'ngModel',
replace: true,
scope: {
ngModel: '=',
ngBind: '=',
name: '=',
id: '=',
onText: '@onText',
offText: '@offText'
},
compile: function(element, attrs) {
if (!attrs.onText) { attrs.onText = "ON"; }
if (!attrs.offText) { attrs.offText = "OFF"; }
var html = "<div class=\"onoffswitch\">" +
"<input type=\"checkbox\" data-ng-model=\"ngModel\" class=\"onoffswitch-checkbox\" name=\"" + attrs.name + "\" id=\"" + attrs.id + "\">" +
"<label for=\"" + attrs.id + "\" class=\"onoffswitch-label\">" +
"<span class=\"onoffswitch-inner\">" +
"<span class=\"onoffswitch-active\">{{onText}}</span>" +
"<span class=\"onoffswitch-inactive\">{{offText}}</span>" +
"</span>" +
"<span class=\"onoffswitch-switch\"></span>" +
"</label>" +
"</div>";
element.replaceWith($(html));
}
}
});
module.directive('kcInput', function() { module.directive('kcInput', function() {
@ -430,16 +489,28 @@ module.filter('remove', function() {
for ( var i = 0; i < input.length; i++) { for ( var i = 0; i < input.length; i++) {
var e = input[i]; var e = input[i];
for (var j = 0; j < remove.length; j++) { if (Array.isArray(remove)) {
for (var j = 0; j < remove.length; j++) {
if (attribute) {
if (remove[j][attribute] == e[attribute]) {
e = null;
break;
}
} else {
if (remove[j] == e) {
e = null;
break;
}
}
}
} else {
if (attribute) { if (attribute) {
if (remove[j][attribute] == e[attribute]) { if (remove[attribute] == e[attribute]) {
e = null; e = null;
break;
} }
} else { } else {
if (remove[j] == e) { if (remove == e) {
e = null; e = null;
break;
} }
} }
} }

View file

@ -19,5 +19,4 @@ function randomString(len) {
randomString += charSet.substring(randomPoz,randomPoz+1); randomString += charSet.substring(randomPoz,randomPoz+1);
} }
return randomString; return randomString;
} }

View file

@ -15,6 +15,7 @@ module.controller('GlobalCtrl', function($scope, $http, Auth, Current, $location
$scope.$watch(function() { $scope.$watch(function() {
return $location.path(); return $location.path();
}, function() { }, function() {
$scope.fragment = $location.path();
$scope.path = $location.path().substring(1).split("/"); $scope.path = $location.path().substring(1).split("/");
}); });
@ -73,6 +74,9 @@ module.controller('RealmDetailCtrl', function($scope, Current, Realm, realm, $ht
$scope.realm.requireSsl = !realm.sslNotRequired; $scope.realm.requireSsl = !realm.sslNotRequired;
} }
$scope.social = $scope.realm.social;
$scope.registrationAllowed = $scope.realm.registrationAllowed;
var oldCopy = angular.copy($scope.realm); var oldCopy = angular.copy($scope.realm);
@ -104,6 +108,8 @@ module.controller('RealmDetailCtrl', function($scope, Current, Realm, realm, $ht
} }
$location.url("/realms/" + id); $location.url("/realms/" + id);
Notifications.success("The realm has been created."); Notifications.success("The realm has been created.");
$scope.social = $scope.realm.social;
$scope.registrationAllowed = $scope.realm.registrationAllowed;
}); });
}); });
} else { } else {
@ -122,6 +128,8 @@ module.controller('RealmDetailCtrl', function($scope, Current, Realm, realm, $ht
}); });
$location.url("/realms/" + id); $location.url("/realms/" + id);
Notifications.success("Your changes have been saved to the realm."); Notifications.success("Your changes have been saved to the realm.");
$scope.social = $scope.realm.social;
$scope.registrationAllowed = $scope.realm.registrationAllowed;
}); });
} }
} else { } else {
@ -158,7 +166,8 @@ module.controller('RealmRequiredCredentialsCtrl', function($scope, Realm, realm,
id : realm.id, realm : realm.realm, social : realm.social, id : realm.id, realm : realm.realm, social : realm.social,
requiredCredentials : realm.requiredCredentials, requiredCredentials : realm.requiredCredentials,
requiredApplicationCredentials : realm.requiredApplicationCredentials, requiredApplicationCredentials : realm.requiredApplicationCredentials,
requiredOAuthClientCredentials : realm.requiredOAuthClientCredentials requiredOAuthClientCredentials : realm.requiredOAuthClientCredentials,
registrationAllowed : realm.registrationAllowed
}; };
$scope.userCredentialOptions = { $scope.userCredentialOptions = {
@ -196,10 +205,156 @@ module.controller('RealmRequiredCredentialsCtrl', function($scope, Realm, realm,
}; };
}); });
module.controller('RealmRegistrationCtrl', function ($scope, Realm, realm, applications, roles, Notifications, ApplicationRole, Application) {
console.log('RealmRegistrationCtrl');
$scope.realm = realm;
$scope.availableRealmRoles = [];
$scope.selectedRealmRoles = [];
$scope.selectedRealmDefRoles = [];
$scope.applications = angular.copy(applications);
$scope.availableAppRoles = [];
$scope.selectedAppRoles = [];
$scope.selectedAppDefRoles = [];
if (!$scope.realm.hasOwnProperty('defaultRoles') || $scope.realm.defaultRoles === null) {
$scope.realm.defaultRoles = [];
}
// Populate available roles. Available roles are neither already assigned or system roles.
for (var i = 0; i < roles.length; i++) {
var item = roles[i].name;
if ($scope.realm.defaultRoles.indexOf(item) < 0) {
$scope.availableRealmRoles.push(item);
}
}
$scope.addRealmDefaultRole = function () {
// Remove selected roles from the Available roles and add them to realm default roles (move from left to right).
for (var i = 0; i < $scope.selectedRealmRoles.length; i++) {
var selectedRole = $scope.selectedRealmRoles[i];
$scope.realm.defaultRoles.push(selectedRole);
var index = $scope.availableRealmRoles.indexOf(selectedRole);
if (index > -1) {
$scope.availableRealmRoles.splice(index, 1);
}
}
// Update/save the realm with new default roles.
Realm.update($scope.realm, function () {
Notifications.success("Realm default roles updated.");
});
};
$scope.deleteRealmDefaultRole = function () {
// Remove selected roles from the realm default roles and add them to available roles (move from right to left).
for (var i = 0; i < $scope.selectedRealmDefRoles.length; i++) {
$scope.availableRealmRoles.push($scope.selectedRealmDefRoles[i]);
var index = $scope.realm.defaultRoles.indexOf($scope.selectedRealmDefRoles[i]);
if (index > -1) {
$scope.realm.defaultRoles.splice(index, 1);
}
}
// Update/save the realm with new default roles.
//var realmCopy = angular.copy($scope.realm);
Realm.update($scope.realm, function () {
Notifications.success("Realm default roles updated.");
});
};
$scope.changeApplication = function () {
$scope.selectedAppRoles = [];
$scope.selectedAppDefRoles = [];
// Populate available roles for selected application
var appDefaultRoles = ApplicationRole.query({realm: $scope.realm.id, application: $scope.application.id}, function () {
if (!$scope.application.hasOwnProperty('defaultRoles') || $scope.application.defaultRoles === null) {
$scope.application.defaultRoles = [];
}
$scope.availableAppRoles = [];
for (var i = 0; i < appDefaultRoles.length; i++) {
var roleName = appDefaultRoles[i].name;
if (systemRoles.indexOf(roleName) < 0 && $scope.application.defaultRoles.indexOf(roleName) < 0) {
$scope.availableAppRoles.push(roleName);
}
}
});
};
$scope.addAppDefaultRole = function () {
// Remove selected roles from the app available roles and add them to app default roles (move from left to right).
for (var i = 0; i < $scope.selectedAppRoles.length; i++) {
var role = $scope.selectedAppRoles[i];
var idx = $scope.application.defaultRoles.indexOf(role);
if (idx < 0) {
$scope.application.defaultRoles.push(role);
}
idx = $scope.availableAppRoles.indexOf(role);
if (idx != -1) {
$scope.availableAppRoles.splice(idx, 1);
}
}
// Update/save the selected application with new default roles.
Application.update({
realm: $scope.realm.id,
id: $scope.application.id
}, $scope.application, function () {
Notifications.success("Your changes have been saved to the application.");
});
};
$scope.rmAppDefaultRole = function () {
// Remove selected roles from the app default roles and add them to app available roles (move from right to left).
for (var i = 0; i < $scope.selectedAppDefRoles.length; i++) {
var role = $scope.selectedAppDefRoles[i];
var idx = $scope.application.defaultRoles.indexOf(role);
if (idx != -1) {
$scope.application.defaultRoles.splice(idx, 1);
}
idx = $scope.availableAppRoles.indexOf(role);
if (idx < 0) {
$scope.availableAppRoles.push(role);
}
}
// Update/save the selected application with new default roles.
Application.update({
realm: $scope.realm.id,
id: $scope.application.id
}, $scope.application, function () {
Notifications.success("Your changes have been saved to the application.");
});
};
});
module.controller('RealmSocialCtrl', function($scope, realm, Realm, $location, Notifications) { module.controller('RealmSocialCtrl', function($scope, realm, Realm, $location, Notifications) {
console.log('RealmSocialCtrl'); console.log('RealmSocialCtrl');
$scope.realm = { id : realm.id, realm : realm.realm, social : realm.social, tokenLifespan : realm.tokenLifespan, accessCodeLifespan : realm.accessCodeLifespan }; $scope.realm = { id : realm.id, realm : realm.realm, social : realm.social, registrationAllowed : realm.registrationAllowed,
tokenLifespan : realm.tokenLifespan, accessCodeLifespan : realm.accessCodeLifespan };
if (!realm["socialProviders"]){ if (!realm["socialProviders"]){
$scope.realm["socialProviders"] = {}; $scope.realm["socialProviders"] = {};
@ -207,8 +362,13 @@ module.controller('RealmSocialCtrl', function($scope, realm, Realm, $location, N
$scope.realm["socialProviders"] = realm.socialProviders; $scope.realm["socialProviders"] = realm.socialProviders;
} }
// Hardcoded provider list // Hardcoded provider list in form of map providerId:providerName
$scope.availableProviders = [ "google", "facebook", "twitter"]; $scope.allProviders = { google:"Google", facebook:"Facebook", twitter:"Twitter" };
$scope.availableProviders = [];
for (var provider in $scope.allProviders){
$scope.availableProviders.push(provider);
}
var oldCopy = angular.copy($scope.realm); var oldCopy = angular.copy($scope.realm);
$scope.changed = false; $scope.changed = false;
@ -226,6 +386,9 @@ module.controller('RealmSocialCtrl', function($scope, realm, Realm, $location, N
// Fill in configured providers // Fill in configured providers
var initSocial = function() { var initSocial = function() {
// postSaveProviders is used for remembering providers which were already validated after pressing the save button
// thanks to this it's easy to distinguish between newly added fields and those already tried to be saved
$scope.postSaveProviders = [];
$scope.unsetProviders = []; $scope.unsetProviders = [];
$scope.configuredProviders = []; $scope.configuredProviders = [];
@ -241,7 +404,7 @@ module.controller('RealmSocialCtrl', function($scope, realm, Realm, $location, N
// If no providers are already configured, you can add any of them // If no providers are already configured, you can add any of them
if ($scope.configuredProviders.length == 0){ if ($scope.configuredProviders.length == 0){
$scope.unsetProviders = $scope.availableProviders; $scope.unsetProviders = $scope.availableProviders.slice(0);
} else { } else {
for (var i = 0; i < $scope.availableProviders.length; i++){ for (var i = 0; i < $scope.availableProviders.length; i++){
var providerId = $scope.availableProviders[i]; var providerId = $scope.availableProviders[i];
@ -270,6 +433,13 @@ module.controller('RealmSocialCtrl', function($scope, realm, Realm, $location, N
delete $scope.realm.socialProviders[pId+".key"]; delete $scope.realm.socialProviders[pId+".key"];
delete $scope.realm.socialProviders[pId+".secret"]; delete $scope.realm.socialProviders[pId+".secret"];
$scope.configuredProviders.remove($scope.configuredProviders.indexOf(pId)); $scope.configuredProviders.remove($scope.configuredProviders.indexOf(pId));
// Removing from postSaveProviders, so the empty fields are not red if the provider is added to the list again
var rId = $scope.postSaveProviders.indexOf(pId);
if (rId > -1){
$scope.postSaveProviders.remove(rId)
}
$scope.unsetProviders.push(pId); $scope.unsetProviders.push(pId);
}; };
@ -280,8 +450,6 @@ module.controller('RealmSocialCtrl', function($scope, realm, Realm, $location, N
}, true); }, true);
$scope.save = function() { $scope.save = function() {
$scope.saveClicked = true;
if ($scope.realmForm.$valid) { if ($scope.realmForm.$valid) {
var realmCopy = angular.copy($scope.realm); var realmCopy = angular.copy($scope.realm);
realmCopy.social = true; realmCopy.social = true;
@ -289,10 +457,12 @@ module.controller('RealmSocialCtrl', function($scope, realm, Realm, $location, N
Realm.update(realmCopy, function () { Realm.update(realmCopy, function () {
$location.url("/realms/" + realm.id + "/social-settings"); $location.url("/realms/" + realm.id + "/social-settings");
Notifications.success("Saved changes to realm"); Notifications.success("Saved changes to realm");
oldCopy = realmCopy;
}); });
} else { } else {
$scope.realmForm.showErrors = true; $scope.realmForm.showErrors = true;
Notifications.error("Some required fields are missing values."); Notifications.error("Some required fields are missing values.");
$scope.postSaveProviders = $scope.configuredProviders.slice(0);
} }
}; };
@ -317,7 +487,10 @@ module.controller('RealmSocialCtrl', function($scope, realm, Realm, $location, N
module.controller('RealmTokenDetailCtrl', function($scope, Realm, realm, $http, $location, Dialog, Notifications) { module.controller('RealmTokenDetailCtrl', function($scope, Realm, realm, $http, $location, Dialog, Notifications) {
console.log('RealmTokenDetailCtrl'); console.log('RealmTokenDetailCtrl');
$scope.realm = { id : realm.id, realm : realm.realm, social : realm.social, tokenLifespan : realm.tokenLifespan, accessCodeLifespan : realm.accessCodeLifespan , accessCodeLifespanUserAction : realm.accessCodeLifespanUserAction }; $scope.realm = { id : realm.id, realm : realm.realm, social : realm.social, registrationAllowed : realm.registrationAllowed,
tokenLifespan : realm.tokenLifespan, accessCodeLifespan : realm.accessCodeLifespan,
accessCodeLifespanUserAction : realm.accessCodeLifespanUserAction };
$scope.realm.tokenLifespanUnit = 'Seconds'; $scope.realm.tokenLifespanUnit = 'Seconds';
$scope.realm.accessCodeLifespanUnit = 'Seconds'; $scope.realm.accessCodeLifespanUnit = 'Seconds';
$scope.realm.accessCodeLifespanUserActionUnit = 'Seconds'; $scope.realm.accessCodeLifespanUserActionUnit = 'Seconds';
@ -376,6 +549,7 @@ module.controller('RealmTokenDetailCtrl', function($scope, Realm, realm, $http,
}); });
module.controller('RoleListCtrl', function($scope, $location, realm, roles) { module.controller('RoleListCtrl', function($scope, $location, realm, roles) {
$scope.realm = realm; $scope.realm = realm;
$scope.roles = roles; $scope.roles = roles;
@ -456,7 +630,7 @@ module.controller('RoleDetailCtrl', function($scope, realm, role, Role, $locatio
module.controller('RealmSMTPSettingsCtrl', function($scope, Current, Realm, realm, $http, $location, Dialog, Notifications) { module.controller('RealmSMTPSettingsCtrl', function($scope, Current, Realm, realm, $http, $location, Dialog, Notifications) {
$scope.realm = { $scope.realm = {
id : realm.id, realm : realm.realm, social : realm.social, id : realm.id, realm : realm.realm, social : realm.social, registrationAllowed : realm.registrationAllowed,
smtpServer: realm.smtpServer smtpServer: realm.smtpServer
}; };

View file

@ -17,8 +17,8 @@ module.service('Dialog', function($dialog) {
var dialog = {}; var dialog = {};
dialog.confirmDelete = function(name, type, success) { dialog.confirmDelete = function(name, type, success) {
var title = 'Delete ' + type.charAt(0).toUpperCase() + type.slice(1); var title = 'Delete ' + type.charAt(0).toUpperCase() + type.slice(1);
var msg = '<p class="primary">Are you sure you want to permanently delete the ' + type + ' "' + name + '"?</p>' + var msg = '<span class="primary">Are you sure you want to permanently delete the ' + type + ' "' + name + '"?</span>' +
'<p>This action can\'t be undone.</p>'; '<span>This action can\'t be undone.</span>';
var btns = [ { var btns = [ {
result : 'cancel', result : 'cancel',
label : 'Cancel' label : 'Cancel'

View file

@ -44,17 +44,7 @@
<div class="form-group clearfix block"> <div class="form-group clearfix block">
<label for="enabled" class="control-label">Enabled</label> <label for="enabled" class="control-label">Enabled</label>
<div class="onoffswitch"> <input ng-model="application.enabled" name="enabled" id="enabled" onoffswitch />
<input type="checkbox" data-ng-model="application.enabled" class="onoffswitch-checkbox"
name="enabled" id="enabled">
<label for="enabled" class="onoffswitch-label">
<span class="onoffswitch-inner">
<span class="onoffswitch-active">ON</span>
<span class="onoffswitch-inactive">OFF</span>
</span>
<span class="onoffswitch-switch"></span>
</label>
</div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="baseUrl" class="control-label">Base URL</label> <label for="baseUrl" class="control-label">Base URL</label>

View file

@ -56,7 +56,7 @@
<label for="applications">Application</label> <label for="applications">Application</label>
<div class="input-group"> <div class="input-group">
<div class="select-rcue"> <div class="select-rcue">
<select id="applications" name="applications" ng-change="changeApplication()" ng-model="targetApp" ng-options="a.name for a in applications"> <select id="applications" name="applications" ng-change="changeApplication()" ng-model="targetApp" ng-options="a.name for a in (applications|remove:application:'id')">
<option value="" selected> Select an Application </option> <option value="" selected> Select an Application </option>
</select> </select>
</div> </div>

View file

@ -3,7 +3,7 @@
<div class="navbar-inner clearfix container"> <div class="navbar-inner clearfix container">
<h1><a href="#/realms/{{realm.id}}"><strong>Keycloak</strong> Central Login</a></h1> <h1><a href="#/realms/{{realm.id}}"><strong>Keycloak</strong> Central Login</a></h1>
<ul class="nav pull-right" data-ng-hide="auth.loggedIn"> <ul class="nav pull-right" data-ng-hide="auth.loggedIn">
<li><a href="/auth-server/rest/saas/login">Login</a></li> <li><a href="/auth-server/rest/saas/login?path={{fragment}}">Login</a></li>
<li><a href="/auth-server/rest/saas/registrations">Register</a></li> <li><a href="/auth-server/rest/saas/registrations">Register</a></li>
</ul> </ul>
<ul class="nav pull-right" data-ng-show="auth.loggedIn"> <ul class="nav pull-right" data-ng-show="auth.loggedIn">
@ -25,7 +25,7 @@
<span class="dropdown-label" data-ng-show="showNav()">Realm:</span> <span class="dropdown-label" data-ng-show="showNav()">Realm:</span>
<div class="select-rcue" data-ng-show="showNav()"> <div class="select-rcue" data-ng-show="showNav()">
<select ng-change="changeRealm()" ng-model="current.realm" ng-options="r.realm for r in current.realms"></select> <select ng-change="changeRealm()" ng-model="current.realm" ng-options="r.realm for r in current.realms"></select>
</div><a href="#/realms/{{realm.id}}" id="refresh" data-ng-show="showNav()"><span class="icon-spinner6">Icon: spinner</span></a> </div><a href="#/realms/{{current.realm.id}}" id="refresh" data-ng-show="showNav()"><span class="icon-spinner6">Icon: spinner</span></a>
</li> </li>
</ul> </ul>
<div class="pull-right" data-ng-show="auth.loggedIn"> <div class="pull-right" data-ng-show="auth.loggedIn">

View file

@ -6,6 +6,7 @@
<ul class="rcue-tabs"> <ul class="rcue-tabs">
<li><a href="#/realms/{{realm.id}}">General</a></li> <li><a href="#/realms/{{realm.id}}">General</a></li>
<li data-ng-show="realm.social"><a href="#/realms/{{realm.id}}/social-settings">Social</a></li> <li data-ng-show="realm.social"><a href="#/realms/{{realm.id}}/social-settings">Social</a></li>
<li data-ng-show="realm.registrationAllowed"><a href="#/realms/{{realm.id}}/registration-settings">Registration</a></li>
<li><a href="#/realms/{{realm.id}}/roles">Roles</a></li> <li><a href="#/realms/{{realm.id}}/roles">Roles</a></li>
<li class="active"><a href="#/realms/{{realm.id}}/required-credentials">Credentials</a></li> <li class="active"><a href="#/realms/{{realm.id}}/required-credentials">Credentials</a></li>
<li><a href="#/realms/{{realm.id}}/token-settings">Token</a></li> <li><a href="#/realms/{{realm.id}}/token-settings">Token</a></li>

View file

@ -5,7 +5,8 @@
<div class="top-nav" data-ng-hide="createRealm"> <div class="top-nav" data-ng-hide="createRealm">
<ul class="rcue-tabs"> <ul class="rcue-tabs">
<li class="active"><a href="#/realms/{{realm.id}}">General</a></li> <li class="active"><a href="#/realms/{{realm.id}}">General</a></li>
<li data-ng-show="realm.social"><a href="#/realms/{{realm.id}}/social-settings">Social</a></li> <li data-ng-show="social"><a href="#/realms/{{realm.id}}/social-settings">Social</a></li>
<li data-ng-show="registrationAllowed"><a href="#/realms/{{realm.id}}/registration-settings">Registration</a></li>
<li><a href="#/realms/{{realm.id}}/roles">Roles</a></li> <li><a href="#/realms/{{realm.id}}/roles">Roles</a></li>
<li><a href="#/realms/{{realm.id}}/required-credentials">Credentials</a></li> <li><a href="#/realms/{{realm.id}}/required-credentials">Credentials</a></li>
<li><a href="#/realms/{{realm.id}}/token-settings">Token</a></li> <li><a href="#/realms/{{realm.id}}/token-settings">Token</a></li>
@ -34,115 +35,41 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="enabled" class="control-label">Enabled</label> <label for="enabled" class="control-label">Enabled</label>
<input ng-model="realm.enabled" name="enabled" id="enabled" onoffswitch />
<div class="onoffswitch">
<input type="checkbox" data-ng-model="realm.enabled" class="onoffswitch-checkbox"
name="enabled" id="enabled">
<label for="enabled" class="onoffswitch-label">
<span class="onoffswitch-inner">
<span class="onoffswitch-active">ON</span>
<span class="onoffswitch-inactive">OFF</span>
</span>
<span class="onoffswitch-switch"></span>
</label>
</div>
</div> </div>
</fieldset> </fieldset>
<fieldset> <fieldset>
<legend uncollapsed><span class="text">Login Options</span></legend> <legend uncollapsed><span class="text">Login Options</span></legend>
<div class="form-group clearfix block"> <div class="form-group clearfix block">
<label for="social" class="control-label">Social login</label> <label for="social" class="control-label">Social login</label>
<div class="onoffswitch"> <input ng-model="realm.social" name="social" id="social" onoffswitch />
<input type="checkbox" data-ng-model="realm.social" class="onoffswitch-checkbox" name="social" id="social">
<label for="social" class="onoffswitch-label">
<span class="onoffswitch-inner">
<span class="onoffswitch-active">ON</span>
<span class="onoffswitch-inactive">OFF</span>
</span>
<span class="onoffswitch-switch"></span>
</label>
</div>
</div> </div>
<div class="form-group clearfix block"> <div class="form-group clearfix block">
<label for="registrationAllowed" class="control-label">User registration</label> <label for="registrationAllowed" class="control-label">User registration</label>
<div class="onoffswitch"> <input ng-model="realm.registrationAllowed" name="registrationAllowed" id="registrationAllowed" onoffswitch />
<input type="checkbox" data-ng-model="realm.registrationAllowed" class="onoffswitch-checkbox" name="registrationAllowed" id="registrationAllowed">
<label for="registrationAllowed" class="onoffswitch-label">
<span class="onoffswitch-inner">
<span class="onoffswitch-active">ON</span>
<span class="onoffswitch-inactive">OFF</span>
</span>
<span class="onoffswitch-switch"></span>
</label>
</div>
</div> </div>
<div class="form-group clearfix block"> <div class="form-group clearfix block">
<label for="resetPasswordAllowed" class="control-label">Reset password</label> <label for="resetPasswordAllowed" class="control-label">Reset password</label>
<div class="onoffswitch"> <input ng-model="realm.resetPasswordAllowed" name="resetPasswordAllowed" id="resetPasswordAllowed" onoffswitch />
<input type="checkbox" data-ng-model="realm.resetPasswordAllowed" class="onoffswitch-checkbox" name="resetPasswordAllowed" id="resetPasswordAllowed">
<label for="resetPasswordAllowed" class="onoffswitch-label">
<span class="onoffswitch-inner">
<span class="onoffswitch-active">ON</span>
<span class="onoffswitch-inactive">OFF</span>
</span>
<span class="onoffswitch-switch"></span>
</label>
</div>
</div> </div>
<div class="form-group clearfix block"> <div class="form-group clearfix block">
<label for="verifyEmail" class="control-label">Verify email</label> <label for="verifyEmail" class="control-label">Verify email</label>
<div class="onoffswitch"> <input ng-model="realm.verifyEmail" name="verifyEmail" id="verifyEmail" onoffswitch />
<input type="checkbox" data-ng-model="realm.verifyEmail" class="onoffswitch-checkbox" name="verifyEmail" id="verifyEmail">
<label for="verifyEmail" class="onoffswitch-label">
<span class="onoffswitch-inner">
<span class="onoffswitch-active">ON</span>
<span class="onoffswitch-inactive">OFF</span>
</span>
<span class="onoffswitch-switch"></span>
</label>
</div>
</div> </div>
<div class="form-group clearfix block"> <div class="form-group clearfix block">
<label for="accountManagement" class="control-label two-lines">User account management</label> <label for="accountManagement" class="control-label two-lines">User account management</label>
<div class="onoffswitch"> <input ng-model="realm.accountManagement" name="accountManagement" id="accountManagement" onoffswitch />
<input type="checkbox" data-ng-model="realm.accountManagement" class="onoffswitch-checkbox"
name="accountManagement" id="accountManagement">
<label for="accountManagement" class="onoffswitch-label">
<span class="onoffswitch-inner">
<span class="onoffswitch-active">ON</span>
<span class="onoffswitch-inactive">OFF</span>
</span>
<span class="onoffswitch-switch"></span>
</label>
</div>
</div> </div>
<div class="form-group clearfix block"> <div class="form-group clearfix block">
<label for="requireSsl" class="control-label">Require SSL</label> <label for="requireSsl" class="control-label">Require SSL</label>
<div class="onoffswitch"> <input ng-model="realm.requireSsl" name="requireSsl" id="requireSsl" onoffswitch />
<input type="checkbox" data-ng-model="realm.requireSsl" class="onoffswitch-checkbox" name="requireSsl" id="requireSsl">
<label for="requireSsl" class="onoffswitch-label">
<span class="onoffswitch-inner">
<span class="onoffswitch-active">ON</span>
<span class="onoffswitch-inactive">OFF</span>
</span>
<span class="onoffswitch-switch"></span>
</label>
</div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="cookieLoginAllowed" class="control-label">Cookie login allowed</label> <label for="cookieLoginAllowed" class="control-label">Cookie login allowed</label>
<div class="onoffswitch"> <input ng-model="realm.cookieLoginAllowed" name="cookieLoginAllowed" id="cookieLoginAllowed" onoffswitch />
<input type="checkbox" data-ng-model="realm.cookieLoginAllowed" class="onoffswitch-checkbox" name="cookieLoginAllowed" id="cookieLoginAllowed">
<label for="cookieLoginAllowed" class="onoffswitch-label">
<span class="onoffswitch-inner">
<span class="onoffswitch-active">ON</span>
<span class="onoffswitch-inactive">OFF</span>
</span>
<span class="onoffswitch-switch"></span>
</label>
</div>
</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="primary" data-ng-show="changed">Save <button type="submit" data-ng-click="save()" class="primary" data-ng-show="changed">Save
</button> </button>

View file

@ -1,5 +1,5 @@
<ul data-ng-hide="createRealm"> <ul data-ng-hide="createRealm">
<li data-ng-class="(!path[2] || path[1] == 'role' || path[2] == 'roles' || path[2] == 'token-settings' || path[2] == 'social-settings' || path[2] == 'required-credentials') && 'active'"><a href="#/realms/{{realm.id}}">Settings</a></li> <li data-ng-class="(!path[2] || path[1] == 'role' || path[2] == 'roles' || path[2] == 'token-settings' || path[2] == 'social-settings' || path[2] == 'required-credentials' || path[2] == 'smtp-settings') && 'active'"><a href="#/realms/{{realm.id}}">Settings</a></li>
<li data-ng-class="(path[2] == 'users' || path[1] == 'user') && 'active'"><a href="#/realms/{{realm.id}}/users">Users</a> <li data-ng-class="(path[2] == 'users' || path[1] == 'user') && 'active'"><a href="#/realms/{{realm.id}}/users">Users</a>
</li> </li>
<li data-ng-class="(path[2] == 'applications' || path[1] == 'application') && 'active'"><a href="#/realms/{{realm.id}}/applications">Applications</a></li> <li data-ng-class="(path[2] == 'applications' || path[1] == 'application') && 'active'"><a href="#/realms/{{realm.id}}/applications">Applications</a></li>

View file

@ -0,0 +1,95 @@
<div id="wrapper" class="container">
<div class="row">
<div class="bs-sidebar col-md-3 clearfix" data-ng-include data-src="'partials/realm-menu.html'"></div>
<div id="content-area" class="col-md-9" role="main">
<div class="top-nav" data-ng-hide="createRealm">
<ul class="rcue-tabs">
<li><a href="#/realms/{{realm.id}}">General</a></li>
<li data-ng-show="realm.social"><a href="#/realms/{{realm.id}}/social-settings">Social</a></li>
<li class="active"><a href="#/realms/{{realm.id}}/registration-settings">Registration</a></li>
<li><a href="#/realms/{{realm.id}}/roles">Roles</a></li>
<li><a href="#/realms/{{realm.id}}/required-credentials">Credentials</a></li>
<li><a href="#/realms/{{realm.id}}/token-settings">Token</a></li>
<li><a href="#/realms/{{realm.id}}/smtp-settings">SMTP</a></li>
</ul>
</div>
<div id="content">
<ol class="breadcrumb">
<li><a href="#/realms/{{realm.id}}">{{realm.realm}}</a></li>
<li><a href="#/realms/{{realm.id}}">Settings</a></li>
<li class="active">Registration</li>
</ol>
<h2><span>{{realm.realm}}</span> Registration Settings</h2>
<form name="realmForm" novalidate>
<fieldset>
<legend uncollapsed><span class="text">Realm Default Roles</span> </legend>
<div class="form-group">
<div class="controls changing-selectors">
<div class="select-title">
<label for="available">Available Roles</label>
<select id="available" class="form-control" multiple size="5"
ng-multiple="true"
ng-model="selectedRealmRoles"
ng-options="r for r in availableRealmRoles">
</select>
</div>
<div class="middle-buttons">
<button type="submit" ng-click="addRealmDefaultRole()" tooltip="Move right" tooltip-placement="right"><span class="icon-arrow-right">Move right</span></button>
<button type="submit" ng-click="rmRealmDefaultRole()" tooltip="Move left" tooltip-placement="right"><span class="icon-arrow-left">Move left</span></button>
</div>
<div class="select-title">
<label for="assigned">Realm Default Roles</label>
<select id="assigned" class="form-control" multiple size=5
ng-multiple="true"
ng-model="selectedRealmDefRoles"
ng-options="r for r in realm.defaultRoles">
</select>
</div>
</div>
</div>
</fieldset>
<fieldset ng-show="applications.length > 0">
<legend uncollapsed><span class="text">Application Default Roles</span> </legend>
<div class="form-group input-select">
<label for="applications">Application</label>
<div class="input-group">
<div class="select-rcue">
<select id="applications" name="applications" ng-change="changeApplication()" ng-model="application" ng-options="a.name for a in applications">
<option value="" selected> Select an Application...</option>
</select>
</div>
</div>
</div>
<div class="form-group" ng-show="application">
<div class="controls changing-selectors application">
<div class="select-title">
<label for="available-app">Available Roles</label>
<select id="available-app" class="form-control" multiple size="5"
ng-multiple="true"
ng-model="selectedAppRoles"
ng-options="r for r in availableAppRoles">
</select>
</div>
<div class="middle-buttons">
<button type="submit" ng-click="addAppDefaultRole()" tooltip="Move right" tooltip-placement="right"><span class="icon-arrow-right">Move right</span></button>
<button type="submit" ng-click="rmAppDefaultRole()" tooltip="Move left" tooltip-placement="right"><span class="icon-arrow-left">Move left</span></button>
</div>
<div class="select-title">
<label for="assigned-app">Assigned Default Roles</label>
<select id="assigned-app" class="form-control" multiple size=5
ng-multiple="true"
ng-model="selectedAppDefRoles"
ng-options="r for r in application.defaultRoles">
</select>
</div>
</div>
</div>
</fieldset>
</form>
</div>
</div>
<div id="container-right-bg"></div>
</div>
</div>

View file

@ -6,6 +6,7 @@
<ul class="rcue-tabs"> <ul class="rcue-tabs">
<li><a href="#/realms/{{realm.id}}">General</a></li> <li><a href="#/realms/{{realm.id}}">General</a></li>
<li data-ng-show="realm.social"><a href="#/realms/{{realm.id}}/social-settings">Social</a></li> <li data-ng-show="realm.social"><a href="#/realms/{{realm.id}}/social-settings">Social</a></li>
<li data-ng-show="realm.registrationAllowed"><a href="#/realms/{{realm.id}}/registration-settings">Registration</a></li>
<li><a href="#/realms/{{realm.id}}/roles">Roles</a></li> <li><a href="#/realms/{{realm.id}}/roles">Roles</a></li>
<li><a href="#/realms/{{realm.id}}/required-credentials">Credentials</a></li> <li><a href="#/realms/{{realm.id}}/required-credentials">Credentials</a></li>
<li><a href="#/realms/{{realm.id}}/token-settings">Token</a></li> <li><a href="#/realms/{{realm.id}}/token-settings">Token</a></li>
@ -42,45 +43,18 @@
</div> </div>
<div class="form-group clearfix"> <div class="form-group clearfix">
<label for="smtpSSL" class="control-label">Enable SSL</label> <label for="smtpSSL" class="control-label">Enable SSL</label>
<div class="onoffswitch"> <input ng-model="realm.smtpServer.ssl" name="smtpSSL" id="smtpSSL" onoffswitch />
<input type="checkbox" data-ng-model="realm.smtpServer.ssl" class="onoffswitch-checkbox" name="smtpSSL" id="smtpSSL">
<label for="smtpSSL" class="onoffswitch-label">
<span class="onoffswitch-inner">
<span class="onoffswitch-active">ON</span>
<span class="onoffswitch-inactive">OFF</span>
</span>
<span class="onoffswitch-switch"></span>
</label>
</div>
</div> </div>
<div class="form-group clearfix"> <div class="form-group clearfix">
<label for="smtpStartTLS" class="control-label">Enable StartTLS</label> <label for="smtpStartTLS" class="control-label">Enable StartTLS</label>
<div class="onoffswitch"> <input ng-model="realm.smtpServer.starttls" name="smtpStartTLS" id="smtpStartTLS" onoffswitch />
<input type="checkbox" data-ng-model="realm.smtpServer.starttls" class="onoffswitch-checkbox" name="smtpStartTLS" id="smtpStartTLS">
<label for="smtpStartTLS" class="onoffswitch-label">
<span class="onoffswitch-inner">
<span class="onoffswitch-active">ON</span>
<span class="onoffswitch-inactive">OFF</span>
</span>
<span class="onoffswitch-switch"></span>
</label>
</div>
</div> </div>
</fieldset> </fieldset>
<fieldset> <fieldset>
<legend collapsed><span class="text">Authentication</span></legend> <legend collapsed><span class="text">Authentication</span></legend>
<div class="form-group clearfix"> <div class="form-group clearfix">
<label for="smtpAuth" class="control-label">Enabled</label> <label for="smtpAuth" class="control-label">Enabled</label>
<div class="onoffswitch"> <input ng-model="realm.smtpServer.auth" name="smtpAuth" id="smtpAuth" onoffswitch />
<input type="checkbox" data-ng-model="realm.smtpServer.auth" class="onoffswitch-checkbox" name="smtpAuth" id="smtpAuth">
<label for="smtpAuth" class="onoffswitch-label">
<span class="onoffswitch-inner">
<span class="onoffswitch-active">ON</span>
<span class="onoffswitch-inactive">OFF</span>
</span>
<span class="onoffswitch-switch"></span>
</label>
</div>
</div> </div>
<div class="form-group clearfix"> <div class="form-group clearfix">
<label for="smtpUsername" class="control-label">Username <span class="required" ng-show="realm.smtpServer.auth">*</span></label> <label for="smtpUsername" class="control-label">Username <span class="required" ng-show="realm.smtpServer.auth">*</span></label>

View file

@ -6,6 +6,7 @@
<ul class="rcue-tabs"> <ul class="rcue-tabs">
<li><a href="#/realms/{{realm.id}}">General</a></li> <li><a href="#/realms/{{realm.id}}">General</a></li>
<li class="active" data-ng-show="realm.social"><a href="#/realms/{{realm.id}}/social-settings">Social</a></li> <li class="active" data-ng-show="realm.social"><a href="#/realms/{{realm.id}}/social-settings">Social</a></li>
<li data-ng-show="realm.registrationAllowed"><a href="#/realms/{{realm.id}}/registration-settings">Registration</a></li>
<li><a href="#/realms/{{realm.id}}/roles">Roles</a></li> <li><a href="#/realms/{{realm.id}}/roles">Roles</a></li>
<li><a href="#/realms/{{realm.id}}/required-credentials">Credentials</a></li> <li><a href="#/realms/{{realm.id}}/required-credentials">Credentials</a></li>
<li><a href="#/realms/{{realm.id}}/token-settings">Token</a></li> <li><a href="#/realms/{{realm.id}}/token-settings">Token</a></li>
@ -30,7 +31,8 @@
<div class="actions"> <div class="actions">
<div class="select-rcue"> <div class="select-rcue">
<select ng-model="newProviderId" <select ng-model="newProviderId"
ng-options="p for p in unsetProviders"></select> ng-options="p as allProviders[p] for p in unsetProviders"
placeholder="Please select"></select>
</div> </div>
<div> <div>
<button ng-click="addProvider()" ng-disabled="">Add Provider</button> <button ng-click="addProvider()" ng-disabled="">Add Provider</button>
@ -49,16 +51,16 @@
<tr ng-repeat="pId in configuredProviders"> <tr ng-repeat="pId in configuredProviders">
<td> <td>
<div class="clearfix"> <div class="clearfix">
<input class="input-small disabled" type="text" placeholder="Key" value="{{pId}}" readonly> <input class="input-small disabled" type="text" value="{{allProviders[pId]}}" readonly>
</div> </div>
</td> </td>
<td> <td>
<input class="input-small" type="text" placeholder="Key" ng-model="realm.socialProviders[pId+'.key']" <input class="input-small" type="text" placeholder="Key" ng-model="realm.socialProviders[pId+'.key']"
ng-class="{'dirty': saveClicked}" required> ng-class="{'dirty': postSaveProviders.indexOf(pId) > -1}" required>
</td> </td>
<td> <td>
<input class="input-small" type="text" placeholder="Secret" ng-model="realm.socialProviders[pId+'.secret']" <input class="input-small" type="text" placeholder="Secret" ng-model="realm.socialProviders[pId+'.secret']"
ng-class="{'dirty': saveClicked}" required> ng-class="{'dirty': postSaveProviders.indexOf(pId) > -1}" required>
</td> </td>
<td> <td>
<div class="action-div"><i class="icon-question" ng-click="openHelp(pId)"></i></div> <div class="action-div"><i class="icon-question" ng-click="openHelp(pId)"></i></div>

View file

@ -6,6 +6,7 @@
<ul class="rcue-tabs"> <ul class="rcue-tabs">
<li><a href="#/realms/{{realm.id}}">General</a></li> <li><a href="#/realms/{{realm.id}}">General</a></li>
<li data-ng-show="realm.social"><a href="#/realms/{{realm.id}}/social-settings">Social</a></li> <li data-ng-show="realm.social"><a href="#/realms/{{realm.id}}/social-settings">Social</a></li>
<li data-ng-show="realm.registrationAllowed"><a href="#/realms/{{realm.id}}/registration-settings">Registration</a></li>
<li><a href="#/realms/{{realm.id}}/roles">Roles</a></li> <li><a href="#/realms/{{realm.id}}/roles">Roles</a></li>
<li><a href="#/realms/{{realm.id}}/required-credentials">Credentials</a></li> <li><a href="#/realms/{{realm.id}}/required-credentials">Credentials</a></li>
<li class="active"><a href="#/realms/{{realm.id}}/token-settings">Token</a></li> <li class="active"><a href="#/realms/{{realm.id}}/token-settings">Token</a></li>

View file

@ -6,6 +6,7 @@
<ul class="rcue-tabs"> <ul class="rcue-tabs">
<li><a href="#/realms/{{realm.id}}">General</a></li> <li><a href="#/realms/{{realm.id}}">General</a></li>
<li data-ng-show="realm.social"><a href="#/realms/{{realm.id}}/social-settings">Social</a></li> <li data-ng-show="realm.social"><a href="#/realms/{{realm.id}}/social-settings">Social</a></li>
<li data-ng-show="realm.registrationAllowed"><a href="#/realms/{{realm.id}}/registration-settings">Registration</a></li>
<li class="active"><a href="#/realms/{{realm.id}}/roles">Roles</a></li> <li class="active"><a href="#/realms/{{realm.id}}/roles">Roles</a></li>
<li><a href="#/realms/{{realm.id}}/required-credentials">Credentials</a></li> <li><a href="#/realms/{{realm.id}}/required-credentials">Credentials</a></li>
<li><a href="#/realms/{{realm.id}}/token-settings">Token</a></li> <li><a href="#/realms/{{realm.id}}/token-settings">Token</a></li>

View file

@ -14,7 +14,6 @@ public class ApplicationRepresentation {
protected String adminUrl; protected String adminUrl;
protected String baseUrl; protected String baseUrl;
protected boolean surrogateAuthRequired; protected boolean surrogateAuthRequired;
protected boolean useRealmMappings;
protected boolean enabled; protected boolean enabled;
protected List<CredentialRepresentation> credentials; protected List<CredentialRepresentation> credentials;
protected List<RoleRepresentation> roles; protected List<RoleRepresentation> roles;
@ -142,14 +141,6 @@ public class ApplicationRepresentation {
return this; return this;
} }
public boolean isUseRealmMappings() {
return useRealmMappings;
}
public void setUseRealmMappings(boolean useRealmMappings) {
this.useRealmMappings = useRealmMappings;
}
public List<String> getRedirectUris() { public List<String> getRedirectUris() {
return redirectUris; return redirectUris;
} }

View file

@ -33,6 +33,7 @@ public class RealmRepresentation {
protected Set<String> requiredApplicationCredentials; protected Set<String> requiredApplicationCredentials;
protected Set<String> requiredOAuthClientCredentials; protected Set<String> requiredOAuthClientCredentials;
protected List<UserRepresentation> users; protected List<UserRepresentation> users;
protected List<UserRepresentation> clients;
protected List<UserRoleMappingRepresentation> roleMappings; protected List<UserRoleMappingRepresentation> roleMappings;
protected List<ScopeMappingRepresentation> scopeMappings; protected List<ScopeMappingRepresentation> scopeMappings;
protected List<SocialMappingRepresentation> socialMappings; protected List<SocialMappingRepresentation> socialMappings;
@ -68,6 +69,10 @@ public class RealmRepresentation {
return users; return users;
} }
public List<UserRepresentation> getClients() {
return clients;
}
public List<ApplicationRepresentation> getApplications() { public List<ApplicationRepresentation> getApplications() {
return applications; return applications;
} }
@ -84,6 +89,10 @@ public class RealmRepresentation {
this.users = users; this.users = users;
} }
public void setClients(List<UserRepresentation> clients) {
this.clients = clients;
}
public UserRepresentation user(String username) { public UserRepresentation user(String username) {
UserRepresentation user = new UserRepresentation(); UserRepresentation user = new UserRepresentation();
user.setUsername(username); user.setUsername(username);

2
dist/assembly.xml vendored
View file

@ -16,7 +16,7 @@
<excludes> <excludes>
<exclude>**/*.sh</exclude> <exclude>**/*.sh</exclude>
<exclude>domain/tmp/auth</exclude> <exclude>domain/tmp/auth</exclude>
<exclude>domain/tmp/auth</exclude> <exclude>standalone/tmp/auth</exclude>
<exclude>**/*-users.properties</exclude> <exclude>**/*-users.properties</exclude>
</excludes> </excludes>
</fileSet> </fileSet>

14
dist/build.xml vendored
View file

@ -1,19 +1,20 @@
<project name="keycloak-dist" basedir="."> <project name="keycloak-dist" basedir=".">
<target name="jboss"> <target name="wildfly">
<unzip src="${org.jboss.as:jboss-as-dist:zip}" dest="${project.build.directory}"/> <unzip src="${org.wildfly:wildfly-dist:zip}" dest="${project.build.directory}"/>
<chmod perm="755"> <chmod perm="755">
<fileset dir="${project.build.directory}/jboss-as-${jboss.version}/bin"> <fileset dir="${project.build.directory}/wildfly-${wildfly.version}/bin">
<include name="**/*.sh"/> <include name="**/*.sh"/>
</fileset> </fileset>
</chmod> </chmod>
<move todir="${build.target.dir}" overwrite="true"> <move todir="${build.target.dir}" overwrite="true">
<fileset dir="${project.build.directory}/jboss-as-${jboss.version}"> <fileset dir="${project.build.directory}/wildfly-${wildfly.version}">
<include name="**/*"/> <include name="**/*"/>
</fileset> </fileset>
</move> </move>
<delete dir="${project.build.directory}/jboss-as-${jboss.version}"/> <delete dir="${project.build.directory}/wildfly-${wildfly.version}"/>
</target> </target>
<!--
<target name="resteasy-modules"> <target name="resteasy-modules">
<get src="http://sourceforge.net/projects/resteasy/files/Resteasy%20JAX-RS/${resteasy.version}/resteasy-jaxrs-${resteasy.version}-all.zip" <get src="http://sourceforge.net/projects/resteasy/files/Resteasy%20JAX-RS/${resteasy.version}/resteasy-jaxrs-${resteasy.version}-all.zip"
dest="${project.build.directory}" skipexisting="true"/> dest="${project.build.directory}" skipexisting="true"/>
@ -27,11 +28,12 @@
<unzip src="${project.build.directory}/resteasy-jboss-modules-${resteasy.version}.zip" <unzip src="${project.build.directory}/resteasy-jboss-modules-${resteasy.version}.zip"
dest="${build.target.dir}/modules"/> dest="${build.target.dir}/modules"/>
</target> </target>
-->
<target name="keycloak-server"> <target name="keycloak-server">
<copy file="${org.keycloak:keycloak-server:war}" <copy file="${org.keycloak:keycloak-server:war}"
tofile="${build.target.dir}/standalone/deployments/auth-server.war" overwrite="true"/> tofile="${build.target.dir}/standalone/deployments/auth-server.war" overwrite="true"/>
</target> </target>
<target name="all" depends="jboss, resteasy-modules, keycloak-server"/> <target name="all" depends="wildfly, keycloak-server"/>
</project> </project>

34
dist/pom.xml vendored
View file

@ -35,9 +35,9 @@
<type>war</type> <type>war</type>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.jboss.as</groupId> <groupId>org.wildfly</groupId>
<artifactId>jboss-as-dist</artifactId> <artifactId>wildfly-dist</artifactId>
<version>${jboss.version}</version> <version>${wildfly.version}</version>
<type>zip</type> <type>zip</type>
</dependency> </dependency>
</dependencies> </dependencies>
@ -66,6 +66,34 @@
</executions> </executions>
</plugin> </plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>xml-maven-plugin</artifactId>
<version>1.0</version>
<executions>
<execution>
<id>generate-resources</id>
<phase>package</phase>
<goals>
<goal>transform</goal>
</goals>
<configuration>
<transformationSets>
<transformationSet>
<dir>${build.target.dir}/standalone/configuration</dir>
<stylesheet>src/main/xslt/standalone.xsl</stylesheet>
<includes>
<include>standalone*.xml</include>
</includes>
<outputDir>${build.target.dir}/standalone/configuration</outputDir>
</transformationSet>
</transformationSets>
<targetDirectory>${project.build.directory}</targetDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId> <artifactId>maven-assembly-plugin</artifactId>

33
dist/src/main/xslt/standalone.xsl vendored Normal file
View file

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xalan="http://xml.apache.org/xalan"
xmlns:j="urn:jboss:domain:1.3"
version="2.0"
exclude-result-prefixes="xalan j">
<xsl:param name="config"/>
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" standalone="no"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()[name(.)='datasources']">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
<datasource jndi-name="java:jboss/datasources/KeycloakDS" pool-name="KeycloakDS" enabled="true" use-java-context="true">
<connection-url>jdbc:h2:${jboss.server.data.dir}/keycloak;DB_CLOSE_DELAY=-1</connection-url>
<driver>h2</driver>
<security>
<user-name>sa</user-name>
<password>sa</password>
</security>
</datasource>
</xsl:copy>
</xsl:template>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

View file

@ -112,6 +112,11 @@
<version>4.1</version> <version>4.1</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<scope>provided</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>

View file

@ -26,7 +26,9 @@
{ "type" : "password", { "type" : "password",
"value" : "password" } "value" : "password" }
] ]
}, }
],
"clients" : [
{ {
"username" : "third-party", "username" : "third-party",
"enabled": true, "enabled": true,
@ -50,10 +52,6 @@
{ {
"username": "bburke@redhat.com", "username": "bburke@redhat.com",
"roles": ["user"] "roles": ["user"]
},
{
"username": "third-party",
"roles": ["KEYCLOAK_IDENTITY_REQUESTER"]
} }
], ],
"scopeMappings": [ "scopeMappings": [
@ -67,7 +65,6 @@
"name": "customer-portal", "name": "customer-portal",
"enabled": true, "enabled": true,
"adminUrl": "http://localhost:8080/customer-portal/j_admin_request", "adminUrl": "http://localhost:8080/customer-portal/j_admin_request",
"useRealmMappings": true,
"credentials": [ "credentials": [
{ {
"type": "password", "type": "password",
@ -79,7 +76,6 @@
"name": "product-portal", "name": "product-portal",
"enabled": true, "enabled": true,
"adminUrl": "http://localhost:8080/product-portal/j_admin_request", "adminUrl": "http://localhost:8080/product-portal/j_admin_request",
"useRealmMappings": true,
"credentials": [ "credentials": [
{ {
"type": "password", "type": "password",
@ -88,4 +84,4 @@
] ]
} }
] ]
} }

View file

@ -21,10 +21,6 @@
<async-supported>true</async-supported> <async-supported>true</async-supported>
</servlet> </servlet>
<listener>
<listener-class>org.keycloak.services.listeners.MongoRunnerListener</listener-class>
</listener>
<filter> <filter>
<filter-name>Keycloak Session Management</filter-name> <filter-name>Keycloak Session Management</filter-name>
<filter-class>org.keycloak.services.filters.KeycloakSessionServletFilter</filter-class> <filter-class>org.keycloak.services.filters.KeycloakSessionServletFilter</filter-class>

View file

@ -95,6 +95,11 @@
<version>4.1</version> <version>4.1</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<scope>provided</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>

View file

@ -26,7 +26,9 @@
{ "type" : "password", { "type" : "password",
"value" : "password" } "value" : "password" }
] ]
}, }
],
"clients" : [
{ {
"username" : "third-party", "username" : "third-party",
"enabled": true, "enabled": true,
@ -50,10 +52,6 @@
{ {
"username": "bburke@redhat.com", "username": "bburke@redhat.com",
"roles": ["user"] "roles": ["user"]
},
{
"username": "third-party",
"roles": ["KEYCLOAK_IDENTITY_REQUESTER"]
} }
], ],
"scopeMappings": [ "scopeMappings": [
@ -67,7 +65,6 @@
"name": "customer-portal", "name": "customer-portal",
"enabled": true, "enabled": true,
"adminUrl": "http://localhost:8080/customer-portal/j_admin_request", "adminUrl": "http://localhost:8080/customer-portal/j_admin_request",
"useRealmMappings": true,
"webOrigins" : [ "http://localhost1:8080"], "webOrigins" : [ "http://localhost1:8080"],
"credentials": [ "credentials": [
{ {
@ -80,7 +77,6 @@
"name": "product-portal", "name": "product-portal",
"enabled": true, "enabled": true,
"adminUrl": "http://localhost:8080/product-portal/j_admin_request", "adminUrl": "http://localhost:8080/product-portal/j_admin_request",
"useRealmMappings": true,
"credentials": [ "credentials": [
{ {
"type": "password", "type": "password",
@ -89,4 +85,4 @@
] ]
} }
] ]
} }

View file

@ -48,7 +48,6 @@
"name": "test-app", "name": "test-app",
"enabled": true, "enabled": true,
"adminUrl": "http://localhost:8081/app/logout", "adminUrl": "http://localhost:8081/app/logout",
"useRealmMappings": true,
"webOrigins": [ "http://localhost", "http://localhost:8000", "http://localhost:8080" ], "webOrigins": [ "http://localhost", "http://localhost:8000", "http://localhost:8080" ],
"credentials": [ "credentials": [
{ {

View file

@ -38,19 +38,7 @@
<artifactId>resteasy-jaxrs</artifactId> <artifactId>resteasy-jaxrs</artifactId>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
</dependency>
<dependency>
<groupId>org.freemarker</groupId> <groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId> <artifactId>freemarker</artifactId>
</dependency> </dependency>

View file

@ -104,7 +104,7 @@ public class FormServiceImpl implements FormService {
Configuration cfg = new Configuration(); Configuration cfg = new Configuration();
try { try {
cfg.setClassForTemplateLoading(FormServiceImpl.class,"/META-INF/resources"); cfg.setClassForTemplateLoading(FormServiceImpl.class,"/META-INF/resources/forms/theme/default");
Template template = cfg.getTemplate(temp); Template template = cfg.getTemplate(temp);
template.process(input, out); template.process(input, out);

View file

@ -1 +0,0 @@
<#include "./theme/" + template.theme + "/access.ftl">

View file

@ -1 +0,0 @@
<#include "./theme/" + template.theme + "/account.ftl">

View file

@ -1 +0,0 @@
<#include "./theme/" + template.theme + "/error.ftl">

View file

@ -1 +0,0 @@
<#include "./theme/" + template.theme + "/login-config-totp.ftl">

View file

@ -1 +0,0 @@
<#include "./theme/" + template.theme + "/login-oauth-grant.ftl">

View file

@ -1 +0,0 @@
<#include "./theme/" + template.theme + "/login-reset-password.ftl">

View file

@ -1 +0,0 @@
<#include "./theme/" + template.theme + "/login-totp.ftl">

View file

@ -1 +0,0 @@
<#include "./theme/" + template.theme + "/login-update-password.ftl">

View file

@ -1 +0,0 @@
<#include "./theme/" + template.theme + "/login-update-profile.ftl">

View file

@ -1 +0,0 @@
<#include "./theme/" + template.theme + "/login-verify-email.ftl">

View file

@ -1 +0,0 @@
<#include "./theme/" + template.theme + "/login.ftl">

View file

@ -1 +0,0 @@
<#include "./theme/" + template.theme + "/password.ftl">

View file

@ -1 +0,0 @@
<#include "./theme/" + template.theme + "/register.ftl">

View file

@ -1 +0,0 @@
<#include "./theme/" + template.theme + "/social.ftl">

View file

@ -71,7 +71,7 @@ body {
width: auto; width: auto;
position: relative; position: relative;
} }
.rcue-login-register .background-area .separator .section, .rcue-login-register .background-area .section,
.rcue-login-register .background-area .social .section { .rcue-login-register .background-area .social .section {
padding-top: 1.5em; padding-top: 1.5em;
padding-bottom: 1.5em; padding-bottom: 1.5em;
@ -303,10 +303,24 @@ a.zocial:before {
.rcue-login-register .background-area .content-area ul li { .rcue-login-register .background-area .content-area ul li {
border-top: 1px solid #34393C; border-top: 1px solid #34393C;
padding: 2em; padding: 2em;
position: relative;
} }
.rcue-login-register .background-area .content-area ul li span:first-child { .rcue-login-register .background-area .content-area ul li span {
font-size: 1.3em; font-size: 1.3em;
line-height: 1.3em;
} }
.rcue-login-register .background-area .content-area ul li span:first-child {
padding-right: 11.5384615384615em;
}
.rcue-login-register .background-area .content-area ul li span.parent {
position: absolute;
left: 26em;
top: 1.53846153846154em;
width: 12.3076923076923em;
}
.rcue-login-register .background-area .content-area ul li span.icon-info { .rcue-login-register .background-area .content-area ul li span.icon-info {
float: right; float: right;
margin-top: 0.5em; margin-top: 0.5em;

View file

@ -19,17 +19,13 @@
</li> </li>
</#list> </#list>
<#list oauth.resourceRolesRequested?keys as resourceRole> <#list oauth.resourceRolesRequested?keys as resource>
<li> <#list oauth.resourceRolesRequested[resource] as role>
<strong>${resourceRole}</strong> <li>
<ul> <span><#if role.description??>${role.description}<#else>${role.name}</#if></span>
<#list oauth.resourceRolesRequested[resourceRole] as role> <span class="parent">in <strong>${resource}</strong></span>
<li> </li>
<span><#if role.description??>${role.description}<#else>${role.name}</#if></span> </#list>
</li>
</#list>
</ul>
</li>
</#list> </#list>
</ul> </ul>

View file

@ -1,5 +1,5 @@
<#import "template-login-action.ftl" as layout> <#import "template-login-action.ftl" as layout>
<@layout.registrationLayout bodyClass="reset"; section> <@layout.registrationLayout bodyClass="reset" isSeparator=true forceSeparator=true; section>
<#if section = "title"> <#if section = "title">
${rb.getString('emailForgotHeader')} ${rb.getString('emailForgotHeader')}
@ -23,6 +23,6 @@
</form> </form>
</div> </div>
<#elseif section = "info" > <#elseif section = "info" >
<p><a href="#">&laquo; Back to Login</a></p> <p><a href="${url.loginUrl}">&laquo; Back to Login</a></p>
</#if> </#if>
</@layout.registrationLayout> </@layout.registrationLayout>

View file

@ -1,5 +1,5 @@
<#import "template-login-action.ftl" as layout> <#import "template-login-action.ftl" as layout>
<@layout.registrationLayout bodyClass="reset"; section> <@layout.registrationLayout bodyClass="reset" isSeparator=false forceSeparator=true; section>
<#if section = "title"> <#if section = "title">
${rb.getString('emailUpdateHeader')} ${rb.getString('emailUpdateHeader')}

View file

@ -1,5 +1,5 @@
<#import "template-login-action.ftl" as layout> <#import "template-login-action.ftl" as layout>
<@layout.registrationLayout bodyClass=""; section> <@layout.registrationLayout bodyClass="" isSeparator=false forceSeparator=true; section>
<#if section = "title"> <#if section = "title">
Update Account Information Update Account Information

View file

@ -1,5 +1,5 @@
<#import "template-login-action.ftl" as layout> <#import "template-login-action.ftl" as layout>
<@layout.registrationLayout bodyClass="email"; section> <@layout.registrationLayout bodyClass="email" isSeparator=false forceSeparator=true; section>
<#if section = "title"> <#if section = "title">
Email verification Email verification

View file

@ -1,4 +1,4 @@
<#macro registrationLayout bodyClass isErrorPage=false> <#macro registrationLayout bodyClass isErrorPage=false isSeparator=false forceSeparator=false>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"> <html xmlns="http://www.w3.org/1999/xhtml">
@ -7,6 +7,7 @@
<title> <title>
<#nested "title"> <#nested "title">
</title> </title>
<link rel="icon" href="${template.formsPath}/theme/${template.theme}/img/favicon.ico">
<link href="${template.themeConfig.styles}" rel="stylesheet" /> <link href="${template.themeConfig.styles}" rel="stylesheet" />
<style type="text/css"> <style type="text/css">
body.rcue-login-register { body.rcue-login-register {
@ -25,7 +26,7 @@
</div> </div>
<#if (template.themeConfig.logo)?has_content> <#if (template.themeConfig.logo)?has_content>
<h1> <h1>
<a href="#" title="Go to the login page"><img src="${template.themeConfig.logo}" alt="Red Hat Logo" /></a> <a href="${url.loginUrl}" title="Go to the login page"><img src="${template.themeConfig.logo}" alt="Red Hat Logo" /></a>
</h1> </h1>
</#if> </#if>
@ -35,7 +36,12 @@
</h2> </h2>
<div class="background-area"> <div class="background-area">
<div class="form-area clearfix"> <#if !forceSeparator && realm?has_content>
<#assign drawSeparator = realm.registrationAllowed>
<#else>
<#assign drawSeparator = isSeparator>
</#if>
<div class="form-area clearfix ${(drawSeparator)?string('separator','')}">
<div class="section app-form"> <div class="section app-form">
<#if !isErrorPage && message?has_content> <#if !isErrorPage && message?has_content>
<#if message.error> <#if message.error>

View file

@ -1,4 +1,4 @@
<#macro registrationLayout bodyClass> <#macro registrationLayout bodyClass isSeparator=false forceSeparator=false>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"> <html xmlns="http://www.w3.org/1999/xhtml">
@ -7,6 +7,7 @@
<title> <title>
<#nested "title"> <#nested "title">
</title> </title>
<link rel="icon" href="${template.formsPath}/theme/${template.theme}/img/favicon.ico">
<link href="${template.themeConfig.styles}" rel="stylesheet" /> <link href="${template.themeConfig.styles}" rel="stylesheet" />
<style type="text/css"> <style type="text/css">
body.rcue-login-register { body.rcue-login-register {
@ -18,7 +19,7 @@
<body class="rcue-login-register ${bodyClass}"> <body class="rcue-login-register ${bodyClass}">
<#if (template.themeConfig.logo)?has_content> <#if (template.themeConfig.logo)?has_content>
<h1> <h1>
<a href="#" title="Go to the login page"><img src="${template.themeConfig.logo}" alt="Red Hat Logo" /></a> <a href="${url.loginUrl}" title="Go to the login page"><img src="${template.themeConfig.logo}" alt="Red Hat Logo" /></a>
</h1> </h1>
</#if> </#if>
@ -28,7 +29,12 @@
</h2> </h2>
<div class="background-area"> <div class="background-area">
<div class="form-area ${(realm.social)?string('social','')} clearfix"> <#if !forceSeparator && realm?has_content>
<#assign drawSeparator = realm.registrationAllowed>
<#else>
<#assign drawSeparator = isSeparator>
</#if>
<div class="form-area ${(realm.social && bodyClass != "register")?string('social','')} ${(drawSeparator)?string('separator','')} clearfix">
<div class="section app-form"> <div class="section app-form">
<h3>Application login area</h3> <h3>Application login area</h3>
<#if message?has_content && message.error> <#if message?has_content && message.error>

View file

@ -4,8 +4,7 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>Edit Account - <#nested "title"></title> <title>Edit Account - <#nested "title"></title>
<!-- TODO replace with actual logo once <link rel="icon" href="${template.formsPath}/theme/${template.theme}/img/favicon.ico">
<link rel="icon" href="img/favicon.ico">
<!-- Frameworks --> <!-- Frameworks -->
<link rel="stylesheet" href="${template.formsPath}/theme/${template.theme}/css/reset.css"> <link rel="stylesheet" href="${template.formsPath}/theme/${template.theme}/css/reset.css">

View file

@ -1 +0,0 @@
<#include "./theme/" + template.theme + "/totp.ftl">

View file

@ -22,15 +22,15 @@ passwordNewConfirm=New Password confirmation
authenticatorCode=One-time-password authenticatorCode=One-time-password
clientCertificate=Client Certificate clientCertificate=Client Certificate
invalidUser=Invalid username or password invalidUser=Invalid username or password.
invalidPassword=Invalid username or password invalidPassword=Invalid username or password.
accountDisabled=Account is disabled, contact admin accountDisabled=Account is disabled, contact admin
missingFirstName=Please specify first name missingFirstName=Please specify first name
missingLastName=Please specify last name missingLastName=Please specify last name
missingEmail=Please specify email missingEmail=Please specify email
missingUsername=Please specify username missingUsername=Please specify username
missingPassword=Please specify password missingPassword=Please specify password.
notMatchPassword=Passwords don't match notMatchPassword=Passwords don't match
missingTotp=Please specify authenticator code missingTotp=Please specify authenticator code
@ -49,7 +49,7 @@ actionTotpWarning=You need to set up the Google Authenticator to activate your a
actionProfileWarning=You need to update your user profile to activate your account. actionProfileWarning=You need to update your user profile to activate your account.
actionPasswordWarning=You need to change your password to activate your account. actionPasswordWarning=You need to change your password to activate your account.
actionEmailWarning=You need to verify your email address to activate your account. actionEmailWarning=You need to verify your email address to activate your account.
actionFollow=Please follow the steps below. actionFollow=Please fill in the fields below.
successHeader=Success! successHeader=Success!
errorHeader=Error! errorHeader=Error!

View file

@ -5,16 +5,14 @@ package org.keycloak.models;
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public interface Constants { public interface Constants {
String INTERNAL_ROLE = "KEYCLOAK_";
String ADMIN_REALM = "Keycloak Administration"; String ADMIN_REALM = "Keycloak Administration";
String ADMIN_CONSOLE_APPLICATION = "Admin Console"; String ADMIN_CONSOLE_APPLICATION = "Admin Console";
String ADMIN_CONSOLE_ADMIN_ROLE = "admin"; String ADMIN_CONSOLE_ADMIN_ROLE = "admin";
String APPLICATION_ROLE = "KEYCLOAK_APPLICATION"; String APPLICATION_ROLE = INTERNAL_ROLE + "_APPLICATION";
String IDENTITY_REQUESTER_ROLE = "KEYCLOAK_IDENTITY_REQUESTER"; String IDENTITY_REQUESTER_ROLE = INTERNAL_ROLE + "_IDENTITY_REQUESTER";
String WILDCARD_ROLE = "*";
String ACCOUNT_APPLICATION = "Account"; String ACCOUNT_APPLICATION = "Account";
String ACCOUNT_PROFILE_ROLE = "view-profile"; String ACCOUNT_PROFILE_ROLE = "view-profile";
String ACCOUNT_MANAGE_ROLE = "manage-account"; String ACCOUNT_MANAGE_ROLE = "manage-account";
String ACCOUNT_MANAGEMENT_APPLICATION = "Account Management";
} }

View file

@ -5,5 +5,7 @@ package org.keycloak.models;
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public interface ModelProvider { public interface ModelProvider {
String getId();
KeycloakSessionFactory createFactory(); KeycloakSessionFactory createFactory();
} }

View file

@ -84,6 +84,8 @@ public interface RealmModel extends RoleContainerModel, RoleMapperModel, ScopeMa
UserModel addUser(String username); UserModel addUser(String username);
boolean deleteUser(String name);
List<String> getDefaultRoles(); List<String> getDefaultRoles();
void addDefaultRole(String name); void addDefaultRole(String name);

View file

@ -34,6 +34,30 @@
<artifactId>hibernate-jpa-2.0-api</artifactId> <artifactId>hibernate-jpa-2.0-api</artifactId>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate.entitymanager.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies> </dependencies>
<build> <build>
<plugins> <plugins>

View file

@ -11,6 +11,12 @@ import javax.persistence.Persistence;
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class JpaModelProvider implements ModelProvider { public class JpaModelProvider implements ModelProvider {
@Override
public String getId() {
return "jpa";
}
@Override @Override
public KeycloakSessionFactory createFactory() { public KeycloakSessionFactory createFactory() {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpa-keycloak-identity-store"); EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpa-keycloak-identity-store");

View file

@ -11,6 +11,7 @@ import org.keycloak.models.SocialLinkModel;
import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import org.keycloak.models.jpa.entities.ApplicationEntity; import org.keycloak.models.jpa.entities.ApplicationEntity;
import org.keycloak.models.jpa.entities.ApplicationUserRoleMappingEntity;
import org.keycloak.models.jpa.entities.CredentialEntity; import org.keycloak.models.jpa.entities.CredentialEntity;
import org.keycloak.models.jpa.entities.OAuthClientEntity; import org.keycloak.models.jpa.entities.OAuthClientEntity;
import org.keycloak.models.jpa.entities.RealmEntity; import org.keycloak.models.jpa.entities.RealmEntity;
@ -456,6 +457,25 @@ public class RealmAdapter implements RealmModel {
return new UserAdapter(entity); return new UserAdapter(entity);
} }
@Override
public boolean deleteUser(String name) {
TypedQuery<UserEntity> query = em.createNamedQuery("getRealmUserByLoginName", UserEntity.class);
query.setParameter("loginName", name);
query.setParameter("realm", realm);
List<UserEntity> results = query.getResultList();
if (results.size() == 0) return false;
UserEntity user = results.get(0);
for (Class r : UserEntity.RELATIONSHIPS) {
em.createQuery("delete from " + r.getSimpleName() + " where user = :user").setParameter("user", user).executeUpdate();
}
em.remove(user);
return true;
}
@Override @Override
public List<String> getDefaultRoles() { public List<String> getDefaultRoles() {
Collection<RoleEntity> entities = realm.getDefaultRoles(); Collection<RoleEntity> entities = realm.getDefaultRoles();
@ -548,8 +568,6 @@ public class RealmAdapter implements RealmModel {
em.persist(applicationData); em.persist(applicationData);
em.flush(); em.flush();
ApplicationModel resource = new ApplicationAdapter(em, applicationData); ApplicationModel resource = new ApplicationAdapter(em, applicationData);
resource.addRole("*");
resource.addScopeMapping(new UserAdapter(user), "*");
em.flush(); em.flush();
return resource; return resource;
} }
@ -643,13 +661,13 @@ public class RealmAdapter implements RealmModel {
for (Map.Entry<String, String> entry : attributes.entrySet()) { for (Map.Entry<String, String> entry : attributes.entrySet()) {
String attribute = null; String attribute = null;
if (entry.getKey().equals(UserModel.LOGIN_NAME)) { if (entry.getKey().equals(UserModel.LOGIN_NAME)) {
attribute = "loginName"; attribute = "lower(loginName)";
} else if (entry.getKey().equalsIgnoreCase(UserModel.FIRST_NAME)) { } else if (entry.getKey().equalsIgnoreCase(UserModel.FIRST_NAME)) {
attribute = "firstName"; attribute = "lower(firstName)";
} else if (entry.getKey().equalsIgnoreCase(UserModel.LAST_NAME)) { } else if (entry.getKey().equalsIgnoreCase(UserModel.LAST_NAME)) {
attribute = "lastName"; attribute = "lower(lastName)";
} else if (entry.getKey().equalsIgnoreCase(UserModel.EMAIL)) { } else if (entry.getKey().equalsIgnoreCase(UserModel.EMAIL)) {
attribute = "email"; attribute = "lower(email)";
} }
if (attribute == null) continue; if (attribute == null) continue;
if (first) { if (first) {
@ -658,9 +676,10 @@ public class RealmAdapter implements RealmModel {
} else { } else {
builder.append(" and "); builder.append(" and ");
} }
builder.append(attribute).append("='").append(entry.getValue()).append("'"); builder.append(attribute).append(" like '%").append(entry.getValue().toLowerCase()).append("%'");
} }
TypedQuery<UserEntity> query = em.createQuery(builder.toString(), UserEntity.class); String q = builder.toString();
TypedQuery<UserEntity> query = em.createQuery(q, UserEntity.class);
List<UserEntity> results = query.getResultList(); List<UserEntity> results = query.getResultList();
List<UserModel> users = new ArrayList<UserModel>(); List<UserModel> users = new ArrayList<UserModel>();
for (UserEntity entity : results) users.add(new UserAdapter(entity)); for (UserEntity entity : results) users.add(new UserAdapter(entity));

View file

@ -4,6 +4,7 @@ import javax.persistence.CascadeType;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.FetchType; import javax.persistence.FetchType;
import javax.persistence.GeneratedValue; import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id; import javax.persistence.Id;
import javax.persistence.JoinTable; import javax.persistence.JoinTable;
import javax.persistence.OneToMany; import javax.persistence.OneToMany;
@ -18,7 +19,7 @@ import java.util.Collection;
@Entity @Entity
public class ApplicationEntity { public class ApplicationEntity {
@Id @Id
@GeneratedValue @GeneratedValue(strategy = GenerationType.IDENTITY)
private String id; private String id;
private String name; private String name;

View file

@ -2,6 +2,7 @@ package org.keycloak.models.jpa.entities;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.GeneratedValue; import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id; import javax.persistence.Id;
import javax.persistence.ManyToOne; import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries; import javax.persistence.NamedQueries;
@ -17,7 +18,7 @@ import javax.persistence.NamedQuery;
@Entity @Entity
public class CredentialEntity { public class CredentialEntity {
@Id @Id
@GeneratedValue @GeneratedValue(strategy = GenerationType.IDENTITY)
protected String id; protected String id;
protected String type; protected String type;

View file

@ -3,6 +3,7 @@ package org.keycloak.models.jpa.entities;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.FetchType; import javax.persistence.FetchType;
import javax.persistence.GeneratedValue; import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id; import javax.persistence.Id;
import javax.persistence.ManyToOne; import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries; import javax.persistence.NamedQueries;
@ -21,7 +22,7 @@ import javax.persistence.OneToOne;
@Entity @Entity
public class OAuthClientEntity { public class OAuthClientEntity {
@Id @Id
@GeneratedValue @GeneratedValue(strategy = GenerationType.IDENTITY)
private String id; private String id;
private String name; private String name;

View file

@ -2,6 +2,7 @@ package org.keycloak.models.jpa.entities;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.GeneratedValue; import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id; import javax.persistence.Id;
/** /**
@ -11,7 +12,7 @@ import javax.persistence.Id;
@Entity @Entity
public class RequiredCredentialEntity { public class RequiredCredentialEntity {
@Id @Id
@GeneratedValue @GeneratedValue(strategy = GenerationType.IDENTITY)
protected String id; protected String id;
protected String type; protected String type;

View file

@ -2,6 +2,7 @@ package org.keycloak.models.jpa.entities;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.GeneratedValue; import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id; import javax.persistence.Id;
/** /**
@ -11,7 +12,7 @@ import javax.persistence.Id;
@Entity @Entity
public class RoleEntity { public class RoleEntity {
@Id @Id
@GeneratedValue @GeneratedValue(strategy = GenerationType.IDENTITY)
private String id; private String id;
private String name; private String name;

View file

@ -2,6 +2,7 @@ package org.keycloak.models.jpa.entities;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.GeneratedValue; import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id; import javax.persistence.Id;
import javax.persistence.ManyToOne; import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries; import javax.persistence.NamedQueries;
@ -19,7 +20,7 @@ import javax.persistence.NamedQuery;
@Entity @Entity
public class SocialLinkEntity { public class SocialLinkEntity {
@Id @Id
@GeneratedValue @GeneratedValue(strategy = GenerationType.IDENTITY)
private long id; private long id;
@ManyToOne @ManyToOne

View file

@ -2,11 +2,13 @@ package org.keycloak.models.jpa.entities;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import javax.persistence.CascadeType;
import javax.persistence.CollectionTable; import javax.persistence.CollectionTable;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.ElementCollection; import javax.persistence.ElementCollection;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.GeneratedValue; import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id; import javax.persistence.Id;
import javax.persistence.ManyToOne; import javax.persistence.ManyToOne;
import javax.persistence.MapKeyColumn; import javax.persistence.MapKeyColumn;
@ -32,8 +34,11 @@ import java.util.Set;
}) })
@Entity @Entity
public class UserEntity { public class UserEntity {
public static final Class[] RELATIONSHIPS = new Class[] { ApplicationUserRoleMappingEntity.class, RealmUserRoleMappingEntity.class, SocialLinkEntity.class };
@Id @Id
@GeneratedValue @GeneratedValue(strategy = GenerationType.IDENTITY)
protected String id; protected String id;
protected String loginName; protected String loginName;
@ -65,7 +70,7 @@ public class UserEntity {
@CollectionTable @CollectionTable
protected Set<String> redirectUris = new HashSet<String>(); protected Set<String> redirectUris = new HashSet<String>();
@OneToMany @OneToMany(cascade = CascadeType.REMOVE, orphanRemoval = true)
protected Collection<CredentialEntity> credentials = new ArrayList<CredentialEntity>(); protected Collection<CredentialEntity> credentials = new ArrayList<CredentialEntity>();
public String getId() { public String getId() {

View file

@ -1,6 +1,7 @@
package org.keycloak.models.jpa.entities; package org.keycloak.models.jpa.entities;
import javax.persistence.GeneratedValue; import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id; import javax.persistence.Id;
import javax.persistence.ManyToOne; import javax.persistence.ManyToOne;
import javax.persistence.MappedSuperclass; import javax.persistence.MappedSuperclass;
@ -12,7 +13,7 @@ import javax.persistence.MappedSuperclass;
@MappedSuperclass @MappedSuperclass
public abstract class UserRoleMappingEntity { public abstract class UserRoleMappingEntity {
@Id @Id
@GeneratedValue @GeneratedValue(strategy = GenerationType.IDENTITY)
protected long id; protected long id;
@ManyToOne @ManyToOne
protected UserEntity user; protected UserEntity user;

View file

@ -0,0 +1 @@
org.keycloak.models.jpa.JpaModelProvider

View file

@ -0,0 +1,58 @@
package org.keycloak.models.mongo;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class PropertiesManager {
private static final String MONGO_HOST = "keycloak.mongodb.host";
private static final String MONGO_PORT = "keycloak.mongodb.port";
private static final String MONGO_DB_NAME = "keycloak.mongodb.databaseName";
private static final String MONGO_DROP_DB_ON_STARTUP = "keycloak.mongodb.dropDatabaseOnStartup";
private static final String BOOTSTRAP_EMBEDDED_MONGO_AT_CONTEXT_INIT = "keycloak.mongodb.bootstrapEmbeddedMongoAtContextInit";
// Port where embedded MongoDB will be started during keycloak bootstrap. Same port will be used by KeycloakApplication then
private static final int MONGO_DEFAULT_PORT_KEYCLOAK_WAR_EMBEDDED = 37017;
// Port where MongoDB instance is normally started on linux. This port should be used if we're not starting embedded instance (keycloak.mongodb.bootstrapEmbeddedMongoAtContextInit is false)
private static final int MONGO_DEFAULT_PORT_KEYCLOAK_WAR = 27017;
// Port where unit tests will start embedded MongoDB instance
public static final int MONGO_DEFAULT_PORT_UNIT_TESTS = 27777;
public static String getMongoHost() {
return System.getProperty(MONGO_HOST, "localhost");
}
public static void setMongoHost(String mongoHost) {
System.setProperty(MONGO_HOST, mongoHost);
}
public static int getMongoPort() {
return Integer.parseInt(System.getProperty(MONGO_PORT, String.valueOf(MONGO_DEFAULT_PORT_KEYCLOAK_WAR_EMBEDDED)));
}
public static void setMongoPort(int mongoPort) {
System.setProperty(MONGO_PORT, String.valueOf(mongoPort));
}
public static String getMongoDbName() {
return System.getProperty(MONGO_DB_NAME, "keycloak");
}
public static void setMongoDbName(String mongoMongoDbName) {
System.setProperty(MONGO_DB_NAME, mongoMongoDbName);
}
public static boolean dropDatabaseOnStartup() {
return Boolean.parseBoolean(System.getProperty(MONGO_DROP_DB_ON_STARTUP, "true"));
}
public static void setDropDatabaseOnStartup(boolean dropDatabaseOnStartup) {
System.setProperty(MONGO_DROP_DB_ON_STARTUP, String.valueOf(dropDatabaseOnStartup));
}
public static boolean bootstrapEmbeddedMongoAtContextInit() {
return isMongoSessionFactory() && Boolean.parseBoolean(System.getProperty(BOOTSTRAP_EMBEDDED_MONGO_AT_CONTEXT_INIT, "true"));
}
}

View file

@ -3,13 +3,31 @@ package org.keycloak.models.mongo.keycloak;
import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.ModelProvider; import org.keycloak.models.ModelProvider;
import java.lang.Override;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class MongoModelProvider implements ModelProvider { public class MongoModelProvider implements ModelProvider {
@Override
public String getId() {
return "mongo";
}
@Override @Override
public KeycloakSessionFactory createFactory() { public KeycloakSessionFactory createFactory() {
return null; //To change body of implemented methods use File | Settings | File Templates. String host = PropertiesManager.getMongoHost();
int port = PropertiesManager.getMongoPort();
String dbName = PropertiesManager.getMongoDbName();
boolean dropDatabaseOnStartup = PropertiesManager.dropDatabaseOnStartup();
// Create MongoDBSessionFactory via reflection now
try {
return new MongoDBSessionFactory(host, port, dbName, dropDatabaseOnStartup);
} catch (Exception e) {
throw new RuntimeException(e);
}
} }
} }

View file

@ -451,8 +451,6 @@ public class RealmAdapter implements RealmModel {
noSQL.saveObject(appData); noSQL.saveObject(appData);
ApplicationModel resource = new ApplicationAdapter(appData, noSQL); ApplicationModel resource = new ApplicationAdapter(appData, noSQL);
resource.addRole("*");
resource.addScopeMapping(resourceUser, "*");
return resource; return resource;
} }

View file

@ -0,0 +1 @@
org.keycloak.models.mongo.keycloak.MongoModelProvider

View file

@ -37,27 +37,22 @@
<dependency> <dependency>
<groupId>org.picketlink</groupId> <groupId>org.picketlink</groupId>
<artifactId>picketlink-idm-api</artifactId> <artifactId>picketlink-idm-api</artifactId>
<scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.picketlink</groupId> <groupId>org.picketlink</groupId>
<artifactId>picketlink-common</artifactId> <artifactId>picketlink-common</artifactId>
<scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.picketlink</groupId> <groupId>org.picketlink</groupId>
<artifactId>picketlink-idm-impl</artifactId> <artifactId>picketlink-idm-impl</artifactId>
<scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.picketlink</groupId> <groupId>org.picketlink</groupId>
<artifactId>picketlink-idm-simple-schema</artifactId> <artifactId>picketlink-idm-simple-schema</artifactId>
<scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.picketlink</groupId> <groupId>org.picketlink</groupId>
<artifactId>picketlink-config</artifactId> <artifactId>picketlink-config</artifactId>
<scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.hibernate.javax.persistence</groupId> <groupId>org.hibernate.javax.persistence</groupId>

View file

@ -37,6 +37,11 @@ public class PicketlinkModelProvider implements ModelProvider {
return new PicketlinkKeycloakSessionFactory(emf, buildPartitionManager()); return new PicketlinkKeycloakSessionFactory(emf, buildPartitionManager());
} }
@Override
public String getId() {
return "picketlink";
}
public static PartitionManager buildPartitionManager() { public static PartitionManager buildPartitionManager() {
IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder(); IdentityConfigurationBuilder builder = new IdentityConfigurationBuilder();

View file

@ -518,6 +518,16 @@ public class RealmAdapter implements RealmModel {
return new UserAdapter(user, getIdm()); return new UserAdapter(user, getIdm());
} }
@Override
public boolean deleteUser(String name) {
User user = findPicketlinkUser(name);
if (user == null) {
return false;
}
getIdm().remove(user);
return true;
}
@Override @Override
public RoleAdapter getRole(String name) { public RoleAdapter getRole(String name) {
Role role = SampleModel.getRole(getIdm(), name); Role role = SampleModel.getRole(getIdm(), name);
@ -615,8 +625,6 @@ public class RealmAdapter implements RealmModel {
resourceRelationship.setApplication(applicationData.getName()); resourceRelationship.setApplication(applicationData.getName());
getRelationshipManager().add(resourceRelationship); getRelationshipManager().add(resourceRelationship);
ApplicationModel resource = new ApplicationAdapter(applicationData, this, partitionManager); ApplicationModel resource = new ApplicationAdapter(applicationData, this, partitionManager);
resource.addRole("*");
resource.addScopeMapping(new UserAdapter(resourceUser, idm), "*");
return resource; return resource;
} }

View file

@ -0,0 +1 @@
org.keycloak.models.picketlink.PicketlinkModelProvider

View file

@ -20,7 +20,7 @@
<dom4j.version>1.6.1</dom4j.version> <dom4j.version>1.6.1</dom4j.version>
<mysql.version>5.1.25</mysql.version> <mysql.version>5.1.25</mysql.version>
<slf4j.version>1.6.1</slf4j.version> <slf4j.version>1.6.1</slf4j.version>
<jboss.version>7.1.1.Final</jboss.version> <wildfly.version>8.0.0.Beta1</wildfly.version>
</properties> </properties>
<url>http://keycloak.org</url> <url>http://keycloak.org</url>

View file

@ -45,11 +45,6 @@
<artifactId>keycloak-model-api</artifactId> <artifactId>keycloak-model-api</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-picketlink</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-model-jpa</artifactId> <artifactId>keycloak-model-jpa</artifactId>
@ -64,6 +59,24 @@
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-social-google</artifactId> <artifactId>keycloak-social-google</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
<exclusions>
<exclusion>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</exclusion>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
<exclusion>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</exclusion>
<exclusion>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
</exclusion>
</exclusions>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
@ -80,56 +93,15 @@
<artifactId>keycloak-forms</artifactId> <artifactId>keycloak-forms</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.picketlink</groupId>
<artifactId>picketlink-idm-api</artifactId>
</dependency>
<dependency>
<groupId>org.picketlink</groupId>
<artifactId>picketlink-idm-impl</artifactId>
</dependency>
<dependency>
<groupId>org.picketlink</groupId>
<artifactId>picketlink-idm-simple-schema</artifactId>
</dependency>
<dependency>
<groupId>org.picketlink</groupId>
<artifactId>picketlink-config</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency> <dependency>
<groupId>org.jboss.resteasy</groupId> <groupId>org.jboss.resteasy</groupId>
<artifactId>jaxrs-api</artifactId> <artifactId>jaxrs-api</artifactId>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.h2database</groupId> <groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>h2</artifactId> <artifactId>jboss-servlet-api_3.0_spec</artifactId>
<version>1.3.161</version> <scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.1</version>
<scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>

View file

@ -0,0 +1,8 @@
<jboss-deployment-structure>
<deployment>
<dependencies>
<module name="org.apache.httpcomponents"/>
<module name="org.codehaus.jackson.jackson-core-asl"/>
</dependencies>
</deployment>
</jboss-deployment-structure>

View file

@ -2,32 +2,26 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0"> version="1.0">
<persistence-unit name="keycloak-identity-store" transaction-type="RESOURCE_LOCAL"> <persistence-unit name="jpa-keycloak-identity-store" transaction-type="RESOURCE_LOCAL">
<jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source> <jta-data-source>java:jboss/datasources/KeycloakDS</jta-data-source>
<class>org.keycloak.models.jpa.entities.ApplicationEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.AttributedTypeEntity</class> <class>org.keycloak.models.jpa.entities.ApplicationScopeMappingEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.AccountTypeEntity</class> <class>org.keycloak.models.jpa.entities.ApplicationUserRoleMappingEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.RoleTypeEntity</class> <class>org.keycloak.models.jpa.entities.CredentialEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.GroupTypeEntity</class> <class>org.keycloak.models.jpa.entities.OAuthClientEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.IdentityTypeEntity</class> <class>org.keycloak.models.jpa.entities.RealmEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.RelationshipTypeEntity</class> <class>org.keycloak.models.jpa.entities.RealmScopeMappingEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.RelationshipIdentityTypeEntity</class> <class>org.keycloak.models.jpa.entities.RealmUserRoleMappingEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.PartitionTypeEntity</class> <class>org.keycloak.models.jpa.entities.RequiredCredentialEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.PasswordCredentialTypeEntity</class> <class>org.keycloak.models.jpa.entities.RoleEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.DigestCredentialTypeEntity</class> <class>org.keycloak.models.jpa.entities.SocialLinkEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.X509CredentialTypeEntity</class> <class>org.keycloak.models.jpa.entities.UserEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.OTPCredentialTypeEntity</class> <class>org.keycloak.models.jpa.entities.UserRoleMappingEntity</class>
<class>org.picketlink.idm.jpa.model.sample.simple.AttributeTypeEntity</class>
<class>org.keycloak.models.picketlink.mappings.RealmEntity</class>
<class>org.keycloak.models.picketlink.mappings.ApplicationEntity</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes> <exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties> <properties>
<property name="hibernate.hbm2ddl.auto" value="create" /> <property name="hibernate.hbm2ddl.auto" value="update" />
<property name="hibernate.show_sql" value="false" />
<property name="hibernate.format_sql" value="false" />
</properties> </properties>
</persistence-unit> </persistence-unit>
</persistence> </persistence>

View file

@ -20,6 +20,10 @@
<load-on-startup>1</load-on-startup> <load-on-startup>1</load-on-startup>
<async-supported>true</async-supported> <async-supported>true</async-supported>
</servlet> </servlet>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
<filter> <filter>
<filter-name>Keycloak Session Management</filter-name> <filter-name>Keycloak Session Management</filter-name>

View file

@ -31,14 +31,15 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-model-picketlink</artifactId> <artifactId>keycloak-model-jpa</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
<scope>provided</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-model-jpa</artifactId> <artifactId>keycloak-model-picketlink</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
<scope>test</scope>
</dependency> </dependency>
<!--<dependency> <!--<dependency>
@ -57,31 +58,6 @@
<artifactId>jboss-logging</artifactId> <artifactId>jboss-logging</artifactId>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>org.picketlink</groupId>
<artifactId>picketlink-idm-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.picketlink</groupId>
<artifactId>picketlink-common</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.picketlink</groupId>
<artifactId>picketlink-idm-impl</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.picketlink</groupId>
<artifactId>picketlink-idm-simple-schema</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.picketlink</groupId>
<artifactId>picketlink-config</artifactId>
<scope>provided</scope>
</dependency>
<dependency> <dependency>
<groupId>org.jboss.resteasy</groupId> <groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId> <artifactId>resteasy-jaxrs</artifactId>
@ -156,21 +132,6 @@
<artifactId>jackson-xc</artifactId> <artifactId>jackson-xc</artifactId>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>org.picketlink</groupId>
<artifactId>picketlink-common</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>provided</scope>
</dependency>
<dependency> <dependency>
<groupId>com.google.zxing</groupId> <groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId> <artifactId>javase</artifactId>
@ -183,7 +144,7 @@
<dependency> <dependency>
<groupId>org.hibernate.javax.persistence</groupId> <groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.0-api</artifactId> <artifactId>hibernate-jpa-2.0-api</artifactId>
<scope>provided</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.h2database</groupId> <groupId>com.h2database</groupId>

View file

@ -1,5 +1,6 @@
package org.keycloak.services.managers; package org.keycloak.services.managers;
import org.jboss.resteasy.logging.Logger;
import org.keycloak.models.ApplicationModel; import org.keycloak.models.ApplicationModel;
import org.keycloak.models.Constants; import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
@ -17,12 +18,15 @@ import java.util.UUID;
*/ */
public class ApplianceBootstrap { public class ApplianceBootstrap {
private static final Logger logger = Logger.getLogger(ApplianceBootstrap.class);
public void initKeycloakAdminRealm(RealmModel realm) {
}
public void bootstrap(KeycloakSession session) { public void bootstrap(KeycloakSession session) {
if (session.getRealm(Constants.ADMIN_REALM) != null) {
return;
}
logger.info("Initializing " + Constants.ADMIN_REALM + " realm");
RealmManager manager = new RealmManager(session); RealmManager manager = new RealmManager(session);
RealmModel realm = manager.createRealm(Constants.ADMIN_REALM, Constants.ADMIN_REALM); RealmModel realm = manager.createRealm(Constants.ADMIN_REALM, Constants.ADMIN_REALM);
realm.setName(Constants.ADMIN_REALM); realm.setName(Constants.ADMIN_REALM);
@ -37,7 +41,6 @@ public class ApplianceBootstrap {
realm.setCookieLoginAllowed(true); realm.setCookieLoginAllowed(true);
realm.setRegistrationAllowed(false); realm.setRegistrationAllowed(false);
manager.generateRealmKeys(realm); manager.generateRealmKeys(realm);
initKeycloakAdminRealm(realm);
ApplicationModel adminConsole = realm.addApplication(Constants.ADMIN_CONSOLE_APPLICATION); ApplicationModel adminConsole = realm.addApplication(Constants.ADMIN_CONSOLE_APPLICATION);
adminConsole.setEnabled(true); adminConsole.setEnabled(true);
@ -59,8 +62,6 @@ public class ApplianceBootstrap {
adminUser.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD); adminUser.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
adminConsole.grantRole(adminUser, adminRole); adminConsole.grantRole(adminUser, adminRole);
} }
} }

View file

@ -102,7 +102,6 @@ public class ApplicationManager {
} }
} }
} }
if (resourceRep.isUseRealmMappings()) realm.addScopeMapping(applicationModel.getApplicationUser(), "*");
return applicationModel; return applicationModel;
} }

View file

@ -68,7 +68,6 @@ public class RealmManager {
public RealmModel createRealm(String id, String name) { public RealmModel createRealm(String id, String name) {
RealmModel realm = identitySession.createRealm(id, name); RealmModel realm = identitySession.createRealm(id, name);
realm.setName(name); realm.setName(name);
realm.addRole(Constants.WILDCARD_ROLE);
realm.addRole(Constants.APPLICATION_ROLE); realm.addRole(Constants.APPLICATION_ROLE);
realm.addRole(Constants.IDENTITY_REQUESTER_ROLE); realm.addRole(Constants.IDENTITY_REQUESTER_ROLE);
return realm; return realm;
@ -129,7 +128,7 @@ public class RealmManager {
} }
private void enableAccountManagement(RealmModel realm) { private void enableAccountManagement(RealmModel realm) {
ApplicationModel application = realm.getApplicationById(Constants.ACCOUNT_APPLICATION); ApplicationModel application = realm.getApplicationNameMap().get(Constants.ACCOUNT_APPLICATION);
if (application == null) { if (application == null) {
application = realm.addApplication(Constants.ACCOUNT_APPLICATION); application = realm.addApplication(Constants.ACCOUNT_APPLICATION);
application.addDefaultRole(Constants.ACCOUNT_PROFILE_ROLE); application.addDefaultRole(Constants.ACCOUNT_PROFILE_ROLE);
@ -224,6 +223,14 @@ public class RealmManager {
} }
} }
if (rep.getClients() != null) {
for (UserRepresentation clientRep : rep.getClients()) {
UserModel client = createUser(newRealm, clientRep);
newRealm.grantRole(client, newRealm.getRole(Constants.IDENTITY_REQUESTER_ROLE));
userMap.put(client.getLoginName(), client);
}
}
if (rep.getRoles() != null) { if (rep.getRoles() != null) {
for (RoleRepresentation roleRep : rep.getRoles()) { for (RoleRepresentation roleRep : rep.getRoles()) {
createRole(newRealm, roleRep); createRole(newRealm, roleRep);
@ -237,7 +244,10 @@ public class RealmManager {
} }
if (rep.getApplications() != null) { if (rep.getApplications() != null) {
createApplications(rep, newRealm); Map<String, ApplicationModel> appMap = createApplications(rep, newRealm);
for (ApplicationModel app : appMap.values()) {
userMap.put(app.getApplicationUser().getLoginName(), app.getApplicationUser());
}
} }
if (rep.getRoleMappings() != null) { if (rep.getRoleMappings() != null) {
@ -398,12 +408,15 @@ public class RealmManager {
} }
protected void createApplications(RealmRepresentation rep, RealmModel realm) { protected Map<String, ApplicationModel> createApplications(RealmRepresentation rep, RealmModel realm) {
Map<String, ApplicationModel> appMap = new HashMap<String, ApplicationModel>();
RoleModel loginRole = realm.getRole(Constants.APPLICATION_ROLE); RoleModel loginRole = realm.getRole(Constants.APPLICATION_ROLE);
ApplicationManager manager = new ApplicationManager(this); ApplicationManager manager = new ApplicationManager(this);
for (ApplicationRepresentation resourceRep : rep.getApplications()) { for (ApplicationRepresentation resourceRep : rep.getApplications()) {
manager.createApplication(realm, loginRole, resourceRep); ApplicationModel app = manager.createApplication(realm, loginRole, resourceRep);
appMap.put(app.getName(), app);
} }
return appMap;
} }
public static UserRepresentation toRepresentation(UserModel user) { public static UserRepresentation toRepresentation(UserModel user) {

View file

@ -46,54 +46,38 @@ public class TokenManager {
public AccessCodeEntry createAccessCode(String scopeParam, String state, String redirect, RealmModel realm, UserModel client, UserModel user) { public AccessCodeEntry createAccessCode(String scopeParam, String state, String redirect, RealmModel realm, UserModel client, UserModel user) {
boolean applicationResource = realm.hasRole(client, realm.getRole(Constants.APPLICATION_ROLE));
AccessCodeEntry code = new AccessCodeEntry(); AccessCodeEntry code = new AccessCodeEntry();
SkeletonKeyScope scopeMap = null; SkeletonKeyScope scopeMap = null;
if (scopeParam != null) scopeMap = decodeScope(scopeParam); if (scopeParam != null) scopeMap = decodeScope(scopeParam);
List<RoleModel> realmRolesRequested = code.getRealmRolesRequested(); List<RoleModel> realmRolesRequested = code.getRealmRolesRequested();
MultivaluedMap<String, RoleModel> resourceRolesRequested = code.getResourceRolesRequested(); MultivaluedMap<String, RoleModel> resourceRolesRequested = code.getResourceRolesRequested();
Set<String> realmMapping = realm.getRoleMappingValues(user); Set<String> realmMapping = realm.getRoleMappingValues(user);
realmMapping.addAll(realm.getDefaultRoles());
if (realmMapping != null && realmMapping.size() > 0 && (scopeMap == null || scopeMap.containsKey("realm"))) { if (realmMapping != null && realmMapping.size() > 0 && (scopeMap == null || scopeMap.containsKey("realm"))) {
Set<String> scope = realm.getScopeMappingValues(client); Set<String> scope = realm.getScopeMappingValues(client);
if (scope.size() > 0) { if (scope.size() > 0) {
Set<String> scopeRequest = null; Set<String> scopeRequest = scopeMap != null ? new HashSet<String>(scopeMap.get("realm")) : null;
if (scopeMap != null) {
if (scopeRequest == null) {
scopeRequest = new HashSet<String>();
}
scopeRequest.addAll(scopeMap.get("realm"));
if (scopeRequest.contains(Constants.WILDCARD_ROLE)) scopeRequest = null;
}
for (String role : realmMapping) { for (String role : realmMapping) {
if ( if ((scopeRequest == null || scopeRequest.contains(role)) && scope.contains(role))
(scopeRequest == null || scopeRequest.contains(role)) &&
(scope.contains("*") || scope.contains(role))
)
realmRolesRequested.add(realm.getRole(role)); realmRolesRequested.add(realm.getRole(role));
} }
} }
} }
for (ApplicationModel resource : realm.getApplications()) { for (ApplicationModel resource : realm.getApplications()) {
Set<String> mapping = resource.getRoleMappingValues(user); if (applicationResource && resource.getApplicationUser().getLoginName().equals(client.getLoginName())) {
mapping.addAll(resource.getDefaultRoles()); resourceRolesRequested.addAll(resource.getName(), resource.getRoles());
if (mapping != null && mapping.size() > 0 && (scopeMap == null || scopeMap.containsKey(resource.getName()))) { } else {
Set<String> scope = resource.getScopeMappingValues(client); Set<String> mapping = resource.getRoleMappingValues(user);
if (scope.size() > 0) { if (mapping != null && mapping.size() > 0 && (scopeMap == null || scopeMap.containsKey(resource.getName()))) {
Set<String> scopeRequest = null; Set<String> scope = resource.getScopeMappingValues(client);
if (scopeMap != null) { if (scope.size() > 0) {
if (scopeRequest == null) { Set<String> scopeRequest = scopeMap != null ? new HashSet<String>(scopeMap.get(resource.getName())) : null;
scopeRequest = new HashSet<String>(); for (String role : mapping) {
if ((scopeRequest == null || scopeRequest.contains(role)) && scope.contains(role))
resourceRolesRequested.add(resource.getName(), resource.getRole(role));
} }
scopeRequest.addAll(scopeMap.get(resource.getName()));
if (scopeRequest.contains(Constants.WILDCARD_ROLE)) scopeRequest = null;
}
for (String role : mapping) {
if (
(scopeRequest == null || scopeRequest.contains(role)) &&
(scope.contains("*") || scope.contains(role))
)
resourceRolesRequested.add(resource.getName(), resource.getRole(role));
} }
} }
} }

View file

@ -373,7 +373,8 @@ public class AccountService {
UserModel client = auth.getClient(); UserModel client = auth.getClient();
if (realm.hasRole(client, Constants.APPLICATION_ROLE)) { if (realm.hasRole(client, Constants.APPLICATION_ROLE)) {
// Tokens from cookies don't have roles // Tokens from cookies don't have roles
if (hasRole(client, Constants.ACCOUNT_MANAGE_ROLE) || (role != null && hasRole(client, role))) { UserModel user = auth.getUser();
if (hasRole(user, Constants.ACCOUNT_MANAGE_ROLE) || (role != null && hasRole(user, role))) {
return true; return true;
} }
} }
@ -389,9 +390,6 @@ public class AccountService {
} }
private boolean hasRole(UserModel user, String role) { private boolean hasRole(UserModel user, String role) {
if (application.getDefaultRoles().contains(role)) {
return true;
}
return application.hasRole(user, role); return application.hasRole(user, role);
} }

View file

@ -1,18 +1,19 @@
package org.keycloak.services.resources; package org.keycloak.services.resources;
import org.jboss.resteasy.logging.Logger;
import org.keycloak.SkeletonKeyContextResolver; import org.keycloak.SkeletonKeyContextResolver;
import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.ModelProvider; import org.keycloak.models.ModelProvider;
import org.keycloak.services.managers.SocialRequestManager; import org.keycloak.services.managers.SocialRequestManager;
import org.keycloak.services.managers.TokenManager; import org.keycloak.services.managers.TokenManager;
import org.keycloak.services.utils.PropertiesManager;
import javax.annotation.PreDestroy; import javax.annotation.PreDestroy;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
import javax.ws.rs.core.Application; import javax.ws.rs.core.Application;
import javax.ws.rs.core.Context; import javax.ws.rs.core.Context;
import java.lang.reflect.Constructor;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator;
import java.util.ServiceLoader;
import java.util.Set; import java.util.Set;
/** /**
@ -21,14 +22,19 @@ import java.util.Set;
*/ */
public class KeycloakApplication extends Application { public class KeycloakApplication extends Application {
private static final Logger log = Logger.getLogger(KeycloakApplication.class);
private static final String MODEL_PROVIDER = "keycloak.model";
private static final String DEFAULT_MODEL_PROVIDER = "jpa";
protected Set<Object> singletons = new HashSet<Object>(); protected Set<Object> singletons = new HashSet<Object>();
protected Set<Class<?>> classes = new HashSet<Class<?>>(); protected Set<Class<?>> classes = new HashSet<Class<?>>();
protected KeycloakSessionFactory factory; protected KeycloakSessionFactory factory;
public KeycloakApplication(@Context ServletContext context) { public KeycloakApplication(@Context ServletContext context) {
KeycloakSessionFactory f = createSessionFactory(); this.factory = createSessionFactory();
this.factory = f;
context.setAttribute(KeycloakSessionFactory.class.getName(), factory); context.setAttribute(KeycloakSessionFactory.class.getName(), factory);
//classes.add(KeycloakSessionCleanupFilter.class); //classes.add(KeycloakSessionCleanupFilter.class);
@ -41,57 +47,36 @@ public class KeycloakApplication extends Application {
classes.add(QRCodeResource.class); classes.add(QRCodeResource.class);
} }
protected KeycloakSessionFactory createSessionFactory() { public static KeycloakSessionFactory createSessionFactory() {
return buildSessionFactory(); ServiceLoader<ModelProvider> providers = ServiceLoader.load(ModelProvider.class);
} String configuredProvider = System.getProperty(MODEL_PROVIDER);
ModelProvider provider = null;
public static KeycloakSessionFactory buildSessionFactory() { if (configuredProvider != null) {
if (PropertiesManager.isMongoSessionFactory()) { for (ModelProvider p : providers) {
return buildMongoDBSessionFactory(); if (p.getId().equals(configuredProvider)) {
} else if (PropertiesManager.isPicketlinkSessionFactory()) { provider = p;
return buildPicketlinkSessionFactory(); }
} else if (PropertiesManager.isJpaSessionFactory()) { }
return buildJpaSessionFactory();
} else { } else {
throw new IllegalStateException("Unknown session factory type: " + PropertiesManager.getSessionFactoryType()); for (ModelProvider p : providers) {
if (provider == null) {
provider = p;
}
if (p.getId().equals(DEFAULT_MODEL_PROVIDER)) {
provider = p;
break;
}
}
} }
}
private static KeycloakSessionFactory buildJpaSessionFactory() { if (provider != null) {
ModelProvider provider = null; log.debug("Model provider: " + provider.getId());
try { return provider.createFactory();
provider = (ModelProvider)Class.forName("org.keycloak.models.jpa.JpaModelProvider").newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
} }
return provider.createFactory();
}
throw new RuntimeException("Model provider not found");
private static KeycloakSessionFactory buildPicketlinkSessionFactory() {
ModelProvider provider = null;
try {
provider = (ModelProvider)Class.forName("org.keycloak.models.picketlink.PicketlinkModelProvider").newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
return provider.createFactory();
}
private static KeycloakSessionFactory buildMongoDBSessionFactory() {
String host = PropertiesManager.getMongoHost();
int port = PropertiesManager.getMongoPort();
String dbName = PropertiesManager.getMongoDbName();
boolean dropDatabaseOnStartup = PropertiesManager.dropDatabaseOnStartup();
// Create MongoDBSessionFactory via reflection now
try {
Class<? extends KeycloakSessionFactory> mongoDBSessionFactoryClass = (Class<? extends KeycloakSessionFactory>)Class.forName("org.keycloak.models.mongo.keycloak.adapters.MongoDBSessionFactory");
Constructor<? extends KeycloakSessionFactory> constr = mongoDBSessionFactoryClass.getConstructor(String.class, int.class, String.class, boolean.class);
return constr.newInstance(host, port, dbName, dropDatabaseOnStartup);
} catch (Exception e) {
throw new RuntimeException(e);
}
} }
public KeycloakSessionFactory getFactory() { public KeycloakSessionFactory getFactory() {
@ -103,9 +88,6 @@ public class KeycloakApplication extends Application {
factory.close(); factory.close();
} }
@Override @Override
public Set<Class<?>> getClasses() { public Set<Class<?>> getClasses() {
return classes; return classes;

View file

@ -263,7 +263,7 @@ public class SaasService {
@Path("login") @Path("login")
@GET @GET
@NoCache @NoCache
public Response loginPage() { public Response loginPage(@QueryParam("path") String path) {
logger.debug("loginPage ********************** <---"); logger.debug("loginPage ********************** <---");
RealmManager realmManager = new RealmManager(session); RealmManager realmManager = new RealmManager(session);
RealmModel realm = getAdminstrationRealm(realmManager); RealmModel realm = getAdminstrationRealm(realmManager);
@ -277,7 +277,7 @@ public class SaasService {
URI redirectUri = uriInfo.getBaseUriBuilder().path(SaasService.class).path(SaasService.class, "loginRedirect").build(); URI redirectUri = uriInfo.getBaseUriBuilder().path(SaasService.class).path(SaasService.class, "loginRedirect").build();
logger.debug("redirectUri: {0}", redirectUri.toString()); logger.debug("redirectUri: {0}", redirectUri.toString());
oauth.setStateCookiePath(redirectUri.getPath()); oauth.setStateCookiePath(redirectUri.getPath());
return oauth.redirect(uriInfo, redirectUri.toString()); return oauth.redirect(uriInfo, redirectUri.toString(), path);
} }
@Path("login-redirect") @Path("login-redirect")
@ -316,7 +316,7 @@ public class SaasService {
logger.debug("state not specified"); logger.debug("state not specified");
throw new BadRequestException(); throw new BadRequestException();
} }
new JaxrsOAuthClient().checkStateCookie(uriInfo, headers); String path = new JaxrsOAuthClient().checkStateCookie(uriInfo, headers);
JWSInput input = new JWSInput(code, providers); JWSInput input = new JWSInput(code, providers);
boolean verifiedCode = false; boolean verifiedCode = false;
@ -358,7 +358,12 @@ public class SaasService {
} }
logger.debug("loginRedirect SUCCESS"); logger.debug("loginRedirect SUCCESS");
NewCookie cookie = authManager.createSaasIdentityCookie(realm, accessCode.getUser(), uriInfo); NewCookie cookie = authManager.createSaasIdentityCookie(realm, accessCode.getUser(), uriInfo);
return Response.status(302).cookie(cookie).location(contextRoot(uriInfo).path(adminPath).build()).build();
URI redirectUri = contextRoot(uriInfo).path(adminPath).build();
if (path != null) {
redirectUri = redirectUri.resolve("#" + path);
}
return Response.status(302).cookie(cookie).location(redirectUri).build();
} finally { } finally {
authManager.expireCookie(AbstractOAuthClient.OAUTH_TOKEN_REQUEST_STATE, uriInfo.getAbsolutePath().getPath()); authManager.expireCookie(AbstractOAuthClient.OAUTH_TOKEN_REQUEST_STATE, uriInfo.getAbsolutePath().getPath());
} }

View file

@ -8,6 +8,7 @@ import org.jboss.resteasy.jwt.JsonSerialization;
import org.jboss.resteasy.logging.Logger; import org.jboss.resteasy.logging.Logger;
import org.jboss.resteasy.spi.HttpRequest; import org.jboss.resteasy.spi.HttpRequest;
import org.jboss.resteasy.spi.HttpResponse; import org.jboss.resteasy.spi.HttpResponse;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.Constants; import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakTransaction; import org.keycloak.models.KeycloakTransaction;
@ -323,6 +324,17 @@ public class TokenService {
realm.updateCredential(user, credentials); realm.updateCredential(user, credentials);
} }
for (String r : realm.getDefaultRoles()) {
realm.grantRole(user, realm.getRole(r));
}
for (ApplicationModel application : realm.getApplications()) {
for (String r : application.getDefaultRoles()) {
application.grantRole(user, application.getRole(r));
}
}
return null; return null;
} }
@ -423,7 +435,7 @@ public class TokenService {
logger.debug("accessRequest SUCCESS"); logger.debug("accessRequest SUCCESS");
AccessTokenResponse res = accessTokenResponse(realm.getPrivateKey(), accessCode.getToken()); AccessTokenResponse res = accessTokenResponse(realm.getPrivateKey(), accessCode.getToken());
return Cors.add(request, Response.ok(res)).allowedOrigins(client).build(); return Cors.add(request, Response.ok(res)).allowedOrigins(client).allowedMethods("POST").build();
} }
protected AccessTokenResponse accessTokenResponse(PrivateKey privateKey, SkeletonKeyToken token) { protected AccessTokenResponse accessTokenResponse(PrivateKey privateKey, SkeletonKeyToken token) {

View file

@ -1,6 +1,7 @@
package org.keycloak.services.resources.admin; package org.keycloak.services.resources.admin;
import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.models.Constants;
import org.keycloak.models.RoleContainerModel; import org.keycloak.models.RoleContainerModel;
import org.keycloak.models.RoleModel; import org.keycloak.models.RoleModel;
import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.representations.idm.RoleRepresentation;
@ -39,9 +40,11 @@ public class RoleContainerResource {
List<RoleModel> roleModels = roleContainer.getRoles(); List<RoleModel> roleModels = roleContainer.getRoles();
List<RoleRepresentation> roles = new ArrayList<RoleRepresentation>(); List<RoleRepresentation> roles = new ArrayList<RoleRepresentation>();
for (RoleModel roleModel : roleModels) { for (RoleModel roleModel : roleModels) {
RoleRepresentation role = new RoleRepresentation(roleModel.getName(), roleModel.getDescription()); if (!roleModel.getName().startsWith(Constants.INTERNAL_ROLE)) {
role.setId(roleModel.getId()); RoleRepresentation role = new RoleRepresentation(roleModel.getName(), roleModel.getDescription());
roles.add(role); role.setId(roleModel.getId());
roles.add(role);
}
} }
return roles; return roles;
} }
@ -52,7 +55,7 @@ public class RoleContainerResource {
@Produces("application/json") @Produces("application/json")
public RoleRepresentation getRole(final @PathParam("id") String id) { public RoleRepresentation getRole(final @PathParam("id") String id) {
RoleModel roleModel = roleContainer.getRoleById(id); RoleModel roleModel = roleContainer.getRoleById(id);
if (roleModel == null) { if (roleModel == null || roleModel.getName().startsWith(Constants.INTERNAL_ROLE)) {
throw new NotFoundException(); throw new NotFoundException();
} }
RoleRepresentation rep = new RoleRepresentation(roleModel.getName(), roleModel.getDescription()); RoleRepresentation rep = new RoleRepresentation(roleModel.getName(), roleModel.getDescription());
@ -65,7 +68,7 @@ public class RoleContainerResource {
@Consumes("application/json") @Consumes("application/json")
public void updateRole(final @PathParam("id") String id, final RoleRepresentation rep) { public void updateRole(final @PathParam("id") String id, final RoleRepresentation rep) {
RoleModel role = roleContainer.getRoleById(id); RoleModel role = roleContainer.getRoleById(id);
if (role == null) { if (role == null || role.getName().startsWith(Constants.INTERNAL_ROLE)) {
throw new NotFoundException(); throw new NotFoundException();
} }
role.setName(rep.getName()); role.setName(rep.getName());
@ -76,7 +79,7 @@ public class RoleContainerResource {
@POST @POST
@Consumes("application/json") @Consumes("application/json")
public Response createRole(final @Context UriInfo uriInfo, final RoleRepresentation rep) { public Response createRole(final @Context UriInfo uriInfo, final RoleRepresentation rep) {
if (roleContainer.getRole(rep.getName()) != null) { if (roleContainer.getRole(rep.getName()) != null || rep.getName().startsWith(Constants.INTERNAL_ROLE)) {
throw new InternalServerErrorException(); // todo appropriate status here. throw new InternalServerErrorException(); // todo appropriate status here.
} }
RoleModel role = roleContainer.addRole(rep.getName()); RoleModel role = roleContainer.addRole(rep.getName());

View file

@ -3,6 +3,7 @@ package org.keycloak.services.resources.admin;
import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.annotations.cache.NoCache;
import org.jboss.resteasy.logging.Logger; import org.jboss.resteasy.logging.Logger;
import org.keycloak.models.ApplicationModel; import org.keycloak.models.ApplicationModel;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel; import org.keycloak.models.RoleModel;
@ -66,8 +67,10 @@ public class UsersResource {
user.setEmail(rep.getEmail()); user.setEmail(rep.getEmail());
user.setFirstName(rep.getFirstName()); user.setFirstName(rep.getFirstName());
user.setLastName(rep.getLastName()); user.setLastName(rep.getLastName());
for (Map.Entry<String, String> attr : rep.getAttributes().entrySet()) { if (rep.getAttributes() != null) {
user.setAttribute(attr.getKey(), attr.getValue()); for (Map.Entry<String, String> attr : rep.getAttributes().entrySet()) {
user.setAttribute(attr.getKey(), attr.getValue());
}
} }
} }
@ -98,12 +101,19 @@ public class UsersResource {
@Produces("application/json") @Produces("application/json")
public UserRepresentation getUser(final @PathParam("username") String username) { public UserRepresentation getUser(final @PathParam("username") String username) {
UserModel user = realm.getUser(username); UserModel user = realm.getUser(username);
if (user == null) { if (user == null || !isUser(user)) {
throw new NotFoundException(); throw new NotFoundException();
} }
return new RealmManager(session).toRepresentation(user); return new RealmManager(session).toRepresentation(user);
} }
@Path("{username}")
@DELETE
@NoCache
public void deleteUser(final @PathParam("username") String username) {
realm.deleteUser(username);
}
@GET @GET
@NoCache @NoCache
@Produces("application/json") @Produces("application/json")
@ -117,7 +127,9 @@ public class UsersResource {
if (search != null) { if (search != null) {
List<UserModel> userModels = manager.searchUsers(search, realm); List<UserModel> userModels = manager.searchUsers(search, realm);
for (UserModel user : userModels) { for (UserModel user : userModels) {
results.add(manager.toRepresentation(user)); if (isUser(user)) {
results.add(manager.toRepresentation(user));
}
} }
} else { } else {
Map<String, String> attributes = new HashMap<String, String>(); Map<String, String> attributes = new HashMap<String, String>();
@ -142,6 +154,10 @@ public class UsersResource {
return results; return results;
} }
private boolean isUser(UserModel user) {
return !realm.hasRole(user, realm.getRole(Constants.IDENTITY_REQUESTER_ROLE)) && !realm.hasRole(user, realm.getRole(Constants.APPLICATION_ROLE));
}
@Path("{username}/role-mappings") @Path("{username}/role-mappings")
@GET @GET
@Produces("application/json") @Produces("application/json")

View file

@ -26,34 +26,34 @@ package org.keycloak.services.resources.flows;
*/ */
public class Pages { public class Pages {
public final static String ACCESS = "/forms/access.ftl"; public final static String ACCESS = "access.ftl";
public final static String ACCOUNT = "/forms/account.ftl"; public final static String ACCOUNT = "account.ftl";
public final static String LOGIN = "/forms/login.ftl"; public final static String LOGIN = "login.ftl";
public final static String LOGIN_TOTP = "/forms/login-totp.ftl"; public final static String LOGIN_TOTP = "login-totp.ftl";
public final static String LOGIN_CONFIG_TOTP = "/forms/login-config-totp.ftl"; public final static String LOGIN_CONFIG_TOTP = "login-config-totp.ftl";
public final static String LOGIN_VERIFY_EMAIL = "/forms/login-verify-email.ftl"; public final static String LOGIN_VERIFY_EMAIL = "login-verify-email.ftl";
public final static String OAUTH_GRANT = "/forms/login-oauth-grant.ftl"; public final static String OAUTH_GRANT = "login-oauth-grant.ftl";
public final static String PASSWORD = "/forms/password.ftl"; public final static String PASSWORD = "password.ftl";
public final static String LOGIN_RESET_PASSWORD = "/forms/login-reset-password.ftl"; public final static String LOGIN_RESET_PASSWORD = "login-reset-password.ftl";
public final static String LOGIN_UPDATE_PASSWORD = "/forms/login-update-password.ftl"; public final static String LOGIN_UPDATE_PASSWORD = "login-update-password.ftl";
public final static String REGISTER = "/forms/register.ftl"; public final static String REGISTER = "register.ftl";
public final static String ERROR = "/forms/error.ftl"; public final static String ERROR = "error.ftl";
public final static String SOCIAL = "/forms/social.ftl"; public final static String SOCIAL = "social.ftl";
public final static String TOTP = "/forms/totp.ftl"; public final static String TOTP = "totp.ftl";
public final static String LOGIN_UPDATE_PROFILE = "/forms/login-update-profile.ftl"; public final static String LOGIN_UPDATE_PROFILE = "login-update-profile.ftl";
} }

View file

@ -4,11 +4,13 @@ import org.junit.Assert;
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.models.ApplicationModel;
import org.keycloak.models.Constants; import org.keycloak.models.Constants;
import org.keycloak.models.OAuthClientModel; import org.keycloak.models.OAuthClientModel;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredCredentialModel; import org.keycloak.models.RequiredCredentialModel;
import org.keycloak.models.RoleModel; import org.keycloak.models.RoleModel;
import org.keycloak.models.SocialLinkModel;
import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.representations.idm.CredentialRepresentation;
@ -173,6 +175,34 @@ public class AdapterTest extends AbstractKeycloakTest {
} }
@Test
public void deleteUser() throws Exception {
test1CreateRealm();
UserModel user = realmModel.addUser("bburke");
user.setAttribute("attr1", "val1");
user.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
RoleModel testRole = realmModel.addRole("test");
realmModel.grantRole(user, testRole);
ApplicationModel app = realmModel.addApplication("test-app");
RoleModel appRole = app.addRole("test");
app.grantRole(user, appRole);
SocialLinkModel socialLink = new SocialLinkModel("google", user.getLoginName());
realmModel.addSocialLink(user, socialLink);
UserCredentialModel cred = new UserCredentialModel();
cred.setType(CredentialRepresentation.PASSWORD);
cred.setValue("password");
realmModel.updateCredential(user, cred);
Assert.assertTrue(realmModel.deleteUser("bburke"));
Assert.assertFalse(realmModel.deleteUser("bburke"));
Assert.assertNull(realmModel.getUser("bburke"));
}
@Test @Test
public void testUserSearch() throws Exception { public void testUserSearch() throws Exception {
test1CreateRealm(); test1CreateRealm();
@ -199,6 +229,15 @@ public class AdapterTest extends AbstractKeycloakTest {
Assert.assertEquals(bburke.getEmail(), "bburke@redhat.com"); Assert.assertEquals(bburke.getEmail(), "bburke@redhat.com");
} }
{
List<UserModel> userModels = adapter.searchUsers("bil burk", realmModel);
Assert.assertEquals(userModels.size(), 1);
UserModel bburke = userModels.get(0);
Assert.assertEquals(bburke.getFirstName(), "Bill");
Assert.assertEquals(bburke.getLastName(), "Burke");
Assert.assertEquals(bburke.getEmail(), "bburke@redhat.com");
}
{ {
List<UserModel> userModels = adapter.searchUsers("bburke@redhat.com", realmModel); List<UserModel> userModels = adapter.searchUsers("bburke@redhat.com", realmModel);
@ -209,6 +248,15 @@ public class AdapterTest extends AbstractKeycloakTest {
Assert.assertEquals(bburke.getEmail(), "bburke@redhat.com"); Assert.assertEquals(bburke.getEmail(), "bburke@redhat.com");
} }
{
List<UserModel> userModels = adapter.searchUsers("rke@redhat.com", realmModel);
Assert.assertEquals(userModels.size(), 1);
UserModel bburke = userModels.get(0);
Assert.assertEquals(bburke.getFirstName(), "Bill");
Assert.assertEquals(bburke.getLastName(), "Burke");
Assert.assertEquals(bburke.getEmail(), "bburke@redhat.com");
}
{ {
List<UserModel> userModels = adapter.searchUsers("bburke", realmModel); List<UserModel> userModels = adapter.searchUsers("bburke", realmModel);
Assert.assertEquals(userModels.size(), 1); Assert.assertEquals(userModels.size(), 1);
@ -218,6 +266,15 @@ public class AdapterTest extends AbstractKeycloakTest {
Assert.assertEquals(bburke.getEmail(), "bburke@redhat.com"); Assert.assertEquals(bburke.getEmail(), "bburke@redhat.com");
} }
{
List<UserModel> userModels = adapter.searchUsers("BurK", realmModel);
Assert.assertEquals(userModels.size(), 1);
UserModel bburke = userModels.get(0);
Assert.assertEquals(bburke.getFirstName(), "Bill");
Assert.assertEquals(bburke.getLastName(), "Burke");
Assert.assertEquals(bburke.getEmail(), "bburke@redhat.com");
}
{ {
List<UserModel> userModels = adapter.searchUsers("Burke", realmModel); List<UserModel> userModels = adapter.searchUsers("Burke", realmModel);
Assert.assertEquals(userModels.size(), 1); Assert.assertEquals(userModels.size(), 1);
@ -293,7 +350,7 @@ public class AdapterTest extends AbstractKeycloakTest {
realmModel.addRole("admin"); realmModel.addRole("admin");
realmModel.addRole("user"); realmModel.addRole("user");
List<RoleModel> roles = realmModel.getRoles(); List<RoleModel> roles = realmModel.getRoles();
Assert.assertEquals(6, roles.size()); Assert.assertEquals(5, roles.size());
UserModel user = realmModel.addUser("bburke"); UserModel user = realmModel.addUser("bburke");
RoleModel role = realmModel.getRole("user"); RoleModel role = realmModel.getRole("user");
realmModel.grantRole(user, role); realmModel.grantRole(user, role);

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