Merge pull request #639 from patriot1burke/master

enhancements
This commit is contained in:
Bill Burke 2014-08-20 11:56:29 -04:00
commit 06c14df0f3
11 changed files with 121 additions and 85 deletions

View file

@ -15,6 +15,7 @@ machine on the network or Internet.
* **customer-app-cli** A pure CLI application that does remote login using OAuth2 browser redirects with the auth server
* **product-app** A WAR application that does remote login using OAuth2 browser redirects with the auth server
* **admin-access-app** A WAR application that does remote REST login to admin console to obtain a list of realm roles from Admin REST API
* **angular-product-app** An Angular JS pure HTML5/Javascript application.
* **database-service** JAX-RS services authenticated by bearer tokens only. The customer and product app invoke on it to get data
* **third-party** Simple WAR that obtain a bearer token using OAuth2 using browser redirects to the auth-server.
* **third-party-cdi** Simple CDI/JSF WAR that obtain a bearer token using OAuth2 using browser redirects to the auth-server.
@ -187,6 +188,24 @@ then using that token to access the Admin REST API.
If you are already logged in, you will not be asked for a username and password, but you will be redirected to
an oauth grant page. This page asks you if you want to grant certain permissions to the third-part app.
Step 9: Angular JS Example
----------------------------------
An Angular JS example using Keycloak to secure it.
[http://localhost:8080/angular-product](http://localhost:8080/angular-product)
If you are already logged in, you will not be asked for a username and password, but you will be redirected to
an oauth grant page. This page asks you if you want to grant certain permissions to the third-part app.
Step 9: Pure HTML5/Javascript Example
----------------------------------
An pure HTML5/Javascript example using Keycloak to secure it.
[http://localhost:8080/customer-portal-js](http://localhost:8080/customer-portal-js)
If you are already logged in, you will not be asked for a username and password, but you will be redirected to
an oauth grant page. This page asks you if you want to grant certain permissions to the third-part app.
Admin Console
==========================

View file

@ -44,6 +44,28 @@ public class AdminClient {
}
}
public static String getContent(HttpEntity entity) throws IOException {
if (entity == null) return null;
InputStream is = entity.getContent();
try {
ByteArrayOutputStream os = new ByteArrayOutputStream();
int c;
while ((c = is.read()) != -1) {
os.write(c);
}
byte[] bytes = os.toByteArray();
String data = new String(bytes);
return data;
} finally {
try {
is.close();
} catch (IOException ignored) {
}
}
}
public static AccessTokenResponse getToken(HttpServletRequest request) throws IOException {
HttpClient client = new HttpClientBuilder()
@ -64,32 +86,14 @@ public class AdminClient {
int status = response.getStatusLine().getStatusCode();
HttpEntity entity = response.getEntity();
if (status != 200) {
throw new IOException("Bad status: " + status);
String json = getContent(entity);
throw new IOException("Bad status: " + status + " response: " + json);
}
if (entity == null) {
throw new IOException("No Entity");
}
InputStream is = entity.getContent();
try {
ByteArrayOutputStream os = new ByteArrayOutputStream();
int c;
while ((c = is.read()) != -1) {
os.write(c);
}
byte[] bytes = os.toByteArray();
String json = new String(bytes);
try {
return JsonSerialization.readValue(json, AccessTokenResponse.class);
} catch (IOException e) {
throw new IOException(json, e);
}
} finally {
try {
is.close();
} catch (IOException ignored) {
}
}
String json = getContent(entity);
return JsonSerialization.readValue(json, AccessTokenResponse.class);
} finally {
client.getConnectionManager().shutdown();
}
@ -108,6 +112,8 @@ public class AdminClient {
List<NameValuePair> formparams = new ArrayList<NameValuePair>();
formparams.add(new BasicNameValuePair(OAuth2Constants.REFRESH_TOKEN, res.getRefreshToken()));
formparams.add(new BasicNameValuePair(OAuth2Constants.CLIENT_ID, "admin-client"));
UrlEncodedFormEntity form = new UrlEncodedFormEntity(formparams, "UTF-8");
post.setEntity(form);
HttpResponse response = client.execute(post);
boolean status = response.getStatusLine().getStatusCode() != 204;
HttpEntity entity = response.getEntity();

View file

@ -3,14 +3,14 @@ Example User Federation Provider
This is an example of user federation backed by a simple properties file. This properties file only contains username/password
key pairs. To deploy, build this directory then take the jar and copy it to the WEB-INF/lib of the keycloak server's
WAR file.
WAR file. You will then have to restart the authentication server.
The ClasspathPropertiesFederationProvider is an example of a readonly provider. If you go to the Users/Federation
page of the admin console you will see this provider listed under "classpath-properties. To configure this provider you
specify a classpath to a properties file in the "path" field of the admin page for this plugin. This example includes
a "test-users.properties" within the JAR that you can use as the variable.
The FilePropertiesFederationProvider is an exxample of a writable provider. It synchronizes changes made to
The FilePropertiesFederationProvider is an example of a writable provider. It synchronizes changes made to
username and password with the properties file. If you go to the Users/Federation page of the admin console you will
see this provider listed under "file-properties". To configure this provider you specify a fully qualified file path to
a properties file in the "path" field of the admin page for this plugin.

View file

@ -347,7 +347,7 @@ module.controller('ApplicationDetailCtrl', function($scope, realm, application,
});
module.controller('ApplicationScopeMappingCtrl', function($scope, $http, realm, application, applications,
module.controller('ApplicationScopeMappingCtrl', function($scope, $http, realm, application, applications, Notifications,
Application,
ApplicationRealmScopeMapping, ApplicationApplicationScopeMapping, ApplicationRole,
ApplicationAvailableRealmScopeMapping, ApplicationAvailableApplicationScopeMapping,
@ -375,6 +375,7 @@ module.controller('ApplicationScopeMappingCtrl', function($scope, $http, realm,
$scope.changed = false;
application = angular.copy($scope.application);
updateRealmRoles();
Notifications.success("Scope mappings updated.");
});
}
@ -399,49 +400,40 @@ module.controller('ApplicationScopeMappingCtrl', function($scope, $http, realm,
}
}
$scope.addRealmRole = function() {
$http.post(authUrl + '/admin/realms/' + realm.realm + '/applications/' + application.name + '/scope-mappings/realm', $scope.selectedRealmRoles)
.success(updateRealmRoles);
};
$scope.deleteRealmRole = function() {
$http.delete(authUrl + '/admin/realms/' + realm.realm + '/applications/' + application.name + '/scope-mappings/realm',
{data : $scope.selectedRealmMappings, headers : {"content-type" : "application/json"}})
.success(updateRealmRoles);
};
$scope.addApplicationRole = function() {
$http.post(authUrl + '/admin/realms/' + realm.realm + '/applications/' + application.name + '/scope-mappings/applications/' + $scope.targetApp.name,
$scope.selectedApplicationRoles).success(updateAppRoles);
};
$scope.deleteApplicationRole = function() {
$http.delete(authUrl + '/admin/realms/' + realm.realm + '/applications/' + application.name + '/scope-mappings/applications/' + $scope.targetApp.name,
{data : $scope.selectedApplicationMappings, headers : {"content-type" : "application/json"}}).success(updateAppRoles);
};
$scope.changeApplication = function() {
updateAppRoles();
};
$scope.addRealmRole = function() {
$http.post(authUrl + '/admin/realms/' + realm.realm + '/applications/' + application.name + '/scope-mappings/realm',
$scope.selectedRealmRoles).success(updateRealmRoles);
$scope.selectedRealmRoles).success(function() {
updateRealmRoles();
Notifications.success("Scope mappings updated.");
});
};
$scope.deleteRealmRole = function() {
$http.delete(authUrl + '/admin/realms/' + realm.realm + '/applications/' + application.name + '/scope-mappings/realm',
{data : $scope.selectedRealmMappings, headers : {"content-type" : "application/json"}}).success(updateRealmRoles);
{data : $scope.selectedRealmMappings, headers : {"content-type" : "application/json"}}).success(function () {
updateRealmRoles();
Notifications.success("Scope mappings updated.");
});
};
$scope.addApplicationRole = function() {
$http.post(authUrl + '/admin/realms/' + realm.realm + '/applications/' + application.name + '/scope-mappings/applications/' + $scope.targetApp.name,
$scope.selectedApplicationRoles).success(updateAppRoles);
$scope.selectedApplicationRoles).success(function () {
updateAppRoles();
Notifications.success("Scope mappings updated.");
});
};
$scope.deleteApplicationRole = function() {
$http.delete(authUrl + '/admin/realms/' + realm.realm + '/applications/' + application.name + '/scope-mappings/applications/' + $scope.targetApp.name,
{data : $scope.selectedApplicationMappings, headers : {"content-type" : "application/json"}}).success(updateAppRoles);
{data : $scope.selectedApplicationMappings, headers : {"content-type" : "application/json"}}).success(function () {
updateAppRoles();
Notifications.success("Scope mappings updated.");
});
};
updateRealmRoles();

View file

@ -182,7 +182,7 @@ module.controller('OAuthClientDetailCtrl', function($scope, realm, oauth, OAuthC
});
module.controller('OAuthClientScopeMappingCtrl', function($scope, $http, realm, oauth, applications,
module.controller('OAuthClientScopeMappingCtrl', function($scope, $http, realm, oauth, applications, Notifications,
OAuthClient,
OAuthClientRealmScopeMapping, OAuthClientApplicationScopeMapping, ApplicationRole,
OAuthClientAvailableRealmScopeMapping, OAuthClientAvailableApplicationScopeMapping,
@ -208,6 +208,7 @@ module.controller('OAuthClientScopeMappingCtrl', function($scope, $http, realm,
}, $scope.oauth, function() {
$scope.changed = false;
oauth = angular.copy($scope.oauth);
Notifications.success("Scope mappings updated.");
});
}
@ -232,49 +233,43 @@ module.controller('OAuthClientScopeMappingCtrl', function($scope, $http, realm,
}
}
$scope.addRealmRole = function() {
$http.post(authUrl + '/admin/realms/' + realm.realm + '/oauth-clients/' + oauth.name + '/scope-mappings/realm', $scope.selectedRealmRoles)
.success(updateRealmRoles);
};
$scope.deleteRealmRole = function() {
$http.delete(authUrl + '/admin/realms/' + realm.realm + '/oauth-clients/' + oauth.name + '/scope-mappings/realm',
{data : $scope.selectedRealmMappings, headers : {"content-type" : "application/json"}})
.success(updateRealmRoles);
};
$scope.addApplicationRole = function() {
$http.post(authUrl + '/admin/realms/' + realm.realm + '/oauth-clients/' + oauth.name + '/scope-mappings/applications/' + $scope.targetApp.name,
$scope.selectedApplicationRoles).success(updateAppRoles);
};
$scope.deleteApplicationRole = function() {
$http.delete(authUrl + '/admin/realms/' + realm.realm + '/oauth-clients/' + oauth.name + '/scope-mappings/applications/' + $scope.targetApp.name,
{data : $scope.selectedApplicationMappings, headers : {"content-type" : "application/json"}}).success(updateAppRoles);
};
$scope.changeApplication = function() {
updateAppRoles();
};
$scope.addRealmRole = function() {
$http.post(authUrl + '/admin/realms/' + realm.realm + '/oauth-clients/' + oauth.name + '/scope-mappings/realm',
$scope.selectedRealmRoles).success(updateRealmRoles);
$scope.selectedRealmRoles).success(function () {
updateRealmRoles();
Notifications.success("Scope mappings updated.");
});
};
$scope.deleteRealmRole = function() {
$http.delete(authUrl + '/admin/realms/' + realm.realm + '/oauth-clients/' + oauth.name + '/scope-mappings/realm',
{data : $scope.selectedRealmMappings, headers : {"content-type" : "application/json"}}).success(updateRealmRoles);
{data : $scope.selectedRealmMappings, headers : {"content-type" : "application/json"}}).success(function () {
updateRealmRoles();
Notifications.success("Scope mappings updated.");
});
};
$scope.addApplicationRole = function() {
$http.post(authUrl + '/admin/realms/' + realm.realm + '/oauth-clients/' + oauth.name + '/scope-mappings/applications/' + $scope.targetApp.name,
$scope.selectedApplicationRoles).success(updateAppRoles);
$scope.selectedApplicationRoles).success(function () {
updateAppRoles();
Notifications.success("Scope mappings updated.");
});
};
$scope.deleteApplicationRole = function() {
$http.delete(authUrl + '/admin/realms/' + realm.realm + '/oauth-clients/' + oauth.name + '/scope-mappings/applications/' + $scope.targetApp.name,
{data : $scope.selectedApplicationMappings, headers : {"content-type" : "application/json"}}).success(updateAppRoles);
{data : $scope.selectedApplicationMappings, headers : {"content-type" : "application/json"}}).success(function () {
updateAppRoles();
Notifications.success("Scope mappings updated.");
});
};
updateRealmRoles();

View file

@ -1,4 +1,4 @@
module.controller('UserRoleMappingCtrl', function($scope, $http, realm, user, applications, RealmRoleMapping,
module.controller('UserRoleMappingCtrl', function($scope, $http, realm, user, applications, Notifications, RealmRoleMapping,
ApplicationRoleMapping, AvailableRealmRoleMapping, AvailableApplicationRoleMapping,
CompositeRealmRoleMapping, CompositeApplicationRoleMapping) {
$scope.realm = realm;
@ -34,6 +34,8 @@ module.controller('UserRoleMappingCtrl', function($scope, $http, realm, user, ap
$scope.selectedApplicationRoles = [];
$scope.selectedApplicationMappings = [];
}
Notifications.success("Role mappings updated.");
});
};
@ -53,6 +55,7 @@ module.controller('UserRoleMappingCtrl', function($scope, $http, realm, user, ap
$scope.selectedApplicationRoles = [];
$scope.selectedApplicationMappings = [];
}
Notifications.success("Role mappings updated.");
});
};
@ -64,6 +67,7 @@ module.controller('UserRoleMappingCtrl', function($scope, $http, realm, user, ap
$scope.applicationComposite = CompositeApplicationRoleMapping.query({realm : realm.realm, userId : user.username, application : $scope.application.name});
$scope.selectedApplicationRoles = [];
$scope.selectedApplicationMappings = [];
Notifications.success("Role mappings updated.");
});
};
@ -75,6 +79,7 @@ module.controller('UserRoleMappingCtrl', function($scope, $http, realm, user, ap
$scope.applicationComposite = CompositeApplicationRoleMapping.query({realm : realm.realm, userId : user.username, application : $scope.application.name});
$scope.selectedApplicationRoles = [];
$scope.selectedApplicationMappings = [];
Notifications.success("Role mappings updated.");
});
};

