keycloak-uncached
Changes
admin-ui/src/main/resources/META-INF/resources/admin/partials/oauth-client-credentials.html 64(+64 -0)
admin-ui/src/main/resources/META-INF/resources/admin/partials/oauth-client-scope-mappings.html 92(+92 -0)
examples/as7-eap-demo/product-app/src/main/java/org/jboss/resteasy/example/oauth/ProductDatabaseClient.java 36(+36 -0)
examples/as7-eap-demo/server/pom.xml 5(+5 -0)
examples/as7-eap-dev/database-service/src/main/java/org/jboss/resteasy/example/oauth/CustomerService.java 26(+0 -26)
examples/as7-eap-dev/database-service/src/main/java/org/jboss/resteasy/example/oauth/DataApplication.java 13(+0 -13)
examples/as7-eap-dev/database-service/src/main/java/org/jboss/resteasy/example/oauth/ProductService.java 26(+0 -26)
examples/as7-eap-dev/database-service/src/main/webapp/WEB-INF/jboss-deployment-structure.xml 9(+0 -9)
examples/as7-eap-dev/server/src/main/webapp/saas/css/img/login-register-social-separators.png 0(+0 -0)
examples/as7-eap-dev/server/src/main/webapp/saas/css/img/login-register-social-separators.svg 19(+0 -19)
examples/pom.xml 1(+0 -1)
model/picketlink/src/main/java/org/keycloak/models/picketlink/mappings/ApplicationData.java 10(+0 -10)
Details
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/index.html b/admin-ui/src/main/resources/META-INF/resources/admin/index.html
index 5a1e6fc..188074d 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/index.html
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/index.html
@@ -43,6 +43,7 @@
<script src="js/app.js"></script>
<script src="js/controllers/realm.js"></script>
<script src="js/controllers/applications.js"></script>
+ <script src="js/controllers/oauth-clients.js"></script>
<script src="js/controllers/users.js"></script>
<script src="js/loaders.js"></script>
<script src="js/services.js"></script>
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/js/app.js b/admin-ui/src/main/resources/META-INF/resources/admin/js/app.js
index 3b1d433..f0e6ddd 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/js/app.js
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/js/app.js
@@ -307,6 +307,77 @@ module.config([ '$routeProvider', function($routeProvider) {
},
controller : 'ApplicationListCtrl'
})
+
+ // OAUTH Client
+
+ .when('/realms/:realm/oauth-clients/:oauth/credentials', {
+ templateUrl : 'partials/oauth-client-credentials.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ application : function(OAuthClientLoader) {
+ return OAuthClientLoader();
+ }
+ },
+ controller : 'OAuthClientCredentialsCtrl'
+ })
+ .when('/realms/:realm/oauth-clients/:oauth/scope-mappings', {
+ templateUrl : 'partials/oauth-client-scope-mappings.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ oauth : function(OAuthClientLoader) {
+ return OAuthClientLoader();
+ },
+ applications : function(ApplicationListLoader) {
+ return ApplicationListLoader();
+ },
+ roles : function(RoleListLoader) {
+ return RoleListLoader();
+ }
+ },
+ controller : 'OAuthClientScopeMappingCtrl'
+ })
+
+ .when('/create/oauth-client/:realm', {
+ templateUrl : 'partials/oauth-client-detail.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ oauth : function() {
+ return {};
+ }
+ },
+ controller : 'OAuthClientDetailCtrl'
+ })
+ .when('/realms/:realm/oauth-clients/:oauth', {
+ templateUrl : 'partials/oauth-client-detail.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ oauth : function(OAuthClientLoader) {
+ return OAuthClientLoader();
+ }
+ },
+ controller : 'OAuthClientDetailCtrl'
+ })
+ .when('/realms/:realm/oauth-clients', {
+ templateUrl : 'partials/oauth-client-list.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ oauthClients : function(OAuthClientListLoader) {
+ return OAuthClientListLoader();
+ }
+ },
+ controller : 'OAuthClientListCtrl'
+ })
+
.when('/', {
templateUrl : 'partials/home.html',
controller : 'HomeCtrl'
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/applications.js b/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/applications.js
index 790e53d..ed435ee 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/applications.js
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/applications.js
@@ -194,6 +194,8 @@ module.controller('ApplicationDetailCtrl', function($scope, realm, application,
$scope.application= angular.copy(application);
} else {
$scope.application = {};
+ $scope.application.webOrigins = [];
+ $scope.application.redirectUris = [];
}
$scope.$watch(function() {
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/oauth-clients.js b/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/oauth-clients.js
new file mode 100755
index 0000000..a0a8cd9
--- /dev/null
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/js/controllers/oauth-clients.js
@@ -0,0 +1,296 @@
+module.controller('OAuthClientCredentialsCtrl', function($scope, $location, realm, oauth, OAuthClientCredentials, Notifications) {
+ $scope.realm = realm;
+ $scope.oauth = oauth;
+
+ var required = realm.requiredOAuthClientCredentials;
+
+ for (var i = 0; i < required.length; i++) {
+ if (required[i] == 'password') {
+ $scope.passwordRequired = true;
+ } else if (required[i] == 'totp') {
+ $scope.totpRequired = true;
+ } else if (required[i] == 'cert') {
+ $scope.certRequired = true;
+ }
+ }
+
+ function randomString(len) {
+ var charSet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
+ var randomString = '';
+ for (var i = 0; i < len; i++) {
+ var randomPoz = Math.floor(Math.random() * charSet.length);
+ randomString += charSet.substring(randomPoz,randomPoz+1);
+ }
+ return randomString;
+ }
+
+ $scope.generateTotp = function() {
+ $scope.totp = randomString(5) + '-' + randomString(5) + '-' + randomString(5);
+ }
+
+ $scope.changePassword = function() {
+ if ($scope.password != $scope.confirmPassword) {
+ Notifications.error("Password and confirmation does not match.");
+ $scope.password = "";
+ $scope.confirmPassword = "";
+ return;
+ }
+ var creds = [
+ {
+ type : "password",
+ value : $scope.password
+ }
+ ];
+
+ OAuthClientCredentials.update({ realm : realm.id, oauth : oauth.id }, creds,
+ function() {
+ Notifications.success('The password has been changed.');
+ $scope.password = null;
+ $scope.confirmPassword = null;
+ },
+ function() {
+ Notifications.error("The password was not changed due to a problem.");
+ $scope.password = null;
+ $scope.confirmPassword = null;
+ }
+ );
+ };
+
+ $scope.changeTotp = function() {
+ var creds = [
+ {
+ type : "totp",
+ value : $scope.totp
+ }
+ ];
+
+ OAuthClientCredentials.update({ realm : realm.id, oauth : oauth.id }, creds,
+ function() {
+ Notifications.success('The totp was changed.');
+ $scope.totp = null;
+ },
+ function() {
+ Notifications.error("The totp was not changed due to a problem.");
+ $scope.totp = null;
+ }
+ );
+ };
+ $scope.$watch(function() {
+ return $location.path();
+ }, function() {
+ $scope.path = $location.path().substring(1).split("/");
+ });
+});
+
+module.controller('OAuthClientListCtrl', function($scope, realm, oauthClients, OAuthClient, $location) {
+ $scope.realm = realm;
+ $scope.oauthClients = oauthClients;
+ $scope.$watch(function() {
+ return $location.path();
+ }, function() {
+ $scope.path = $location.path().substring(1).split("/");
+ });
+});
+
+module.controller('OAuthClientDetailCtrl', function($scope, realm, oauth, OAuthClient, $location, Dialog, Notifications) {
+ $scope.realm = realm;
+ $scope.create = !oauth.id;
+ if (!$scope.create) {
+ $scope.oauth= angular.copy(oauth);
+ } else {
+ $scope.oauth = {};
+ $scope.oauth.webOrigins = [];
+ $scope.oauth.redirectUris = [];
+ }
+
+ $scope.$watch(function() {
+ return $location.path();
+ }, function() {
+ $scope.path = $location.path().substring(1).split("/");
+ });
+
+ $scope.$watch('oauth', function() {
+ if (!angular.equals($scope.oauth, oauth)) {
+ $scope.changed = true;
+ }
+ }, true);
+
+ $scope.deleteWebOrigin = function(index) {
+ $scope.oauth.webOrigins.splice(index, 1);
+ }
+ $scope.addWebOrigin = function() {
+ $scope.oauth.webOrigins.push($scope.newWebOrigin);
+ $scope.newWebOrigin = "";
+ }
+ $scope.deleteRedirectUri = function(index) {
+ $scope.oauth.redirectUris.splice(index, 1);
+ }
+ $scope.addRedirectUri = function() {
+ $scope.oauth.redirectUris.push($scope.newRedirectUri);
+ $scope.newRedirectUri = "";
+ }
+
+ $scope.save = function() {
+ if ($scope.create) {
+ OAuthClient.save({
+ realm: realm.id
+ }, $scope.oauth, function (data, headers) {
+ $scope.changed = false;
+ var l = headers().location;
+ var id = l.substring(l.lastIndexOf("/") + 1);
+ $location.url("/realms/" + realm.id + "/oauth-clients/" + id);
+ Notifications.success("The oauth client has been created.");
+ });
+ } else {
+ OAuthClient.update({
+ realm : realm.id,
+ id : oauth.id
+ }, $scope.oauth, function() {
+ $scope.changed = false;
+ oauth = angular.copy($scope.oauth);
+ Notifications.success("Your changes have been saved to the oauth client.");
+ });
+ }
+ };
+
+ $scope.reset = function() {
+ $scope.oauth = angular.copy(oauth);
+ $scope.changed = false;
+ };
+
+ $scope.cancel = function() {
+ $location.url("/realms/" + realm.id + "/oauth-clients");
+ };
+
+ $scope.remove = function() {
+ Dialog.confirmDelete($scope.oauth.name, 'oauth', function() {
+ $scope.oauth.$remove({
+ realm : realm.id,
+ id : $scope.oauth.id
+ }, function() {
+ $location.url("/realms/" + realm.id + "/oauth-clients");
+ Notifications.success("The oauth client has been deleted.");
+ });
+ });
+ };
+
+
+});
+
+module.controller('OAuthClientScopeMappingCtrl', function($scope, $http, realm, oauth, roles, applications, OAuthClientRealmScopeMapping, OAuthClientApplicationScopeMapping, ApplicationRole) {
+ $scope.realm = realm;
+ $scope.oauth = oauth;
+ $scope.realmRoles = angular.copy(roles);
+ $scope.selectedRealmRoles = [];
+ $scope.selectedRealmMappings = [];
+ $scope.realmMappings = [];
+ $scope.applications = applications;
+ $scope.applicationRoles = [];
+ $scope.selectedApplicationRoles = [];
+ $scope.selectedApplicationMappings = [];
+ $scope.applicationMappings = [];
+
+
+
+ $scope.realmMappings = OAuthClientRealmScopeMapping.query({realm : realm.id, oauth : oauth.id}, function(){
+ for (var i = 0; i < $scope.realmMappings.length; i++) {
+ var role = $scope.realmMappings[i];
+ for (var j = 0; j < $scope.realmRoles.length; j++) {
+ var realmRole = $scope.realmRoles[j];
+ if (realmRole.id == role.id) {
+ var idx = $scope.realmRoles.indexOf(realmRole);
+ if (idx != -1) {
+ $scope.realmRoles.splice(idx, 1);
+ break;
+ }
+ }
+ }
+ }
+ });
+
+ $scope.addRealmRole = function() {
+ $http.post('/auth-server/rest/saas/admin/realms/' + realm.id + '/oauth-clients/' + oauth.id + '/scope-mappings/realm',
+ $scope.selectedRealmRoles).success(function() {
+ for (var i = 0; i < $scope.selectedRealmRoles.length; i++) {
+ var role = $scope.selectedRealmRoles[i];
+ var idx = $scope.realmRoles.indexOf($scope.selectedRealmRoles[i]);
+ if (idx != -1) {
+ $scope.realmRoles.splice(idx, 1);
+ $scope.realmMappings.push(role);
+ }
+ }
+ $scope.selectRealmRoles = [];
+ });
+ };
+
+ $scope.deleteRealmRole = function() {
+ $http.delete('/auth-server/rest/saas/admin/realms/' + realm.id + '/oauth-clients/' + oauth.id + '/scope-mappings/realm',
+ {data : $scope.selectedRealmMappings, headers : {"content-type" : "application/json"}}).success(function() {
+ for (var i = 0; i < $scope.selectedRealmMappings.length; i++) {
+ var role = $scope.selectedRealmMappings[i];
+ var idx = $scope.realmMappings.indexOf($scope.selectedRealmMappings[i]);
+ if (idx != -1) {
+ $scope.realmMappings.splice(idx, 1);
+ $scope.realmRoles.push(role);
+ }
+ }
+ $scope.selectedRealmMappings = [];
+ });
+ };
+
+ $scope.addApplicationRole = function() {
+ $http.post('/auth-server/rest/saas/admin/realms/' + realm.id + '/oauth-clients/' + oauth.id + '/scope-mappings/applications/' + $scope.targetApp.id,
+ $scope.selectedApplicationRoles).success(function() {
+ for (var i = 0; i < $scope.selectedApplicationRoles.length; i++) {
+ var role = $scope.selectedApplicationRoles[i];
+ var idx = $scope.applicationRoles.indexOf($scope.selectedApplicationRoles[i]);
+ if (idx != -1) {
+ $scope.applicationRoles.splice(idx, 1);
+ $scope.applicationMappings.push(role);
+ }
+ }
+ $scope.selectedApplicationRoles = [];
+ });
+ };
+
+ $scope.deleteApplicationRole = function() {
+ $http.delete('/auth-server/rest/saas/admin/realms/' + realm.id + '/oauth-clients/' + oauth.id + '/scope-mappings/applications/' + $scope.targetApp.id,
+ {data : $scope.selectedApplicationMappings, headers : {"content-type" : "application/json"}}).success(function() {
+ for (var i = 0; i < $scope.selectedApplicationMappings.length; i++) {
+ var role = $scope.selectedApplicationMappings[i];
+ var idx = $scope.applicationMappings.indexOf($scope.selectedApplicationMappings[i]);
+ if (idx != -1) {
+ $scope.applicationMappings.splice(idx, 1);
+ $scope.applicationRoles.push(role);
+ }
+ }
+ $scope.selectedApplicationMappings = [];
+ });
+ };
+
+
+ $scope.changeApplication = function() {
+ $scope.applicationRoles = ApplicationRole.query({realm : realm.id, application : $scope.targetApp.id}, function() {
+ $scope.applicationMappings = OAuthClientApplicationScopeMapping.query({realm : realm.id, oauth : oauth.id, targetApp : $scope.targetApp.id}, function(){
+ for (var i = 0; i < $scope.applicationMappings.length; i++) {
+ var role = $scope.applicationMappings[i];
+ for (var j = 0; j < $scope.applicationRoles.length; j++) {
+ var realmRole = $scope.applicationRoles[j];
+ if (realmRole.id == role.id) {
+ var idx = $scope.applicationRoles.indexOf(realmRole);
+ if (idx != -1) {
+ $scope.applicationRoles.splice(idx, 1);
+ break;
+ }
+ }
+ }
+ }
+ });
+
+ }
+ );
+ };
+
+
+
+});
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/js/loaders.js b/admin-ui/src/main/resources/META-INF/resources/admin/js/loaders.js
index 41d74a6..7b72c48 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/js/loaders.js
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/js/loaders.js
@@ -134,4 +134,21 @@ module.factory('RoleMappingLoader', function(Loader, RoleMapping, $route, $q) {
role : $route.current.params.role
}
});
-});
\ No newline at end of file
+});
+
+module.factory('OAuthClientLoader', function(Loader, OAuthClient, $route, $q) {
+ return Loader.get(OAuthClient, function() {
+ return {
+ realm : $route.current.params.realm,
+ id : $route.current.params.oauth
+ }
+ });
+});
+
+module.factory('OAuthClientListLoader', function(Loader, OAuthClient, $route, $q) {
+ return Loader.query(OAuthClient, function() {
+ return {
+ realm : $route.current.params.realm
+ }
+ });
+});
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/js/services.js b/admin-ui/src/main/resources/META-INF/resources/admin/js/services.js
index b666617..88d4ec3 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/js/services.js
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/js/services.js
@@ -249,6 +249,45 @@ module.factory('ApplicationOrigins', function($resource) {
});
});
+module.factory('OAuthClient', function($resource) {
+ return $resource('/auth-server/rest/saas/admin/realms/:realm/oauth-clients/:id', {
+ realm : '@realm',
+ id : '@id'
+ }, {
+ update : {
+ method : 'PUT'
+ }
+ });
+});
+
+module.factory('OAuthClientCredentials', function($resource) {
+ return $resource('/auth-server/rest/saas/admin/realms/:realm/oauth-clients/:oauth/credentials', {
+ realm : '@realm',
+ oauth : '@oauth'
+ }, {
+ update : {
+ method : 'PUT',
+ isArray : true
+ }
+ });
+});
+
+module.factory('OAuthClientRealmScopeMapping', function($resource) {
+ return $resource('/auth-server/rest/saas/admin/realms/:realm/oauth-clients/:oauth/scope-mappings/realm', {
+ realm : '@realm',
+ oauth : '@oauth'
+ });
+});
+
+module.factory('OAuthClientApplicationScopeMapping', function($resource) {
+ return $resource('/auth-server/rest/saas/admin/realms/:realm/oauth-clients/:oauth/scope-mappings/applications/:targetApp', {
+ realm : '@realm',
+ oauth : '@oauth',
+ targetApp : '@targetApp'
+ });
+});
+
+
module.factory('Current', function(Realm, $route) {
var current = {};
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-detail.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-detail.html
index 6d1ffa1..2a21202 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-detail.html
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-detail.html
@@ -47,14 +47,6 @@
<input ng-model="application.enabled" name="enabled" id="enabled" onoffswitch />
</div>
<div class="form-group">
- <label for="baseUrl" class="control-label">Base URL</label>
-
- <div class="controls">
- <input class="input-small" type="text" name="baseUrl" id="baseUrl"
- data-ng-model="application.baseUrl">
- </div>
- </div>
- <div class="form-group">
<label for="adminUrl" class="control-label">Admin URL</label>
<div class="controls">
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-list.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-list.html
index 16a1697..d472c61 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-list.html
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/application-list.html
@@ -36,12 +36,11 @@
<tr data-ng-show="applications.length > 0">
<th>Application Name</th>
<th>Enabled</th>
- <th>Base URL</th>
</tr>
</thead>
<tfoot data-ng-show="applications && applications.length > 5"> <!-- todo -->
<tr>
- <td colspan="3">
+ <td colspan="2">
<div class="table-nav">
<a href="#" class="first disabled">First page</a><a href="#" class="prev disabled">Previous
page</a><span><strong>1-8</strong> of <strong>10</strong></span><a href="#"
@@ -55,7 +54,6 @@
<tr ng-repeat="app in applications">
<td><a href="#/realms/{{realm.id}}/applications/{{app.id}}">{{app.name}}</a></td>
<td>{{app.enabled}}</td>
- <td ng-class="{'text-muted': !app.baseUrl}">{{app.baseUrl || "Not defined"}}</td>
</tr>
</tbody>
</table>
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/oauth-client-credentials.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/oauth-client-credentials.html
new file mode 100755
index 0000000..01d63c5
--- /dev/null
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/oauth-client-credentials.html
@@ -0,0 +1,64 @@
+<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">
+ <ul class="rcue-tabs">
+ <li><a href="#/realms/{{realm.id}}/oauth-clients/{{oauth.id}}">Settings</a></li>
+ <li class="active"><a href="#/realms/{{realm.id}}/oauth-clients/{{oauth.id}}/credentials">Credentials</a></li>
+ <li><a href="#/realms/{{realm.id}}/oauth-clients/{{oauth.id}}/scope-mappings">Scope</a></li>
+ </ul>
+ </div>
+ <div id="content">
+ <ol class="breadcrumb" data-ng-hide="create">
+ <li><a href="#/realms/{{realm.id}}">{{realm.realm}}</a></li>
+ <li><a href="#/realms/{{realm.id}}/oauth-clients">OAuth Clients</a></li>
+ <li><a href="#/realms/{{realm.id}}/oauth-clients/{{oauth.id}}">{{oauth.name}}</a></li>
+ <li class="active">Credentials</li>
+ </ol>
+ <h2 data-ng-hide="create"><span>{{oauth.name}}</span> Credentials</h2>
+ <form name="credentialForm" novalidate >
+ <fieldset data-ng-show="passwordRequired">
+ <legend uncollapsed><span class="text">Change Password</span></legend>
+ <div class="form-group">
+ <label for="password">New Password</label>
+ <div class="controls">
+ <input type="password" id="password" name="password" data-ng-model="password" autofocus
+ required>
+ </div>
+ </div>
+ <div class="form-group">
+ <label class="two-lines" for="password">New Password Confirmation</label>
+ <div class="controls">
+ <input type="password" id="confirmPassword" name="confirmPassword" data-ng-model="confirmPassword"
+ required>
+ </div>
+ </div>
+ <div class="form-actions">
+ <button type="submit" data-ng-click="changePassword()" class="primary" ng-show="password != null">Save
+ </button>
+ </div>
+ </fieldset>
+ <fieldset data-ng-show="totpRequired">
+ <legend uncollapsed><span class="text">Change TOTP Key</span></legend>
+ <div class="form-group">
+ <label for="totp">New Key</label>
+ <div class="controls">
+ <input type="text" id="totp" name="totp" data-ng-model="totp" autofocus
+ required>
+ <button type="submit" data-ng-click="generateTotp()">Generate
+ </button>
+ </div>
+ </div>
+ <div class="form-actions">
+ <label></label>
+ <button type="submit" data-ng-click="changeTotp()" class="primary" ng-show="totp != null">Save
+ </button>
+ </div>
+ </fieldset>
+ </form>
+ </div>
+ </div>
+ <div id="container-right-bg"></div>
+ </div>
+</div>
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/oauth-client-detail.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/oauth-client-detail.html
new file mode 100755
index 0000000..22427f3
--- /dev/null
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/oauth-client-detail.html
@@ -0,0 +1,99 @@
+<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-show="!create">
+ <ul class="rcue-tabs">
+ <li class="active"><a href="#/realms/{{realm.id}}/oauth-clients/{{oauth.id}}">Settings</a></li>
+ <li><a href="#/realms/{{realm.id}}/oauth-clients/{{oauth.id}}/credentials">Credentials</a></li>
+ <li><a href="#/realms/{{realm.id}}/oauth-clients/{{oauth.id}}/scope-mappings">Scope</a></li>
+ </ul>
+ </div>
+ <div class="top-nav" data-ng-show="create">
+ <ul class="rcue-tabs">
+ <li></li>
+ </ul>
+ </div>
+ <div id="content">
+ <ol class="breadcrumb" data-ng-show="create">
+ <li><a href="#/realms/{{realm.id}}">{{realm.realm}}</a></li>
+ <li><a href="#/realms/{{realm.id}}/oauth-clients">OAuth Clients</a></li>
+ <li class="active">Add OAuth Client</li>
+ </ol>
+ <h2 class="pull-left" data-ng-show="create"><span>{{realm.realm}}</span> Add OAuth Client</h2>
+ <p class="subtitle" data-ng-show="create"><span class="required">*</span> Required fields</p>
+ <ol class="breadcrumb" data-ng-hide="create">
+ <li><a href="#/realms/{{realm.id}}">{{realm.realm}}</a></li>
+ <li><a href="#/realms/{{realm.id}}/oauth-clients">OAuth Clients</a></li>
+ <li><a href="#/realms/{{realm.id}}/oauth-clients/{{oauth.id}}">{{oauth.name}}</a></li>
+ <li class="active">Settings</li>
+ </ol>
+ <h2 data-ng-hide="create"><span>{{oauth.name}}</span> Settings</h2>
+ <form name="oauthForm" novalidate>
+ <fieldset class="border-top">
+ <div class="form-group">
+ <label for="name">Name <span class="required" data-ng-show="create">*</span></label>
+ <div class="controls">
+ <input type="text" id="name" name="name" data-ng-model="oauth.name" autofocus
+ required>
+ </div>
+ </div>
+
+ <div class="form-group clearfix block">
+ <label for="enabled" class="control-label">Enabled</label>
+ <input ng-model="oauth.enabled" name="enabled" id="enabled" onoffswitch />
+ </div>
+ <div class="form-group">
+ <label for="newWebOrigin" class="control-label">Web Origin</label>
+ <div class="controls">
+ <div ng-repeat="webOrigin in oauth.webOrigins" class="item-deletable">
+ <input class="input-small" type="text" data-ng-class="{'input-below':!$first}"
+ name="webOrigin" id="webOrigin" data-ng-model="webOrigin" readonly />
+ <button type="button" data-ng-click="deleteWebOrigin($index)" class="btn-delete">
+ Delete</button>
+ </div>
+ <input class="input-small" type="text" name="newWebOrigin" id="newWebOrigin"
+ placeholder="New Web Origin..." data-ng-model="newWebOrigin"
+ data-ng-class="{'input-below':oauth.webOrigins.length}" />
+ <button data-ng-click="addWebOrigin()" ng-show="newWebOrigin.length > 0">Add</button>
+ </div>
+ </div>
+ <div class="form-group">
+ <label for="newRedirectUri" class="control-label">Redirect URI</label>
+ <div class="controls">
+ <div ng-repeat="redirectUri in oauth.redirectUris" class="item-deletable">
+ <input class="input-small" type="text" data-ng-class="{'input-below':!$first}"
+ name="redirectUri" id="redirectUri" data-ng-model="redirectUri" readonly />
+ <button type="button" data-ng-click="deleteRedirectUri($index)" class="btn-delete">
+ Delete</button>
+ </div>
+ <input class="input-small" type="text" name="newRedirectUri" id="newRedirectUri"
+ placeholder="New Redirect URI..." data-ng-model="newRedirectUri"
+ data-ng-class="{'input-below':oauth.redirectUris.length}" />
+ <button data-ng-click="addRedirectUri()" ng-show="newRedirectUri.length > 0">Add</button>
+ </div>
+ </div>
+ </fieldset>
+ <div class="form-actions" data-ng-show="create">
+ <button type="submit" kc-save data-ng-show="changed" class="primary">Save
+ </button>
+ <button type="submit" data-ng-click="cancel()">Cancel
+ </button>
+ </div>
+
+ <div class="form-actions" data-ng-show="!create">
+ <button type="submit" kc-save class="primary" data-ng-show="changed">Save
+ </button>
+ <button type="submit" kc-reset data-ng-show="changed">Clear changes
+ </button>
+ <button type="submit" data-ng-click="remove()" class="destructive" data-ng-hide="changed">
+ Delete
+ </button>
+ </div>
+
+ </form>
+ </div>
+ </div>
+ <div id="container-right-bg"></div>
+ </div>
+</div>
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/oauth-client-list.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/oauth-client-list.html
new file mode 100755
index 0000000..e7bb3c6
--- /dev/null
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/oauth-client-list.html
@@ -0,0 +1,67 @@
+<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">
+ <ul class="rcue-tabs">
+ <li></li>
+ </ul>
+ </div>
+ <div id="content">
+ <ol class="breadcrumb">
+ <li><a href="#/realms/{{realm.id}}">{{realm.realm}}</a></li>
+ <li class="active">OAuth Clients</li>
+ </ol>
+ <h2><span>{{realm.realm}}</span> OAuth Clients</h2>
+ <div class="feedback info inline" data-ng-show="!oauthClients || oauthClients.length == 0">
+ <p><strong>You have not configured oauth clients.</strong> <a class="button" href="#/create/oauth-client/{{realm.id}}">Add OAuth Client</a></p>
+ </div>
+ <table data-ng-hide="oauthClients.length == 0">
+ <thead>
+ <tr>
+ <th class="rcue-table-actions" colspan="3">
+ <div class="search-comp clearfix">
+ <input type="text" placeholder="Search..." class="search"
+ onkeyup="if(event.keyCode == 13){$(this).next('button').click();}">
+ <button class="icon-search" tooltip-placement="right"
+ tooltip="Search by oauth client name.">
+ Icon: search
+ </button>
+ </div>
+ <div class="actions">
+ <a class="button" href="#/create/oauth-client/{{realm.id}}">Add OAuth Client</a>
+ </div>
+ </th>
+ </tr>
+ <tr data-ng-show="oauthClients.length > 0">
+ <th>OAuth Client Name</th>
+ <th>Enabled</th>
+ </tr>
+ </thead>
+ <tfoot data-ng-show="oauthClients && oauthClients.length > 5"> <!-- todo -->
+ <tr>
+ <td colspan="2">
+ <div class="table-nav">
+ <a href="#" class="first disabled">First page</a><a href="#" class="prev disabled">Previous
+ page</a><span><strong>1-8</strong> of <strong>10</strong></span><a href="#"
+ class="next">Next
+ page</a><a href="#" class="last">Last page</a>
+ </div>
+ </td>
+ </tr>
+ </tfoot>
+ <tbody>
+ <tr ng-repeat="client in oauthClients">
+ <td><a href="#/realms/{{realm.id}}/oauth-clients/{{client.id}}">{{client.name}}</a></td>
+ <td>{{client.enabled}}</td>
+ </tr>
+ </tbody>
+ </table>
+ <div class="feedback warning inline" data-ng-show="search && oauthClients.length == 0">
+ <p><strong>Your search returned no results.</strong><br>Try modifying the query and try again.</p>
+ </div>
+ </div>
+ </div>
+ <div id="container-right-bg"></div>
+ </div>
+</div>
\ No newline at end of file
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/oauth-client-scope-mappings.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/oauth-client-scope-mappings.html
new file mode 100755
index 0000000..c2d0308
--- /dev/null
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/oauth-client-scope-mappings.html
@@ -0,0 +1,92 @@
+<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">
+ <ul class="rcue-tabs">
+ <li><a href="#/realms/{{realm.id}}/oauth-clients/{{oauth.id}}">Settings</a></li>
+ <li><a href="#/realms/{{realm.id}}/oauth-clients/{{oauth.id}}/credentials">Credentials</a></li>
+ <li class="active"><a href="#/realms/{{realm.id}}/oauth-clients/{{oauth.id}}/scope-mappings">Scope</a></li>
+ </ul>
+ </div>
+ <div id="content">
+ <ol class="breadcrumb" data-ng-hide="create">
+ <li><a href="#/realms/{{realm.id}}">{{realm.realm}}</a></li>
+ <li><a href="#/realms/{{realm.id}}/oauth-clients">OAuth Clients</a></li>
+ <li><a href="#/realms/{{realm.id}}/oauth-clients/{{oauth.id}}">{{oauth.name}}</a></li>
+ <li class="active">Scope</li>
+ </ol>
+ <h2 class="pull-left"><span>{{oauth.name}}</span> Scope Mappings</h2>
+ <p class="subtitle"></p>
+ <form name="realmForm" novalidate>
+ <fieldset>
+ <legend uncollapsed><span class="text">Realm 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.name for r in realmRoles">
+ </select>
+ </div>
+ <div class="middle-buttons">
+ <button type="submit" ng-click="addRealmRole()" tooltip="Move right" tooltip-placement="right"><span class="icon-arrow-right">Move right</span></button>
+ <button type="submit" ng-click="deleteRealmRole()" tooltip="Move left" tooltip-placement="right"><span class="icon-arrow-left">Move left</span></button>
+ </div>
+ <div class="select-title">
+ <label for="assigned">Assigned Roles</label>
+ <select id="assigned" class="form-control" multiple size=5
+ ng-multiple="true"
+ ng-model="selectedRealmMappings"
+ ng-options="r.name for r in realmMappings">
+ </select>
+ </div>
+ </div>
+ </div>
+ </fieldset>
+
+ <fieldset ng-show="applications.length > 0">
+ <legend collapsed><span class="text">Application 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="targetApp" 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="targetApp">
+ <div class="controls changing-selectors application">
+ <div class="select-title">
+ <label for="app-available">Available Roles</label>
+ <select id="app-available" class="form-control" multiple size="5"
+ ng-multiple="true"
+ ng-model="selectedApplicationRoles"
+ ng-options="r.name for r in applicationRoles">
+ </select>
+ </div>
+ <div class="middle-buttons">
+ <button type="submit" ng-click="addApplicationRole()" tooltip="Move right" tooltip-placement="right"><span class="icon-arrow-right">Move right</span></button>
+ <button type="submit" ng-click="deleteApplicationRole()" tooltip="Move left" tooltip-placement="right"><span class="icon-arrow-left">Move left</span></button>
+ </div>
+ <div class="select-title">
+ <label for="app-assigned">Assigned Roles</label>
+ <select id="app-assigned" class="form-control" multiple size=5
+ ng-multiple="true"
+ ng-model="selectedApplicationMappings"
+ ng-options="r.name for r in applicationMappings">
+ </select>
+ </div>
+ </div>
+ </div>
+ </fieldset>
+ </form>
+ </div>
+ </div>
+ <div id="container-right-bg"></div>
+ </div>
+</div>
\ No newline at end of file
diff --git a/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-menu.html b/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-menu.html
index 87e0ed1..40739e8 100755
--- a/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-menu.html
+++ b/admin-ui/src/main/resources/META-INF/resources/admin/partials/realm-menu.html
@@ -3,5 +3,6 @@
<li data-ng-class="(path[2] == 'users' || path[1] == 'user') && 'active'"><a href="#/realms/{{realm.id}}/users">Users</a>
</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] == 'oauth-clients' || path[1] == 'oauth-client') && 'active'"><a href="#/realms/{{realm.id}}/oauth-clients">OAuth Clients</a></li>
</ul>
diff --git a/core/src/main/java/org/keycloak/representations/idm/ApplicationRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/ApplicationRepresentation.java
index bab05a0..dee8ae5 100755
--- a/core/src/main/java/org/keycloak/representations/idm/ApplicationRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/ApplicationRepresentation.java
@@ -12,7 +12,6 @@ public class ApplicationRepresentation {
protected String id;
protected String name;
protected String adminUrl;
- protected String baseUrl;
protected boolean surrogateAuthRequired;
protected boolean enabled;
protected List<CredentialRepresentation> credentials;
@@ -116,14 +115,6 @@ public class ApplicationRepresentation {
this.adminUrl = adminUrl;
}
- public String getBaseUrl() {
- return baseUrl;
- }
-
- public void setBaseUrl(String baseUrl) {
- this.baseUrl = baseUrl;
- }
-
public List<CredentialRepresentation> getCredentials() {
return credentials;
}
diff --git a/core/src/main/java/org/keycloak/representations/idm/OAuthClientRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/OAuthClientRepresentation.java
index 9f1b821..cf46b1e 100755
--- a/core/src/main/java/org/keycloak/representations/idm/OAuthClientRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/OAuthClientRepresentation.java
@@ -10,9 +10,10 @@ import java.util.List;
public class OAuthClientRepresentation {
protected String id;
protected String name;
- protected String baseUrl;
+ protected List<String> redirectUris;
+ protected List<String> webOrigins;
protected boolean enabled;
- protected List<ScopeMappingRepresentation> scopeMappings;
+ protected List<CredentialRepresentation> credentials;
public String getId() {
return id;
@@ -38,23 +39,27 @@ public class OAuthClientRepresentation {
this.enabled = enabled;
}
- public List<ScopeMappingRepresentation> getScopeMappings() {
- return scopeMappings;
+ public List<String> getRedirectUris() {
+ return redirectUris;
}
- public ScopeMappingRepresentation scopeMapping(String username) {
- ScopeMappingRepresentation mapping = new ScopeMappingRepresentation();
- mapping.setUsername(username);
- if (scopeMappings == null) scopeMappings = new ArrayList<ScopeMappingRepresentation>();
- scopeMappings.add(mapping);
- return mapping;
+ public void setRedirectUris(List<String> redirectUris) {
+ this.redirectUris = redirectUris;
}
- public String getBaseUrl() {
- return baseUrl;
+ public List<String> getWebOrigins() {
+ return webOrigins;
}
- public void setBaseUrl(String baseUrl) {
- this.baseUrl = baseUrl;
+ public void setWebOrigins(List<String> webOrigins) {
+ this.webOrigins = webOrigins;
+ }
+
+ public List<CredentialRepresentation> getCredentials() {
+ return credentials;
+ }
+
+ public void setCredentials(List<CredentialRepresentation> credentials) {
+ this.credentials = credentials;
}
}
diff --git a/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java
index 3b9b4d1..7ae7dd2 100755
--- a/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java
@@ -33,11 +33,11 @@ public class RealmRepresentation {
protected Set<String> requiredApplicationCredentials;
protected Set<String> requiredOAuthClientCredentials;
protected List<UserRepresentation> users;
- protected List<UserRepresentation> clients;
protected List<UserRoleMappingRepresentation> roleMappings;
protected List<ScopeMappingRepresentation> scopeMappings;
protected List<SocialMappingRepresentation> socialMappings;
protected List<ApplicationRepresentation> applications;
+ protected List<OAuthClientRepresentation> oauthClients;
protected Map<String, String> socialProviders;
protected Map<String, String> smtpServer;
@@ -69,10 +69,6 @@ public class RealmRepresentation {
return users;
}
- public List<UserRepresentation> getClients() {
- return clients;
- }
-
public List<ApplicationRepresentation> getApplications() {
return applications;
}
@@ -89,10 +85,6 @@ public class RealmRepresentation {
this.users = users;
}
- public void setClients(List<UserRepresentation> clients) {
- this.clients = clients;
- }
-
public UserRepresentation user(String username) {
UserRepresentation user = new UserRepresentation();
user.setUsername(username);
@@ -308,4 +300,12 @@ public class RealmRepresentation {
public void setSmtpServer(Map<String, String> smtpServer) {
this.smtpServer = smtpServer;
}
+
+ public List<OAuthClientRepresentation> getOauthClients() {
+ return oauthClients;
+ }
+
+ public void setOauthClients(List<OAuthClientRepresentation> oauthClients) {
+ this.oauthClients = oauthClients;
+ }
}
diff --git a/examples/as7-eap-demo/product-app/src/main/java/org/jboss/resteasy/example/oauth/ProductDatabaseClient.java b/examples/as7-eap-demo/product-app/src/main/java/org/jboss/resteasy/example/oauth/ProductDatabaseClient.java
new file mode 100755
index 0000000..dc98ec7
--- /dev/null
+++ b/examples/as7-eap-demo/product-app/src/main/java/org/jboss/resteasy/example/oauth/ProductDatabaseClient.java
@@ -0,0 +1,36 @@
+package org.jboss.resteasy.example.oauth;
+
+import org.jboss.resteasy.client.jaxrs.ResteasyClient;
+import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
+import org.keycloak.SkeletonKeySession;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ProductDatabaseClient
+{
+ public static List<String> getProducts(HttpServletRequest request)
+ {
+ SkeletonKeySession session = (SkeletonKeySession)request.getAttribute(SkeletonKeySession.class.getName());
+ ResteasyClient client = new ResteasyClientBuilder()
+ .trustStore(session.getMetadata().getTruststore())
+ .hostnameVerification(ResteasyClientBuilder.HostnameVerificationPolicy.ANY).build();
+ try
+ {
+ Response response = client.target("http://localhost:8080/database/products").request()
+ .header(HttpHeaders.AUTHORIZATION, "Bearer " + session.getTokenString()).get();
+ return response.readEntity(new GenericType<List<String>>(){});
+ }
+ finally
+ {
+ client.close();
+ }
+ }
+}
examples/as7-eap-demo/server/pom.xml 5(+5 -0)
diff --git a/examples/as7-eap-demo/server/pom.xml b/examples/as7-eap-demo/server/pom.xml
index 8b82f4b..94bff30 100755
--- a/examples/as7-eap-demo/server/pom.xml
+++ b/examples/as7-eap-demo/server/pom.xml
@@ -37,6 +37,11 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-model-jpa</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-social-core</artifactId>
<version>${project.version}</version>
</dependency>
diff --git a/examples/as7-eap-demo/server/src/main/resources/META-INF/testrealm.json b/examples/as7-eap-demo/server/src/main/resources/META-INF/testrealm.json
index cc267ee..21b5f99 100755
--- a/examples/as7-eap-demo/server/src/main/resources/META-INF/testrealm.json
+++ b/examples/as7-eap-demo/server/src/main/resources/META-INF/testrealm.json
@@ -28,16 +28,6 @@
]
}
],
- "clients" : [
- {
- "username" : "third-party",
- "enabled": true,
- "credentials" : [
- { "type" : "password",
- "value" : "password" }
- ]
- }
- ],
"roles": [
{
"name": "user",
@@ -58,7 +48,16 @@
{
"username": "third-party",
"roles": ["user"]
+ },
+ {
+ "username": "customer-portal",
+ "roles": ["user"]
+ },
+ {
+ "username": "product-portal",
+ "roles": ["user"]
}
+
],
"applications": [
{
@@ -83,5 +82,18 @@
}
]
}
+ ],
+ "oauthClients": [
+ {
+ "name": "third-party",
+ "enabled": true,
+ "credentials": [
+ {
+ "type": "password",
+ "value": "password"
+ }
+ ]
+ }
]
+
}
examples/pom.xml 1(+0 -1)
diff --git a/examples/pom.xml b/examples/pom.xml
index 664977a..d491f21 100755
--- a/examples/pom.xml
+++ b/examples/pom.xml
@@ -35,6 +35,5 @@
</build>
<modules>
<module>as7-eap-demo</module>
- <module>as7-eap-dev</module>
</modules>
</project>
diff --git a/model/api/src/main/java/org/keycloak/models/ApplicationModel.java b/model/api/src/main/java/org/keycloak/models/ApplicationModel.java
index e5374b8..9039f4f 100755
--- a/model/api/src/main/java/org/keycloak/models/ApplicationModel.java
+++ b/model/api/src/main/java/org/keycloak/models/ApplicationModel.java
@@ -29,10 +29,6 @@ public interface ApplicationModel extends RoleContainerModel, RoleMapperModel, S
void setManagementUrl(String url);
- String getBaseUrl();
-
- void setBaseUrl(String url);
-
List<String> getDefaultRoles();
void addDefaultRole(String name);
diff --git a/model/api/src/main/java/org/keycloak/models/RealmModel.java b/model/api/src/main/java/org/keycloak/models/RealmModel.java
index 57ec2ae..d4af49f 100755
--- a/model/api/src/main/java/org/keycloak/models/RealmModel.java
+++ b/model/api/src/main/java/org/keycloak/models/RealmModel.java
@@ -140,6 +140,8 @@ public interface RealmModel extends RoleContainerModel, RoleMapperModel, ScopeMa
OAuthClientModel addOAuthClient(String name);
OAuthClientModel getOAuthClient(String name);
+ OAuthClientModel getOAuthClientById(String id);
+ boolean removeOAuthClient(String id);
List<OAuthClientModel> getOAuthClients();
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/ApplicationAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/ApplicationAdapter.java
index 1ca34b6..ad5f9e1 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/ApplicationAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/ApplicationAdapter.java
@@ -83,15 +83,6 @@ public class ApplicationAdapter implements ApplicationModel {
}
@Override
- public String getBaseUrl() {
- return null;
- }
-
- @Override
- public void setBaseUrl(String url) {
- }
-
- @Override
public RoleModel getRole(String name) {
Collection<RoleEntity> roles = application.getRoles();
if (roles == null) return null;
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
index 96e539a..7a47448 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
@@ -575,9 +575,13 @@ public class RealmAdapter implements RealmModel {
return false;
}
realm.getApplications().remove(application);
- removeUser(application.getApplicationUser());
em.createQuery("delete from " + ApplicationScopeMappingEntity.class.getSimpleName() + " where application = :application").setParameter("application", application).executeUpdate();
em.createQuery("delete from " + ApplicationUserRoleMappingEntity.class.getSimpleName() + " where application = :application").setParameter("application", application).executeUpdate();
+ em.createQuery("delete from " + ApplicationScopeMappingEntity.class.getSimpleName() + " where user = :user").setParameter("user", application.getApplicationUser()).executeUpdate();
+ em.createQuery("delete from " + RealmScopeMappingEntity.class.getSimpleName() + " where user = :user").setParameter("user", application.getApplicationUser()).executeUpdate();
+ em.createQuery("delete from " + ApplicationUserRoleMappingEntity.class.getSimpleName() + " where user = :user").setParameter("user", application.getApplicationUser()).executeUpdate();
+ em.createQuery("delete from " + RealmUserRoleMappingEntity.class.getSimpleName() + " where user = :user").setParameter("user", application.getApplicationUser()).executeUpdate();
+ removeUser(application.getApplicationUser());
em.remove(application);
return true;
}
@@ -724,6 +728,19 @@ public class RealmAdapter implements RealmModel {
}
@Override
+ public boolean removeOAuthClient(String id) {
+ OAuthClientEntity client = em.find(OAuthClientEntity.class, id);
+ em.createQuery("delete from " + ApplicationScopeMappingEntity.class.getSimpleName() + " where user = :user").setParameter("user", client.getAgent()).executeUpdate();
+ em.createQuery("delete from " + RealmScopeMappingEntity.class.getSimpleName() + " where user = :user").setParameter("user", client.getAgent()).executeUpdate();
+ em.createQuery("delete from " + ApplicationUserRoleMappingEntity.class.getSimpleName() + " where user = :user").setParameter("user", client.getAgent()).executeUpdate();
+ em.createQuery("delete from " + RealmUserRoleMappingEntity.class.getSimpleName() + " where user = :user").setParameter("user", client.getAgent()).executeUpdate();
+ removeUser(client.getAgent());
+ em.remove(client);
+ return true;
+ }
+
+
+ @Override
public OAuthClientModel getOAuthClient(String name) {
TypedQuery<OAuthClientEntity> query = em.createNamedQuery("findOAuthClientByUser", OAuthClientEntity.class);
query.setParameter("name", name);
@@ -731,7 +748,15 @@ public class RealmAdapter implements RealmModel {
List<OAuthClientEntity> entities = query.getResultList();
if (entities.size() == 0) return null;
return new OAuthClientAdapter(entities.get(0));
- }
+ }
+
+ @Override
+ public OAuthClientModel getOAuthClientById(String id) {
+ OAuthClientEntity client = em.find(OAuthClientEntity.class, id);
+ if (client == null) return null;
+ return new OAuthClientAdapter(client);
+ }
+
@Override
public List<OAuthClientModel> getOAuthClients() {
diff --git a/model/picketlink/src/main/java/org/keycloak/models/picketlink/ApplicationAdapter.java b/model/picketlink/src/main/java/org/keycloak/models/picketlink/ApplicationAdapter.java
index 98a510a..e6fe85b 100755
--- a/model/picketlink/src/main/java/org/keycloak/models/picketlink/ApplicationAdapter.java
+++ b/model/picketlink/src/main/java/org/keycloak/models/picketlink/ApplicationAdapter.java
@@ -106,17 +106,6 @@ public class ApplicationAdapter implements ApplicationModel {
}
@Override
- public String getBaseUrl() {
- return applicationData.getBaseUrl();
- }
-
- @Override
- public void setBaseUrl(String url) {
- applicationData.setBaseUrl(url);
- updateApplication();
- }
-
- @Override
public RoleAdapter getRole(String name) {
Role role = SampleModel.getRole(getIdm(), name);
if (role == null) return null;
diff --git a/model/picketlink/src/main/java/org/keycloak/models/picketlink/mappings/ApplicationData.java b/model/picketlink/src/main/java/org/keycloak/models/picketlink/mappings/ApplicationData.java
index 5ce4aca..3adb8f3 100755
--- a/model/picketlink/src/main/java/org/keycloak/models/picketlink/mappings/ApplicationData.java
+++ b/model/picketlink/src/main/java/org/keycloak/models/picketlink/mappings/ApplicationData.java
@@ -13,7 +13,6 @@ public class ApplicationData extends AbstractPartition {
private boolean enabled;
private boolean surrogateAuthRequired;
private String managementUrl;
- private String baseUrl;
private User resourceUser;
private String[] defaultRoles;
@@ -69,15 +68,6 @@ public class ApplicationData extends AbstractPartition {
}
@AttributeProperty
- public String getBaseUrl() {
- return baseUrl;
- }
-
- public void setBaseUrl(String baseUrl) {
- this.baseUrl = baseUrl;
- }
-
- @AttributeProperty
public String[] getDefaultRoles() {
return defaultRoles;
}
diff --git a/model/picketlink/src/main/java/org/keycloak/models/picketlink/mappings/ApplicationEntity.java b/model/picketlink/src/main/java/org/keycloak/models/picketlink/mappings/ApplicationEntity.java
index 0e8d23b..88af1a6 100755
--- a/model/picketlink/src/main/java/org/keycloak/models/picketlink/mappings/ApplicationEntity.java
+++ b/model/picketlink/src/main/java/org/keycloak/models/picketlink/mappings/ApplicationEntity.java
@@ -31,8 +31,6 @@ public class ApplicationEntity implements Serializable {
private boolean surrogateAuthRequired;
@AttributeValue
private String managementUrl;
- @AttributeValue
- private String baseUrl;
@AttributeValue
private String[] defaultRoles;
@@ -82,14 +80,6 @@ public class ApplicationEntity implements Serializable {
this.managementUrl = managementUrl;
}
- public String getBaseUrl() {
- return baseUrl;
- }
-
- public void setBaseUrl(String baseUrl) {
- this.baseUrl = baseUrl;
- }
-
public AccountTypeEntity getResourceUser() {
return resourceUser;
}
diff --git a/model/picketlink/src/main/java/org/keycloak/models/picketlink/RealmAdapter.java b/model/picketlink/src/main/java/org/keycloak/models/picketlink/RealmAdapter.java
index 3f6631c..1300b53 100755
--- a/model/picketlink/src/main/java/org/keycloak/models/picketlink/RealmAdapter.java
+++ b/model/picketlink/src/main/java/org/keycloak/models/picketlink/RealmAdapter.java
@@ -747,6 +747,19 @@ public class RealmAdapter implements RealmModel {
}
@Override
+ public boolean removeOAuthClient(String id) {
+ RelationshipQuery<OAuthClientRelationship> query = getRelationshipManager().createRelationshipQuery(OAuthClientRelationship.class);
+ query.setParameter(OAuthClientRelationship.REALM, realm.getName());
+ query.setParameter(OAuthClientRelationship.ID, id);
+ List<OAuthClientRelationship> results = query.getResultList();
+ if (results.size() == 0) return false;
+ OAuthClientRelationship relationship = results.get(0);
+ getRelationshipManager().remove(relationship);
+ return true;
+ }
+
+
+ @Override
public OAuthClientModel getOAuthClient(String name) {
User user = findPicketlinkUser(name);
if (user == null) return null;
@@ -758,6 +771,17 @@ public class RealmAdapter implements RealmModel {
}
@Override
+ public OAuthClientModel getOAuthClientById(String id) {
+ RelationshipQuery<OAuthClientRelationship> query = getRelationshipManager().createRelationshipQuery(OAuthClientRelationship.class);
+ query.setParameter(OAuthClientRelationship.REALM, realm.getName());
+ query.setParameter(OAuthClientRelationship.ID, id);
+ List<OAuthClientRelationship> results = query.getResultList();
+ if (results.size() == 0) return null;
+ return new OAuthClientAdapter(results.get(0), getIdm(), getRelationshipManager());
+ }
+
+
+ @Override
public List<OAuthClientModel> getOAuthClients() {
RelationshipQuery<OAuthClientRelationship> query = getRelationshipManager().createRelationshipQuery(OAuthClientRelationship.class);
query.setParameter(OAuthClientRelationship.REALM, realm.getName());
diff --git a/services/src/main/java/org/keycloak/services/managers/ApplicationManager.java b/services/src/main/java/org/keycloak/services/managers/ApplicationManager.java
index 51c5e53..8081979 100755
--- a/services/src/main/java/org/keycloak/services/managers/ApplicationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/ApplicationManager.java
@@ -36,13 +36,20 @@ public class ApplicationManager {
this.realmManager = realmManager;
}
+ /**
+ * Does not create scope or role mappings!
+ *
+ * @param realm
+ * @param loginRole
+ * @param resourceRep
+ * @return
+ */
public ApplicationModel createApplication(RealmModel realm, RoleModel loginRole, ApplicationRepresentation resourceRep) {
logger.debug("************ CREATE APPLICATION: {0}" + resourceRep.getName());
ApplicationModel applicationModel = realm.addApplication(resourceRep.getName());
applicationModel.setEnabled(resourceRep.isEnabled());
applicationModel.setManagementUrl(resourceRep.getAdminUrl());
applicationModel.setSurrogateAuthRequired(resourceRep.isSurrogateAuthRequired());
- applicationModel.setBaseUrl(resourceRep.getBaseUrl());
applicationModel.updateApplication();
UserModel resourceUser = applicationModel.getApplicationUser();
@@ -80,6 +87,10 @@ public class ApplicationManager {
applicationModel.updateDefaultRoles(resourceRep.getDefaultRoles());
}
+ return applicationModel;
+ }
+
+ public void createMappings(RealmModel realm, ApplicationRepresentation resourceRep, ApplicationModel applicationModel) {
if (resourceRep.getRoleMappings() != null) {
for (UserRoleMappingRepresentation mapping : resourceRep.getRoleMappings()) {
UserModel user = realm.getUser(mapping.getUsername());
@@ -107,7 +118,6 @@ public class ApplicationManager {
}
}
}
- return applicationModel;
}
public ApplicationModel createApplication(RealmModel realm, ApplicationRepresentation resourceRep) {
@@ -119,7 +129,6 @@ public class ApplicationManager {
resource.setName(rep.getName());
resource.setEnabled(rep.isEnabled());
resource.setManagementUrl(rep.getAdminUrl());
- resource.setBaseUrl(rep.getBaseUrl());
resource.setSurrogateAuthRequired(rep.isSurrogateAuthRequired());
resource.updateApplication();
@@ -145,7 +154,6 @@ public class ApplicationManager {
rep.setEnabled(applicationModel.isEnabled());
rep.setAdminUrl(applicationModel.getManagementUrl());
rep.setSurrogateAuthRequired(applicationModel.isSurrogateAuthRequired());
- rep.setBaseUrl(applicationModel.getBaseUrl());
Set<String> redirectUris = applicationModel.getApplicationUser().getRedirectUris();
if (redirectUris != null) {
diff --git a/services/src/main/java/org/keycloak/services/managers/OAuthClientManager.java b/services/src/main/java/org/keycloak/services/managers/OAuthClientManager.java
index 7840566..651db2f 100755
--- a/services/src/main/java/org/keycloak/services/managers/OAuthClientManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/OAuthClientManager.java
@@ -4,8 +4,16 @@ import org.keycloak.models.Constants;
import org.keycloak.models.OAuthClientModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.OAuthClientRepresentation;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
@@ -26,11 +34,30 @@ public class OAuthClientManager {
public OAuthClientModel create(OAuthClientRepresentation rep) {
OAuthClientModel model = create(rep.getName());
- model.getOAuthAgent().setEnabled(rep.isEnabled());
+ update(rep, model);
+ UserModel resourceUser = model.getOAuthAgent();
+ if (rep.getCredentials() != null) {
+ for (CredentialRepresentation cred : rep.getCredentials()) {
+ UserCredentialModel credential = new UserCredentialModel();
+ credential.setType(cred.getType());
+ credential.setValue(cred.getValue());
+ realm.updateCredential(resourceUser, credential);
+ }
+ }
return model;
}
public void update(OAuthClientRepresentation rep, OAuthClientModel model) {
+ model.getOAuthAgent().setEnabled(rep.isEnabled());
+ List<String> redirectUris = rep.getRedirectUris();
+ if (redirectUris != null) {
+ model.getOAuthAgent().setRedirectUris(new HashSet<String>(redirectUris));
+ }
+
+ List<String> webOrigins = rep.getWebOrigins();
+ if (webOrigins != null) {
+ model.getOAuthAgent().setWebOrigins(new HashSet<String>(webOrigins));
+ }
}
public static OAuthClientRepresentation toRepresentation(OAuthClientModel model) {
@@ -38,6 +65,15 @@ public class OAuthClientManager {
rep.setId(model.getId());
rep.setName(model.getOAuthAgent().getLoginName());
rep.setEnabled(model.getOAuthAgent().isEnabled());
+ Set<String> redirectUris = model.getOAuthAgent().getRedirectUris();
+ if (redirectUris != null) {
+ rep.setRedirectUris(new LinkedList<String>(redirectUris));
+ }
+
+ Set<String> webOrigins = model.getOAuthAgent().getWebOrigins();
+ if (webOrigins != null) {
+ rep.setWebOrigins(new LinkedList<String>(webOrigins));
+ }
return rep;
}
}
diff --git a/services/src/main/java/org/keycloak/services/managers/RealmManager.java b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
index 347aca8..07bf34a 100755
--- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
@@ -4,6 +4,7 @@ import org.jboss.resteasy.logging.Logger;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.OAuthClientModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredCredentialModel;
import org.keycloak.models.RoleModel;
@@ -13,6 +14,7 @@ import org.keycloak.models.UserModel;
import org.keycloak.models.UserModel.RequiredAction;
import org.keycloak.representations.idm.ApplicationRepresentation;
import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.representations.idm.OAuthClientRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.representations.idm.ScopeMappingRepresentation;
@@ -227,14 +229,6 @@ 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) {
for (RoleRepresentation roleRep : rep.getRoles()) {
createRole(newRealm, roleRep);
@@ -247,13 +241,34 @@ public class RealmManager {
}
}
+ Map<String, ApplicationModel> appMap = null;
if (rep.getApplications() != null) {
- Map<String, ApplicationModel> appMap = createApplications(rep, newRealm);
+ appMap = createApplications(rep, newRealm);
for (ApplicationModel app : appMap.values()) {
userMap.put(app.getApplicationUser().getLoginName(), app.getApplicationUser());
}
}
+ if (rep.getOauthClients() != null) {
+ Map<String, OAuthClientModel> oauthMap = createOAuthClients(rep, newRealm);
+ for (OAuthClientModel app : oauthMap.values()) {
+ userMap.put(app.getOAuthAgent().getLoginName(), app.getOAuthAgent());
+ }
+
+ }
+
+ // Now that all possible users are created (users, apps, and oauth clients), do role mappings and scope mappings
+
+ if (rep.getApplications() != null) {
+ ApplicationManager manager = new ApplicationManager(this);
+ for (ApplicationRepresentation appRep : rep.getApplications()) {
+ ApplicationModel model = appMap.get(appRep.getName());
+ manager.createMappings(newRealm, appRep, model);
+
+ }
+ }
+
+
if (rep.getRoleMappings() != null) {
for (UserRoleMappingRepresentation mapping : rep.getRoleMappings()) {
UserModel user = userMap.get(mapping.getUsername());
@@ -381,6 +396,17 @@ public class RealmManager {
return appMap;
}
+ protected Map<String, OAuthClientModel> createOAuthClients(RealmRepresentation realmRep, RealmModel realm) {
+ Map<String, OAuthClientModel> appMap = new HashMap<String, OAuthClientModel>();
+ OAuthClientManager manager = new OAuthClientManager(realm);
+ for (OAuthClientRepresentation rep : realmRep.getOauthClients()) {
+ OAuthClientModel app = manager.create(rep);
+ appMap.put(app.getOAuthAgent().getLoginName(), app);
+ }
+ return appMap;
+ }
+
+
public static UserRepresentation toRepresentation(UserModel user) {
UserRepresentation rep = new UserRepresentation();
rep.setUsername(user.getLoginName());
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientResource.java b/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientResource.java
index f7a2286..138bc3a 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientResource.java
@@ -12,6 +12,7 @@ import org.keycloak.services.managers.OAuthClientManager;
import org.keycloak.services.managers.RealmManager;
import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
@@ -46,11 +47,16 @@ public class OAuthClientResource {
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)
- public OAuthClientRepresentation getApplication() {
- OAuthClientManager manager = new OAuthClientManager(realm);
+ public OAuthClientRepresentation getOAuthClient() {
return OAuthClientManager.toRepresentation(oauthClient);
}
+ @DELETE
+ @NoCache
+ public void deleteOAuthClient() {
+ realm.removeOAuthClient(oauthClient.getId());
+ }
+
@Path("credentials")
@PUT
@Consumes("application/json")
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientsResource.java b/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientsResource.java
index 408a48f..6e4101b 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientsResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/OAuthClientsResource.java
@@ -54,12 +54,12 @@ public class OAuthClientsResource {
public Response createOAuthClient(final @Context UriInfo uriInfo, final OAuthClientRepresentation rep) {
OAuthClientManager resourceManager = new OAuthClientManager(realm);
OAuthClientModel oauth = resourceManager.create(rep);
- return Response.created(uriInfo.getAbsolutePathBuilder().path(oauth.getOAuthAgent().getLoginName()).build()).build();
+ return Response.created(uriInfo.getAbsolutePathBuilder().path(oauth.getId()).build()).build();
}
@Path("{id}")
public OAuthClientResource getOAuthClient(final @PathParam("id") String id) {
- OAuthClientModel oauth = realm.getOAuthClient(id);
+ OAuthClientModel oauth = realm.getOAuthClientById(id);
if (oauth == null) {
throw new NotFoundException();
}
diff --git a/services/src/test/java/org/keycloak/test/ApplicationModelTest.java b/services/src/test/java/org/keycloak/test/ApplicationModelTest.java
index e322481..f96e089 100755
--- a/services/src/test/java/org/keycloak/test/ApplicationModelTest.java
+++ b/services/src/test/java/org/keycloak/test/ApplicationModelTest.java
@@ -40,7 +40,6 @@ public class ApplicationModelTest extends AbstractKeycloakServerTest {
realm = manager.createRealm("original");
application = realm.addApplication("application");
- application.setBaseUrl("http://base");
application.setManagementUrl("http://management");
application.setName("app-name");
application.addRole("role-1");
@@ -83,7 +82,6 @@ public class ApplicationModelTest extends AbstractKeycloakServerTest {
public static void assertEquals(ApplicationModel expected, ApplicationModel actual) {
Assert.assertEquals(expected.getName(), actual.getName());
- Assert.assertEquals(expected.getBaseUrl(), actual.getBaseUrl());
Assert.assertEquals(expected.getManagementUrl(), actual.getManagementUrl());
Assert.assertEquals(expected.getDefaultRoles(), actual.getDefaultRoles());
diff --git a/services/src/test/resources/testrealm-demo.json b/services/src/test/resources/testrealm-demo.json
index 9e96d21..295c70e 100755
--- a/services/src/test/resources/testrealm-demo.json
+++ b/services/src/test/resources/testrealm-demo.json
@@ -25,9 +25,9 @@
]
}
],
- "clients" : [
+ "oauthClients" : [
{
- "username" : "third-party",
+ "name" : "third-party",
"enabled": true,
"credentials" : [
{ "type" : "Password",
diff --git a/testsuite/integration/src/test/resources/testrealm.json b/testsuite/integration/src/test/resources/testrealm.json
index 819e313..66809f5 100755
--- a/testsuite/integration/src/test/resources/testrealm.json
+++ b/testsuite/integration/src/test/resources/testrealm.json
@@ -32,9 +32,9 @@
]
}
],
- "clients" : [
+ "oauthClients" : [
{
- "username" : "third-party",
+ "name" : "third-party",
"enabled": true,
"credentials" : [
{ "type" : "password",