Merge pull request #244 from stianst/master

Added view roles, including updating console to make forms read-only
This commit is contained in:
Stian Thorgersen 2014-02-26 17:39:51 +00:00
commit 4ea7808d5f
38 changed files with 454 additions and 201 deletions

View file

@ -521,6 +521,8 @@ module.factory('errorInterceptor', function($q, $window, $rootScope, $location,
console.log('session timeout?');
Auth.loggedIn = false;
window.location = '/auth/rest/admin/login?path=' + $location.path();
} else if (response.status == 403) {
Notifications.error("Forbidden");
} else if (response.status == 404) {
Notifications.error("Not found");
} else if (response.status) {
@ -785,6 +787,7 @@ module.directive('kcReadOnly', function() {
element.find('input').attr('disabled', 'disabled');
element.find('button').attr('disabled', 'disabled');
element.find('select').attr('disabled', 'disabled');
element.find('textarea').attr('disabled', 'disabled');
}
}
};

View file

@ -22,6 +22,22 @@ module.controller('GlobalCtrl', function($scope, $http, Auth, Current, $location
$scope.access = {
admin: data.admin,
get viewRealm() {
return getAccess(Current.realm.realm, 'view-realm') || this.viewRealm;
},
get viewApplications() {
return getAccess(Current.realm.realm, 'view-applications') || this.manageApplications;
},
get viewClients() {
return getAccess(Current.realm.realm, 'view-clients') || this.manageClients;
},
get viewUsers() {
return getAccess(Current.realm.realm, 'view-users') || this.manageClients;
},
get manageRealm() {
return getAccess(Current.realm.realm, 'manage-realm');
},

View file

@ -16,7 +16,7 @@
<li class="active">Credentials</li>
</ol>
<h2 data-ng-hide="create"><span>{{application.name}}</span> Credentials</h2>
<form class="form-horizontal" name="credentialForm" novalidate>
<form class="form-horizontal" name="credentialForm" novalidate kc-read-only="!access.manageApplications">
<fieldset >
<legend><span class="text">Client Secret</span></legend>
<div class="form-group">
@ -27,7 +27,7 @@
</div>
</div>
</fieldset>
<div class="pull-right form-actions">
<div class="pull-right form-actions" data-ng-show="access.manageApplications">
<button type="submit" data-ng-click="changePassword()" class="btn btn-primary btn-lg">Regenerate Secret
</button>
</div>

View file

@ -23,7 +23,7 @@
<li class="active">Settings</li>
</ol>
<h2 data-ng-hide="create"><span>{{application.name}}</span> Settings</h2>
<form class="form-horizontal" name="applicationForm" novalidate>
<form class="form-horizontal" name="applicationForm" novalidate kc-read-only="!access.manageApplications">
<fieldset class="border-top">
<div class="form-group">
<label class="col-sm-2 control-label" for="name">Name <span class="required" data-ng-show="create">*</span></label>
@ -108,11 +108,11 @@
</div>
</fieldset>
<div class="pull-right form-actions" data-ng-show="create">
<div class="pull-right form-actions" data-ng-show="create && access.manageApplications">
<button kc-cancel data-ng-click="cancel()">Cancel</button>
<button kc-save data-ng-show="changed">Save</button>
</div>
<div class="pull-right form-actions" data-ng-show="!create">
<div class="pull-right form-actions" data-ng-show="!create && access.manageApplications">
<button kc-reset data-ng-show="changed">Clear changes</button>
<button kc-save data-ng-show="changed">Save</button>
<button kc-delete data-ng-click="remove()" data-ng-hide="changed">Delete</button>

View file

@ -29,7 +29,7 @@
<h2 data-ng-hide="create"><span>{{application.name}}</span> {{role.name}}</h2>
<h2 data-ng-show="create"><span>{{application.name}}</span> Add Role</h2>
<form class="form-horizontal" name="realmForm" novalidate>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageApplications">
<span class="fieldset-notice"><span class="required">*</span> Required fields</span>
<fieldset class="border-top">
@ -134,11 +134,11 @@
</div>
</fieldset>
<div class="pull-right form-actions" data-ng-show="create">
<div class="pull-right form-actions" data-ng-show="create && access.manageApplications">
<button kc-cancel data-ng-click="cancel()">Cancel</button>
<button kc-save data-ng-show="changed">Save</button>
</div>
<div class="pull-right form-actions" data-ng-show="!create">
<div class="pull-right form-actions" data-ng-show="!create && access.manageApplications">
<button kc-reset data-ng-show="changed">Clear changes</button>
<button kc-save data-ng-show="changed">Save</button>
<button kc-delete data-ng-click="remove()" data-ng-hide="changed">Delete</button>

View file

@ -21,7 +21,7 @@
<table class="table">
<thead>
<tr>
<th class="kc-table-actions" colspan="3">
<th class="kc-table-actions" colspan="3" data-ng-show="access.manageApplications">
<div class="pull-right">
<a class="btn btn-primary" href="#/create/role/{{realm.realm}}/applications/{{application.name}}">Add Role</a>
<!-- <button class="remove disabled">Remove</button> -->

View file

@ -19,7 +19,7 @@
</ol>
<h2><span>{{application.name}}</span> Scope Mappings</h2>
<p class="subtitle"></p>
<form class="form-horizontal" name="realmForm" novalidate>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageApplications">
<fieldset>
<legend><span class="text">Realm Roles</span></legend>
<div class="form-group col-sm-10">
@ -59,7 +59,7 @@
<div class="col-sm-4">
<div class="input-group">
<div class="select-kc">
<select id="applications" name="applications" ng-change="changeApplication()" ng-model="targetApp" ng-options="a.name for a in (applications|remove:application:'id')">
<select id="applications" name="applications" ng-change="changeApplication()" ng-model="targetApp" ng-options="a.name for a in (applications|remove:application:'id')" ng-disabled="false">
<option value="" selected> Select an Application </option>
</select>
</div>

View file

@ -15,7 +15,7 @@
<li class="active">Credentials</li>
</ol>
<h2 data-ng-hide="create"><span>{{oauth.name}}</span> Credentials</h2>
<form class="form-horizontal" name="credentialForm" novalidate>
<form class="form-horizontal" name="credentialForm" novalidate kc-read-only="!access.manageClients">
<fieldset >
<legend><span class="text">Client Secret</span></legend>
<div class="form-group">
@ -26,7 +26,7 @@
</div>
</div>
</fieldset>
<div class="pull-right form-actions">
<div class="pull-right form-actions" data-ng-show="access.manageClients">
<button type="submit" data-ng-click="changePassword()" class="btn btn-primary btn-lg">Regenerate Secret
</button>
</div>

View file

@ -21,7 +21,7 @@
<li class="active">Settings</li>
</ol>
<h2 data-ng-hide="create"><span>{{oauth.name}}</span> Settings</h2>
<form class="form-horizontal" name="oauthForm" novalidate>
<form class="form-horizontal" name="oauthForm" novalidate kc-read-only="!access.manageClients">
<fieldset class="border-top">
<div class="form-group">
@ -90,11 +90,11 @@
</div>
</fieldset>
<div class="pull-right form-actions" data-ng-show="create">
<div class="pull-right form-actions" data-ng-show="create && access.manageClients">
<button kc-cancel data-ng-click="cancel()">Cancel</button>
<button kc-save data-ng-show="changed">Save</button>
</div>
<div class="pull-right form-actions" data-ng-show="!create">
<div class="pull-right form-actions" data-ng-show="!create && access.manageClients">
<button kc-reset data-ng-show="changed">Clear changes</button>
<button kc-save data-ng-show="changed">Save</button>
<button kc-delete data-ng-click="remove()" data-ng-hide="changed">Delete</button>

View file

@ -17,7 +17,7 @@
</ol>
<h2><span>{{oauth.name}}</span> Scope Mappings</h2>
<p class="subtitle"></p>
<form class="form-horizontal" name="realmForm" novalidate>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageClients">
<fieldset>
<legend><span class="text">Realm Roles</span></legend>
<div class="form-group col-sm-10">
@ -57,7 +57,7 @@
<div class="col-sm-4">
<div class="input-group">
<div class="select-kc">
<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)" ng-disabled="false">
<option value="" selected> Select an Application </option>
</select>
</div>

View file

@ -9,9 +9,9 @@
<li class="active">Required Credentials</li>
</ol>
<h2><span>{{realm.realm}}</span> Credentials</h2>
<form class="form-horizontal" name="realmForm" novalidate>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
<fieldset class="border-top">
<legend uncollapsed><span class="text">Realm Credentials Settings</span></legend>
<legend><span class="text">Realm Credentials Settings</span></legend>
<div class="form-group">
<label class="col-sm-2 control-label" for="user" class="control-label two-lines">Required User Credentials</label>
@ -21,7 +21,7 @@
</div>
</fieldset>
<fieldset class="border-top">
<legend uncollapsed><span class="text">Realm Password Policy</span></legend>
<legend><span class="text">Realm Password Policy</span></legend>
<table class="table">
<caption class="hidden">Table of Password Policies</caption>
<thead>
@ -63,7 +63,7 @@
</tbody>
</table>
</fieldset>
<div class="pull-right form-actions">
<div class="pull-right form-actions" data-ng-show="access.manageRealm">
<button kc-reset data-ng-show="changed">Clear changes</button>
<button kc-save data-ng-show="changed">Save</button>
</div>

View file

@ -8,7 +8,7 @@
<li class="active">Registration</li>
</ol>
<h2><span>{{realm.realm}}</span> Default Roles</h2>
<form class="form-horizontal" name="realmForm" novalidate>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
<fieldset>
<legend><span class="text">Realm Default Roles</span> </legend>
<div class="form-group">
@ -48,7 +48,7 @@
<div class="col-sm-4">
<div class="input-group">
<div class="select-kc">
<select id="applications" name="applications" ng-change="changeApplication()" ng-model="application" ng-options="a.name for a in applications">
<select id="applications" name="applications" ng-change="changeApplication()" ng-model="application" ng-options="a.name for a in applications" ng-disabled="false">
<option value="" selected> Select an Application...</option>
</select>
</div>

View file

@ -7,11 +7,11 @@
<li><a href="#/realms/{{realm.realm}}">Settings</a></li>
<li class="active">General</li>
</ol>
<div data-ng-show="access.manageRealm">
<div data-ng-show="access.viewRealm">
<h2 class="pull-left" data-ng-show="createRealm">Add Realm</h2>
<h2 data-ng-hide="createRealm"><span>{{realm.realm}}</span> General Settings</h2>
<p class="subtitle" data-ng-show="createRealm"><span class="required">*</span> Required fields</p>
<form class="form-horizontal" name="realmForm" novalidate>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
<fieldset>
<legend class="aj-collapse open"><span class="text">Required Settings</span></legend>
<div class="form-group">
@ -91,18 +91,18 @@
</div>
</fieldset>
<div class="pull-right form-actions" data-ng-show="createRealm">
<div class="pull-right form-actions" data-ng-show="createRealm && access.manageRealm">
<button kc-cancel data-ng-click="cancel()">Cancel</button>
<button kc-save data-ng-show="changed">Save</button>
</div>
<div class="pull-right form-actions" data-ng-show="!createRealm">
<div class="pull-right form-actions" data-ng-show="!createRealm && access.manageRealm">
<button kc-reset data-ng-show="changed">Clear changes</button>
<button kc-save data-ng-show="changed">Save</button>
<button kc-delete data-ng-click="remove()" data-ng-hide="changed">Delete</button>
</div>
</form>
</div>
<div data-ng-hide="access.manageRealm">
<div data-ng-hide="access.viewRealm">
<h2 ><span>{{realm.realm}}</span></h2>
</div>

View file

@ -8,7 +8,7 @@
<li class="active">Keys</li>
</ol>
<h2><span>{{realm.realm}}</span> Keys</h2>
<form class="form-horizontal" name="realmForm" novalidate>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
<fieldset class="border-top">
<div class="form-group">
<label class="col-sm-2 control-label" for="publicKey">Public key</label>
@ -19,7 +19,7 @@
</div>
</div>
</fieldset>
<div class="pull-right form-actions">
<div class="pull-right form-actions" data-ng-show="access.manageRealm">
<button class="btn btn-primary btn-lg" type="submit" data-ng-click="generate()">Generate new keys</button>
</div>
</form>

View file

@ -1,9 +1,9 @@
<ul data-ng-hide="createRealm">
<li data-ng-show="access.manageRealm" data-ng-class="((!path[2] || path[1] == 'role' || path[2] == 'roles' || path[2] == 'token-settings' ||
<li data-ng-show="access.viewRealm" 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] == 'default-roles' || path[2] == 'registration-settings' ||
path[2] == 'keys-settings' || path[2] == 'smtp-settings') && path[3] != 'applications') && 'active'"><a href="#/realms/{{realm.realm}}">Settings</a></li>
<li data-ng-show="access.manageUsers" data-ng-class="(path[2] == 'users' || path[1] == 'user') && 'active'"><a href="#/realms/{{realm.realm}}/users">Users</a>
<li data-ng-show="access.viewUsers" data-ng-class="(path[2] == 'users' || path[1] == 'user') && 'active'"><a href="#/realms/{{realm.realm}}/users">Users</a>
</li>
<li data-ng-show="access.manageApplications" data-ng-class="(path[2] == 'applications' || path[1] == 'application' || path[3] == 'applications') && 'active'"><a href="#/realms/{{realm.realm}}/applications">Applications</a></li>
<li data-ng-show="access.manageClients" data-ng-class="(path[2] == 'oauth-clients' || path[1] == 'oauth-client') && 'active'"><a href="#/realms/{{realm.realm}}/oauth-clients">OAuth Clients</a></li>
<li data-ng-show="access.viewApplications" data-ng-class="(path[2] == 'applications' || path[1] == 'application' || path[3] == 'applications') && 'active'"><a href="#/realms/{{realm.realm}}/applications">Applications</a></li>
<li data-ng-show="access.viewClients" data-ng-class="(path[2] == 'oauth-clients' || path[1] == 'oauth-client') && 'active'"><a href="#/realms/{{realm.realm}}/oauth-clients">OAuth Clients</a></li>
</ul>

