admin ui login
This commit is contained in:
parent
1d0b510637
commit
540385fec5
22 changed files with 449 additions and 134 deletions
|
@ -7,7 +7,8 @@ import org.keycloak.services.models.KeycloakSession;
|
|||
import org.keycloak.services.models.RealmModel;
|
||||
import org.keycloak.services.models.RequiredCredentialModel;
|
||||
import org.keycloak.services.resources.KeycloakApplication;
|
||||
import org.keycloak.services.resources.RegistrationService;
|
||||
import org.keycloak.services.resources.SaasService;
|
||||
import org.keycloak.services.resources.SaasService;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
@ -35,12 +36,12 @@ public class DemoApplication extends KeycloakApplication {
|
|||
defaultRealm.setEnabled(true);
|
||||
defaultRealm.setTokenLifespan(300);
|
||||
defaultRealm.setAccessCodeLifespan(60);
|
||||
defaultRealm.setSslNotRequired(false);
|
||||
defaultRealm.setSslNotRequired(true);
|
||||
defaultRealm.setCookieLoginAllowed(true);
|
||||
defaultRealm.setRegistrationAllowed(true);
|
||||
manager.generateRealmKeys(defaultRealm);
|
||||
defaultRealm.addRequiredCredential(RequiredCredentialModel.PASSWORD);
|
||||
defaultRealm.addRole(RegistrationService.REALM_CREATOR_ROLE);
|
||||
defaultRealm.addRole(SaasService.REALM_CREATOR_ROLE);
|
||||
|
||||
RealmRepresentation rep = loadJson("META-INF/testrealm.json");
|
||||
RealmModel realm = manager.createRealm("demo", rep.getRealm());
|
||||
|
|
|
@ -61,11 +61,11 @@
|
|||
<hr/>
|
||||
<% String errorMessage = (String)request.getAttribute("KEYCLOAK_LOGIN_ERROR_MESSAGE");
|
||||
if (errorMessage != null) { %>
|
||||
<div id="error-message" class="alert alert-block alert-error" style="block"><%=errorMessage%></div>
|
||||
<div id="error-message" class="alert alert-block alert-error" ><%=errorMessage%></div>
|
||||
<% } %>
|
||||
<form class="form-horizontal" name="loginForm" action="<%=request.getAttribute("KEYCLOAK_LOGIN_ACTION")%>" method="POST">
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="username">User Name</label>
|
||||
<label class="control-label">User Name</label>
|
||||
|
||||
<div class="controls">
|
||||
<% if (username != null) { %>
|
||||
|
@ -123,5 +123,6 @@
|
|||
</div>
|
||||
<footer>
|
||||
<p>Powered By Keycloak</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -163,7 +163,7 @@ public class OAuthManagedResourceValve extends FormAuthenticator implements Life
|
|||
|
||||
protected void remoteLogout(JWSInput token, HttpServletResponse response) throws IOException {
|
||||
try {
|
||||
log.debug("->> remoteLogout: ");
|
||||
log.info("->> remoteLogout: ");
|
||||
LogoutAction action = JsonSerialization.fromBytes(LogoutAction.class, token.getContent());
|
||||
if (action.isExpired()) {
|
||||
log.warn("admin request failed, expired token");
|
||||
|
|
|
@ -175,8 +175,9 @@ public class ServletOAuthLogin {
|
|||
return false;
|
||||
}
|
||||
// reset the cookie
|
||||
Cookie reset = new Cookie(stateCookie.getName(), stateCookie.getValue());
|
||||
reset.setPath(stateCookie.getPath());
|
||||
log.info("** reseting application state cookie");
|
||||
Cookie reset = new Cookie(realmInfo.getStateCookieName(), "");
|
||||
reset.setPath(getDefaultCookiePath());
|
||||
reset.setMaxAge(0);
|
||||
response.addCookie(reset);
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package org.keycloak.services.managers;
|
||||
|
||||
import org.jboss.resteasy.jose.jws.JWSBuilder;
|
||||
import org.jboss.resteasy.jwt.JsonSerialization;
|
||||
import org.jboss.resteasy.logging.Logger;
|
||||
import org.jboss.resteasy.spi.HttpResponse;
|
||||
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
||||
|
@ -11,6 +13,7 @@ import org.keycloak.services.models.RealmModel;
|
|||
import org.keycloak.services.models.RequiredCredentialModel;
|
||||
import org.keycloak.services.models.UserModel;
|
||||
import org.keycloak.services.resources.RealmsResource;
|
||||
import org.keycloak.services.resources.SaasService;
|
||||
|
||||
import javax.ws.rs.NotAuthorizedException;
|
||||
import javax.ws.rs.core.Cookie;
|
||||
|
@ -31,6 +34,7 @@ import java.util.Set;
|
|||
public class AuthenticationManager {
|
||||
protected Logger logger = Logger.getLogger(AuthenticationManager.class);
|
||||
public static final String FORM_USERNAME = "username";
|
||||
public static final String KEYCLOAK_IDENTITY_COOKIE = "KEYCLOAK_IDENTITY";
|
||||
|
||||
/**
|
||||
* Grabs token from headers, authenticates, authorizes
|
||||
|
@ -44,21 +48,99 @@ public class AuthenticationManager {
|
|||
return realm.isRealmAdmin(user);
|
||||
}
|
||||
|
||||
public SkeletonKeyToken createIdentityToken(RealmModel realm, String username) {
|
||||
SkeletonKeyToken token = new SkeletonKeyToken();
|
||||
token.id(RealmManager.generateId());
|
||||
token.issuedNow();
|
||||
token.principal(username);
|
||||
token.audience(realm.getId());
|
||||
if (realm.getTokenLifespan() > 0) {
|
||||
token.expiration((System.currentTimeMillis() / 1000) + realm.getTokenLifespan());
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
|
||||
public NewCookie createLoginCookie(RealmModel realm, UserModel user, UriInfo uriInfo) {
|
||||
String cookieName = KEYCLOAK_IDENTITY_COOKIE;
|
||||
URI uri = RealmsResource.realmBaseUrl(uriInfo).build(realm.getId());
|
||||
String cookiePath = uri.getPath();
|
||||
return createLoginCookie(realm, user, cookieName, cookiePath);
|
||||
}
|
||||
|
||||
public NewCookie createSaasIdentityCookie(RealmModel realm, UserModel user, UriInfo uriInfo) {
|
||||
String cookieName = SaasService.SAAS_IDENTITY_COOKIE;
|
||||
URI uri = SaasService.saasCookiePath(uriInfo).build();
|
||||
String cookiePath = uri.getPath();
|
||||
return createLoginCookie(realm, user, cookieName, cookiePath);
|
||||
}
|
||||
|
||||
|
||||
protected NewCookie createLoginCookie(RealmModel realm, UserModel user, String cookieName, String cookiePath) {
|
||||
SkeletonKeyToken identityToken = createIdentityToken(realm, user.getLoginName());
|
||||
String encoded = encodeToken(realm, identityToken);
|
||||
boolean secureOnly = !realm.isSslNotRequired();
|
||||
logger.info("creatingLoginCookie - name: " + cookieName + " path: " + cookiePath);
|
||||
NewCookie cookie = new NewCookie(cookieName, encoded, cookiePath, null, null, NewCookie.DEFAULT_MAX_AGE, secureOnly, false);
|
||||
return cookie;
|
||||
}
|
||||
|
||||
protected String encodeToken(RealmModel realm, Object token) {
|
||||
byte[] tokenBytes = null;
|
||||
try {
|
||||
tokenBytes = JsonSerialization.toByteArray(token, false);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
String encodedToken = new JWSBuilder()
|
||||
.content(tokenBytes)
|
||||
.rsa256(realm.getPrivateKey());
|
||||
return encodedToken;
|
||||
}
|
||||
|
||||
|
||||
public void expireIdentityCookie(RealmModel realm, UriInfo uriInfo) {
|
||||
URI uri = RealmsResource.realmBaseUrl(uriInfo).build(realm.getId());
|
||||
logger.info("Expiring identity cookie");
|
||||
String path = uri.getPath();
|
||||
String cookieName = KEYCLOAK_IDENTITY_COOKIE;
|
||||
expireCookie(cookieName, path);
|
||||
}
|
||||
|
||||
public void expireSaasIdentityCookie(UriInfo uriInfo) {
|
||||
URI uri = SaasService.saasCookiePath(uriInfo).build();
|
||||
String cookiePath = uri.getPath();
|
||||
expireCookie(SaasService.SAAS_IDENTITY_COOKIE, cookiePath);
|
||||
}
|
||||
|
||||
protected void expireCookie(String cookieName, String path) {
|
||||
HttpResponse response = ResteasyProviderFactory.getContextData(HttpResponse.class);
|
||||
if (response == null) {
|
||||
logger.info("can't expire identity cookie, no HttpResponse");
|
||||
return;
|
||||
}
|
||||
logger.info("Expiring identity cookie");
|
||||
NewCookie expireIt = new NewCookie(TokenManager.KEYCLOAK_IDENTITY_COOKIE, "", uri.getPath(), null, "Expiring cookie", 0, false);
|
||||
logger.info("Expiring cookie: " + cookieName + " path: " + path);
|
||||
NewCookie expireIt = new NewCookie(cookieName, "", path, null, "Expiring cookie", 0, false);
|
||||
response.addNewCookie(expireIt);
|
||||
}
|
||||
|
||||
public UserModel authenticateIdentityCookie(RealmModel realm, UriInfo uriInfo, HttpHeaders headers) {
|
||||
Cookie cookie = headers.getCookies().get(TokenManager.KEYCLOAK_IDENTITY_COOKIE);
|
||||
if (cookie == null) return null;
|
||||
String cookieName = KEYCLOAK_IDENTITY_COOKIE;
|
||||
return authenticateIdentityCookie(realm, uriInfo, headers, cookieName);
|
||||
}
|
||||
|
||||
public UserModel authenticateSaasIdentityCookie(RealmModel realm, UriInfo uriInfo, HttpHeaders headers) {
|
||||
String cookieName = SaasService.SAAS_IDENTITY_COOKIE;
|
||||
return authenticateIdentityCookie(realm, uriInfo, headers, cookieName);
|
||||
}
|
||||
|
||||
|
||||
protected UserModel authenticateIdentityCookie(RealmModel realm, UriInfo uriInfo, HttpHeaders headers, String cookieName) {
|
||||
Cookie cookie = headers.getCookies().get(cookieName);
|
||||
if (cookie == null) {
|
||||
logger.info("authenticateCookie could not find cookie: " + cookieName);
|
||||
return null;
|
||||
}
|
||||
|
||||
String tokenString = cookie.getValue();
|
||||
try {
|
||||
|
@ -112,7 +194,6 @@ public class AuthenticationManager {
|
|||
}
|
||||
|
||||
public boolean authenticateForm(RealmModel realm, UserModel user, MultivaluedMap<String, String> formData) {
|
||||
String username = user.getLoginName();
|
||||
Set<String> types = new HashSet<String>();
|
||||
|
||||
for (RequiredCredentialModel credential : realm.getRequiredCredentials()) {
|
||||
|
|
|
@ -2,6 +2,7 @@ package org.keycloak.services.managers;
|
|||
|
||||
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
|
||||
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
|
||||
import org.jboss.resteasy.logging.Logger;
|
||||
import org.keycloak.TokenIdGenerator;
|
||||
import org.keycloak.representations.idm.admin.LogoutAction;
|
||||
import org.keycloak.services.models.RealmModel;
|
||||
|
@ -17,6 +18,7 @@ import java.util.List;
|
|||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class ResourceAdminManager {
|
||||
protected Logger logger = Logger.getLogger(ResourceAdminManager.class);
|
||||
|
||||
public void logoutAll(RealmModel realm) {
|
||||
singleLogOut(realm, null);
|
||||
|
@ -28,6 +30,7 @@ public class ResourceAdminManager {
|
|||
.build();
|
||||
|
||||
List<ResourceModel> resources = realm.getResources();
|
||||
logger.info("logging out " + resources.size() + " resoures.");
|
||||
for (ResourceModel resource : resources) {
|
||||
logoutResource(realm, resource, user, client);
|
||||
}
|
||||
|
@ -38,7 +41,9 @@ public class ResourceAdminManager {
|
|||
String token = new TokenManager().encodeToken(realm, adminAction);
|
||||
Form form = new Form();
|
||||
form.param("token", token);
|
||||
Response response = client.target(resource.getManagementUrl()).queryParam("action", "logout").request().post(Entity.form(form));
|
||||
String managementUrl = resource.getManagementUrl();
|
||||
logger.info("logout user: " + user + " resource: " + resource.getName() + " url" + managementUrl);
|
||||
Response response = client.target(managementUrl).queryParam("action", "logout").request().post(Entity.form(form));
|
||||
boolean success = response.getStatus() == 204;
|
||||
response.close();
|
||||
return success;
|
||||
|
|
|
@ -30,7 +30,6 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
*/
|
||||
public class TokenManager {
|
||||
|
||||
public static final String KEYCLOAK_IDENTITY_COOKIE = "KEYCLOAK_IDENTITY";
|
||||
protected Map<String, AccessCodeEntry> accessCodeMap = new ConcurrentHashMap<String, AccessCodeEntry>();
|
||||
|
||||
public void clearAccessCodes() {
|
||||
|
@ -45,14 +44,6 @@ public class TokenManager {
|
|||
return accessCodeMap.remove(key);
|
||||
}
|
||||
|
||||
public NewCookie createLoginCookie(RealmModel realm, UserModel user, UriInfo uriInfo) {
|
||||
SkeletonKeyToken identityToken = createIdentityToken(realm, user.getLoginName());
|
||||
String encoded = encodeToken(realm, identityToken);
|
||||
URI uri = RealmsResource.realmBaseUrl(uriInfo).build(realm.getId());
|
||||
boolean secureOnly = !realm.isSslNotRequired();
|
||||
NewCookie cookie = new NewCookie(KEYCLOAK_IDENTITY_COOKIE, encoded, uri.getPath(), null, null, NewCookie.DEFAULT_MAX_AGE, secureOnly, true);
|
||||
return cookie;
|
||||
}
|
||||
|
||||
public AccessCodeEntry createAccessCode(String scopeParam, String state, String redirect, RealmModel realm, UserModel client, UserModel user) {
|
||||
AccessCodeEntry code = new AccessCodeEntry();
|
||||
|
@ -212,17 +203,6 @@ public class TokenManager {
|
|||
return token;
|
||||
}
|
||||
|
||||
public SkeletonKeyToken createIdentityToken(RealmModel realm, String username) {
|
||||
SkeletonKeyToken token = new SkeletonKeyToken();
|
||||
token.id(RealmManager.generateId());
|
||||
token.issuedNow();
|
||||
token.principal(username);
|
||||
token.audience(realm.getId());
|
||||
if (realm.getTokenLifespan() > 0) {
|
||||
token.expiration((System.currentTimeMillis() / 1000) + realm.getTokenLifespan());
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
public String encodeToken(RealmModel realm, Object token) {
|
||||
byte[] tokenBytes = null;
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package org.keycloak.services.models.picketlink;
|
||||
|
||||
import org.jboss.resteasy.spi.HttpRequest;
|
||||
import org.jboss.resteasy.spi.NotImplementedYetException;
|
||||
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
||||
import org.keycloak.services.models.KeycloakSession;
|
||||
import org.keycloak.services.models.KeycloakTransaction;
|
||||
import org.keycloak.services.models.RealmModel;
|
||||
|
@ -17,6 +19,7 @@ import java.util.concurrent.atomic.AtomicLong;
|
|||
public class PicketlinkKeycloakSession implements KeycloakSession {
|
||||
public static ThreadLocal<EntityManager> currentEntityManager = new ThreadLocal<EntityManager>();
|
||||
public static ThreadLocal<Exception> setWhere = new ThreadLocal<Exception>();
|
||||
public static ThreadLocal<String> setFromPath = new ThreadLocal<String>();
|
||||
protected PartitionManager partitionManager;
|
||||
protected EntityManager entityManager;
|
||||
|
||||
|
@ -31,7 +34,14 @@ public class PicketlinkKeycloakSession implements KeycloakSession {
|
|||
if (currentEntityManager.get() != null)
|
||||
{
|
||||
setWhere.get().printStackTrace();
|
||||
throw new IllegalStateException("Thread local was leaked!");
|
||||
String path = setFromPath.get();
|
||||
if (path == null) path = "???";
|
||||
|
||||
throw new IllegalStateException("Thread local was leaked! from path: " + path);
|
||||
}
|
||||
HttpRequest request = ResteasyProviderFactory.getContextData(HttpRequest.class);
|
||||
if (request != null) {
|
||||
setFromPath.set(request.getUri().getPath());
|
||||
}
|
||||
currentEntityManager.set(entityManager);
|
||||
setWhere.set(new Exception());
|
||||
|
@ -75,9 +85,10 @@ public class PicketlinkKeycloakSession implements KeycloakSession {
|
|||
|
||||
@Override
|
||||
public void close() {
|
||||
if (entityManager.getTransaction().isActive()) entityManager.getTransaction().rollback();
|
||||
setFromPath.set(null);
|
||||
setWhere.set(null);
|
||||
currentEntityManager.set(null);
|
||||
if (entityManager.getTransaction().isActive()) entityManager.getTransaction().rollback();
|
||||
if (entityManager.isOpen()) entityManager.close();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -390,11 +390,17 @@ public class RealmAdapter implements RealmModel {
|
|||
|
||||
@Override
|
||||
public ResourceModel addResource(String name) {
|
||||
ResourceData resourceData = new ResourceData(name);
|
||||
ResourceData resourceData = new ResourceData(RealmManager.generateId());
|
||||
User resourceUser = new User(name);
|
||||
idm.add(resourceUser);
|
||||
resourceData.setResourceUser(resourceUser);
|
||||
resourceData.setResourceName(name);
|
||||
resourceData.setResourceUser(resourceUser);
|
||||
partitionManager.add(resourceData);
|
||||
ResourceRelationship resourceRelationship = new ResourceRelationship();
|
||||
resourceRelationship.setRealm(realm.getName());
|
||||
resourceRelationship.setResource(resourceData.getName());
|
||||
getRelationshipManager().add(resourceRelationship);
|
||||
ResourceModel resource = new ResourceAdapter(resourceData, this, partitionManager);
|
||||
resource.addRole("*");
|
||||
resource.addScope(new UserAdapter(resourceUser, idm), "*");
|
||||
|
|
|
@ -23,6 +23,7 @@ public class ResourceData extends AbstractPartition {
|
|||
super(name);
|
||||
}
|
||||
|
||||
@AttributeProperty
|
||||
public String getResourceName() {
|
||||
return resourceName;
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ public class ResourceEntity implements Serializable {
|
|||
private PartitionTypeEntity partitionTypeEntity;
|
||||
|
||||
@AttributeValue
|
||||
private String realmName;
|
||||
private String resourceName;
|
||||
@AttributeValue
|
||||
private boolean enabled;
|
||||
@AttributeValue
|
||||
|
@ -45,12 +45,12 @@ public class ResourceEntity implements Serializable {
|
|||
this.partitionTypeEntity = partitionTypeEntity;
|
||||
}
|
||||
|
||||
public String getRealmName() {
|
||||
return realmName;
|
||||
public String getResourceName() {
|
||||
return resourceName;
|
||||
}
|
||||
|
||||
public void setRealmName(String realmName) {
|
||||
this.realmName = realmName;
|
||||
public void setResourceName(String realmName) {
|
||||
this.resourceName = realmName;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
package org.keycloak.services.models.picketlink.relationships;
|
||||
|
||||
import org.keycloak.services.models.picketlink.mappings.RealmData;
|
||||
import org.picketlink.idm.model.AbstractAttributedType;
|
||||
import org.picketlink.idm.model.Attribute;
|
||||
import org.picketlink.idm.model.Relationship;
|
||||
import org.picketlink.idm.model.annotation.AttributeProperty;
|
||||
import org.picketlink.idm.query.AttributeParameter;
|
||||
import org.picketlink.idm.query.RelationshipQueryParameter;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
|
|
@ -1,15 +1,10 @@
|
|||
package org.keycloak.services.models.picketlink.relationships;
|
||||
|
||||
import org.keycloak.services.models.picketlink.mappings.RealmData;
|
||||
import org.keycloak.services.models.picketlink.mappings.ResourceData;
|
||||
import org.picketlink.idm.model.AbstractAttributedType;
|
||||
import org.picketlink.idm.model.Attribute;
|
||||
import org.picketlink.idm.model.Relationship;
|
||||
import org.picketlink.idm.model.annotation.AttributeProperty;
|
||||
import org.picketlink.idm.model.sample.Agent;
|
||||
import org.picketlink.idm.model.sample.User;
|
||||
import org.picketlink.idm.query.AttributeParameter;
|
||||
import org.picketlink.idm.query.RelationshipQueryParameter;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
@ -20,9 +15,10 @@ public class ResourceRelationship extends AbstractAttributedType implements Rela
|
|||
|
||||
public static final AttributeParameter REALM = new AttributeParameter("realm");
|
||||
|
||||
protected String realm;
|
||||
protected String resource;
|
||||
public ResourceRelationship() {
|
||||
}
|
||||
|
||||
@AttributeProperty
|
||||
public String getRealm() {
|
||||
return (String)getAttribute("realm").getValue();
|
||||
}
|
||||
|
@ -32,6 +28,7 @@ public class ResourceRelationship extends AbstractAttributedType implements Rela
|
|||
}
|
||||
|
||||
|
||||
@AttributeProperty
|
||||
public String getResource() {
|
||||
return (String)getAttribute("resource").getValue();
|
||||
}
|
||||
|
|
4
services/src/main/java/org/keycloak/services/resources/AbstractLoginService.java
Normal file → Executable file
4
services/src/main/java/org/keycloak/services/resources/AbstractLoginService.java
Normal file → Executable file
|
@ -11,6 +11,7 @@ import org.jboss.resteasy.spi.HttpRequest;
|
|||
import org.jboss.resteasy.spi.HttpResponse;
|
||||
import org.keycloak.services.JspRequestParameters;
|
||||
import org.keycloak.services.managers.AccessCodeEntry;
|
||||
import org.keycloak.services.managers.AuthenticationManager;
|
||||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.services.managers.TokenManager;
|
||||
import org.keycloak.services.models.RealmModel;
|
||||
|
@ -34,6 +35,7 @@ public abstract class AbstractLoginService {
|
|||
|
||||
protected RealmModel realm;
|
||||
protected TokenManager tokenManager;
|
||||
protected AuthenticationManager authManager = new AuthenticationManager();
|
||||
|
||||
public AbstractLoginService(RealmModel realm, TokenManager tokenManager) {
|
||||
this.realm = realm;
|
||||
|
@ -69,7 +71,7 @@ public abstract class AbstractLoginService {
|
|||
redirectUri.queryParam("state", state);
|
||||
Response.ResponseBuilder location = Response.status(302).location(redirectUri.build());
|
||||
if (realm.isCookieLoginAllowed()) {
|
||||
location.cookie(tokenManager.createLoginCookie(realm, accessCode.getUser(), uriInfo));
|
||||
location.cookie(authManager.createLoginCookie(realm, accessCode.getUser(), uriInfo));
|
||||
}
|
||||
return location.build();
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ public class KeycloakApplication extends Application {
|
|||
singletons.add(filter);
|
||||
classes.add(KeycloakSessionResponseFilter.class);
|
||||
classes.add(SkeletonKeyContextResolver.class);
|
||||
classes.add(RegistrationService.class);
|
||||
classes.add(SaasService.class);
|
||||
}
|
||||
|
||||
protected KeycloakSessionFactory createSessionFactory() {
|
||||
|
|
|
@ -5,8 +5,6 @@ import org.keycloak.representations.idm.RealmRepresentation;
|
|||
import org.keycloak.services.managers.AuthenticationManager;
|
||||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.services.managers.TokenManager;
|
||||
import org.keycloak.services.models.KeycloakSession;
|
||||
import org.keycloak.services.models.KeycloakSessionFactory;
|
||||
import org.keycloak.services.models.RealmModel;
|
||||
import org.keycloak.services.models.RoleModel;
|
||||
import org.keycloak.services.models.UserModel;
|
||||
|
@ -88,7 +86,7 @@ public class RealmsResource {
|
|||
}
|
||||
SocialService socialService = new SocialService(realm, tokenManager, socialRequestManager);
|
||||
resourceContext.initResource(socialService);
|
||||
return socialService;
|
||||
return socialService;
|
||||
}
|
||||
}.call();
|
||||
}
|
||||
|
@ -121,7 +119,7 @@ public class RealmsResource {
|
|||
RealmManager realmManager = new RealmManager(session);
|
||||
RealmModel defaultRealm = realmManager.getRealm(RealmModel.DEFAULT_REALM);
|
||||
UserModel realmCreator = new AuthenticationManager().authenticateBearerToken(defaultRealm, headers);
|
||||
RoleModel creatorRole = defaultRealm.getRole(RegistrationService.REALM_CREATOR_ROLE);
|
||||
RoleModel creatorRole = defaultRealm.getRole(SaasService.REALM_CREATOR_ROLE);
|
||||
if (!defaultRealm.hasRole(realmCreator, creatorRole)) {
|
||||
logger.warn("not a realm creator");
|
||||
throw new NotAuthorizedException("Bearer");
|
||||
|
|
|
@ -1,70 +0,0 @@
|
|||
package org.keycloak.services.resources;
|
||||
|
||||
import org.jboss.resteasy.logging.Logger;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.services.models.KeycloakSessionFactory;
|
||||
import org.keycloak.services.models.RealmModel;
|
||||
import org.keycloak.services.models.RoleModel;
|
||||
import org.keycloak.services.models.UserModel;
|
||||
import org.keycloak.services.models.UserCredentialModel;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.ForbiddenException;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import java.net.URI;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
@Path("/registrations")
|
||||
public class RegistrationService {
|
||||
protected static final Logger logger = Logger.getLogger(RegistrationService.class);
|
||||
public static final String REALM_CREATOR_ROLE = "realm-creator";
|
||||
|
||||
@Context
|
||||
protected UriInfo uriInfo;
|
||||
|
||||
@POST
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
public Response register(final UserRepresentation newUser) {
|
||||
return new Transaction() {
|
||||
@Override
|
||||
protected Response callImpl() {
|
||||
RealmManager realmManager = new RealmManager(session);
|
||||
RealmModel defaultRealm = realmManager.defaultRealm();
|
||||
if (!defaultRealm.isEnabled()) {
|
||||
throw new ForbiddenException();
|
||||
}
|
||||
if (!defaultRealm.isRegistrationAllowed()) {
|
||||
throw new ForbiddenException();
|
||||
}
|
||||
UserModel user = defaultRealm.getUser(newUser.getUsername());
|
||||
if (user != null) {
|
||||
return Response.status(400).type("text/plain").entity("user exists").build();
|
||||
}
|
||||
|
||||
user = defaultRealm.addUser(newUser.getUsername());
|
||||
for (CredentialRepresentation cred : newUser.getCredentials()) {
|
||||
UserCredentialModel credModel = new UserCredentialModel();
|
||||
credModel.setType(cred.getType());
|
||||
credModel.setValue(cred.getValue());
|
||||
defaultRealm.updateCredential(user, credModel);
|
||||
}
|
||||
RoleModel realmCreator = defaultRealm.getRole(REALM_CREATOR_ROLE);
|
||||
defaultRealm.grantRole(user, realmCreator);
|
||||
URI uri = uriInfo.getBaseUriBuilder().path(RealmsResource.class).path(user.getLoginName()).build();
|
||||
return Response.created(uri).build();
|
||||
}
|
||||
}.call();
|
||||
}
|
||||
|
||||
|
||||
}
|
296
services/src/main/java/org/keycloak/services/resources/SaaSService.java
Executable file
296
services/src/main/java/org/keycloak/services/resources/SaaSService.java
Executable file
|
@ -0,0 +1,296 @@
|
|||
package org.keycloak.services.resources;
|
||||
|
||||
import org.jboss.resteasy.logging.Logger;
|
||||
import org.jboss.resteasy.spi.HttpRequest;
|
||||
import org.jboss.resteasy.spi.HttpResponse;
|
||||
import org.jboss.resteasy.spi.NotImplementedYetException;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.representations.idm.RequiredCredentialRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.services.managers.AuthenticationManager;
|
||||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.services.models.KeycloakSession;
|
||||
import org.keycloak.services.models.RealmModel;
|
||||
import org.keycloak.services.models.RoleModel;
|
||||
import org.keycloak.services.models.UserModel;
|
||||
import org.keycloak.services.models.UserCredentialModel;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.ForbiddenException;
|
||||
import javax.ws.rs.FormParam;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.NotFoundException;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import javax.ws.rs.core.NewCookie;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import java.net.URI;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
@Path("/saas")
|
||||
public class SaasService {
|
||||
protected static final Logger logger = Logger.getLogger(SaasService.class);
|
||||
public static final String REALM_CREATOR_ROLE = "realm-creator";
|
||||
public static final String SAAS_IDENTITY_COOKIE = "KEYCLOAK_SAAS_IDENTITY";
|
||||
|
||||
@Context
|
||||
protected UriInfo uriInfo;
|
||||
|
||||
@Context
|
||||
protected HttpRequest request;
|
||||
|
||||
@Context
|
||||
HttpResponse response;
|
||||
|
||||
protected String saasLoginPath = "/saas/saas-login.jsp";
|
||||
protected String saasRegisterPath = "/saas/saas-register.jsp";
|
||||
protected String adminPath = "/saas/admin/index.html";
|
||||
protected AuthenticationManager authManager = new AuthenticationManager();
|
||||
|
||||
public static class WhoAmI {
|
||||
protected String userId;
|
||||
protected String displayName;
|
||||
|
||||
public WhoAmI() {
|
||||
}
|
||||
|
||||
public WhoAmI(String userId, String displayName) {
|
||||
this.userId = userId;
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(String userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public void setDisplayName(String displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
}
|
||||
|
||||
@Path("whoami")
|
||||
@GET
|
||||
@Produces("application/json")
|
||||
public Response whoAmI(final @Context HttpHeaders headers) {
|
||||
return new Transaction() {
|
||||
@Override
|
||||
public Response callImpl()
|
||||
{
|
||||
logger.info("WHOAMI start.");
|
||||
RealmManager realmManager = new RealmManager(session);
|
||||
RealmModel realm = realmManager.defaultRealm();
|
||||
if (realm == null) throw new NotFoundException();
|
||||
UserModel user = authManager.authenticateSaasIdentityCookie(realm, uriInfo, headers);
|
||||
if (user == null) {
|
||||
return Response.status(404).build();
|
||||
}
|
||||
logger.info("WHOAMI: " + user.getLoginName());
|
||||
return Response.ok(new WhoAmI(user.getLoginName(), user.getLoginName())).build();
|
||||
}
|
||||
}.call();
|
||||
}
|
||||
|
||||
@Path("isLoggedIn.js")
|
||||
@GET
|
||||
@Produces("application/javascript")
|
||||
public String isLoggedIn(final @Context HttpHeaders headers) {
|
||||
return new Transaction() {
|
||||
@Override
|
||||
public String callImpl()
|
||||
{
|
||||
logger.info("WHOAMI Javascript start.");
|
||||
RealmManager realmManager = new RealmManager(session);
|
||||
RealmModel realm = realmManager.defaultRealm();
|
||||
if (realm == null) {
|
||||
return "var keycloakCookieLoggedIn = false;";
|
||||
|
||||
}
|
||||
UserModel user = authManager.authenticateSaasIdentityCookie(realm, uriInfo, headers);
|
||||
if (user == null) {
|
||||
return "var keycloakCookieLoggedIn = false;";
|
||||
}
|
||||
logger.info("WHOAMI: " + user.getLoginName());
|
||||
return "var keycloakCookieLoggedIn = true;";
|
||||
}
|
||||
}.call();
|
||||
}
|
||||
|
||||
|
||||
public static UriBuilder contextRoot(UriInfo uriInfo) {
|
||||
return UriBuilder.fromUri(uriInfo.getBaseUri()).replacePath("/auth-server");
|
||||
}
|
||||
|
||||
public static UriBuilder saasCookiePath(UriInfo uriInfo) {
|
||||
return contextRoot(uriInfo).path("rest").path(SaasService.class);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Path("logout")
|
||||
@GET
|
||||
public void logout() {
|
||||
new Transaction() {
|
||||
@Override
|
||||
protected void runImpl() {
|
||||
authManager.expireSaasIdentityCookie(uriInfo);
|
||||
request.forward(saasLoginPath);
|
||||
}
|
||||
}.run();
|
||||
}
|
||||
|
||||
@Path("logout-cookie")
|
||||
@GET
|
||||
public void logoutCookie() {
|
||||
logger.info("*** logoutCookie");
|
||||
new Transaction() {
|
||||
@Override
|
||||
protected void runImpl() {
|
||||
authManager.expireSaasIdentityCookie(uriInfo);
|
||||
}
|
||||
}.run();
|
||||
}
|
||||
|
||||
|
||||
@Path("login")
|
||||
@POST
|
||||
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
||||
public void processLogin(final MultivaluedMap<String, String> formData) {
|
||||
logger.info("processLogin start");
|
||||
new Transaction() {
|
||||
@Override
|
||||
protected void runImpl() {
|
||||
RealmManager realmManager = new RealmManager(session);
|
||||
RealmModel realm = realmManager.defaultRealm();
|
||||
if (realm == null) throw new NotFoundException();
|
||||
|
||||
if (!realm.isEnabled()) {
|
||||
throw new NotImplementedYetException();
|
||||
}
|
||||
String username = formData.getFirst("username");
|
||||
UserModel user = realm.getUser(username);
|
||||
if (user == null) {
|
||||
logger.info("Not Authenticated! Incorrect user name");
|
||||
request.setAttribute("KEYCLOAK_LOGIN_ERROR_MESSAGE", "Incorrect user name.");
|
||||
request.forward(saasLoginPath);
|
||||
return;
|
||||
}
|
||||
if (!user.isEnabled()) {
|
||||
logger.info("NAccount is disabled, contact admin.");
|
||||
request.setAttribute("KEYCLOAK_LOGIN_ERROR_MESSAGE", "Account is disabled, contact admin.");
|
||||
request.forward(saasLoginPath);
|
||||
return;
|
||||
}
|
||||
|
||||
boolean authenticated = authManager.authenticateForm(realm, user, formData);
|
||||
if (!authenticated) {
|
||||
logger.info("Not Authenticated! Invalid credentials");
|
||||
request.setAttribute("KEYCLOAK_LOGIN_ERROR_MESSAGE", "Invalid credentials.");
|
||||
request.forward(saasLoginPath);
|
||||
return;
|
||||
}
|
||||
|
||||
NewCookie cookie = authManager.createSaasIdentityCookie(realm, user, uriInfo);
|
||||
response.addNewCookie(cookie);
|
||||
request.forward(adminPath);
|
||||
}
|
||||
}.run();
|
||||
}
|
||||
|
||||
@Path("registrations")
|
||||
@POST
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
public Response register(final UserRepresentation newUser) {
|
||||
return new Transaction() {
|
||||
@Override
|
||||
protected Response callImpl() {
|
||||
RealmManager realmManager = new RealmManager(session);
|
||||
RealmModel defaultRealm = realmManager.defaultRealm();
|
||||
UserModel user = registerMe(defaultRealm, newUser);
|
||||
if (user == null) {
|
||||
return Response.status(400).type("text/plain").entity("Already exists").build();
|
||||
}
|
||||
URI uri = uriInfo.getBaseUriBuilder().path(RealmsResource.class).path(user.getLoginName()).build();
|
||||
return Response.created(uri).build();
|
||||
}
|
||||
}.call();
|
||||
}
|
||||
|
||||
@Path("registrations")
|
||||
@POST
|
||||
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
||||
public Response processRegister(final @FormParam("name") String name,
|
||||
final @FormParam("email") String email,
|
||||
final @FormParam("username") String username,
|
||||
final @FormParam("password") String password,
|
||||
final @FormParam("password-confirm") String confirm) {
|
||||
if (!password.equals(confirm)) {
|
||||
request.setAttribute("KEYCLOAK_LOGIN_ERROR_MESSAGE", "Password confirmation doesn't match.");
|
||||
request.forward(saasRegisterPath);
|
||||
return null;
|
||||
}
|
||||
return new Transaction() {
|
||||
@Override
|
||||
protected Response callImpl() {
|
||||
RealmManager realmManager = new RealmManager(session);
|
||||
RealmModel defaultRealm = realmManager.defaultRealm();
|
||||
UserRepresentation newUser = new UserRepresentation();
|
||||
newUser.setUsername(username);
|
||||
newUser.credential(RequiredCredentialRepresentation.PASSWORD, password, false);
|
||||
UserModel user = registerMe(defaultRealm, newUser);
|
||||
if (user == null) {
|
||||
request.setAttribute("KEYCLOAK_LOGIN_ERROR_MESSAGE", "Username already exists.");
|
||||
request.forward(saasRegisterPath);
|
||||
return null;
|
||||
|
||||
}
|
||||
NewCookie cookie = authManager.createSaasIdentityCookie(defaultRealm, user, uriInfo);
|
||||
return Response.status(302).location(contextRoot(uriInfo).path(adminPath).build()).cookie(cookie).build();
|
||||
}
|
||||
}.call();
|
||||
}
|
||||
|
||||
|
||||
protected UserModel registerMe(RealmModel defaultRealm, UserRepresentation newUser) {
|
||||
if (!defaultRealm.isEnabled()) {
|
||||
throw new ForbiddenException();
|
||||
}
|
||||
if (!defaultRealm.isRegistrationAllowed()) {
|
||||
throw new ForbiddenException();
|
||||
}
|
||||
UserModel user = defaultRealm.getUser(newUser.getUsername());
|
||||
if (user != null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
user = defaultRealm.addUser(newUser.getUsername());
|
||||
for (CredentialRepresentation cred : newUser.getCredentials()) {
|
||||
UserCredentialModel credModel = new UserCredentialModel();
|
||||
credModel.setType(cred.getType());
|
||||
credModel.setValue(cred.getValue());
|
||||
defaultRealm.updateCredential(user, credModel);
|
||||
}
|
||||
RoleModel realmCreator = defaultRealm.getRole(REALM_CREATOR_ROLE);
|
||||
defaultRealm.grantRole(user, realmCreator);
|
||||
return user;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -49,7 +49,6 @@ public class TokenService extends AbstractLoginService {
|
|||
@Context
|
||||
protected SecurityContext securityContext;
|
||||
|
||||
protected AuthenticationManager authManager = new AuthenticationManager();
|
||||
private ResourceAdminManager resourceAdminManager = new ResourceAdminManager();
|
||||
|
||||
public TokenService(RealmModel realm, TokenManager tokenManager) {
|
||||
|
@ -115,7 +114,7 @@ public class TokenService extends AbstractLoginService {
|
|||
throw new NotAuthorizedException("FORM");
|
||||
}
|
||||
tokenManager = new TokenManager();
|
||||
SkeletonKeyToken token = tokenManager.createIdentityToken(realm, username);
|
||||
SkeletonKeyToken token = authManager.createIdentityToken(realm, username);
|
||||
String encoded = tokenManager.encodeToken(realm, token);
|
||||
AccessTokenResponse res = accessTokenResponse(token, encoded);
|
||||
return Response.ok(res, MediaType.APPLICATION_JSON_TYPE).build();
|
||||
|
|
|
@ -12,10 +12,13 @@ import org.keycloak.services.models.KeycloakSession;
|
|||
import org.keycloak.services.models.KeycloakSessionFactory;
|
||||
import org.keycloak.services.models.RealmModel;
|
||||
import org.keycloak.services.models.RequiredCredentialModel;
|
||||
import org.keycloak.services.models.ResourceModel;
|
||||
import org.keycloak.services.models.UserModel;
|
||||
import org.keycloak.services.resources.KeycloakApplication;
|
||||
import org.keycloak.services.resources.RegistrationService;
|
||||
import org.keycloak.services.resources.SaasService;
|
||||
import org.keycloak.services.resources.SaasService;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
|
@ -56,17 +59,21 @@ public class ImportTest {
|
|||
defaultRealm.setRegistrationAllowed(true);
|
||||
manager.generateRealmKeys(defaultRealm);
|
||||
defaultRealm.addRequiredCredential(RequiredCredentialModel.PASSWORD);
|
||||
defaultRealm.addRole(RegistrationService.REALM_CREATOR_ROLE);
|
||||
defaultRealm.addRole(SaasService.REALM_CREATOR_ROLE);
|
||||
|
||||
RealmRepresentation rep = KeycloakTestBase.loadJson("testrealm.json");
|
||||
RealmModel realm = manager.createRealm("demo", rep.getRealm());
|
||||
manager.importRealm(rep, realm);
|
||||
List<RequiredCredentialModel> creds = realm.getRequiredCredentials();
|
||||
Assert.assertEquals(1, creds.size());
|
||||
|
||||
UserModel user = realm.getUser("loginclient");
|
||||
Assert.assertNotNull(user);
|
||||
Set<String> scopes = realm.getScope(user);
|
||||
System.out.println("Scopes size: " + scopes.size());
|
||||
Assert.assertTrue(scopes.contains("*"));
|
||||
List<ResourceModel> resources = realm.getResources();
|
||||
Assert.assertEquals(2, resources.size());
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,8 @@ package org.keycloak.test;
|
|||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.services.models.RealmModel;
|
||||
import org.keycloak.services.models.RequiredCredentialModel;
|
||||
import org.keycloak.services.resources.RegistrationService;
|
||||
import org.keycloak.services.resources.SaasService;
|
||||
import org.keycloak.services.resources.SaasService;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
@ -21,7 +22,7 @@ public class InstallationManager {
|
|||
defaultRealm.setRegistrationAllowed(true);
|
||||
manager.generateRealmKeys(defaultRealm);
|
||||
defaultRealm.addRequiredCredential(RequiredCredentialModel.PASSWORD);
|
||||
defaultRealm.addRole(RegistrationService.REALM_CREATOR_ROLE);
|
||||
defaultRealm.addRole(SaasService.REALM_CREATOR_ROLE);
|
||||
}
|
||||
|
||||
public boolean isInstalled(RealmManager manager) {
|
||||
|
|
|
@ -64,7 +64,7 @@ public class RealmCreationTest {
|
|||
user.credential(RequiredCredentialRepresentation.PASSWORD, "geheim", false);
|
||||
|
||||
WebTarget target = client.target(generateURL("/"));
|
||||
Response response = target.path("registrations").request().post(Entity.json(user));
|
||||
Response response = target.path("saas/registrations").request().post(Entity.json(user));
|
||||
Assert.assertEquals(201, response.getStatus());
|
||||
response.close();
|
||||
|
||||
|
|
Loading…
Reference in a new issue