View file

@ -4,9 +4,18 @@
<li><a href="#/realms/{{realm.realm}}/users">User List</a></li>
<li><a href="#/realms/{{realm.realm}}/user-federation">Federation</a></li>
</ul>
<h2></h2>
<div id="content">
<h2 class="pull-left"><span>{{realm.realm}}</span> Provider Settings</h2>
<ol class="breadcrumb" data-ng-hide="create">
<li><a href="#/realms/{{realm.realm}}/user-federation">Federation Providers</a></li>
<li><a href="#/realms/{{realm.realm}}/user-federation/providers/{{instance.providerName}}/{{instance.id}}">{{instance.displayName}}</a></li>
<li class="active">Provider Settings</li>
</ol>
<ol class="breadcrumb" data-ng-show="create">
<li><a href="#/realms/{{realm.realm}}/user-federation">Federation Providers</a></li>
<li class="active">Add Federation Provider</li>
</ol>
<h2 class="pull-left" data-ng-hide="create">Provider Settings</h2>
<h2 class="pull-left" data-ng-show="create">Add Federation Provider</h2>
<p class="subtitle"><span class="required">*</span> Required fields</p>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
@ -70,7 +79,7 @@
<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>
<button kc-save>Save</button>
</div>
<div class="pull-right form-actions" data-ng-show="!create && access.manageUsers">