View file

@ -8,10 +8,10 @@
<li class="active">SMTP Configuration</li>
</ol>
<h2><span>{{realm.realm}}</span> Email Server Settings</h2>
<form class="form-horizontal" name="realmForm" novalidate>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
<span class="fieldset-notice"><span class="required">*</span> Required fields</span>
<fieldset>
<legend uncollapsed><span class="text">Required Settings</span></legend>
<legend><span class="text">Required Settings</span></legend>
<div class="form-group clearfix">
<label class="col-sm-2 control-label" for="smtpHost">Host <span class="required">*</span></label>
<div class="col-sm-4">
@ -44,7 +44,7 @@
</div>
</fieldset>
<fieldset>
<legend uncollapsed><span class="text">Authentication</span></legend>
<legend><span class="text">Authentication</span></legend>
<div class="form-group clearfix">
<label class="col-sm-2 control-label" for="smtpAuth">Enable Authentication</label>
<div class="col-sm-4">
@ -65,7 +65,7 @@
</div>
</fieldset>
<div class="pull-right form-actions">
<div class="pull-right form-actions" data-ng-show="access.manageRealm">
<button data-kc-reset data-ng-show="changed">Clear changes</button>
<button data-kc-save data-ng-show="changed">Save</button>
</div>

View file

@ -8,7 +8,7 @@
<li class="active">Token</li>
</ol>
<h2><span>{{realm.realm}}</span> Token Settings</h2>
<form class="form-horizontal" name="realmForm" novalidate>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
<fieldset class="border-top">
<div class="form-group">
<label class="col-sm-2 control-label" for="rememberMe">Remember Me</label>
@ -113,7 +113,7 @@
</div>
</div>
</fieldset>
<div class="pull-right form-actions">
<div class="pull-right form-actions" data-ng-show="access.manageRealm">
<button kc-reset data-ng-show="changed">Clear changes</button>
<button kc-save data-ng-show="changed">Save</button>
</div>

View file

@ -28,7 +28,7 @@
</ol>
<h2 data-ng-show="create"><span>{{realm.realm}}</span> Add Role</h2>
<p class="subtitle" data-ng-show="create"><span class="required">*</span> Required fields</p>
<form class="form-horizontal" name="realmForm" novalidate>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
<fieldset>
<div class="form-group">
<label class="col-sm-2 control-label" for="name">Role name <span class="required" data-ng-show="create">*</span></label>
@ -96,7 +96,7 @@
<div class="col-sm-4">
<div class="input-group">
<div class="select-kc">
<select id="applications" name="applications" ng-change="changeApplication()" ng-model="compositeApp" ng-options="a.name for a in applications">
<select id="applications" name="applications" ng-change="changeApplication()" ng-model="compositeApp" ng-options="a.name for a in applications" ng-disabled="false">
<option value="" selected> Select an Application...</option>
</select>
</div>
@ -133,7 +133,7 @@
</div>
</fieldset>
<div class="pull-right form-actions" data-ng-show="!create">
<div class="pull-right form-actions" data-ng-show="!create && access.manageRealm">
<button kc-reset data-ng-show="changed">Clear changes</button>
<button kc-save data-ng-show="changed">Save</button>
<button kc-delete data-ng-click="remove()" data-ng-hide="changed">Delete</button>

