This commit is contained in:
Bill Burke 2013-10-17 13:53:46 -04:00
parent 6705819984
commit 2a6b6ebef5
11 changed files with 212 additions and 35 deletions

View file

@ -21,12 +21,14 @@ import java.util.concurrent.atomic.AtomicLong;
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class AbstractOAuthClient { public class AbstractOAuthClient {
public static final String OAUTH_TOKEN_REQUEST_STATE = "OAuth_Token_Request_State";
protected String clientId; protected String clientId;
protected String password; protected String password;
protected KeyStore truststore; protected KeyStore truststore;
protected String authUrl; protected String authUrl;
protected String codeUrl; protected String codeUrl;
protected String stateCookieName = "OAuth_Token_Request_State"; protected String stateCookieName = OAUTH_TOKEN_REQUEST_STATE;
protected String stateCookiePath;
protected Client client; protected Client client;
protected boolean isSecure; protected boolean isSecure;
protected final AtomicLong counter = new AtomicLong(); protected final AtomicLong counter = new AtomicLong();
@ -35,6 +37,9 @@ public class AbstractOAuthClient {
return counter.getAndIncrement() + "/" + UUID.randomUUID().toString(); return counter.getAndIncrement() + "/" + UUID.randomUUID().toString();
} }
/**
* Creates a Client for obtaining access token from code
*/
public void start() { public void start() {
if (client == null) { if (client == null) {
client = new ResteasyClientBuilder().trustStore(truststore) client = new ResteasyClientBuilder().trustStore(truststore)
@ -44,6 +49,9 @@ public class AbstractOAuthClient {
} }
} }
/**
* closes cllient
*/
public void stop() { public void stop() {
client.close(); client.close();
} }
@ -76,6 +84,8 @@ public class AbstractOAuthClient {
return authUrl; return authUrl;
} }
public void setAuthUrl(String authUrl) { public void setAuthUrl(String authUrl) {
this.authUrl = authUrl; this.authUrl = authUrl;
} }
@ -96,6 +106,14 @@ public class AbstractOAuthClient {
this.stateCookieName = stateCookieName; this.stateCookieName = stateCookieName;
} }
public String getStateCookiePath() {
return stateCookiePath;
}
public void setStateCookiePath(String stateCookiePath) {
this.stateCookiePath = stateCookiePath;
}
public Client getClient() { public Client getClient() {
return client; return client;
} }
@ -128,7 +146,6 @@ public class AbstractOAuthClient {
} }
protected String stripOauthParametersFromRedirect(String uri) { protected String stripOauthParametersFromRedirect(String uri) {
System.out.println("******************** redirect_uri: " + uri);
UriBuilder builder = UriBuilder.fromUri(uri) UriBuilder builder = UriBuilder.fromUri(uri)
.replaceQueryParam("code", null) .replaceQueryParam("code", null)
.replaceQueryParam("state", null); .replaceQueryParam("state", null);

View file

@ -1,5 +1,6 @@
package org.keycloak.jaxrs; package org.keycloak.jaxrs;
import org.jboss.resteasy.logging.Logger;
import org.keycloak.AbstractOAuthClient; import org.keycloak.AbstractOAuthClient;
import javax.ws.rs.BadRequestException; import javax.ws.rs.BadRequestException;
@ -19,6 +20,7 @@ import java.net.URI;
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class JaxrsOAuthClient extends AbstractOAuthClient { public class JaxrsOAuthClient extends AbstractOAuthClient {
protected static final Logger logger = Logger.getLogger(JaxrsOAuthClient.class);
public Response redirect(UriInfo uriInfo, String redirectUri) { public Response redirect(UriInfo uriInfo, String redirectUri) {
String state = getStateCode(); String state = getStateCode();
@ -27,26 +29,42 @@ public class JaxrsOAuthClient extends AbstractOAuthClient {
.queryParam("redirect_uri", redirectUri) .queryParam("redirect_uri", redirectUri)
.queryParam("state", state) .queryParam("state", state)
.build(); .build();
NewCookie cookie = new NewCookie(stateCookieName, state, uriInfo.getBaseUri().getPath(), null, null, -1, true); NewCookie cookie = new NewCookie(getStateCookieName(), state, getStateCookiePath(uriInfo), null, null, -1, isSecure, true);
logger.info("NewCookie: " + cookie.toString());
return Response.status(302) return Response.status(302)
.location(url) .location(url)
.cookie(cookie).build(); .cookie(cookie).build();
} }
public String getBearerToken(UriInfo uriInfo, HttpHeaders headers) throws BadRequestException, InternalServerErrorException { public String getStateCookiePath(UriInfo uriInfo) {
String error = uriInfo.getQueryParameters().getFirst("error"); if (stateCookiePath != null) return stateCookiePath;
if (error != null) throw new BadRequestException(new Exception("OAuth error: " + error)); return uriInfo.getBaseUri().getPath();
Cookie stateCookie = headers.getCookies().get(stateCookieName);
if (stateCookie == null) throw new BadRequestException(new Exception("state cookie not set"));
;
String state = uriInfo.getQueryParameters().getFirst("state");
if (state == null) throw new BadRequestException(new Exception("state parameter was null"));
if (!state.equals(stateCookie.getValue())) {
throw new BadRequestException(new Exception("state parameter invalid"));
} }
String code = uriInfo.getQueryParameters().getFirst("code");
public String getBearerToken(UriInfo uriInfo, HttpHeaders headers) throws BadRequestException, InternalServerErrorException {
String error = getError(uriInfo);
if (error != null) throw new BadRequestException(new Exception("OAuth error: " + error));
checkStateCookie(uriInfo, headers);
String code = getAccessCode(uriInfo);
if (code == null) throw new BadRequestException(new Exception("code parameter was null")); if (code == null) throw new BadRequestException(new Exception("code parameter was null"));
return resolveBearerToken(uriInfo.getRequestUri().toString(), code); return resolveBearerToken(uriInfo.getRequestUri().toString(), code);
} }
public String getError(UriInfo uriInfo) {
return uriInfo.getQueryParameters().getFirst("error");
}
public String getAccessCode(UriInfo uriInfo) {
return uriInfo.getQueryParameters().getFirst("code");
}
public void checkStateCookie(UriInfo uriInfo, HttpHeaders headers) {
Cookie stateCookie = headers.getCookies().get(stateCookieName);
if (stateCookie == null) throw new BadRequestException("state cookie not set");
String state = uriInfo.getQueryParameters().getFirst("state");
if (state == null) throw new BadRequestException("state parameter was null");
if (!state.equals(stateCookie.getValue())) {
throw new BadRequestException("state parameter invalid");
}
}
} }

View file

@ -51,12 +51,13 @@ public class ServletOAuthClient extends AbstractOAuthClient {
.queryParam("redirect_uri", redirectUri) .queryParam("redirect_uri", redirectUri)
.queryParam("state", state) .queryParam("state", state)
.build(); .build();
String cookiePath = request.getContextPath(); String stateCookiePath = this.stateCookiePath;
if (cookiePath.equals("")) cookiePath = "/"; if (stateCookiePath == null) stateCookiePath = request.getContextPath();
if (stateCookiePath.equals("")) stateCookiePath = "/";
Cookie cookie = new Cookie(stateCookieName, state); Cookie cookie = new Cookie(stateCookieName, state);
cookie.setSecure(isSecure); cookie.setSecure(isSecure);
cookie.setPath(cookiePath); cookie.setPath(stateCookiePath);
response.addCookie(cookie); response.addCookie(cookie);
response.sendRedirect(url.toString()); response.sendRedirect(url.toString());
} }

8
forms/src/main/java/org/keycloak/forms/UrlBean.java Normal file → Executable file
View file

@ -70,20 +70,12 @@ public class UrlBean {
} }
public String getLoginAction() { public String getLoginAction() {
if (realm.isSaas()) {
return Urls.saasLoginAction(baseURI).toString();
} else {
return Urls.realmLoginAction(baseURI, realm.getId()).toString(); return Urls.realmLoginAction(baseURI, realm.getId()).toString();
} }
}
public String getLoginUrl() { public String getLoginUrl() {
if (realm.isSaas()) {
return Urls.saasLoginPage(baseURI).toString();
} else {
return Urls.realmLoginPage(baseURI, realm.getId()).toString(); return Urls.realmLoginPage(baseURI, realm.getId()).toString();
} }
}
public String getPasswordUrl() { public String getPasswordUrl() {
return Urls.accountPasswordPage(baseURI, realm.getId()).toString(); return Urls.accountPasswordPage(baseURI, realm.getId()).toString();

View file

@ -1,5 +1,6 @@
package org.keycloak.services.managers; package org.keycloak.services.managers;
import org.keycloak.models.RealmModel;
import org.keycloak.representations.SkeletonKeyToken; import org.keycloak.representations.SkeletonKeyToken;
import org.keycloak.models.RoleModel; import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
@ -23,6 +24,7 @@ public class AccessCodeEntry {
protected String redirectUri; protected String redirectUri;
protected long expiration; protected long expiration;
protected RealmModel realm;
protected SkeletonKeyToken token; protected SkeletonKeyToken token;
protected UserModel user; protected UserModel user;
protected Set<RequiredAction> requiredActions; protected Set<RequiredAction> requiredActions;
@ -38,6 +40,14 @@ public class AccessCodeEntry {
return id; return id;
} }
public RealmModel getRealm() {
return realm;
}
public void setRealm(RealmModel realm) {
this.realm = realm;
}
public String getCode() { public String getCode() {
return code; return code;
} }

View file

@ -26,6 +26,7 @@ public class ApplianceBootstrap {
realm.addRequiredResourceCredential(CredentialRepresentation.PASSWORD); realm.addRequiredResourceCredential(CredentialRepresentation.PASSWORD);
realm.setTokenLifespan(300); realm.setTokenLifespan(300);
realm.setAccessCodeLifespan(60); realm.setAccessCodeLifespan(60);
realm.setAccessCodeLifespanUserAction(300);
realm.setSslNotRequired(true); realm.setSslNotRequired(true);
realm.setCookieLoginAllowed(true); realm.setCookieLoginAllowed(true);
realm.setRegistrationAllowed(false); realm.setRegistrationAllowed(false);
@ -49,7 +50,7 @@ public class ApplianceBootstrap {
password.setType(UserCredentialModel.PASSWORD); password.setType(UserCredentialModel.PASSWORD);
password.setValue("admin"); password.setValue("admin");
realm.updateCredential(adminUser, password); realm.updateCredential(adminUser, password);
//adminUser.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD); adminUser.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
adminConsole.grantRole(adminUser, adminRole); adminConsole.grantRole(adminUser, adminRole);

View file

@ -99,7 +99,7 @@ public class AuthenticationManager {
expireCookie(SaasService.SAAS_IDENTITY_COOKIE, cookiePath); expireCookie(SaasService.SAAS_IDENTITY_COOKIE, cookiePath);
} }
protected void expireCookie(String cookieName, String path) { public void expireCookie(String cookieName, String path) {
HttpResponse response = ResteasyProviderFactory.getContextData(HttpResponse.class); HttpResponse response = ResteasyProviderFactory.getContextData(HttpResponse.class);
if (response == null) { if (response == null) {
logger.info("can't expire identity cookie, no HttpResponse"); logger.info("can't expire identity cookie, no HttpResponse");

View file

@ -3,6 +3,7 @@ package org.keycloak.services.managers;
import org.jboss.resteasy.jose.Base64Url; import org.jboss.resteasy.jose.Base64Url;
import org.jboss.resteasy.jose.jws.JWSBuilder; import org.jboss.resteasy.jose.jws.JWSBuilder;
import org.jboss.resteasy.jwt.JsonSerialization; import org.jboss.resteasy.jwt.JsonSerialization;
import org.jboss.resteasy.logging.Logger;
import org.keycloak.models.*; import org.keycloak.models.*;
import org.keycloak.representations.SkeletonKeyScope; import org.keycloak.representations.SkeletonKeyScope;
import org.keycloak.representations.SkeletonKeyToken; import org.keycloak.representations.SkeletonKeyToken;
@ -22,6 +23,7 @@ import java.util.concurrent.ConcurrentHashMap;
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class TokenManager { public class TokenManager {
protected static final Logger logger = Logger.getLogger(TokenManager.class);
protected Map<String, AccessCodeEntry> accessCodeMap = new ConcurrentHashMap<String, AccessCodeEntry>(); protected Map<String, AccessCodeEntry> accessCodeMap = new ConcurrentHashMap<String, AccessCodeEntry>();
@ -86,6 +88,9 @@ public class TokenManager {
createToken(code, realm, client, user); createToken(code, realm, client, user);
logger.info("tokenmanager: access code id: " + code.getId());
logger.info("accesscode setExpiration: " + (System.currentTimeMillis() / 1000) + realm.getAccessCodeLifespan());
code.setRealm(realm);
code.setExpiration((System.currentTimeMillis() / 1000) + realm.getAccessCodeLifespan()); code.setExpiration((System.currentTimeMillis() / 1000) + realm.getAccessCodeLifespan());
code.setClient(client); code.setClient(client);
code.setUser(user); code.setUser(user);

View file

@ -47,10 +47,9 @@ public class KeycloakApplication extends Application {
TokenManager tokenManager = new TokenManager(); TokenManager tokenManager = new TokenManager();
singletons.add(new RealmsResource(tokenManager)); singletons.add(new RealmsResource(tokenManager));
singletons.add(new SaasService(tokenManager));
singletons.add(new SocialResource(tokenManager, new SocialRequestManager())); singletons.add(new SocialResource(tokenManager, new SocialRequestManager()));
classes.add(SkeletonKeyContextResolver.class); classes.add(SkeletonKeyContextResolver.class);
classes.add(SaasService.class);
} }
protected KeycloakSessionFactory createSessionFactory() { protected KeycloakSessionFactory createSessionFactory() {

View file

@ -23,6 +23,7 @@ package org.keycloak.services.resources;
import org.jboss.resteasy.jose.jws.JWSInput; import org.jboss.resteasy.jose.jws.JWSInput;
import org.jboss.resteasy.jose.jws.crypto.RSAProvider; import org.jboss.resteasy.jose.jws.crypto.RSAProvider;
import org.jboss.resteasy.logging.Logger;
import org.jboss.resteasy.spi.HttpRequest; import org.jboss.resteasy.spi.HttpRequest;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserCredentialModel;
@ -52,6 +53,7 @@ import java.util.Set;
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/ */
public class RequiredActionsService { public class RequiredActionsService {
protected static final Logger logger = Logger.getLogger(RequiredActionsService.class);
private RealmModel realm; private RealmModel realm;
@ -134,10 +136,13 @@ public class RequiredActionsService {
@POST @POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response updatePassword(final MultivaluedMap<String, String> formData) { public Response updatePassword(final MultivaluedMap<String, String> formData) {
logger.info("updatePassword");
AccessCodeEntry accessCode = getAccessCodeEntry(RequiredAction.UPDATE_PASSWORD); AccessCodeEntry accessCode = getAccessCodeEntry(RequiredAction.UPDATE_PASSWORD);
if (accessCode == null) { if (accessCode == null) {
logger.info("updatePassword access code is null");
return forwardToErrorPage(); return forwardToErrorPage();
} }
logger.info("updatePassword has access code");
UserModel user = getUser(accessCode); UserModel user = getUser(accessCode);
@ -158,6 +163,8 @@ public class RequiredActionsService {
realm.updateCredential(user, credentials); realm.updateCredential(user, credentials);
logger.info("updatePassword updated credential");
user.removeRequiredAction(RequiredAction.UPDATE_PASSWORD); user.removeRequiredAction(RequiredAction.UPDATE_PASSWORD);
if (accessCode != null) { if (accessCode != null) {
accessCode.getRequiredActions().remove(RequiredAction.UPDATE_PASSWORD); accessCode.getRequiredActions().remove(RequiredAction.UPDATE_PASSWORD);
@ -257,6 +264,7 @@ public class RequiredActionsService {
private AccessCodeEntry getAccessCodeEntry(RequiredAction requiredAction) { private AccessCodeEntry getAccessCodeEntry(RequiredAction requiredAction) {
String code = uriInfo.getQueryParameters().getFirst(FormFlows.CODE); String code = uriInfo.getQueryParameters().getFirst(FormFlows.CODE);
if (code == null) { if (code == null) {
logger.info("getAccessCodeEntry code as not in query param");
return null; return null;
} }
@ -265,24 +273,31 @@ public class RequiredActionsService {
try { try {
verifiedCode = RSAProvider.verify(input, realm.getPublicKey()); verifiedCode = RSAProvider.verify(input, realm.getPublicKey());
} catch (Exception ignored) { } catch (Exception ignored) {
logger.info("getAccessCodeEntry code failed verification");
return null; return null;
} }
if (!verifiedCode) { if (!verifiedCode) {
logger.info("getAccessCodeEntry code failed verification2");
return null; return null;
} }
String key = input.readContent(String.class); String key = input.readContent(String.class);
AccessCodeEntry accessCodeEntry = tokenManager.getAccessCode(key); AccessCodeEntry accessCodeEntry = tokenManager.getAccessCode(key);
if (accessCodeEntry == null) { if (accessCodeEntry == null) {
logger.info("getAccessCodeEntry access code entry null");
return null; return null;
} }
if (accessCodeEntry.isExpired()) { if (accessCodeEntry.isExpired()) {
logger.info("getAccessCodeEntry: access code id: " + accessCodeEntry.getId());
logger.info("getAccessCodeEntry access code entry expired: " + accessCodeEntry.getExpiration());
logger.info("getAccessCodeEntry current time: " + (System.currentTimeMillis() / 1000));
return null; return null;
} }
if (accessCodeEntry.getRequiredActions() == null || !accessCodeEntry.getRequiredActions().contains(requiredAction)) { if (accessCodeEntry.getRequiredActions() == null || !accessCodeEntry.getRequiredActions().contains(requiredAction)) {
logger.info("getAccessCodeEntry required actions null || entry does not contain required action: " + (accessCodeEntry.getRequiredActions() == null) + "|" + !accessCodeEntry.getRequiredActions().contains(requiredAction) );
return null; return null;
} }
@ -303,6 +318,7 @@ public class RequiredActionsService {
return Flows.forms(realm, request, uriInfo).setAccessCode(accessCode).setUser(user) return Flows.forms(realm, request, uriInfo).setAccessCode(accessCode).setUser(user)
.forwardToAction(requiredActions.iterator().next()); .forwardToAction(requiredActions.iterator().next());
} else { } else {
logger.info("redirectOauth: redirecting to: " + accessCode.getRedirectUri());
accessCode.setExpiration((System.currentTimeMillis() / 1000) + realm.getAccessCodeLifespan()); accessCode.setExpiration((System.currentTimeMillis() / 1000) + realm.getAccessCodeLifespan());
return Flows.oauth(realm, request, uriInfo, authManager, tokenManager).redirectAccessCode(accessCode, return Flows.oauth(realm, request, uriInfo, authManager, tokenManager).redirectAccessCode(accessCode,
accessCode.getState(), accessCode.getRedirectUri()); accessCode.getState(), accessCode.getRedirectUri());

View file

@ -1,21 +1,33 @@
package org.keycloak.services.resources; package org.keycloak.services.resources;
import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.annotations.cache.NoCache;
import org.jboss.resteasy.jose.jws.JWSInput;
import org.jboss.resteasy.jose.jws.crypto.RSAProvider;
import org.jboss.resteasy.logging.Logger; import org.jboss.resteasy.logging.Logger;
import org.jboss.resteasy.spi.HttpRequest; import org.jboss.resteasy.spi.HttpRequest;
import org.jboss.resteasy.spi.HttpResponse; import org.jboss.resteasy.spi.HttpResponse;
import org.jboss.resteasy.spi.NotImplementedYetException; import org.jboss.resteasy.spi.NotImplementedYetException;
import org.keycloak.AbstractOAuthClient;
import org.keycloak.jaxrs.JaxrsOAuthClient;
import org.keycloak.models.*; import org.keycloak.models.*;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.services.managers.AccessCodeEntry;
import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.AuthenticationManager.AuthenticationStatus; import org.keycloak.services.managers.AuthenticationManager.AuthenticationStatus;
import org.keycloak.services.managers.RealmManager; import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.managers.TokenManager;
import org.keycloak.services.messages.Messages; import org.keycloak.services.messages.Messages;
import org.keycloak.services.resources.admin.RealmsAdminResource; import org.keycloak.services.resources.admin.RealmsAdminResource;
import org.keycloak.services.resources.flows.Flows; import org.keycloak.services.resources.flows.Flows;
import org.keycloak.services.resources.flows.OAuthFlows;
import javax.ws.rs.*; import javax.ws.rs.*;
import javax.ws.rs.container.ResourceContext; import javax.ws.rs.container.ResourceContext;
import javax.ws.rs.core.*; import javax.ws.rs.core.*;
import javax.ws.rs.ext.Providers;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -42,8 +54,17 @@ public class SaasService {
@Context @Context
protected ResourceContext resourceContext; protected ResourceContext resourceContext;
@Context
protected Providers providers;
protected String adminPath = "/admin/index.html"; protected String adminPath = "/admin/index.html";
protected AuthenticationManager authManager = new AuthenticationManager(); protected AuthenticationManager authManager = new AuthenticationManager();
protected TokenManager tokenManager;
public SaasService(TokenManager tokenManager) {
this.tokenManager = tokenManager;
}
public static class WhoAmI { public static class WhoAmI {
protected String userId; protected String userId;
@ -168,7 +189,99 @@ public class SaasService {
RealmModel realm = getAdminstrationRealm(realmManager); RealmModel realm = getAdminstrationRealm(realmManager);
authManager.expireSaasIdentityCookie(uriInfo); authManager.expireSaasIdentityCookie(uriInfo);
return Flows.forms(realm, request, uriInfo).forwardToLogin(); JaxrsOAuthClient oauth = new JaxrsOAuthClient();
String authUrl = TokenService.loginPageUrl(uriInfo).build(Constants.ADMIN_REALM).toString();
logger.info("authUrl: " + authUrl);
oauth.setAuthUrl(authUrl);
oauth.setClientId(Constants.ADMIN_CONSOLE_APPLICATION);
URI redirectUri = uriInfo.getBaseUriBuilder().path(SaasService.class).path(SaasService.class, "loginRedirect").build();
logger.info("redirectUri: " + redirectUri.toString());
oauth.setStateCookiePath(redirectUri.getPath());
return oauth.redirect(uriInfo, redirectUri.toString());
}
@Path("login-redirect")
@GET
@NoCache
public Response loginRedirect(@QueryParam("code") String code,
@QueryParam("state") String state,
@QueryParam("error") String error,
@Context HttpHeaders headers
) {
try {
logger.info("loginRedirect ********************** <---");
if (error != null) {
logger.debug("error from oauth");
throw new ForbiddenException("error");
}
RealmManager realmManager = new RealmManager(session);
RealmModel realm = getAdminstrationRealm(realmManager);
if (!realm.isEnabled()) {
logger.debug("realm not enabled");
throw new ForbiddenException();
}
ApplicationModel adminConsole = realm.getApplicationNameMap().get(Constants.ADMIN_CONSOLE_APPLICATION);
UserModel adminConsoleUser = adminConsole.getApplicationUser();
if (!adminConsole.isEnabled() || !adminConsoleUser.isEnabled()) {
logger.debug("admin app not enabled");
throw new ForbiddenException();
}
if (code == null) {
logger.debug("code not specified");
throw new BadRequestException();
}
if (state == null) {
logger.debug("state not specified");
throw new BadRequestException();
}
new JaxrsOAuthClient().checkStateCookie(uriInfo, headers);
JWSInput input = new JWSInput(code, providers);
boolean verifiedCode = false;
try {
verifiedCode = RSAProvider.verify(input, realm.getPublicKey());
} catch (Exception ignored) {
logger.debug("Failed to verify signature", ignored);
}
if (!verifiedCode) {
logger.debug("unverified access code");
throw new BadRequestException();
}
String key = input.readContent(String.class);
AccessCodeEntry accessCode = tokenManager.pullAccessCode(key);
if (accessCode == null) {
logger.debug("bad access code");
throw new BadRequestException();
}
if (accessCode.isExpired()) {
logger.debug("access code expired");
throw new BadRequestException();
}
if (!accessCode.getToken().isActive()) {
logger.debug("access token expired");
throw new BadRequestException();
}
if (!accessCode.getRealm().getId().equals(realm.getId())) {
logger.debug("bad realm");
throw new BadRequestException();
}
if (!adminConsoleUser.getLoginName().equals(accessCode.getClient().getLoginName())) {
logger.debug("bad client");
throw new BadRequestException();
}
if (!adminConsole.hasRole(accessCode.getUser(), Constants.ADMIN_CONSOLE_ADMIN_ROLE)) {
logger.debug("not allowed");
throw new ForbiddenException();
}
logger.info("loginRedirect SUCCESS");
NewCookie cookie = authManager.createSaasIdentityCookie(realm, accessCode.getUser(), uriInfo);
return Response.status(302).cookie(cookie).location(contextRoot(uriInfo).path(adminPath).build()).build();
} finally {
authManager.expireCookie(AbstractOAuthClient.OAUTH_TOKEN_REQUEST_STATE, uriInfo.getAbsolutePath().getPath());
}
} }
@Path("logout") @Path("logout")
@ -178,8 +291,9 @@ public class SaasService {
RealmManager realmManager = new RealmManager(session); RealmManager realmManager = new RealmManager(session);
RealmModel realm = getAdminstrationRealm(realmManager); RealmModel realm = getAdminstrationRealm(realmManager);
authManager.expireSaasIdentityCookie(uriInfo); authManager.expireSaasIdentityCookie(uriInfo);
authManager.expireIdentityCookie(realm, uriInfo);
return Flows.forms(realm, request, uriInfo).forwardToLogin(); return Response.status(302).location(uriInfo.getBaseUriBuilder().path(SaasService.class).path(SaasService.class, "loginPage").build()).build();
} }
@Path("logout-cookie") @Path("logout-cookie")
@ -199,6 +313,8 @@ public class SaasService {
RealmModel realm = getAdminstrationRealm(realmManager); RealmModel realm = getAdminstrationRealm(realmManager);
if (realm == null) if (realm == null)
throw new NotFoundException(); throw new NotFoundException();
ApplicationModel adminConsole = realm.getApplicationNameMap().get(Constants.ADMIN_CONSOLE_APPLICATION);
UserModel adminConsoleUser = adminConsole.getApplicationUser();
if (!realm.isEnabled()) { if (!realm.isEnabled()) {
throw new NotImplementedYetException(); throw new NotImplementedYetException();
@ -208,6 +324,8 @@ public class SaasService {
AuthenticationStatus status = authManager.authenticateForm(realm, user, formData); AuthenticationStatus status = authManager.authenticateForm(realm, user, formData);
OAuthFlows oauth = Flows.oauth(realm, request, uriInfo, authManager, tokenManager);
switch (status) { switch (status) {
case SUCCESS: case SUCCESS:
NewCookie cookie = authManager.createSaasIdentityCookie(realm, user, uriInfo); NewCookie cookie = authManager.createSaasIdentityCookie(realm, user, uriInfo);
@ -216,7 +334,7 @@ public class SaasService {
return Flows.forms(realm, request, uriInfo).setError(Messages.ACCOUNT_DISABLED).setFormData(formData) return Flows.forms(realm, request, uriInfo).setError(Messages.ACCOUNT_DISABLED).setFormData(formData)
.forwardToLogin(); .forwardToLogin();
case ACTIONS_REQUIRED: case ACTIONS_REQUIRED:
return Flows.forms(realm, request, uriInfo).forwardToAction(user.getRequiredActions().iterator().next()); return oauth.processAccessCode(null, "n", contextRoot(uriInfo).path(adminPath).build().toString(), adminConsoleUser, user);
default: default:
return Flows.forms(realm, request, uriInfo).setError(Messages.INVALID_USER).setFormData(formData) return Flows.forms(realm, request, uriInfo).setError(Messages.INVALID_USER).setFormData(formData)
.forwardToLogin(); .forwardToLogin();