View file

@ -4,9 +4,18 @@
<li><a href="#/realms/{{realm.realm}}/users">User List</a></li>
<li><a href="#/realms/{{realm.realm}}/user-federation">Federation</a></li>
</ul>
<h2></h2>
<div id="content">
<h2 class="pull-left"><span>{{realm.realm}}</span> Ldap Server Settings</h2>
<ol class="breadcrumb" data-ng-hide="create">
<li><a href="#/realms/{{realm.realm}}/user-federation">Federation Providers</a></li>
<li><a href="#/realms/{{realm.realm}}/user-federation/providers/{{instance.providerName}}/{{instance.id}}">{{instance.displayName}}</a></li>
<li class="active">LDAP Settings</li>
</ol>
<ol class="breadcrumb" data-ng-show="create">
<li><a href="#/realms/{{realm.realm}}/user-federation">Federation Providers</a></li>
<li class="active">Add LDAP Provider</li>
</ol>
<h2 class="pull-left" data-ng-hide="create">LDAP Provider Settings</h2>
<h2 class="pull-left" data-ng-show="create">Add LDAP Provider</h2>
<p class="subtitle"><span class="required">*</span> Required fields</p>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">

View file

@ -5,10 +5,10 @@
</div>
<div id="content-area" class="col-md-9" role="main">
<ul class="nav nav-tabs nav-tabs-pf">
<li class="active"><a href="">Choose realm to manage</a></li>
<li class="active"><a href="">Realm List</a></li>
</ul>
<div id="content">
<h2 class="margin-top">Realms</h2>
<h2 class="margin-top">Choose Realm to Manage</h2>
<table class="table table-striped table-bordered">
<thead>
<tr>

View file

@ -229,7 +229,7 @@ public class TokenService {
}
if (!realm.isPasswordCredentialGrantAllowed()) {
return createError("not_enabled", "Resource Owner Password Credentials Grant not enabled", Response.Status.FORBIDDEN);
return createError("not_enabled", "Direct Grant REST API not enabled", Response.Status.FORBIDDEN);
}
audit.event(EventType.LOGIN).detail(Details.AUTH_METHOD, "oauth_credentials").detail(Details.RESPONSE_TYPE, "token");

View file

@ -192,6 +192,7 @@ public class UserFederationResource {
@GET
@Path("instances")
@Produces("application/json")
@NoCache
public List<UserFederationProviderRepresentation> getUserFederationInstances() {
logger.info("getUserFederationInstances");
auth.requireManage();