View file

@ -13,7 +13,7 @@
<table class="table">
<thead>
<tr>
<th class="kc-table-actions" colspan="3">
<th class="kc-table-actions" colspan="3" data-ng-show="access.manageRealm">
<div class="pull-right">
<a class="btn btn-primary" href="#/create/role/{{realm.realm}}">Add Role</a>
<!-- <button class="remove disabled">Remove</button> -->

View file

@ -2,7 +2,7 @@
<div id="content-area" class="col-md-9" role="main">
<ul class="nav nav-tabs nav-tabs-pf" data-ng-show="!create">
<li><a href="#/realms/{{realm.realm}}/users/{{user.username}}">Attributes</a></li>
<li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/user-credentials">Credentials</a></li>
<li data-ng-show="access.manageUsers"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/user-credentials">Credentials</a></li>
<li class="active"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/role-mappings">Role Mappings</a></li>
</ul>
<div id="content">
@ -13,7 +13,7 @@
<li class="active">Role Mappings</li>
</ol>
<h2><span>{{user.username}}'s</span> Role Mappings</h2>
<form class="form-horizontal" name="realmForm" novalidate>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageUsers">
<fieldset>
<legend><span class="text">Realm Roles</span> </legend>
@ -54,7 +54,7 @@
<div class="col-sm-4">
<div class="input-group">
<div class="select-kc">
<select id="applications" name="applications" ng-change="changeApplication()" ng-model="application" ng-options="a.name for a in applications">
<select id="applications" name="applications" ng-change="changeApplication()" ng-model="application" ng-options="a.name for a in applications" ng-disabled="false">
<option value="" selected> Select an Application...</option>
</select>
</div>

View file

@ -3,7 +3,7 @@
<ul class="nav nav-tabs nav-tabs-pf" data-ng-show="!create">
<li class="active"><a href="#/realms/{{realm.realm}}/users/{{user.username}}">Attributes</a></li>
<li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/user-credentials">Credentials</a></li>
<li data-ng-show="access.manageUsers"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/user-credentials">Credentials</a></li>
<li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/role-mappings">Role Mappings</a></li>
</ul>
@ -22,7 +22,7 @@
</ol>
<h2 data-ng-hide="create"><span>{{user.username}}'s</span> Attributes</h2>
<form class="form-horizontal" name="userForm" novalidate>
<form class="form-horizontal" name="userForm" novalidate kc-read-only="!access.manageUsers">
<span class="fieldset-notice"><span class="required">*</span> Required fields</span>
<fieldset class="border-top">
<div class="form-group">
@ -79,12 +79,12 @@
</div>
</div>
</fieldset>
<div class="pull-right form-actions" data-ng-show="create">
<div class="pull-right form-actions" data-ng-show="create && access.manageUsers">
<button kc-cancel data-ng-click="cancel()">Cancel</button>
<button kc-save data-ng-show="changed">Save</button>
</div>
<div class="pull-right form-actions" data-ng-show="!create">
<div class="pull-right form-actions" data-ng-show="!create && access.manageUsers">
<button kc-reset data-ng-show="changed">Clear changes</button>
<button kc-save data-ng-show="changed">Save</button>
<button kc-delete data-ng-click="remove()" data-ng-hide="changed">Delete</button>

View file

@ -1,10 +1,10 @@
<ul class="nav nav-tabs nav-tabs-pf">
<li ng-class="{active: !path[2]}"><a href="#/realms/{{realm.realm}}">General</a></li>
<li ng-class="{active: path[2] == 'social'}" data-ng-show="kcSocial && auth.hasAccess(realm.realm, 'manage-realm')"><a href="#/realms/{{realm.realm}}/social-settings">Social</a></li>
<li ng-class="{active: path[2] == 'roles'}" data-ng-show="access.manageRealm"><a href="#/realms/{{realm.realm}}/roles">Roles</a></li>
<li ng-class="{active: path[2] == 'default-roles'}" data-ng-show="access.manageRealm"><a href="#/realms/{{realm.realm}}/default-roles">Default Roles</a></li>
<li ng-class="{active: path[2] == 'required-credentials'}" data-ng-show="access.manageRealm"><a href="#/realms/{{realm.realm}}/required-credentials">Credentials</a></li>
<li ng-class="{active: path[2] == 'token-settings'}" data-ng-show="access.manageRealm"><a href="#/realms/{{realm.realm}}/token-settings">Token</a></li>
<li ng-class="{active: path[2] == 'keys-settings'}" data-ng-show="access.manageRealm"><a href="#/realms/{{realm.realm}}/keys-settings">Keys</a></li>
<li ng-class="{active: path[2] == 'smtp-settings'}" data-ng-show="access.manageRealm"><a href="#/realms/{{realm.realm}}/smtp-settings">Email</a></li>
<li ng-class="{active: path[2] == 'social'}" data-ng-show="kcSocial && access.viewRealm"><a href="#/realms/{{realm.realm}}/social-settings">Social</a></li>
<li ng-class="{active: path[2] == 'roles'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/roles">Roles</a></li>
<li ng-class="{active: path[2] == 'default-roles'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/default-roles">Default Roles</a></li>
<li ng-class="{active: path[2] == 'required-credentials'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/required-credentials">Credentials</a></li>
<li ng-class="{active: path[2] == 'token-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/token-settings">Token</a></li>
<li ng-class="{active: path[2] == 'keys-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/keys-settings">Keys</a></li>
<li ng-class="{active: path[2] == 'smtp-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/smtp-settings">Email</a></li>
</ul>

View file

