Merge pull request #149 from stianst/master
Added oauth client installation to admin console. Rename JWT prn to sub.
This commit is contained in:
commit
626839f0f3
26 changed files with 197 additions and 22 deletions
|
@ -11,7 +11,7 @@
|
||||||
<link rel="stylesheet" href="/auth-server/admin-ui/css/reset.css">
|
<link rel="stylesheet" href="/auth-server/admin-ui/css/reset.css">
|
||||||
<link rel="stylesheet" href="/auth-server/admin-ui/bootstrap-3.0.0-wip/css/bootstrap.css">
|
<link rel="stylesheet" href="/auth-server/admin-ui/bootstrap-3.0.0-wip/css/bootstrap.css">
|
||||||
<link rel="stylesheet" href="/auth-server/admin-ui/css/sprites.css">
|
<link rel="stylesheet" href="/auth-server/admin-ui/css/sprites.css">
|
||||||
<link rel="stylesheet" href='http://fonts.googleapis.com/css?family=Open+Sans:400,300,300italic,400italic,600,600italic,700,700italic,800,800italic'>
|
<link rel="stylesheet" href='//fonts.googleapis.com/css?family=Open+Sans:400,300,300italic,400italic,600,600italic,700,700italic,800,800italic'>
|
||||||
|
|
||||||
<!-- RCUE styles -->
|
<!-- RCUE styles -->
|
||||||
<link rel="stylesheet" href="/auth-server/admin-ui/css/base.css">
|
<link rel="stylesheet" href="/auth-server/admin-ui/css/base.css">
|
||||||
|
|
|
@ -396,7 +396,21 @@ module.config([ '$routeProvider', function($routeProvider) {
|
||||||
},
|
},
|
||||||
controller : 'OAuthClientScopeMappingCtrl'
|
controller : 'OAuthClientScopeMappingCtrl'
|
||||||
})
|
})
|
||||||
|
.when('/realms/:realm/oauth-clients/:oauth/installation', {
|
||||||
|
templateUrl : 'partials/oauth-client-installation.html',
|
||||||
|
resolve : {
|
||||||
|
realm : function(RealmLoader) {
|
||||||
|
return RealmLoader();
|
||||||
|
},
|
||||||
|
oauth : function(OAuthClientLoader) {
|
||||||
|
return OAuthClientLoader();
|
||||||
|
},
|
||||||
|
installation : function(OAuthClientInstallationLoader) {
|
||||||
|
return OAuthClientInstallationLoader();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
controller : 'OAuthClientInstallationCtrl'
|
||||||
|
})
|
||||||
.when('/create/oauth-client/:realm', {
|
.when('/create/oauth-client/:realm', {
|
||||||
templateUrl : 'partials/oauth-client-detail.html',
|
templateUrl : 'partials/oauth-client-detail.html',
|
||||||
resolve : {
|
resolve : {
|
||||||
|
|
|
@ -294,3 +294,11 @@ module.controller('OAuthClientScopeMappingCtrl', function($scope, $http, realm,
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
module.controller('OAuthClientInstallationCtrl', function($scope, realm, installation, oauth, OAuthClientInstallation, $routeParams) {
|
||||||
|
$scope.realm = realm;
|
||||||
|
$scope.oauth = oauth;
|
||||||
|
$scope.installation = installation;
|
||||||
|
$scope.download = OAuthClientInstallation.url({ realm: $routeParams.realm, oauth: $routeParams.oauth });
|
||||||
|
});
|
||||||
|
|
|
@ -152,3 +152,12 @@ module.factory('OAuthClientListLoader', function(Loader, OAuthClient, $route, $q
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
module.factory('OAuthClientInstallationLoader', function(Loader, OAuthClientInstallation, $route, $q) {
|
||||||
|
return Loader.get(OAuthClientInstallation, function() {
|
||||||
|
return {
|
||||||
|
realm : $route.current.params.realm,
|
||||||
|
oauth : $route.current.params.oauth
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -291,6 +291,22 @@ module.factory('OAuthClientApplicationScopeMapping', function($resource) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
module.factory('OAuthClientInstallation', function($resource) {
|
||||||
|
var url = '/auth-server/rest/saas/admin/realms/:realm/oauth-clients/:oauth/installation';
|
||||||
|
var resource = $resource('/auth-server/rest/saas/admin/realms/:realm/oauth-clients/:oauth/installation', {
|
||||||
|
realm : '@realm',
|
||||||
|
oauth : '@oauth'
|
||||||
|
}, {
|
||||||
|
update : {
|
||||||
|
method : 'PUT'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
resource.url = function(parameters) {
|
||||||
|
return url.replace(':realm', parameters.realm).replace(':oauth', parameters.oauth);
|
||||||
|
}
|
||||||
|
return resource;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
module.factory('Current', function(Realm, $route) {
|
module.factory('Current', function(Realm, $route) {
|
||||||
var current = {};
|
var current = {};
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
<li><a href="#/realms/{{realm.id}}/oauth-clients/{{oauth.id}}">Settings</a></li>
|
<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 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>
|
<li><a href="#/realms/{{realm.id}}/oauth-clients/{{oauth.id}}/scope-mappings">Scope</a></li>
|
||||||
|
<li><a href="#/realms/{{realm.id}}/oauth-clients/{{oauth.id}}/installation">Installation</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div id="content">
|
<div id="content">
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
<li class="active"><a href="#/realms/{{realm.id}}/oauth-clients/{{oauth.id}}">Settings</a></li>
|
<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}}/credentials">Credentials</a></li>
|
||||||
<li><a href="#/realms/{{realm.id}}/oauth-clients/{{oauth.id}}/scope-mappings">Scope</a></li>
|
<li><a href="#/realms/{{realm.id}}/oauth-clients/{{oauth.id}}/scope-mappings">Scope</a></li>
|
||||||
|
<li><a href="#/realms/{{realm.id}}/oauth-clients/{{oauth.id}}/installation">Installation</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="top-nav" data-ng-show="create">
|
<div class="top-nav" data-ng-show="create">
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
<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><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>
|
||||||
|
<li class="active"><a href="#/realms/{{realm.id}}/oauth-clients/{{oauth.id}}/installation">Installation</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="top-nav" data-ng-show="create">
|
||||||
|
<ul class="rcue-tabs">
|
||||||
|
<li></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div id="content">
|
||||||
|
<a class="button primary" href="{{download}}" download="keycloak.json" type="submit">Download</a></br>
|
||||||
|
<textarea style="width: 100%;" rows="20" onclick="this.select()">{{installation | json}}</textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="container-right-bg"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -7,6 +7,7 @@
|
||||||
<li><a href="#/realms/{{realm.id}}/oauth-clients/{{oauth.id}}">Settings</a></li>
|
<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><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>
|
<li class="active"><a href="#/realms/{{realm.id}}/oauth-clients/{{oauth.id}}/scope-mappings">Scope</a></li>
|
||||||
|
<li><a href="#/realms/{{realm.id}}/oauth-clients/{{oauth.id}}/installation">Installation</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div id="content">
|
<div id="content">
|
||||||
|
|
|
@ -32,7 +32,7 @@ public class RSATokenVerifier {
|
||||||
if (!token.isActive()) {
|
if (!token.isActive()) {
|
||||||
throw new VerificationException("Token is not active.");
|
throw new VerificationException("Token is not active.");
|
||||||
}
|
}
|
||||||
String user = token.getPrincipal();
|
String user = token.getSubject();
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
throw new VerificationException("Token user was null");
|
throw new VerificationException("Token user was null");
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,8 +22,8 @@ public class JsonWebToken implements Serializable {
|
||||||
protected String issuer;
|
protected String issuer;
|
||||||
@JsonProperty("aud")
|
@JsonProperty("aud")
|
||||||
protected String audience;
|
protected String audience;
|
||||||
@JsonProperty("prn")
|
@JsonProperty("sub")
|
||||||
protected String principal;
|
protected String subject;
|
||||||
@JsonProperty("typ")
|
@JsonProperty("typ")
|
||||||
protected String type;
|
protected String type;
|
||||||
|
|
||||||
|
@ -116,12 +116,12 @@ public class JsonWebToken implements Serializable {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPrincipal() {
|
public String getSubject() {
|
||||||
return principal;
|
return subject;
|
||||||
}
|
}
|
||||||
|
|
||||||
public JsonWebToken principal(String principal) {
|
public JsonWebToken principal(String principal) {
|
||||||
this.principal = principal;
|
this.subject = principal;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -97,7 +97,7 @@ public class RSAVerifierTest {
|
||||||
.rsa256(idpPair.getPrivate());
|
.rsa256(idpPair.getPrivate());
|
||||||
SkeletonKeyToken token = verifySkeletonKeyToken(encoded);
|
SkeletonKeyToken token = verifySkeletonKeyToken(encoded);
|
||||||
Assert.assertTrue(token.getResourceAccess("service").getRoles().contains("admin"));
|
Assert.assertTrue(token.getResourceAccess("service").getRoles().contains("admin"));
|
||||||
Assert.assertEquals("CN=Client", token.getPrincipal());
|
Assert.assertEquals("CN=Client", token.getSubject());
|
||||||
}
|
}
|
||||||
|
|
||||||
private SkeletonKeyToken verifySkeletonKeyToken(String encoded) throws VerificationException {
|
private SkeletonKeyToken verifySkeletonKeyToken(String encoded) throws VerificationException {
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
@IMPORT url("css/forms.css");
|
@IMPORT url("css/forms.css");
|
||||||
@IMPORT url("css/zocial/zocial.css");
|
@IMPORT url("css/zocial/zocial.css");
|
||||||
@IMPORT url("css/login-register.css");
|
@IMPORT url("css/login-register.css");
|
||||||
@IMPORT url("http://fonts.googleapis.com/css?family=Open+Sans:400,300,300italic,400italic,600,600italic,700,700italic,800,800italic");
|
@IMPORT url("//fonts.googleapis.com/css?family=Open+Sans:400,300,300italic,400italic,600,600italic,700,700italic,800,800italic");
|
||||||
|
|
||||||
.zocial.google {
|
.zocial.google {
|
||||||
background-color: #dd4b39 !important;
|
background-color: #dd4b39 !important;
|
||||||
|
|
|
@ -102,7 +102,7 @@ public class CatalinaBearerTokenAuthenticator {
|
||||||
}
|
}
|
||||||
surrogate = chain[0].getSubjectX500Principal().getName();
|
surrogate = chain[0].getSubjectX500Principal().getName();
|
||||||
}
|
}
|
||||||
SkeletonKeyPrincipal skeletonKeyPrincipal = new SkeletonKeyPrincipal(token.getPrincipal(), surrogate);
|
SkeletonKeyPrincipal skeletonKeyPrincipal = new SkeletonKeyPrincipal(token.getSubject(), surrogate);
|
||||||
principal = new CatalinaSecurityContextHelper().createPrincipal(request.getContext().getRealm(), skeletonKeyPrincipal, roles);
|
principal = new CatalinaSecurityContextHelper().createPrincipal(request.getContext().getRealm(), skeletonKeyPrincipal, roles);
|
||||||
request.setUserPrincipal(principal);
|
request.setUserPrincipal(principal);
|
||||||
request.setAuthType("OAUTH_BEARER");
|
request.setAuthType("OAUTH_BEARER");
|
||||||
|
|
|
@ -65,11 +65,11 @@ public class CatalinaSecurityContextHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the Principal given the authenticated Subject. Currently the first principal that is not of type {@code Group} is
|
* Get the Principal given the authenticated Subject. Currently the first subject that is not of type {@code Group} is
|
||||||
* considered or the single principal inside the CallerPrincipal group.
|
* considered or the single subject inside the CallerPrincipal group.
|
||||||
*
|
*
|
||||||
* @param subject
|
* @param subject
|
||||||
* @return the authenticated principal
|
* @return the authenticated subject
|
||||||
*/
|
*/
|
||||||
protected Principal getPrincipal(Subject subject) {
|
protected Principal getPrincipal(Subject subject) {
|
||||||
Principal principal = null;
|
Principal principal = null;
|
||||||
|
|
|
@ -227,7 +227,7 @@ public class OAuthAuthenticatorValve extends FormAuthenticator implements Lifecy
|
||||||
SkeletonKeyToken.Access access = token.getRealmAccess();
|
SkeletonKeyToken.Access access = token.getRealmAccess();
|
||||||
if (access != null) roles.addAll(access.getRoles());
|
if (access != null) roles.addAll(access.getRoles());
|
||||||
}
|
}
|
||||||
SkeletonKeyPrincipal skp = new SkeletonKeyPrincipal(token.getPrincipal(), null);
|
SkeletonKeyPrincipal skp = new SkeletonKeyPrincipal(token.getSubject(), null);
|
||||||
GenericPrincipal principal = new CatalinaSecurityContextHelper().createPrincipal(context.getRealm(), skp, roles);
|
GenericPrincipal principal = new CatalinaSecurityContextHelper().createPrincipal(context.getRealm(), skp, roles);
|
||||||
Session session = request.getSessionInternal(true);
|
Session session = request.getSessionInternal(true);
|
||||||
session.setPrincipal(principal);
|
session.setPrincipal(principal);
|
||||||
|
@ -235,7 +235,7 @@ public class OAuthAuthenticatorValve extends FormAuthenticator implements Lifecy
|
||||||
SkeletonKeySession skSession = new SkeletonKeySession(oauth.getTokenString(), token, realmConfiguration.getMetadata());
|
SkeletonKeySession skSession = new SkeletonKeySession(oauth.getTokenString(), token, realmConfiguration.getMetadata());
|
||||||
session.setNote(SkeletonKeySession.class.getName(), skSession);
|
session.setNote(SkeletonKeySession.class.getName(), skSession);
|
||||||
|
|
||||||
String username = token.getPrincipal();
|
String username = token.getSubject();
|
||||||
log.debug("userSessionManage.login: " + username);
|
log.debug("userSessionManage.login: " + username);
|
||||||
userSessionManagement.login(session, username);
|
userSessionManagement.login(session, username);
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,7 +71,7 @@ public class JaxrsBearerTokenFilter implements ContainerRequestFilter {
|
||||||
ResteasyProviderFactory.pushContext(SkeletonKeySession.class, skSession);
|
ResteasyProviderFactory.pushContext(SkeletonKeySession.class, skSession);
|
||||||
String callerPrincipal = securityContext.getUserPrincipal() != null ? securityContext.getUserPrincipal().getName() : null;
|
String callerPrincipal = securityContext.getUserPrincipal() != null ? securityContext.getUserPrincipal().getName() : null;
|
||||||
|
|
||||||
final SkeletonKeyPrincipal principal = new SkeletonKeyPrincipal(token.getPrincipal(), callerPrincipal);
|
final SkeletonKeyPrincipal principal = new SkeletonKeyPrincipal(token.getSubject(), callerPrincipal);
|
||||||
final boolean isSecure = securityContext.isSecure();
|
final boolean isSecure = securityContext.isSecure();
|
||||||
final SkeletonKeyToken.Access access;
|
final SkeletonKeyToken.Access access;
|
||||||
if (resourceMetadata.getResourceName() != null) {
|
if (resourceMetadata.getResourceName() != null) {
|
||||||
|
|
|
@ -98,7 +98,7 @@ public class KeycloakAuthenticationMechanism implements AuthenticationMechanism
|
||||||
}
|
}
|
||||||
|
|
||||||
protected SkeletonKeyPrincipal completeAuthentication(SecurityContext securityContext, SkeletonKeyToken token, String surrogate) {
|
protected SkeletonKeyPrincipal completeAuthentication(SecurityContext securityContext, SkeletonKeyToken token, String surrogate) {
|
||||||
final SkeletonKeyPrincipal skeletonKeyPrincipal = new SkeletonKeyPrincipal(token.getPrincipal(), surrogate);
|
final SkeletonKeyPrincipal skeletonKeyPrincipal = new SkeletonKeyPrincipal(token.getSubject(), surrogate);
|
||||||
Set<String> roles = null;
|
Set<String> roles = null;
|
||||||
if (adapterConfig.isUseResourceRoleMappings()) {
|
if (adapterConfig.isUseResourceRoleMappings()) {
|
||||||
SkeletonKeyToken.Access access = token.getResourceAccess(resourceMetadata.getResourceName());
|
SkeletonKeyToken.Access access = token.getResourceAccess(resourceMetadata.getResourceName());
|
||||||
|
|
|
@ -172,7 +172,7 @@ public class AuthenticationManager {
|
||||||
|
|
||||||
Auth auth = new Auth(token);
|
Auth auth = new Auth(token);
|
||||||
|
|
||||||
UserModel user = realm.getUser(token.getPrincipal());
|
UserModel user = realm.getUser(token.getSubject());
|
||||||
if (user == null || !user.isEnabled()) {
|
if (user == null || !user.isEnabled()) {
|
||||||
logger.debug("Unknown user in identity cookie");
|
logger.debug("Unknown user in identity cookie");
|
||||||
expireIdentityCookie(realm, uriInfo);
|
expireIdentityCookie(realm, uriInfo);
|
||||||
|
@ -219,7 +219,7 @@ public class AuthenticationManager {
|
||||||
|
|
||||||
Auth auth = new Auth(token);
|
Auth auth = new Auth(token);
|
||||||
|
|
||||||
UserModel user = realm.getUser(token.getPrincipal());
|
UserModel user = realm.getUser(token.getSubject());
|
||||||
if (user == null || !user.isEnabled()) {
|
if (user == null || !user.isEnabled()) {
|
||||||
throw new NotAuthorizedException("invalid_user");
|
throw new NotAuthorizedException("invalid_user");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,23 @@
|
||||||
package org.keycloak.services.managers;
|
package org.keycloak.services.managers;
|
||||||
|
|
||||||
|
import org.keycloak.models.ApplicationModel;
|
||||||
import org.keycloak.models.Constants;
|
import org.keycloak.models.Constants;
|
||||||
import org.keycloak.models.OAuthClientModel;
|
import org.keycloak.models.OAuthClientModel;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RoleModel;
|
import org.keycloak.models.RoleModel;
|
||||||
import org.keycloak.models.UserCredentialModel;
|
import org.keycloak.models.UserCredentialModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.representations.adapters.config.BaseAdapterConfig;
|
||||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||||
import org.keycloak.representations.idm.OAuthClientRepresentation;
|
import org.keycloak.representations.idm.OAuthClientRepresentation;
|
||||||
|
import org.keycloak.services.resources.flows.Urls;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -76,4 +82,26 @@ public class OAuthClientManager {
|
||||||
}
|
}
|
||||||
return rep;
|
return rep;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BaseAdapterConfig toInstallationRepresentation(RealmModel realmModel, OAuthClientModel model, URI baseUri) {
|
||||||
|
BaseAdapterConfig rep = new BaseAdapterConfig();
|
||||||
|
rep.setRealm(realmModel.getId());
|
||||||
|
rep.setRealmKey(realmModel.getPublicKeyPem());
|
||||||
|
rep.setSslNotRequired(realmModel.isSslNotRequired());
|
||||||
|
|
||||||
|
rep.setAuthUrl(Urls.realmLoginPage(baseUri, realmModel.getId()).toString());
|
||||||
|
rep.setCodeUrl(Urls.realmCode(baseUri, realmModel.getId()).toString());
|
||||||
|
rep.setUseResourceRoleMappings(false);
|
||||||
|
|
||||||
|
rep.setResource(model.getOAuthAgent().getLoginName());
|
||||||
|
|
||||||
|
Map<String, String> creds = new HashMap<String, String>();
|
||||||
|
creds.put(CredentialRepresentation.PASSWORD, "INSERT CLIENT PASSWORD");
|
||||||
|
if (model.getOAuthAgent().isTotp()) {
|
||||||
|
creds.put(CredentialRepresentation.TOTP, "INSERT CLIENT TOTP");
|
||||||
|
}
|
||||||
|
rep.setCredentials(creds);
|
||||||
|
|
||||||
|
return rep;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ import org.keycloak.services.validation.Validation;
|
||||||
|
|
||||||
import javax.ws.rs.Consumes;
|
import javax.ws.rs.Consumes;
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.NotAcceptableException;
|
||||||
import javax.ws.rs.NotAuthorizedException;
|
import javax.ws.rs.NotAuthorizedException;
|
||||||
import javax.ws.rs.POST;
|
import javax.ws.rs.POST;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
|
@ -129,6 +130,10 @@ public class TokenService {
|
||||||
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
public Response grantIdentityToken(final MultivaluedMap<String, String> form) {
|
public Response grantIdentityToken(final MultivaluedMap<String, String> form) {
|
||||||
|
if (!checkSsl()) {
|
||||||
|
throw new NotAcceptableException("HTTPS required");
|
||||||
|
}
|
||||||
|
|
||||||
String username = form.getFirst(AuthenticationManager.FORM_USERNAME);
|
String username = form.getFirst(AuthenticationManager.FORM_USERNAME);
|
||||||
if (username == null) {
|
if (username == null) {
|
||||||
throw new NotAuthorizedException("No user");
|
throw new NotAuthorizedException("No user");
|
||||||
|
@ -155,6 +160,10 @@ public class TokenService {
|
||||||
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
public Response grantAccessToken(final MultivaluedMap<String, String> form) {
|
public Response grantAccessToken(final MultivaluedMap<String, String> form) {
|
||||||
|
if (!checkSsl()) {
|
||||||
|
throw new NotAcceptableException("HTTPS required");
|
||||||
|
}
|
||||||
|
|
||||||
String username = form.getFirst(AuthenticationManager.FORM_USERNAME);
|
String username = form.getFirst(AuthenticationManager.FORM_USERNAME);
|
||||||
if (username == null) {
|
if (username == null) {
|
||||||
throw new NotAuthorizedException("No user");
|
throw new NotAuthorizedException("No user");
|
||||||
|
@ -187,6 +196,10 @@ public class TokenService {
|
||||||
logger.debug("TokenService.processLogin");
|
logger.debug("TokenService.processLogin");
|
||||||
OAuthFlows oauth = Flows.oauth(realm, request, uriInfo, authManager, tokenManager);
|
OAuthFlows oauth = Flows.oauth(realm, request, uriInfo, authManager, tokenManager);
|
||||||
|
|
||||||
|
if (!checkSsl()) {
|
||||||
|
return oauth.forwardToSecurityFailure("HTTPS required");
|
||||||
|
}
|
||||||
|
|
||||||
if (!realm.isEnabled()) {
|
if (!realm.isEnabled()) {
|
||||||
return oauth.forwardToSecurityFailure("Realm not enabled.");
|
return oauth.forwardToSecurityFailure("Realm not enabled.");
|
||||||
}
|
}
|
||||||
|
@ -360,6 +373,11 @@ public class TokenService {
|
||||||
@Produces("application/json")
|
@Produces("application/json")
|
||||||
public Response accessCodeToToken(final MultivaluedMap<String, String> formData) {
|
public Response accessCodeToToken(final MultivaluedMap<String, String> formData) {
|
||||||
logger.debug("accessRequest <---");
|
logger.debug("accessRequest <---");
|
||||||
|
|
||||||
|
if (!checkSsl()) {
|
||||||
|
throw new NotAcceptableException("HTTPS required");
|
||||||
|
}
|
||||||
|
|
||||||
if (!realm.isEnabled()) {
|
if (!realm.isEnabled()) {
|
||||||
throw new NotAuthorizedException("Realm not enabled");
|
throw new NotAuthorizedException("Realm not enabled");
|
||||||
}
|
}
|
||||||
|
@ -480,6 +498,10 @@ public class TokenService {
|
||||||
logger.info("TokenService.loginPage");
|
logger.info("TokenService.loginPage");
|
||||||
OAuthFlows oauth = Flows.oauth(realm, request, uriInfo, authManager, tokenManager);
|
OAuthFlows oauth = Flows.oauth(realm, request, uriInfo, authManager, tokenManager);
|
||||||
|
|
||||||
|
if (!checkSsl()) {
|
||||||
|
return oauth.forwardToSecurityFailure("HTTPS required");
|
||||||
|
}
|
||||||
|
|
||||||
if (!realm.isEnabled()) {
|
if (!realm.isEnabled()) {
|
||||||
logger.warn("Realm not enabled");
|
logger.warn("Realm not enabled");
|
||||||
return oauth.forwardToSecurityFailure("Realm not enabled");
|
return oauth.forwardToSecurityFailure("Realm not enabled");
|
||||||
|
@ -529,6 +551,10 @@ public class TokenService {
|
||||||
logger.info("**********registerPage()");
|
logger.info("**********registerPage()");
|
||||||
OAuthFlows oauth = Flows.oauth(realm, request, uriInfo, authManager, tokenManager);
|
OAuthFlows oauth = Flows.oauth(realm, request, uriInfo, authManager, tokenManager);
|
||||||
|
|
||||||
|
if (!checkSsl()) {
|
||||||
|
return oauth.forwardToSecurityFailure("HTTPS required");
|
||||||
|
}
|
||||||
|
|
||||||
if (!realm.isEnabled()) {
|
if (!realm.isEnabled()) {
|
||||||
logger.warn("Realm not enabled");
|
logger.warn("Realm not enabled");
|
||||||
return oauth.forwardToSecurityFailure("Realm not enabled");
|
return oauth.forwardToSecurityFailure("Realm not enabled");
|
||||||
|
@ -581,6 +607,10 @@ public class TokenService {
|
||||||
public Response processOAuth(final MultivaluedMap<String, String> formData) {
|
public Response processOAuth(final MultivaluedMap<String, String> formData) {
|
||||||
OAuthFlows oauth = Flows.oauth(realm, request, uriInfo, authManager, tokenManager);
|
OAuthFlows oauth = Flows.oauth(realm, request, uriInfo, authManager, tokenManager);
|
||||||
|
|
||||||
|
if (!checkSsl()) {
|
||||||
|
return oauth.forwardToSecurityFailure("HTTPS required");
|
||||||
|
}
|
||||||
|
|
||||||
String code = formData.getFirst("code");
|
String code = formData.getFirst("code");
|
||||||
JWSInput input = new JWSInput(code);
|
JWSInput input = new JWSInput(code);
|
||||||
boolean verifiedCode = false;
|
boolean verifiedCode = false;
|
||||||
|
@ -628,4 +658,20 @@ public class TokenService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean checkSsl() {
|
||||||
|
if (realm.isSslNotRequired()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uriInfo.getBaseUri().getScheme().equals("https")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("https".equals(headers.getHeaderString("X-Forwarded-Proto"))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,10 +6,13 @@ import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.OAuthClientModel;
|
import org.keycloak.models.OAuthClientModel;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserCredentialModel;
|
import org.keycloak.models.UserCredentialModel;
|
||||||
|
import org.keycloak.representations.adapters.config.BaseAdapterConfig;
|
||||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||||
import org.keycloak.representations.idm.OAuthClientRepresentation;
|
import org.keycloak.representations.idm.OAuthClientRepresentation;
|
||||||
|
import org.keycloak.services.managers.ApplicationManager;
|
||||||
import org.keycloak.services.managers.OAuthClientManager;
|
import org.keycloak.services.managers.OAuthClientManager;
|
||||||
import org.keycloak.services.managers.RealmManager;
|
import org.keycloak.services.managers.RealmManager;
|
||||||
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
|
||||||
import javax.ws.rs.Consumes;
|
import javax.ws.rs.Consumes;
|
||||||
import javax.ws.rs.DELETE;
|
import javax.ws.rs.DELETE;
|
||||||
|
@ -17,7 +20,10 @@ import javax.ws.rs.GET;
|
||||||
import javax.ws.rs.PUT;
|
import javax.ws.rs.PUT;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
|
import javax.ws.rs.core.UriInfo;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -29,6 +35,8 @@ public class OAuthClientResource {
|
||||||
protected RealmModel realm;
|
protected RealmModel realm;
|
||||||
protected OAuthClientModel oauthClient;
|
protected OAuthClientModel oauthClient;
|
||||||
protected KeycloakSession session;
|
protected KeycloakSession session;
|
||||||
|
@Context
|
||||||
|
protected UriInfo uriInfo;
|
||||||
|
|
||||||
public OAuthClientResource(RealmModel realm, OAuthClientModel oauthClient, KeycloakSession session) {
|
public OAuthClientResource(RealmModel realm, OAuthClientModel oauthClient, KeycloakSession session) {
|
||||||
this.realm = realm;
|
this.realm = realm;
|
||||||
|
@ -51,6 +59,18 @@ public class OAuthClientResource {
|
||||||
return OAuthClientManager.toRepresentation(oauthClient);
|
return OAuthClientManager.toRepresentation(oauthClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@NoCache
|
||||||
|
@Path("installation")
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public String getInstallation() throws IOException {
|
||||||
|
OAuthClientManager manager = new OAuthClientManager(realm);
|
||||||
|
BaseAdapterConfig rep = manager.toInstallationRepresentation(realm, oauthClient, uriInfo.getBaseUri());
|
||||||
|
|
||||||
|
// TODO Temporary solution to pretty-print
|
||||||
|
return JsonSerialization.mapper.writerWithDefaultPrettyPrinter().writeValueAsString(rep);
|
||||||
|
}
|
||||||
|
|
||||||
@DELETE
|
@DELETE
|
||||||
@NoCache
|
@NoCache
|
||||||
public void deleteOAuthClient() {
|
public void deleteOAuthClient() {
|
||||||
|
|
|
@ -15,6 +15,7 @@ import javax.ws.rs.POST;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.PathParam;
|
import javax.ws.rs.PathParam;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.container.ResourceContext;
|
||||||
import javax.ws.rs.core.Context;
|
import javax.ws.rs.core.Context;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
|
@ -32,6 +33,9 @@ public class OAuthClientsResource {
|
||||||
|
|
||||||
protected KeycloakSession session;
|
protected KeycloakSession session;
|
||||||
|
|
||||||
|
@Context
|
||||||
|
protected ResourceContext resourceContext;
|
||||||
|
|
||||||
public OAuthClientsResource(RealmModel realm, KeycloakSession session) {
|
public OAuthClientsResource(RealmModel realm, KeycloakSession session) {
|
||||||
this.realm = realm;
|
this.realm = realm;
|
||||||
this.session = session;
|
this.session = session;
|
||||||
|
@ -64,6 +68,7 @@ public class OAuthClientsResource {
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
OAuthClientResource oAuthClientResource = new OAuthClientResource(realm, oauth, session);
|
OAuthClientResource oAuthClientResource = new OAuthClientResource(realm, oauth, session);
|
||||||
|
resourceContext.initResource(oAuthClientResource);
|
||||||
return oAuthClientResource;
|
return oAuthClientResource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,7 @@ public class RealmAdminResource extends RoleContainerResource {
|
||||||
@Path("oauth-clients")
|
@Path("oauth-clients")
|
||||||
public OAuthClientsResource getOAuthClients() {
|
public OAuthClientsResource getOAuthClients() {
|
||||||
OAuthClientsResource oauth = new OAuthClientsResource(realm, session);
|
OAuthClientsResource oauth = new OAuthClientsResource(realm, session);
|
||||||
|
resourceContext.initResource(oauth);
|
||||||
return oauth;
|
return oauth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,7 @@ public class AccessTokenTest {
|
||||||
|
|
||||||
SkeletonKeyToken token = oauth.verifyToken(response.getAccessToken());
|
SkeletonKeyToken token = oauth.verifyToken(response.getAccessToken());
|
||||||
|
|
||||||
Assert.assertEquals("test-user@localhost", token.getPrincipal());
|
Assert.assertEquals("test-user@localhost", token.getSubject());
|
||||||
|
|
||||||
Assert.assertEquals(1, token.getRealmAccess().getRoles().size());
|
Assert.assertEquals(1, token.getRealmAccess().getRoles().size());
|
||||||
Assert.assertTrue(token.getRealmAccess().isUserInRole("user"));
|
Assert.assertTrue(token.getRealmAccess().isUserInRole("user"));
|
||||||
|
|
|
@ -102,7 +102,7 @@ public class SocialLoginTest {
|
||||||
|
|
||||||
SkeletonKeyToken token = oauth.verifyToken(response.getAccessToken());
|
SkeletonKeyToken token = oauth.verifyToken(response.getAccessToken());
|
||||||
|
|
||||||
Assert.assertEquals("dummy-user", token.getPrincipal());
|
Assert.assertEquals("dummy-user", token.getSubject());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
Loading…
Reference in a new issue