@ -9,12 +9,17 @@ public class AdminRoles {
public static String ADMIN = "admin";
public static String VIEW_REALM = "view-realm";
public static String VIEW_USERS = "view-users";
public static String VIEW_APPLICATIONS = "view-applications";
public static String VIEW_CLIENTS = "view-clients";
public static String MANAGE_REALM = "manage-realm";
public static String MANAGE_USERS = "manage-users";
public static String MANAGE_APPLICATIONS = "manage-applications";
public static String MANAGE_CLIENTS = "manage-clients";
public static String[] ALL_REALM_ROLES = {MANAGE_REALM, MANAGE_USERS, MANAGE_APPLICATIONS, MANAGE_CLIENTS};
public static String[] ALL_REALM_ROLES = {VIEW_REALM, VIEW_USERS, VIEW_APPLICATIONS, VIEW_CLIENTS, MANAGE_REALM, MANAGE_USERS, MANAGE_APPLICATIONS, MANAGE_CLIENTS};
public static String getAdminApp(RealmModel realm) {
return realm.getName() + APP_SUFFIX;

View file

@ -1,23 +1,14 @@
package org.keycloak.services.managers;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.Constants;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.representations.AccessToken;
import javax.ws.rs.ForbiddenException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class Auth {
private final boolean cookie;
private final RealmModel realm;
private final AccessToken token;
@ -62,45 +53,25 @@ public class Auth {
return token;
}
public void require(String role) {
if (!has(role)) {
throw new ForbiddenException();
}
}
public void require(String app, String role) {
if (!has(app, role)) {
throw new ForbiddenException();
}
}
public void require(ApplicationModel app, String role) {
if (!has(app, role)) {
throw new ForbiddenException();
}
}
public void requireOneOf(String app, String... roles) {
if(!hasOneOf(app, roles)) {
throw new ForbiddenException();
}
}
public void requireOneOf(ApplicationModel app, String... roles) {
if(!hasOneOf(app, roles)) {
throw new ForbiddenException();
}
}
public boolean has(String role) {
if (cookie) {
public boolean hasRealmRole(String role) {
if (cookie) {
return realm.hasRole(user, realm.getRole(role));
} else {
return token.getRealmAccess() != null && token.getRealmAccess().isUserInRole(role);
AccessToken.Access access = token.getRealmAccess();
return access != null && access.isUserInRole(role);
}
}
public boolean has(String app, String role) {
public boolean hasOneOfRealmRole(String... roles) {
for (String r : roles) {
if (hasRealmRole(r)) {
return true;
}
}
return false;
}
public boolean hasAppRole(String app, String role) {
if (cookie) {
return realm.hasRole(user, realm.getApplicationByName(app).getRole(role));
} else {
@ -109,30 +80,13 @@ public class Auth {
}
}
public boolean has(ApplicationModel app, String role) {
if (cookie) {
return realm.hasRole(user, app.getRole(role));
} else {
AccessToken.Access access = token.getResourceAccess(app.getName());
return access != null && access.isUserInRole(role);
}
}
public boolean hasOneOf(String app, String... roles) {
public boolean hasOneOfAppRole(String app, String... roles) {
for (String r : roles) {
if (has(app, r)) {
if (hasAppRole(app, r)) {
return true;
}
}
return false;
}
public boolean hasOneOf(ApplicationModel app, String... roles) {
for (String r : roles) {
if (has(app, r)) {
return true;
}
}
return false;
}
}

View file

@ -81,7 +81,7 @@ public class AccountService {
Auth auth = getAuth(false);
if (auth != null) {
try {
auth.require(application, AccountRoles.MANAGE_ACCOUNT);
require(auth, AccountRoles.MANAGE_ACCOUNT);
} catch (ForbiddenException e) {
return Flows.forms(realm, request, uriInfo).setError("No access").createErrorPage();
}
@ -113,7 +113,7 @@ public class AccountService {
return forwardToPage(null, AccountPages.ACCOUNT);
} else if (types.contains(MediaType.APPLICATION_JSON_TYPE)) {
Auth auth = getAuth(true);
auth.requireOneOf(application, AccountRoles.MANAGE_ACCOUNT, AccountRoles.VIEW_PROFILE);
requireOneOf(auth, AccountRoles.MANAGE_ACCOUNT, AccountRoles.VIEW_PROFILE);
return Cors.add(request, Response.ok(ModelToRepresentation.toRepresentation(auth.getUser()))).auth().allowedOrigins(auth.getClient()).build();
} else {
@ -138,7 +138,7 @@ public class AccountService {
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response processAccountUpdate(final MultivaluedMap<String, String> formData) {
Auth auth = getAuth(true);
auth.require(application, AccountRoles.MANAGE_ACCOUNT);
require(auth, AccountRoles.MANAGE_ACCOUNT);
UserModel user = auth.getUser();
@ -160,7 +160,7 @@ public class AccountService {
@GET
public Response processTotpRemove() {
Auth auth = getAuth(true);
auth.require(application, AccountRoles.MANAGE_ACCOUNT);
require(auth, AccountRoles.MANAGE_ACCOUNT);
UserModel user = auth.getUser();
user.setTotp(false);
@ -174,7 +174,7 @@ public class AccountService {
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response processTotpUpdate(final MultivaluedMap<String, String> formData) {
Auth auth = getAuth(true);
auth.require(application, AccountRoles.MANAGE_ACCOUNT);
require(auth, AccountRoles.MANAGE_ACCOUNT);
UserModel user = auth.getUser();
@ -204,7 +204,7 @@ public class AccountService {
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response processPasswordUpdate(final MultivaluedMap<String, String> formData) {
Auth auth = getAuth(true);
auth.require(application, AccountRoles.MANAGE_ACCOUNT);
require(auth, AccountRoles.MANAGE_ACCOUNT);
UserModel user = auth.getUser();
@ -345,4 +345,16 @@ public class AccountService {
return null;
}
public void require(Auth auth, String role) {
if (!auth.hasAppRole(application.getName(), role)) {
throw new ForbiddenException();
}
}
public void requireOneOf(Auth auth, String... roles) {
if (!auth.hasOneOfAppRole(application.getName(), roles)) {
throw new ForbiddenException();
}
}
}

View file

@ -234,6 +234,7 @@ public class AdminService {
if (auth == null) {
throw new NotAuthorizedException("Bearer");
}
RealmsAdminResource adminResource = new RealmsAdminResource(auth, tokenManager);
resourceContext.initResource(adminResource);
return adminResource;

View file

@ -34,9 +34,10 @@ import java.util.Set;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class ApplicationResource extends RoleContainerResource {
public class ApplicationResource {
protected static final Logger logger = Logger.getLogger(RealmAdminResource.class);
protected RealmModel realm;
private RealmAuth auth;
protected ApplicationModel application;
protected KeycloakSession session;
@Context
@ -49,16 +50,20 @@ public class ApplicationResource extends RoleContainerResource {
return (KeycloakApplication)keycloak;
}
public ApplicationResource(RealmModel realm, ApplicationModel applicationModel, KeycloakSession session) {
super(realm, applicationModel);
public ApplicationResource(RealmModel realm, RealmAuth auth, ApplicationModel applicationModel, KeycloakSession session) {
this.realm = realm;
this.auth = auth;
this.application = applicationModel;
this.session = session;
auth.init(RealmAuth.Resource.APPLICATION);
}
@PUT
@Consumes(MediaType.APPLICATION_JSON)
public void update(final ApplicationRepresentation rep) {
auth.requireManage();
ApplicationManager applicationManager = new ApplicationManager(new RealmManager(session));
applicationManager.updateApplication(rep, application);
}
@ -68,6 +73,8 @@ public class ApplicationResource extends RoleContainerResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public ApplicationRepresentation getApplication() {
auth.requireView();
ApplicationManager applicationManager = new ApplicationManager(new RealmManager(session));
return applicationManager.toRepresentation(application);
}
@ -78,6 +85,8 @@ public class ApplicationResource extends RoleContainerResource {
@Path("installation/json")
@Produces(MediaType.APPLICATION_JSON)
public String getInstallation() throws IOException {
auth.requireView();
ApplicationManager applicationManager = new ApplicationManager(new RealmManager(session));
Object rep = applicationManager.toInstallationRepresentation(realm, application, getKeycloakApplication().getBaseUri(uriInfo));
@ -90,6 +99,8 @@ public class ApplicationResource extends RoleContainerResource {
@Path("installation/jboss")
@Produces(MediaType.TEXT_PLAIN)
public String getJBossInstallation() throws IOException {
auth.requireView();
ApplicationManager applicationManager = new ApplicationManager(new RealmManager(session));
return applicationManager.toJBossSubsystemConfig(realm, application, getKeycloakApplication().getBaseUri(uriInfo));
}
@ -97,6 +108,8 @@ public class ApplicationResource extends RoleContainerResource {
@DELETE
@NoCache
public void deleteApplication() {
auth.requireManage();
realm.removeApplication(application.getId());
}
@ -105,6 +118,8 @@ public class ApplicationResource extends RoleContainerResource {
@Produces("application/json")
@Consumes("application/json")
public CredentialRepresentation regenerateSecret() {
auth.requireManage();
logger.debug("regenerateSecret");
UserCredentialModel cred = new ApplicationManager().generateSecret(realm, application);
CredentialRepresentation rep = ModelToRepresentation.toRepresentation(cred);
@ -115,6 +130,8 @@ public class ApplicationResource extends RoleContainerResource {
@GET
@Produces("application/json")
public CredentialRepresentation getClientSecret() {
auth.requireView();
logger.debug("getClientSecret");
UserCredentialModel model = realm.getSecret(application.getApplicationUser());
if (model == null) throw new NotFoundException("Application does not have a secret");
@ -124,7 +141,12 @@ public class ApplicationResource extends RoleContainerResource {
@Path("scope-mappings")
public ScopeMappedResource getScopeMappedResource() {
return new ScopeMappedResource(realm, application.getApplicationUser(), session);
return new ScopeMappedResource(realm, auth, application.getApplicationUser(), session);
}
@Path("roles")
public RoleContainerResource getRoleContainerResource() {
return new RoleContainerResource(realm, auth, application);
}
@Path("allowed-origins")
@ -132,6 +154,8 @@ public class ApplicationResource extends RoleContainerResource {
@Produces("application/json")
public Set<String> getAllowedOrigins()
{
auth.requireView();
return application.getApplicationUser().getWebOrigins();
}
@ -140,6 +164,8 @@ public class ApplicationResource extends RoleContainerResource {
@Consumes("application/json")
public void updateAllowedOrigins(Set<String> allowedOrigins)
{
auth.requireManage();
application.getApplicationUser().setWebOrigins(allowedOrigins);
}
@ -148,6 +174,8 @@ public class ApplicationResource extends RoleContainerResource {
@Consumes("application/json")
public void deleteAllowedOrigins(Set<String> allowedOrigins)
{
auth.requireManage();
for (String origin : allowedOrigins) {
application.getApplicationUser().removeWebOrigin(origin);
}

View file

@ -32,6 +32,7 @@ import java.util.List;
public class ApplicationsResource {
protected static final Logger logger = Logger.getLogger(RealmAdminResource.class);
protected RealmModel realm;
private RealmAuth auth;
@Context
protected ResourceContext resourceContext;
@ -39,19 +40,32 @@ public class ApplicationsResource {
@Context
protected KeycloakSession session;
public ApplicationsResource(RealmModel realm) {
public ApplicationsResource(RealmModel realm, RealmAuth auth) {
this.realm = realm;
this.auth = auth;
auth.init(RealmAuth.Resource.APPLICATION);
}
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public List<ApplicationRepresentation> getApplications() {
auth.requireAny();
List<ApplicationRepresentation> rep = new ArrayList<ApplicationRepresentation>();
List<ApplicationModel> applicationModels = realm.getApplications();
ApplicationManager resourceManager = new ApplicationManager(new RealmManager(session));
boolean view = auth.hasView();
for (ApplicationModel applicationModel : applicationModels) {
rep.add(resourceManager.toRepresentation(applicationModel));
if (view) {
rep.add(resourceManager.toRepresentation(applicationModel));
} else {
ApplicationRepresentation app = new ApplicationRepresentation();
app.setName(applicationModel.getName());
rep.add(app);
}
}
return rep;
}
@ -59,6 +73,8 @@ public class ApplicationsResource {
@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response createApplication(final @Context UriInfo uriInfo, final ApplicationRepresentation rep) {
auth.requireManage();
if (realm.getApplicationNameMap().containsKey(rep.getName())) {
return Flows.errors().exists("Application " + rep.getName() + " already exists");
}
@ -73,7 +89,7 @@ public class ApplicationsResource {
if (applicationModel == null) {
throw new NotFoundException("Could not find application: " + name);
}
ApplicationResource applicationResource = new ApplicationResource(realm, applicationModel, session);
ApplicationResource applicationResource = new ApplicationResource(realm, auth, applicationModel, session);
resourceContext.initResource(applicationResource);
return applicationResource;
}

View file

@ -38,6 +38,7 @@ import java.util.List;
public class OAuthClientResource {
protected static final Logger logger = Logger.getLogger(RealmAdminResource.class);
protected RealmModel realm;
private RealmAuth auth;
protected OAuthClientModel oauthClient;
protected KeycloakSession session;
@Context
@ -50,15 +51,20 @@ public class OAuthClientResource {
return (KeycloakApplication)application;
}
public OAuthClientResource(RealmModel realm, OAuthClientModel oauthClient, KeycloakSession session) {
public OAuthClientResource(RealmModel realm, RealmAuth auth, OAuthClientModel oauthClient, KeycloakSession session) {
this.realm = realm;
this.auth = auth;
this.oauthClient = oauthClient;
this.session = session;
auth.init(RealmAuth.Resource.CLIENT);
}
@PUT
@Consumes(MediaType.APPLICATION_JSON)
public void update(final OAuthClientRepresentation rep) {
auth.requireManage();
OAuthClientManager manager = new OAuthClientManager(realm);
manager.update(rep, oauthClient);
}
@ -68,6 +74,8 @@ public class OAuthClientResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public OAuthClientRepresentation getOAuthClient() {
auth.requireView();
return OAuthClientManager.toRepresentation(oauthClient);
}
@ -76,6 +84,8 @@ public class OAuthClientResource {
@Path("installation")
@Produces(MediaType.APPLICATION_JSON)
public String getInstallation() throws IOException {
auth.requireView();
OAuthClientManager manager = new OAuthClientManager(realm);
Object rep = manager.toInstallationRepresentation(realm, oauthClient, getApplication().getBaseUri(uriInfo));
@ -86,6 +96,8 @@ public class OAuthClientResource {
@DELETE
@NoCache
public void deleteOAuthClient() {
auth.requireManage();
realm.removeOAuthClient(oauthClient.getId());
}
@ -94,6 +106,8 @@ public class OAuthClientResource {
@Produces("application/json")
@Consumes("application/json")
public CredentialRepresentation regenerateSecret() {
auth.requireManage();
logger.debug("regenerateSecret");
UserCredentialModel cred = UserCredentialModel.generateSecret();
realm.updateCredential(oauthClient.getOAuthAgent(), cred);
@ -105,6 +119,8 @@ public class OAuthClientResource {
@GET
@Produces("application/json")
public CredentialRepresentation getClientSecret() {
auth.requireView();
logger.debug("getClientSecret");
UserCredentialModel model = realm.getSecret(oauthClient.getOAuthAgent());
if (model == null) throw new NotFoundException("Application does not have a secret");
@ -113,7 +129,7 @@ public class OAuthClientResource {
@Path("scope-mappings")
public ScopeMappedResource getScopeMappedResource() {
return new ScopeMappedResource(realm, oauthClient.getOAuthAgent(), session);
return new ScopeMappedResource(realm, auth, oauthClient.getOAuthAgent(), session);
}

View file

@ -35,10 +35,14 @@ public class OAuthClientsResource {
@Context
protected ResourceContext resourceContext;
private RealmAuth auth;
public OAuthClientsResource(RealmModel realm, KeycloakSession session) {
public OAuthClientsResource(RealmModel realm, RealmAuth auth, KeycloakSession session) {
this.auth = auth;
this.realm = realm;
this.session = session;
auth.init(RealmAuth.Resource.CLIENT);
}
@GET
@ -47,8 +51,16 @@ public class OAuthClientsResource {
public List<OAuthClientRepresentation> getOAuthClients() {
List<OAuthClientRepresentation> rep = new ArrayList<OAuthClientRepresentation>();
List<OAuthClientModel> oauthModels = realm.getOAuthClients();
boolean view = auth.hasView();
for (OAuthClientModel oauth : oauthModels) {
rep.add(OAuthClientManager.toRepresentation(oauth));
if (view) {
rep.add(OAuthClientManager.toRepresentation(oauth));
} else {
OAuthClientRepresentation client = new OAuthClientRepresentation();
client.setName(oauth.getOAuthAgent().getLoginName());
rep.add(client);
}
}
return rep;
}
@ -56,6 +68,8 @@ public class OAuthClientsResource {
@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response createOAuthClient(final @Context UriInfo uriInfo, final OAuthClientRepresentation rep) {
auth.requireManage();
OAuthClientManager resourceManager = new OAuthClientManager(realm);
OAuthClientModel oauth = resourceManager.create(rep);
return Response.created(uriInfo.getAbsolutePathBuilder().path(oauth.getId()).build()).build();
@ -63,11 +77,13 @@ public class OAuthClientsResource {
@Path("{id}")
public OAuthClientResource getOAuthClient(final @PathParam("id") String id) {
auth.requireView();
OAuthClientModel oauth = realm.getOAuthClientById(id);
if (oauth == null) {
throw new NotFoundException();
}
OAuthClientResource oAuthClientResource = new OAuthClientResource(realm, oauth, session);
OAuthClientResource oAuthClientResource = new OAuthClientResource(realm, auth, oauth, session);
resourceContext.initResource(oAuthClientResource);
return oAuthClientResource;
}

View file

@ -19,9 +19,9 @@ import javax.ws.rs.core.Context;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class RealmAdminResource extends RoleContainerResource {
public class RealmAdminResource {
protected static final Logger logger = Logger.getLogger(RealmAdminResource.class);
protected Auth auth;
protected RealmAuth auth;
protected RealmModel realm;
private TokenManager tokenManager;
@ -31,43 +31,43 @@ public class RealmAdminResource extends RoleContainerResource {
@Context
protected KeycloakSession session;
public RealmAdminResource(Auth auth, RealmModel realm, TokenManager tokenManager) {
super(realm, realm);
public RealmAdminResource(RealmAuth auth, RealmModel realm, TokenManager tokenManager) {
this.auth = auth;
this.realm = realm;
this.tokenManager = tokenManager;
auth.init(RealmAuth.Resource.REALM);
}
@Path("applications")
public ApplicationsResource getApplications() {
auth.require(AdminRoles.getAdminApp(realm), AdminRoles.MANAGE_APPLICATIONS);
ApplicationsResource applicationsResource = new ApplicationsResource(realm);
ApplicationsResource applicationsResource = new ApplicationsResource(realm, auth);
resourceContext.initResource(applicationsResource);
return applicationsResource;
}
@Path("oauth-clients")
public OAuthClientsResource getOAuthClients() {
auth.require(AdminRoles.getAdminApp(realm), AdminRoles.MANAGE_CLIENTS);
OAuthClientsResource oauth = new OAuthClientsResource(realm, session);
OAuthClientsResource oauth = new OAuthClientsResource(realm, auth, session);
resourceContext.initResource(oauth);
return oauth;
}
@Path("roles")
public RoleContainerResource getRoleContainerResource() {
return new RoleContainerResource(realm, auth, realm);
}
@GET
@NoCache
@Produces("application/json")
public RealmRepresentation getRealm() {
String realmAdminApp = AdminRoles.getAdminApp(realm);
if (auth.has(realmAdminApp, AdminRoles.MANAGE_REALM)) {
if (auth.hasView()) {
return ModelToRepresentation.toRepresentation(realm);
} else {
auth.requireOneOf(AdminRoles.getAdminApp(realm), AdminRoles.ALL_REALM_ROLES);
auth.requireAny();
RealmRepresentation rep = new RealmRepresentation();
rep.setId(realm.getId());
rep.setRealm(realm.getName());
return rep;
@ -77,15 +77,15 @@ public class RealmAdminResource extends RoleContainerResource {
@PUT
@Consumes("application/json")
public void updateRealm(final RealmRepresentation rep) {
auth.require(AdminRoles.getAdminApp(realm), AdminRoles.MANAGE_REALM);
auth.requireManage();
logger.debug("updating realm: " + realm.getName());
new RealmManager(session).updateRealm(rep, realm);
}
@DELETE
public void deleteRealms() {
auth.require(AdminRoles.getAdminApp(realm), AdminRoles.MANAGE_REALM);
public void deleteRealm() {
auth.requireManage();
if (!new RealmManager(session).removeRealm(realm)) {
throw new NotFoundException();
@ -94,18 +94,14 @@ public class RealmAdminResource extends RoleContainerResource {
@Path("users")
public UsersResource users() {
auth.require(AdminRoles.getAdminApp(realm), AdminRoles.MANAGE_USERS);
UsersResource users = new UsersResource(realm, tokenManager);
UsersResource users = new UsersResource(realm, auth, tokenManager);
resourceContext.initResource(users);
return users;
}
@Path("roles-by-id")
public RoleByIdResource rolesById() {
auth.require(AdminRoles.getAdminApp(realm), AdminRoles.MANAGE_REALM);
RoleByIdResource resource = new RoleByIdResource(realm);
RoleByIdResource resource = new RoleByIdResource(realm, auth);
resourceContext.initResource(resource);
return resource;
}

View file

@ -0,0 +1,88 @@
package org.keycloak.services.resources.admin;
import org.keycloak.models.AdminRoles;
import org.keycloak.services.managers.Auth;
import javax.ws.rs.ForbiddenException;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class RealmAuth {
private Resource resource;
public enum Resource {
APPLICATION, CLIENT, USER, REALM
}
private Auth auth;
private String realmAdminApp;
public RealmAuth(Auth auth, String realmAdminApp) {
this.auth = auth;
this.realmAdminApp = realmAdminApp;
}
public RealmAuth init(Resource resource) {
this.resource = resource;
return this;
}
public void requireAny() {
if (!auth.hasOneOfAppRole(realmAdminApp, AdminRoles.ALL_REALM_ROLES)) {
throw new ForbiddenException();
}
}
public boolean hasView() {
return auth.hasOneOfAppRole(realmAdminApp, getViewRole(resource), getManageRole(resource));
}
public boolean hasManage() {
return auth.hasOneOfAppRole(realmAdminApp, getManageRole(resource));
}
public void requireView() {
if (!hasView()) {
throw new ForbiddenException();
}
}
public void requireManage() {
if (!hasManage()) {
throw new ForbiddenException();
}
}
private String getViewRole(Resource resource) {
switch (resource) {
case APPLICATION:
return AdminRoles.VIEW_APPLICATIONS;
case CLIENT:
return AdminRoles.VIEW_CLIENTS;
case USER:
return AdminRoles.VIEW_USERS;
case REALM:
return AdminRoles.VIEW_REALM;
default:
throw new IllegalStateException();
}
}
private String getManageRole(Resource resource) {
switch (resource) {
case APPLICATION:
return AdminRoles.MANAGE_APPLICATIONS;
case CLIENT:
return AdminRoles.MANAGE_CLIENTS;
case USER:
return AdminRoles.MANAGE_USERS;
case REALM:
return AdminRoles.MANAGE_REALM;
default:
throw new IllegalStateException();
}
}
}

View file

@ -6,8 +6,6 @@ import org.jboss.resteasy.plugins.providers.multipart.InputPart;
import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput;
import org.jboss.resteasy.util.GenericType;
import org.keycloak.models.AdminRoles;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.representations.idm.RealmRepresentation;
@ -67,11 +65,10 @@ public class RealmsAdminResource {
for (RealmModel realm : realms) {
String realmAdminApp = AdminRoles.getAdminApp(realm);
if (auth.has(realmAdminApp, AdminRoles.MANAGE_REALM)) {
if (auth.hasAppRole(realmAdminApp, AdminRoles.MANAGE_REALM)) {
reps.add(ModelToRepresentation.toRepresentation(realm));
} else if (auth.hasOneOf(realmAdminApp, AdminRoles.ALL_REALM_ROLES)) {
} else if (auth.hasOneOfAppRole(realmAdminApp, AdminRoles.ALL_REALM_ROLES)) {
RealmRepresentation rep = new RealmRepresentation();
rep.setId(realm.getId());
rep.setRealm(realm.getName());
reps.add(rep);
}
@ -90,7 +87,9 @@ public class RealmsAdminResource {
@POST
@Consumes("application/json")
public Response importRealm(@Context final UriInfo uriInfo, final RealmRepresentation rep) {
auth.require(AdminRoles.ADMIN);
if (!auth.hasRealmRole(AdminRoles.ADMIN)) {
throw new ForbiddenException();
}
logger.debug("importRealm: {0}", rep.getRealm());
RealmManager realmManager = new RealmManager(session);
@ -107,7 +106,9 @@ public class RealmsAdminResource {
@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadRealm(MultipartFormDataInput input) throws IOException {
auth.require(AdminRoles.ADMIN);
if (!auth.hasRealmRole(AdminRoles.ADMIN)) {
throw new ForbiddenException();
}
Map<String, List<InputPart>> uploadForm = input.getFormDataMap();
List<InputPart> inputParts = uploadForm.get("file");
@ -128,9 +129,9 @@ public class RealmsAdminResource {
RealmModel realm = realmManager.getRealmByName(name);
if (realm == null) throw new NotFoundException("{realm} = " + name);
auth.requireOneOf(AdminRoles.getAdminApp(realm), AdminRoles.ALL_REALM_ROLES);
RealmAuth realmAuth = new RealmAuth(auth, AdminRoles.getAdminApp(realm));
RealmAdminResource adminResource = new RealmAdminResource(auth, realm, tokenManager);
RealmAdminResource adminResource = new RealmAdminResource(realmAuth, realm, tokenManager);
resourceContext.initResource(adminResource);
return adminResource;
}

View file

@ -1,14 +1,13 @@
package org.keycloak.services.resources.admin;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.Constants;
import org.keycloak.models.OAuthClientModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleContainerModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.services.managers.ModelToRepresentation;
import org.keycloak.services.resources.admin.RoleResource;
import org.keycloak.services.resources.flows.Flows;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
@ -19,10 +18,6 @@ import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@ -33,8 +28,14 @@ import java.util.Set;
* @version $Revision: 1 $
*/
public class RoleByIdResource extends RoleResource {
public RoleByIdResource(RealmModel realm) {
private final RealmModel realm;
private final RealmAuth auth;
public RoleByIdResource(RealmModel realm, RealmAuth auth) {
super(realm);
this.realm = realm;
this.auth = auth;
}
@Path("{role-id}")
@ -43,6 +44,7 @@ public class RoleByIdResource extends RoleResource {
@Produces("application/json")
public RoleRepresentation getRole(final @PathParam("role-id") String id) {
RoleModel roleModel = getRoleModel(id);
auth.requireView();
return getRole(roleModel);
}
@ -51,6 +53,19 @@ public class RoleByIdResource extends RoleResource {
if (roleModel == null || roleModel.getName().startsWith(Constants.INTERNAL_ROLE)) {
throw new NotFoundException("Could not find role with id: " + id);
}
RealmAuth.Resource r = null;
if (roleModel.getContainer() instanceof RealmModel) {
r = RealmAuth.Resource.REALM;
} else if (roleModel.getContainer() instanceof ApplicationModel) {
r = RealmAuth.Resource.APPLICATION;
} else if (roleModel.getContainer() instanceof OAuthClientModel) {
r = RealmAuth.Resource.CLIENT;
} else if (roleModel.getContainer() instanceof UserModel) {
r = RealmAuth.Resource.USER;
}
auth.init(r);
return roleModel;
}
@ -59,6 +74,7 @@ public class RoleByIdResource extends RoleResource {
@NoCache
public void deleteRole(final @PathParam("role-id") String id) {
RoleModel role = getRoleModel(id);
auth.requireManage();
deleteRole(role);
}
@ -67,6 +83,7 @@ public class RoleByIdResource extends RoleResource {
@Consumes("application/json")
public void updateRole(final @PathParam("role-id") String id, final RoleRepresentation rep) {
RoleModel role = getRoleModel(id);
auth.requireManage();
updateRole(rep, role);
}
@ -75,6 +92,7 @@ public class RoleByIdResource extends RoleResource {
@Consumes("application/json")
public void addComposites(final @PathParam("role-id") String id, List<RoleRepresentation> roles) {
RoleModel role = getRoleModel(id);
auth.requireManage();
addComposites(roles, role);
}
@ -84,6 +102,7 @@ public class RoleByIdResource extends RoleResource {
@Produces("application/json")
public Set<RoleRepresentation> getRoleComposites(final @PathParam("role-id") String id) {
RoleModel role = getRoleModel(id);
auth.requireView();
return getRoleComposites(role);
}
@ -93,6 +112,7 @@ public class RoleByIdResource extends RoleResource {
@Produces("application/json")
public Set<RoleRepresentation> getRealmRoleComposites(final @PathParam("role-id") String id) {
RoleModel role = getRoleModel(id);
auth.requireView();
return getRealmRoleComposites(role);
}
@ -103,6 +123,7 @@ public class RoleByIdResource extends RoleResource {
public Set<RoleRepresentation> getApplicationRoleComposites(final @PathParam("role-id") String id,
final @PathParam("app") String appName) {
RoleModel role = getRoleModel(id);
auth.requireView();
return getApplicationRoleComposites(appName, role);
}
@ -112,8 +133,8 @@ public class RoleByIdResource extends RoleResource {
@Consumes("application/json")
public void deleteComposites(final @PathParam("role-id") String id, List<RoleRepresentation> roles) {
RoleModel role = getRoleModel(id);
auth.requireManage();
deleteComposites(roles, role);
}
}

View file

@ -1,7 +1,6 @@
package org.keycloak.services.resources.admin;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.Constants;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleContainerModel;
@ -10,13 +9,19 @@ import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.services.managers.ModelToRepresentation;
import org.keycloak.services.resources.flows.Flows;
import javax.ws.rs.*;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@ -25,18 +30,24 @@ import java.util.Set;
* @version $Revision: 1 $
*/
public class RoleContainerResource extends RoleResource {
private final RealmModel realm;
private final RealmAuth auth;
protected RoleContainerModel roleContainer;
public RoleContainerResource(RealmModel realm, RoleContainerModel roleContainer) {
public RoleContainerResource(RealmModel realm, RealmAuth auth, RoleContainerModel roleContainer) {
super(realm);
this.realm = realm;
this.auth = auth;
this.roleContainer = roleContainer;
}
@Path("roles")
@Path("")
@GET
@NoCache
@Produces("application/json")
public List<RoleRepresentation> getRoles() {
auth.requireAny();
Set<RoleModel> roleModels = roleContainer.getRoles();
List<RoleRepresentation> roles = new ArrayList<RoleRepresentation>();
for (RoleModel roleModel : roleModels) {
@ -47,10 +58,12 @@ public class RoleContainerResource extends RoleResource {
return roles;
}
@Path("roles")
@Path("")
@POST
@Consumes("application/json")
public Response createRole(final @Context UriInfo uriInfo, final RoleRepresentation rep) {
auth.requireManage();
if (roleContainer.getRole(rep.getName()) != null || rep.getName().startsWith(Constants.INTERNAL_ROLE)) {
return Flows.errors().exists("Role with name " + rep.getName() + " already exists");
}
@ -62,11 +75,13 @@ public class RoleContainerResource extends RoleResource {
return Response.created(uriInfo.getAbsolutePathBuilder().path(role.getName()).build()).build();
}
@Path("roles/{role-name}")
@Path("{role-name}")
@GET
@NoCache
@Produces("application/json")
public RoleRepresentation getRole(final @PathParam("role-name") String roleName) {
auth.requireView();
RoleModel roleModel = roleContainer.getRole(roleName);
if (roleModel == null || roleModel.getName().startsWith(Constants.INTERNAL_ROLE)) {
throw new NotFoundException("Could not find role: " + roleName);
@ -74,10 +89,12 @@ public class RoleContainerResource extends RoleResource {
return getRole(roleModel);
}
@Path("roles/{role-name}")
@Path("{role-name}")
@DELETE
@NoCache
public void deleteRole(final @PathParam("role-name") String roleName) {
auth.requireManage();
RoleModel role = roleContainer.getRole(roleName);
if (role == null) {
throw new NotFoundException("Could not find role: " + roleName);
@ -85,10 +102,12 @@ public class RoleContainerResource extends RoleResource {
deleteRole(role);
}
@Path("roles/{role-name}")
@Path("{role-name}")
@PUT
@Consumes("application/json")
public void updateRole(final @PathParam("role-name") String roleName, final RoleRepresentation rep) {
auth.requireManage();
RoleModel role = roleContainer.getRole(roleName);
if (role == null || role.getName().startsWith(Constants.INTERNAL_ROLE)) {
throw new NotFoundException("Could not find role: " + roleName);
@ -96,10 +115,12 @@ public class RoleContainerResource extends RoleResource {
updateRole(rep, role);
}
@Path("roles/{role-name}/composites")
@Path("{role-name}/composites")
@POST
@Consumes("application/json")
public void addComposites(final @PathParam("role-name") String roleName, List<RoleRepresentation> roles) {
auth.requireManage();
RoleModel role = roleContainer.getRole(roleName);
if (role == null || role.getName().startsWith(Constants.INTERNAL_ROLE)) {
throw new NotFoundException("Could not find role: " + roleName);
@ -107,11 +128,13 @@ public class RoleContainerResource extends RoleResource {
addComposites(roles, role);
}
@Path("roles/{role-name}/composites")
@Path("{role-name}/composites")
@GET
@NoCache
@Produces("application/json")
public Set<RoleRepresentation> getRoleComposites(final @PathParam("role-name") String roleName) {
auth.requireManage();
RoleModel role = roleContainer.getRole(roleName);
if (role == null || role.getName().startsWith(Constants.INTERNAL_ROLE)) {
throw new NotFoundException("Could not find role: " + roleName);
@ -119,11 +142,13 @@ public class RoleContainerResource extends RoleResource {
return getRoleComposites(role);
}
@Path("roles/{role-name}/composites/realm")
@Path("{role-name}/composites/realm")
@GET
@NoCache
@Produces("application/json")
public Set<RoleRepresentation> getRealmRoleComposites(final @PathParam("role-name") String roleName) {
auth.requireManage();
RoleModel role = roleContainer.getRole(roleName);
if (role == null || role.getName().startsWith(Constants.INTERNAL_ROLE)) {
throw new NotFoundException("Could not find role: " + roleName);
@ -131,12 +156,14 @@ public class RoleContainerResource extends RoleResource {
return getRealmRoleComposites(role);
}
@Path("roles/{role-name}/composites/application/{app}")
@Path("{role-name}/composites/application/{app}")
@GET
@NoCache
@Produces("application/json")
public Set<RoleRepresentation> getApplicationRoleComposites(final @PathParam("role-name") String roleName,
final @PathParam("app") String appName) {
auth.requireManage();
RoleModel role = roleContainer.getRole(roleName);
if (role == null || role.getName().startsWith(Constants.INTERNAL_ROLE)) {
throw new NotFoundException("Could not find role: " + roleName);
@ -145,10 +172,12 @@ public class RoleContainerResource extends RoleResource {
}
@Path("roles/{role-name}/composites")
@Path("{role-name}/composites")
@DELETE
@Consumes("application/json")
public void deleteComposites(final @PathParam("role-name") String roleName, List<RoleRepresentation> roles) {
auth.requireManage();
RoleModel role = roleContainer.getRole(roleName);
if (role == null || role.getName().startsWith(Constants.INTERNAL_ROLE)) {
throw new NotFoundException("Could not find role: " + roleName);

View file

@ -16,7 +16,7 @@ import java.util.Set;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class RoleResource {
public abstract class RoleResource {
protected RealmModel realm;
public RoleResource(RealmModel realm) {

View file

@ -32,11 +32,13 @@ import java.util.Set;
*/
public class ScopeMappedResource {
protected RealmModel realm;
private RealmAuth auth;
protected UserModel agent;
protected KeycloakSession session;
public ScopeMappedResource(RealmModel realm, UserModel account, KeycloakSession session) {
public ScopeMappedResource(RealmModel realm, RealmAuth auth, UserModel account, KeycloakSession session) {
this.realm = realm;
this.auth = auth;
this.agent = account;
this.session = session;
}
@ -45,6 +47,8 @@ public class ScopeMappedResource {
@Produces("application/json")
@NoCache
public MappingsRepresentation getScopeMappings() {
auth.requireView();
MappingsRepresentation all = new MappingsRepresentation();
Set<RoleModel> realmMappings = realm.getRealmScopeMappings(agent);
RealmManager manager = new RealmManager(session);
@ -83,6 +87,8 @@ public class ScopeMappedResource {
@Produces("application/json")
@NoCache
public List<RoleRepresentation> getRealmScopeMappings() {
auth.requireView();
Set<RoleModel> realmMappings = realm.getRealmScopeMappings(agent);
List<RoleRepresentation> realmMappingsRep = new ArrayList<RoleRepresentation>();
RealmManager manager = new RealmManager(session);
@ -96,6 +102,8 @@ public class ScopeMappedResource {
@POST
@Consumes("application/json")
public void addRealmScopeMappings(List<RoleRepresentation> roles) {
auth.requireManage();
for (RoleRepresentation role : roles) {
RoleModel roleModel = realm.getRoleById(role.getId());
if (roleModel == null) {
@ -111,6 +119,8 @@ public class ScopeMappedResource {
@DELETE
@Consumes("application/json")
public void deleteRealmScopeMappings(List<RoleRepresentation> roles) {
auth.requireManage();
if (roles == null) {
Set<RoleModel> roleModels = realm.getRealmScopeMappings(agent);
for (RoleModel roleModel : roleModels) {
@ -133,6 +143,8 @@ public class ScopeMappedResource {
@Produces("application/json")
@NoCache
public List<RoleRepresentation> getApplicationScopeMappings(@PathParam("app") String appName) {
auth.requireView();
ApplicationModel app = realm.getApplicationByName(appName);
if (app == null) {
@ -151,6 +163,8 @@ public class ScopeMappedResource {
@POST
@Consumes("application/json")
public void addApplicationScopeMapping(@PathParam("app") String appName, List<RoleRepresentation> roles) {
auth.requireManage();
ApplicationModel app = realm.getApplicationByName(appName);
if (app == null) {
@ -171,6 +185,8 @@ public class ScopeMappedResource {
@DELETE
@Consumes("application/json")
public void deleteApplicationScopeMapping(@PathParam("app") String appName, List<RoleRepresentation> roles) {
auth.requireManage();
ApplicationModel app = realm.getApplicationByName(appName);
if (app == null) {

View file

@ -13,6 +13,7 @@ import org.keycloak.representations.idm.*;
import org.keycloak.services.email.EmailException;
import org.keycloak.services.email.EmailSender;
import org.keycloak.services.managers.AccessCodeEntry;
import org.keycloak.services.managers.Auth;
import org.keycloak.services.managers.ModelToRepresentation;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.managers.TokenManager;
@ -50,11 +51,15 @@ public class UsersResource {
protected RealmModel realm;
private RealmAuth auth;
private TokenManager tokenManager;
public UsersResource(RealmModel realm, TokenManager tokenManager) {
public UsersResource(RealmModel realm, RealmAuth auth, TokenManager tokenManager) {
this.auth = auth;
this.realm = realm;
this.tokenManager = tokenManager;
auth.init(RealmAuth.Resource.USER);
}
@Context
@ -71,6 +76,8 @@ public class UsersResource {
@PUT
@Consumes("application/json")
public void updateUser(final @PathParam("username") String username, final UserRepresentation rep) {
auth.requireManage();
UserModel user = realm.getUser(username);
if (user == null) {
throw new NotFoundException();
@ -81,6 +88,8 @@ public class UsersResource {
@POST
@Consumes("application/json")
public Response createUser(final @Context UriInfo uriInfo, final UserRepresentation rep) {
auth.requireManage();
if (realm.getUser(rep.getUsername()) != null) {
return Flows.errors().exists("User with username " + rep.getUsername() + " already exists");
}
@ -125,6 +134,8 @@ public class UsersResource {
@NoCache
@Produces("application/json")
public UserRepresentation getUser(final @PathParam("username") String username) {
auth.requireView();
UserModel user = realm.getUser(username);
if (user == null || !isUser(user)) {
throw new NotFoundException();
@ -136,6 +147,8 @@ public class UsersResource {
@DELETE
@NoCache
public void deleteUser(final @PathParam("username") String username) {
auth.requireManage();
realm.removeUser(username);
}
@ -147,6 +160,8 @@ public class UsersResource {
@QueryParam("firstName") String first,
@QueryParam("email") String email,
@QueryParam("username") String username) {
auth.requireView();
RealmManager manager = new RealmManager(session);
List<UserRepresentation> results = new ArrayList<UserRepresentation>();
List<UserModel> userModels;
@ -191,6 +206,8 @@ public class UsersResource {
@Produces("application/json")
@NoCache
public MappingsRepresentation getRoleMappings(@PathParam("username") String username) {
auth.requireView();
UserModel user = realm.getUser(username);
if (user == null) {
throw new NotFoundException();
@ -234,6 +251,8 @@ public class UsersResource {
@Produces("application/json")
@NoCache
public List<RoleRepresentation> getRealmRoleMappings(@PathParam("username") String username) {
auth.requireView();
UserModel user = realm.getUser(username);
if (user == null) {
throw new NotFoundException();
@ -252,6 +271,8 @@ public class UsersResource {
@POST
@Consumes("application/json")
public void addRealmRoleMappings(@PathParam("username") String username, List<RoleRepresentation> roles) {
auth.requireManage();
logger.debug("** addRealmRoleMappings: {0}", roles);
UserModel user = realm.getUser(username);
if (user == null) {
@ -273,6 +294,8 @@ public class UsersResource {
@DELETE
@Consumes("application/json")
public void deleteRealmRoleMappings(@PathParam("username") String username, List<RoleRepresentation> roles) {
auth.requireManage();
logger.debug("deleteRealmRoleMappings");
UserModel user = realm.getUser(username);
if (user == null) {
@ -301,6 +324,8 @@ public class UsersResource {
@Produces("application/json")
@NoCache
public List<RoleRepresentation> getApplicationRoleMappings(@PathParam("username") String username, @PathParam("app") String appName) {
auth.requireView();
logger.debug("getApplicationRoleMappings");
UserModel user = realm.getUser(username);
@ -327,6 +352,8 @@ public class UsersResource {
@POST
@Consumes("application/json")
public void addApplicationRoleMapping(@PathParam("username") String username, @PathParam("app") String appName, List<RoleRepresentation> roles) {
auth.requireManage();
logger.debug("addApplicationRoleMapping");
UserModel user = realm.getUser(username);
if (user == null) {
@ -353,6 +380,8 @@ public class UsersResource {
@DELETE
@Consumes("application/json")
public void deleteApplicationRoleMapping(@PathParam("username") String username, @PathParam("app") String appName, List<RoleRepresentation> roles) {
auth.requireManage();
UserModel user = realm.getUser(username);
if (user == null) {
throw new NotFoundException();
@ -389,6 +418,8 @@ public class UsersResource {
@PUT
@Consumes("application/json")
public void resetPassword(@PathParam("username") String username, CredentialRepresentation pass) {
auth.requireManage();
UserModel user = realm.getUser(username);
if (user == null) {
throw new NotFoundException();
@ -406,6 +437,8 @@ public class UsersResource {
@PUT
@Consumes("application/json")
public void removeTotp(@PathParam("username") String username) {
auth.requireManage();
UserModel user = realm.getUser(username);
if (user == null) {
throw new NotFoundException();
@ -418,6 +451,8 @@ public class UsersResource {
@PUT
@Consumes("application/json")
public Response resetPasswordEmail(@PathParam("username") String username) {
auth.requireManage();
UserModel user = realm.getUser(username);
if (user == null) {
throw new NotFoundException();