diff --git a/examples/as7-eap-demo/server/src/main/resources/META-INF/testrealm.json b/examples/as7-eap-demo/server/src/main/resources/META-INF/testrealm.json index 2fa8a91bab..2a711d09ac 100755 --- a/examples/as7-eap-demo/server/src/main/resources/META-INF/testrealm.json +++ b/examples/as7-eap-demo/server/src/main/resources/META-INF/testrealm.json @@ -5,6 +5,7 @@ "accessCodeLifespan": 10, "sslNotRequired": true, "cookieLoginAllowed": true, + "registrationAllowed": true, "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=", "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "requiredCredentials": [ "password" ], diff --git a/forms/src/main/java/org/keycloak/sdk/FormsBean.java b/forms/src/main/java/org/keycloak/forms/FormsBean.java similarity index 100% rename from forms/src/main/java/org/keycloak/sdk/FormsBean.java rename to forms/src/main/java/org/keycloak/forms/FormsBean.java diff --git a/forms/src/main/resources/META-INF/resources/faces-config.xml b/forms/src/main/resources/META-INF/faces-config.xml similarity index 100% rename from forms/src/main/resources/META-INF/resources/faces-config.xml rename to forms/src/main/resources/META-INF/faces-config.xml diff --git a/services/src/main/java/org/keycloak/services/managers/RealmManager.java b/services/src/main/java/org/keycloak/services/managers/RealmManager.java index 524fb889bb..1586236a8d 100755 --- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java +++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java @@ -103,6 +103,7 @@ public class RealmManager { newRealm.setAccessCodeLifespan(rep.getAccessCodeLifespan()); newRealm.setSslNotRequired(rep.isSslNotRequired()); newRealm.setCookieLoginAllowed(rep.isCookieLoginAllowed()); + newRealm.setRegistrationAllowed(rep.isRegistrationAllowed()); if (rep.getPrivateKey() == null || rep.getPublicKey() == null) { generateRealmKeys(newRealm); } else { diff --git a/services/src/main/java/org/keycloak/services/models/RequiredCredentialModel.java b/services/src/main/java/org/keycloak/services/models/RequiredCredentialModel.java index a72bcbad3a..de99a7c18b 100755 --- a/services/src/main/java/org/keycloak/services/models/RequiredCredentialModel.java +++ b/services/src/main/java/org/keycloak/services/models/RequiredCredentialModel.java @@ -62,19 +62,19 @@ public class RequiredCredentialModel { PASSWORD.setType(CredentialRepresentation.PASSWORD); PASSWORD.setInput(true); PASSWORD.setSecret(true); - PASSWORD.setFormLabel("Password"); + PASSWORD.setFormLabel("password"); map.put(PASSWORD.getType(), PASSWORD); TOTP = new RequiredCredentialModel(); TOTP.setType(CredentialRepresentation.TOTP); TOTP.setInput(true); TOTP.setSecret(false); - TOTP.setFormLabel("Authenticator Code"); + TOTP.setFormLabel("authenticatorCode"); map.put(TOTP.getType(), TOTP); CLIENT_CERT = new RequiredCredentialModel(); CLIENT_CERT.setType(CredentialRepresentation.CLIENT_CERT); CLIENT_CERT.setInput(false); CLIENT_CERT.setSecret(false); - CLIENT_CERT.setFormLabel("Client Certificate"); + CLIENT_CERT.setFormLabel("clientCertificate"); map.put(CLIENT_CERT.getType(), CLIENT_CERT); BUILT_IN = Collections.unmodifiableMap(map); } diff --git a/services/src/main/java/org/keycloak/services/resources/OAuthUtil.java b/services/src/main/java/org/keycloak/services/resources/OAuthUtil.java deleted file mode 100644 index e834965dbd..0000000000 --- a/services/src/main/java/org/keycloak/services/resources/OAuthUtil.java +++ /dev/null @@ -1,104 +0,0 @@ -package org.keycloak.services.resources; - -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriBuilder; -import javax.ws.rs.core.UriInfo; - -import org.jboss.resteasy.logging.Logger; -import org.jboss.resteasy.spi.HttpRequest; -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; -import org.keycloak.services.models.RoleModel; -import org.keycloak.services.models.UserModel; - -public class OAuthUtil { - - private static final Logger log = Logger.getLogger(OAuthUtil.class); - - public final static String securityFailurePath = "/saas/securityFailure.jsp"; - public final static String oauthFormPath = "/saas/oauthGrantForm.jsp"; - - public static Response processAccessCode(RealmModel realm, TokenManager tokenManager, AuthenticationManager authManager, - HttpRequest request, UriInfo uriInfo, - String scopeParam, String state, - String redirect, - UserModel client, UserModel user) { - RoleModel resourceRole = realm.getRole(RealmManager.RESOURCE_ROLE); - RoleModel identityRequestRole = realm.getRole(RealmManager.IDENTITY_REQUESTER_ROLE); - boolean isResource = realm.hasRole(client, resourceRole); - if (!isResource && !realm.hasRole(client, identityRequestRole)) { - securityFailureForward(request, "Login requester not allowed to request login."); - return null; - } - AccessCodeEntry accessCode = tokenManager.createAccessCode(scopeParam, state, redirect, realm, client, user); - log.info("processAccessCode: isResource: " + isResource); - log.info( - "processAccessCode: go to oauth page?: " - + (!isResource && (accessCode.getRealmRolesRequested().size() > 0 || accessCode - .getResourceRolesRequested().size() > 0))); - if (!isResource - && (accessCode.getRealmRolesRequested().size() > 0 || accessCode.getResourceRolesRequested().size() > 0)) { - oauthGrantPage(realm, request, uriInfo, accessCode, client); - return null; - } - return redirectAccessCode(realm, authManager, uriInfo, accessCode, state, redirect); - } - - public static void securityFailureForward(HttpRequest request, String message) { - log.error(message); - request.setAttribute(JspRequestParameters.KEYCLOAK_SECURITY_FAILURE_MESSAGE, message); - request.forward(securityFailurePath); - } - - public static Response redirectAccessCode(RealmModel realm, AuthenticationManager authManager, UriInfo uriInfo, - AccessCodeEntry accessCode, - String state, String redirect) { - String code = accessCode.getCode(); - UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam("code", code); - log.info("redirectAccessCode: state: " + state); - if (state != null) - redirectUri.queryParam("state", state); - Response.ResponseBuilder location = Response.status(302).location(redirectUri.build()); - if (realm.isCookieLoginAllowed()) { - location.cookie(authManager.createLoginCookie(realm, accessCode.getUser(), uriInfo)); - } - return location.build(); - } - - public static void forwardToLoginForm(RealmModel realm, HttpRequest request, UriInfo uriInfo, String redirect, - String clientId, String scopeParam, String state) { - request.setAttribute(RealmModel.class.getName(), realm); - - request.setAttribute("KEYCLOAK_LOGIN_PAGE", Urls.realmLoginPage(uriInfo, realm.getId())); - request.setAttribute("KEYCLOAK_LOGIN_ACTION", Urls.realmLoginAction(uriInfo, realm.getId())); - - request.setAttribute("KEYCLOAK_REGISTRATION_PAGE", Urls.realmRegisterPage(uriInfo, realm.getId())); - request.setAttribute("KEYCLOAK_REGISTRATION_ACTION", Urls.realmRegisterAction(uriInfo, realm.getId())); - - request.setAttribute("KEYCLOAK_SOCIAL_LOGIN", Urls.socialRedirectToProviderAuth(uriInfo, realm.getId())); - - // RESTEASY eats the form data, so we send via an attribute - request.setAttribute("redirect_uri", redirect); - request.setAttribute("client_id", clientId); - request.setAttribute("scope", scopeParam); - request.setAttribute("state", state); - - request.forward(Pages.loginForm); - } - - public static void oauthGrantPage(RealmModel realm, HttpRequest request, UriInfo uriInfo, AccessCodeEntry accessCode, - UserModel client) { - request.setAttribute("realmRolesRequested", accessCode.getRealmRolesRequested()); - request.setAttribute("resourceRolesRequested", accessCode.getResourceRolesRequested()); - request.setAttribute("client", client); - request.setAttribute("action", TokenService.processOAuthUrl(uriInfo).build(realm.getId()).toString()); - request.setAttribute("code", accessCode.getCode()); - - request.forward(oauthFormPath); - } - -} diff --git a/services/src/main/java/org/keycloak/services/resources/Pages.java b/services/src/main/java/org/keycloak/services/resources/Pages.java deleted file mode 100644 index ae5629715b..0000000000 --- a/services/src/main/java/org/keycloak/services/resources/Pages.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.keycloak.services.resources; - -public class Pages { - - public final static String loginForm = "/sdk/login.xhtml"; - public final static String registerForm = "/sdk/register.xhtml"; - -} diff --git a/services/src/main/java/org/keycloak/services/resources/SaasService.java b/services/src/main/java/org/keycloak/services/resources/SaasService.java index af8b1aaef7..0a54871fd1 100755 --- a/services/src/main/java/org/keycloak/services/resources/SaasService.java +++ b/services/src/main/java/org/keycloak/services/resources/SaasService.java @@ -14,9 +14,11 @@ import org.keycloak.services.models.RoleModel; import org.keycloak.services.models.UserCredentialModel; import org.keycloak.services.models.UserModel; import org.keycloak.services.resources.admin.RealmsAdminResource; +import org.keycloak.services.resources.flows.Flows; import javax.ws.rs.*; import javax.ws.rs.core.*; + import java.net.URI; import java.util.StringTokenizer; @@ -81,7 +83,8 @@ public class SaasService { public Response callImpl() { RealmManager realmManager = new RealmManager(session); RealmModel realm = realmManager.defaultRealm(); - if (realm == null) throw new NotFoundException(); + if (realm == null) + throw new NotFoundException(); UserModel user = authManager.authenticateSaasIdentityCookie(realm, uriInfo, headers); if (user == null) { return Response.status(401).build(); @@ -102,7 +105,8 @@ public class SaasService { public Response callImpl() { RealmManager realmManager = new RealmManager(session); RealmModel realm = realmManager.defaultRealm(); - if (realm == null) throw new NotFoundException(); + if (realm == null) + throw new NotFoundException(); UserModel user = authManager.authenticateSaasIdentityCookie(realm, uriInfo, headers); if (user == null) { return Response.status(401).build(); @@ -137,7 +141,6 @@ public class SaasService { }.call(); } - public static UriBuilder contextRoot(UriInfo uriInfo) { return UriBuilder.fromUri(uriInfo.getBaseUri()).replacePath("/auth-server"); } @@ -153,7 +156,8 @@ public class SaasService { protected RealmsAdminResource callImpl() { RealmManager realmManager = new RealmManager(session); RealmModel saasRealm = realmManager.defaultRealm(); - if (saasRealm == null) throw new NotFoundException(); + if (saasRealm == null) + throw new NotFoundException(); UserModel admin = authManager.authenticateSaasIdentity(saasRealm, uriInfo, headers); if (admin == null) { throw new NotAuthorizedException("Bearer"); @@ -178,7 +182,8 @@ public class SaasService { RealmManager realmManager = new RealmManager(session); RealmModel realm = realmManager.defaultRealm(); authManager.expireSaasIdentityCookie(uriInfo); - forwardToLoginForm(realm, null, null); + + Flows.forms(realm, request).forwardToLogin(); } }.run(); } @@ -193,7 +198,8 @@ public class SaasService { RealmManager realmManager = new RealmManager(session); RealmModel realm = realmManager.defaultRealm(); authManager.expireSaasIdentityCookie(uriInfo); - forwardToRegisterForm(realm, null, null); + + Flows.forms(realm, request).forwardToRegistration(); } }.run(); } @@ -208,12 +214,12 @@ public class SaasService { RealmManager realmManager = new RealmManager(session); RealmModel realm = realmManager.defaultRealm(); authManager.expireSaasIdentityCookie(uriInfo); - forwardToLoginForm(realm, null, null); + + Flows.forms(realm, request).forwardToLogin(); } }.run(); } - @Path("logout-cookie") @GET @NoCache @@ -227,45 +233,6 @@ public class SaasService { }.run(); } - protected void forwardToLoginForm(RealmModel realm, String error, MultivaluedMap formData) { - if (error != null) { - request.setAttribute("KEYCLOAK_LOGIN_ERROR_MESSAGE", error); - } - - if (formData != null) { - request.setAttribute("KEYCLOAK_FORM_DATA", formData); - } - - forwardToForm(realm, Pages.loginForm); - } - - protected void forwardToRegisterForm(RealmModel realm, String error, MultivaluedMap formData) { - if (error != null) { - request.setAttribute("KEYCLOAK_LOGIN_ERROR_MESSAGE", error); - } - - if (formData != null) { - request.setAttribute("KEYCLOAK_FORM_DATA", formData); - } - - forwardToForm(realm, Pages.registerForm); - } - - protected void forwardToForm(RealmModel realm, String form) { - request.setAttribute(RealmModel.class.getName(), realm); - - request.setAttribute("KEYCLOAK_LOGIN_PAGE", Urls.saasLoginPage(uriInfo)); - request.setAttribute("KEYCLOAK_LOGIN_ACTION", Urls.saasLoginAction(uriInfo)); - - request.setAttribute("KEYCLOAK_REGISTRATION_PAGE", Urls.saasRegisterPage(uriInfo)); - request.setAttribute("KEYCLOAK_REGISTRATION_ACTION", Urls.saasRegisterAction(uriInfo)); - - request.setAttribute("KEYCLOAK_SOCIAL_LOGIN", Urls.socialRedirectToProviderAuth(uriInfo, realm.getId())); - - request.forward(form); - } - - @Path("login") @POST @Consumes(MediaType.APPLICATION_FORM_URLENCODED) @@ -276,7 +243,8 @@ public class SaasService { protected Response callImpl() { RealmManager realmManager = new RealmManager(session); RealmModel realm = realmManager.defaultRealm(); - if (realm == null) throw new NotFoundException(); + if (realm == null) + throw new NotFoundException(); if (!realm.isEnabled()) { throw new NotImplementedYetException(); @@ -285,26 +253,27 @@ public class SaasService { UserModel user = realm.getUser(username); if (user == null) { logger.info("Not Authenticated! Incorrect user name"); - forwardToLoginForm(realm, "Invalid username or password", formData); - return null; + + return Flows.forms(realm, request).setError("Invalid username or password").setFormData(formData) + .forwardToLogin(); } if (!user.isEnabled()) { logger.info("NAccount is disabled, contact admin."); - forwardToLoginForm(realm, "Account is disabled, contact admin.", formData); - return null; + + return Flows.forms(realm, request).setError("Invalid username or password") + .setFormData(formData).forwardToLogin(); } boolean authenticated = authManager.authenticateForm(realm, user, formData); if (!authenticated) { logger.info("Not Authenticated! Invalid credentials"); - forwardToLoginForm(realm, "Invalid username or password", formData); - return null; + + return Flows.forms(realm, request).setError("Invalid username or password").setFormData(formData) + .forwardToLogin(); } NewCookie cookie = authManager.createSaasIdentityCookie(realm, user, uriInfo); - return Response.status(302) - .cookie(cookie) - .location(contextRoot(uriInfo).path(adminPath).build()).build(); + return Response.status(302).cookie(cookie).location(contextRoot(uriInfo).path(adminPath).build()).build(); } }.call(); } @@ -340,8 +309,8 @@ public class SaasService { String error = validateRegistrationForm(formData); if (error != null) { - forwardToRegisterForm(defaultRealm, error, formData); - return null; + return Flows.forms(defaultRealm, request).setError(error).setFormData(formData) + .forwardToRegistration(); } UserRepresentation newUser = new UserRepresentation(); @@ -366,16 +335,16 @@ public class SaasService { last = token; } } - if (first == null) first = new StringBuffer(); + if (first == null) + first = new StringBuffer(); newUser.setFirstName(first.toString()); newUser.setLastName(last); } newUser.credential(CredentialRepresentation.PASSWORD, formData.getFirst("password")); UserModel user = registerMe(defaultRealm, newUser); if (user == null) { - request.setAttribute("KEYCLOAK_LOGIN_ERROR_MESSAGE", "Username already exists."); - forwardToRegisterForm(defaultRealm, "Username already exists.", formData); - return null; + return Flows.forms(defaultRealm, request).setError("Username already exists.") + .setFormData(formData).forwardToRegistration(); } NewCookie cookie = authManager.createSaasIdentityCookie(defaultRealm, user, uriInfo); @@ -384,7 +353,6 @@ public class SaasService { }.call(); } - protected UserModel registerMe(RealmModel defaultRealm, UserRepresentation newUser) { if (!defaultRealm.isEnabled()) { throw new ForbiddenException(); diff --git a/services/src/main/java/org/keycloak/services/resources/SocialResource.java b/services/src/main/java/org/keycloak/services/resources/SocialResource.java index 35ed9ab2f9..f29f908dd7 100644 --- a/services/src/main/java/org/keycloak/services/resources/SocialResource.java +++ b/services/src/main/java/org/keycloak/services/resources/SocialResource.java @@ -16,7 +16,6 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; -import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriInfo; import org.jboss.resteasy.logging.Logger; @@ -25,7 +24,11 @@ import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.managers.RealmManager; import org.keycloak.services.managers.TokenManager; import org.keycloak.services.models.RealmModel; +import org.keycloak.services.models.RoleModel; import org.keycloak.services.models.UserModel; +import org.keycloak.services.resources.flows.Flows; +import org.keycloak.services.resources.flows.OAuthFlows; +import org.keycloak.services.resources.flows.Urls; import org.keycloak.social.AuthCallback; import org.keycloak.social.AuthRequest; import org.keycloak.social.RequestDetails; @@ -61,19 +64,6 @@ public class SocialResource { this.socialRequestManager = socialRequestManager; } - public static UriBuilder socialServiceBaseUrl(UriInfo uriInfo) { - UriBuilder base = uriInfo.getBaseUriBuilder().path(SocialResource.class); - return base; - } - - public static UriBuilder redirectToProviderAuthUrl(UriInfo uriInfo) { - return socialServiceBaseUrl(uriInfo).path(SocialResource.class, "redirectToProviderAuth"); - } - - public static UriBuilder callbackUrl(UriInfo uriInfo) { - return socialServiceBaseUrl(uriInfo).path(SocialResource.class, "callback"); - } - @GET @Path("callback") public Response callback() throws URISyntaxException { @@ -86,10 +76,32 @@ public class SocialResource { String realmId = requestData.getClientAttribute("realmId"); + RealmManager realmManager = new RealmManager(session); + RealmModel realm = realmManager.getRealm(realmId); + + OAuthFlows oauth = Flows.oauth(realm, request, uriInfo, authManager, tokenManager); + + if (!realm.isEnabled()) { + return oauth.forwardToSecurityFailure("Realm not enabled."); + } + + if (!realm.isEnabled()) { + return oauth.forwardToSecurityFailure("Realm not enabled."); + } + + String clientId = requestData.getClientAttributes().get("clientId"); + + UserModel client = realm.getUser(clientId); + if (client == null) { + return oauth.forwardToSecurityFailure("Unknown login requester."); + } + if (!client.isEnabled()) { + return oauth.forwardToSecurityFailure("Login requester not enabled."); + } + String key = System.getProperty("keycloak.social." + requestData.getProviderId() + ".key"); String secret = System.getProperty("keycloak.social." + requestData.getProviderId() + ".secret"); - String callbackUri = callbackUrl(uriInfo).build().toString(); - + String callbackUri = Urls.socialCallback(uriInfo.getBaseUri()).toString(); SocialProviderConfig config = new SocialProviderConfig(key, secret, callbackUri); AuthCallback callback = new AuthCallback(requestData.getSocialAttributes(), queryParams); @@ -99,28 +111,7 @@ public class SocialResource { socialUser = provider.processCallback(config, callback); } catch (SocialProviderException e) { logger.warn("Failed to process social callback", e); - OAuthUtil.securityFailureForward(request, "Failed to process social callback"); - return null; - } - - RealmManager realmManager = new RealmManager(session); - RealmModel realm = realmManager.getRealm(realmId); - - if (!realm.isEnabled()) { - OAuthUtil.securityFailureForward(request, "Realm not enabled."); - return null; - } - - String clientId = requestData.getClientAttributes().get("clientId"); - - UserModel client = realm.getUser(clientId); - if (client == null) { - OAuthUtil.securityFailureForward(request, "Unknown login requester."); - return null; - } - if (!client.isEnabled()) { - OAuthUtil.securityFailureForward(request, "Login requester not enabled."); - return null; + return oauth.forwardToSecurityFailure("Failed to process social callback"); } // TODO Lookup user based on attribute for provider id - this is so a user can have a friendly username + link a @@ -133,20 +124,20 @@ public class SocialResource { user.setAttribute(provider.getId() + ".id", socialUser.getId()); // TODO Grant default roles for realm when available - realm.grantRole(user, realm.getRole("user")); + RoleModel defaultRole = realm.getRole("user"); + + realm.grantRole(user, defaultRole); } if (!user.isEnabled()) { - OAuthUtil.securityFailureForward(request, "Your account is not enabled."); - return null; + return oauth.forwardToSecurityFailure("Your account is not enabled."); } String scope = requestData.getClientAttributes().get("scope"); String state = requestData.getClientAttributes().get("state"); String redirectUri = requestData.getClientAttributes().get("redirectUri"); - return OAuthUtil.processAccessCode(realm, tokenManager, authManager, request, uriInfo, scope, state, - redirectUri, client, user); + return oauth.processAccessCode(scope, state, redirectUri, client, user); } }.call(); } @@ -159,13 +150,12 @@ public class SocialResource { @QueryParam("redirect_uri") final String redirectUri) { SocialProvider provider = getProvider(providerId); if (provider == null) { - OAuthUtil.securityFailureForward(request, "Social provider not found"); - return null; + return Flows.pages(request).forwardToSecurityFailure("Social provider not found"); } String key = System.getProperty("keycloak.social." + providerId + ".key"); String secret = System.getProperty("keycloak.social." + providerId + ".secret"); - String callbackUri = callbackUrl(uriInfo).build().toString(); + String callbackUri = Urls.socialCallback(uriInfo.getBaseUri()).toString(); SocialProviderConfig config = new SocialProviderConfig(key, secret, callbackUri); @@ -181,9 +171,7 @@ public class SocialResource { return Response.status(Status.FOUND).location(authRequest.getAuthUri()).build(); } catch (Throwable t) { - logger.error("Failed to redirect to social auth", t); - OAuthUtil.securityFailureForward(request, "Failed to redirect to social auth"); - return null; + return Flows.pages(request).forwardToSecurityFailure("Failed to redirect to social auth"); } } diff --git a/services/src/main/java/org/keycloak/services/resources/TokenService.java b/services/src/main/java/org/keycloak/services/resources/TokenService.java index a9b1e8f4fb..65b30efff7 100755 --- a/services/src/main/java/org/keycloak/services/resources/TokenService.java +++ b/services/src/main/java/org/keycloak/services/resources/TokenService.java @@ -10,6 +10,8 @@ import org.jboss.resteasy.spi.HttpRequest; import org.jboss.resteasy.spi.HttpResponse; import org.keycloak.representations.AccessTokenResponse; import org.keycloak.representations.SkeletonKeyToken; +import org.keycloak.representations.idm.CredentialRepresentation; +import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.services.managers.AccessCodeEntry; import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.managers.RealmManager; @@ -17,9 +19,13 @@ import org.keycloak.services.managers.ResourceAdminManager; import org.keycloak.services.managers.TokenManager; import org.keycloak.services.models.RealmModel; import org.keycloak.services.models.RoleModel; +import org.keycloak.services.models.UserCredentialModel; import org.keycloak.services.models.UserModel; +import org.keycloak.services.resources.flows.Flows; +import org.keycloak.services.resources.flows.OAuthFlows; import javax.ws.rs.Consumes; +import javax.ws.rs.ForbiddenException; import javax.ws.rs.GET; import javax.ws.rs.NotAuthorizedException; import javax.ws.rs.POST; @@ -30,14 +36,17 @@ 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.SecurityContext; import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriInfo; import javax.ws.rs.ext.Providers; + import java.security.PrivateKey; import java.util.HashMap; import java.util.Map; +import java.util.StringTokenizer; /** * @author Bill Burke @@ -72,8 +81,7 @@ public class TokenService { } public static UriBuilder tokenServiceBaseUrl(UriInfo uriInfo) { - UriBuilder base = uriInfo.getBaseUriBuilder() - .path(RealmsResource.class).path(RealmsResource.class, "getTokenService"); + UriBuilder base = uriInfo.getBaseUriBuilder().path(RealmsResource.class).path(RealmsResource.class, "getTokenService"); return base; } @@ -104,7 +112,6 @@ public class TokenService { return tokenServiceBaseUrl(uriInfo).path(TokenService.class, "processOAuth"); } - @Path("grants/identity-token") @POST @Consumes(MediaType.APPLICATION_FORM_URLENCODED) @@ -174,52 +181,126 @@ public class TokenService { @Path("auth/request/login") @POST @Consumes(MediaType.APPLICATION_FORM_URLENCODED) - public Response processLogin(final MultivaluedMap formData) { + public Response processLogin(@QueryParam("client_id") final String clientId, @QueryParam("scope") final String scopeParam, + @QueryParam("state") final String state, @QueryParam("redirect_uri") final String redirect, + final MultivaluedMap formData) { return new Transaction() { protected Response callImpl() { - String clientId = formData.getFirst("client_id"); - String scopeParam = formData.getFirst("scope"); - String state = formData.getFirst("state"); - String redirect = formData.getFirst("redirect_uri"); + OAuthFlows oauth = Flows.oauth(realm, request, uriInfo, authManager, tokenManager); if (!realm.isEnabled()) { - OAuthUtil.securityFailureForward(request, "Realm not enabled."); - return null; + return oauth.forwardToSecurityFailure("Realm not enabled."); } UserModel client = realm.getUser(clientId); if (client == null) { - OAuthUtil.securityFailureForward(request, "Unknown login requester."); - return null; + return oauth.forwardToSecurityFailure("Unknown login requester."); } if (!client.isEnabled()) { - OAuthUtil.securityFailureForward(request, "Login requester not enabled."); - return null; + return oauth.forwardToSecurityFailure("Login requester not enabled."); } String username = formData.getFirst("username"); UserModel user = realm.getUser(username); if (user == null) { logger.error("Incorrect user name."); - request.setAttribute("KEYCLOAK_LOGIN_ERROR_MESSAGE", "Invalid username or password"); - request.setAttribute("KEYCLOAK_FORM_DATA", formData); - OAuthUtil.forwardToLoginForm(realm, request, uriInfo, redirect, clientId, scopeParam, state); - return null; + + return Flows.forms(realm, request).setError("Invalid username or password").setFormData(formData) + .forwardToLogin(); } if (!user.isEnabled()) { - OAuthUtil.securityFailureForward(request, "Your account is not enabled."); - return null; + return oauth.forwardToSecurityFailure("Your account is not enabled."); } boolean authenticated = authManager.authenticateForm(realm, user, formData); if (!authenticated) { logger.error("Authentication failed"); - request.setAttribute("username", username); - request.setAttribute("KEYCLOAK_LOGIN_ERROR_MESSAGE", "Invalid username or password"); - request.setAttribute("KEYCLOAK_FORM_DATA", formData); - OAuthUtil.forwardToLoginForm(realm, request, uriInfo, redirect, clientId, scopeParam, state); - return null; + + return Flows.forms(realm, request).setError("Invalid username or password").setFormData(formData) + .forwardToLogin(); } - return OAuthUtil.processAccessCode(realm, tokenManager, authManager, request, uriInfo, scopeParam, state, - redirect, client, user); + return oauth.processAccessCode(scopeParam, state, redirect, client, user); + } + }.call(); + } + + @Path("registrations") + @POST + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + public Response processRegister(@QueryParam("client_id") final String clientId, + @QueryParam("scope") final String scopeParam, @QueryParam("state") final String state, + @QueryParam("redirect_uri") final String redirect, final MultivaluedMap formData) { + return new Transaction() { + @Override + protected Response callImpl() { + OAuthFlows oauth = Flows.oauth(realm, request, uriInfo, authManager, tokenManager); + + if (!realm.isEnabled()) { + return oauth.forwardToSecurityFailure("Realm not enabled"); + } + UserModel client = realm.getUser(clientId); + if (client == null) { + return oauth.forwardToSecurityFailure("Unknown login requester."); + } + + if (!client.isEnabled()) { + return oauth.forwardToSecurityFailure("Login requester not enabled."); + } + + if (!realm.isRegistrationAllowed()) { + return oauth.forwardToSecurityFailure("Registration not allowed"); + } + + String error = validateRegistrationForm(formData); + if (error != null) { + return Flows.forms(realm, request).setError(error).setFormData(formData).forwardToRegistration(); + } + + String username = formData.getFirst("username"); + + UserModel user = realm.getUser(username); + if (user != null) { + return Flows.forms(realm, request).setError("Username already exists.").setFormData(formData) + .forwardToRegistration(); + } + + user = realm.addUser(username); + + String fullname = formData.getFirst("name"); + if (fullname != null) { + StringTokenizer tokenizer = new StringTokenizer(fullname, " "); + StringBuffer first = null; + String last = ""; + while (tokenizer.hasMoreTokens()) { + String token = tokenizer.nextToken(); + if (tokenizer.hasMoreTokens()) { + if (first == null) { + first = new StringBuffer(); + } else { + first.append(" "); + } + first.append(token); + } else { + last = token; + } + } + if (first == null) + first = new StringBuffer(); + user.setFirstName(first.toString()); + user.setLastName(last); + } + + user.setEmail(formData.getFirst("email")); + + UserCredentialModel credentials = new UserCredentialModel(); + credentials.setType(CredentialRepresentation.PASSWORD); + credentials.setValue(formData.getFirst("password")); + realm.updateCredential(user, credentials); + + // TODO Grant default roles for realm when available + RoleModel defaultRole = realm.getRole("user"); + + realm.grantRole(user, defaultRole); + + return processLogin(clientId, scopeParam, state, redirect, formData); } }.call(); } @@ -276,7 +357,6 @@ public class TokenService { return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build(); } - JWSInput input = new JWSInput(code, providers); boolean verifiedCode = false; try { @@ -288,7 +368,8 @@ public class TokenService { Map res = new HashMap(); res.put("error", "invalid_grant"); res.put("error_description", "Unable to verify code signature"); - return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res).build(); + return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res) + .build(); } String key = input.readContent(String.class); AccessCodeEntry accessCode = tokenManager.pullAccessCode(key); @@ -296,25 +377,29 @@ public class TokenService { Map res = new HashMap(); res.put("error", "invalid_grant"); res.put("error_description", "Code not found"); - return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res).build(); + return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res) + .build(); } if (accessCode.isExpired()) { Map res = new HashMap(); res.put("error", "invalid_grant"); res.put("error_description", "Code is expired"); - return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res).build(); + return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res) + .build(); } if (!accessCode.getToken().isActive()) { Map res = new HashMap(); res.put("error", "invalid_grant"); res.put("error_description", "Token expired"); - return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res).build(); + return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res) + .build(); } if (!client.getLoginName().equals(accessCode.getClient().getLoginName())) { Map res = new HashMap(); res.put("error", "invalid_grant"); res.put("error_description", "Auth error"); - return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res).build(); + return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res) + .build(); } logger.info("accessRequest SUCCESS"); AccessTokenResponse res = accessTokenResponse(realm.getPrivateKey(), accessCode.getToken()); @@ -331,9 +416,7 @@ public class TokenService { } catch (Exception e) { throw new RuntimeException(e); } - String encodedToken = new JWSBuilder() - .content(tokenBytes) - .rsa256(privateKey); + String encodedToken = new JWSBuilder().content(tokenBytes).rsa256(privateKey); return accessTokenResponse(token, encodedToken); } @@ -352,25 +435,25 @@ public class TokenService { @Path("login") @GET public Response loginPage(final @QueryParam("response_type") String responseType, - final @QueryParam("redirect_uri") String redirect, - final @QueryParam("client_id") String clientId, - final @QueryParam("scope") String scopeParam, - final @QueryParam("state") String state) { + final @QueryParam("redirect_uri") String redirect, final @QueryParam("client_id") String clientId, + final @QueryParam("scope") String scopeParam, final @QueryParam("state") String state) { return new Transaction() { protected Response callImpl() { + OAuthFlows oauth = Flows.oauth(realm, request, uriInfo, authManager, tokenManager); + if (!realm.isEnabled()) { - OAuthUtil.securityFailureForward(request, "Realm not enabled"); + oauth.forwardToSecurityFailure("Realm not enabled"); return null; } UserModel client = realm.getUser(clientId); if (client == null) { - OAuthUtil.securityFailureForward(request, "Unknown login requester."); + oauth.forwardToSecurityFailure("Unknown login requester."); transaction.rollback(); return null; } if (!client.isEnabled()) { - OAuthUtil.securityFailureForward(request, "Login requester not enabled."); + oauth.forwardToSecurityFailure("Login requester not enabled."); transaction.rollback(); session.close(); return null; @@ -380,7 +463,7 @@ public class TokenService { RoleModel identityRequestRole = realm.getRole(RealmManager.IDENTITY_REQUESTER_ROLE); boolean isResource = realm.hasRole(client, resourceRole); if (!isResource && !realm.hasRole(client, identityRequestRole)) { - OAuthUtil.securityFailureForward(request, "Login requester not allowed to request login."); + oauth.forwardToSecurityFailure("Login requester not allowed to request login."); transaction.rollback(); session.close(); return null; @@ -389,12 +472,42 @@ public class TokenService { UserModel user = authManager.authenticateIdentityCookie(realm, uriInfo, headers); if (user != null) { logger.info(user.getLoginName() + " already logged in."); - return OAuthUtil.processAccessCode(realm, tokenManager, authManager, request, uriInfo, scopeParam, state, - redirect, client, user); + return oauth.processAccessCode(scopeParam, state, redirect, client, user); } - OAuthUtil.forwardToLoginForm(realm, request, uriInfo, redirect, clientId, scopeParam, state); - return null; + return Flows.forms(realm, request).forwardToLogin(); + } + }.call(); + } + + @Path("registrations") + @GET + public Response registerPage(final @QueryParam("response_type") String responseType, + final @QueryParam("redirect_uri") String redirect, final @QueryParam("client_id") String clientId, + final @QueryParam("scope") String scopeParam, final @QueryParam("state") String state) { + return new Transaction() { + protected Response callImpl() { + OAuthFlows oauth = Flows.oauth(realm, request, uriInfo, authManager, tokenManager); + + if (!realm.isEnabled()) { + return oauth.forwardToSecurityFailure("Realm not enabled"); + } + UserModel client = realm.getUser(clientId); + if (client == null) { + return oauth.forwardToSecurityFailure("Unknown login requester."); + } + + if (!client.isEnabled()) { + return oauth.forwardToSecurityFailure("Login requester not enabled."); + } + + if (!realm.isRegistrationAllowed()) { + return oauth.forwardToSecurityFailure("Registration not allowed"); + } + + authManager.expireIdentityCookie(realm, uriInfo); + + return Flows.forms(realm, request).forwardToRegistration(); } }.call(); } @@ -425,6 +538,8 @@ public class TokenService { public Response processOAuth(final MultivaluedMap formData) { return new Transaction() { protected Response callImpl() { + OAuthFlows oauth = Flows.oauth(realm, request, uriInfo, authManager, tokenManager); + String code = formData.getFirst("code"); JWSInput input = new JWSInput(code, providers); boolean verifiedCode = false; @@ -434,16 +549,12 @@ public class TokenService { logger.debug("Failed to verify signature", ignored); } if (!verifiedCode) { - OAuthUtil.securityFailureForward(request, "Illegal access code."); - session.close(); - return null; + return oauth.forwardToSecurityFailure("Illegal access code."); } String key = input.readContent(String.class); AccessCodeEntry accessCodeEntry = tokenManager.getAccessCode(key); if (accessCodeEntry == null) { - OAuthUtil.securityFailureForward(request, "Unknown access code."); - session.close(); - return null; + return oauth.forwardToSecurityFailure("Unknown access code."); } String redirect = accessCodeEntry.getRedirectUri(); @@ -453,16 +564,45 @@ public class TokenService { return redirectAccessDenied(redirect, state); } - return OAuthUtil.redirectAccessCode(realm, authManager, uriInfo, accessCodeEntry, state, redirect); + return oauth.redirectAccessCode(accessCodeEntry, state, redirect); } }.call(); } protected Response redirectAccessDenied(String redirect, String state) { UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam("error", "access_denied"); - if (state != null) redirectUri.queryParam("state", state); + if (state != null) + redirectUri.queryParam("state", state); Response.ResponseBuilder location = Response.status(302).location(redirectUri.build()); return location.build(); } + private String validateRegistrationForm(MultivaluedMap formData) { + if (isEmpty(formData.getFirst("name"))) { + return "Please specify full name"; + } + + if (isEmpty(formData.getFirst("email"))) { + return "Please specify email"; + } + + if (isEmpty(formData.getFirst("username"))) { + return "Please specify username"; + } + + if (isEmpty(formData.getFirst("password"))) { + return "Please specify password"; + } + + if (!formData.getFirst("password").equals(formData.getFirst("password-confirm"))) { + return "Password confirmation doesn't match."; + } + + return null; + } + + private boolean isEmpty(String s) { + return s == null || s.length() == 0; + } + } diff --git a/services/src/main/java/org/keycloak/services/resources/Urls.java b/services/src/main/java/org/keycloak/services/resources/Urls.java deleted file mode 100644 index 7e2b2e85b1..0000000000 --- a/services/src/main/java/org/keycloak/services/resources/Urls.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.keycloak.services.resources; - -import java.net.URI; - -import javax.ws.rs.core.UriInfo; - -public class Urls { - - public static URI realmLoginAction(UriInfo uriInfo, String realmId) { - return TokenService.processLoginUrl(uriInfo).build(realmId); - } - - public static URI realmLoginPage(UriInfo uriInfo, String realmId) { - return uriInfo.getBaseUriBuilder().path(SaasService.class).path(SaasService.class, "processLogin").build(); - } - - public static URI realmRegisterAction(UriInfo uriInfo, String realmId) { - return URI.create("not-implemented-yet"); - } - - public static URI realmRegisterPage(UriInfo uriInfo, String realmId) { - return URI.create("not-implemented-yet"); - } - - public static URI saasLoginAction(UriInfo uriInfo) { - return uriInfo.getBaseUriBuilder().path(SaasService.class).path(SaasService.class, "processLogin").build(); - } - - public static URI saasLoginPage(UriInfo uriInfo) { - return uriInfo.getBaseUriBuilder().path(SaasService.class).path(SaasService.class, "loginPage").build(); - } - - public static URI saasRegisterAction(UriInfo uriInfo) { - return uriInfo.getBaseUriBuilder().path(SaasService.class).path(SaasService.class, "processRegister").build(); - } - - public static URI saasRegisterPage(UriInfo uriInfo) { - return uriInfo.getBaseUriBuilder().path(SaasService.class).path(SaasService.class, "registerPage").build(); - } - - public static URI socialRedirectToProviderAuth(UriInfo uriInfo, String realmId) { - return SocialResource.redirectToProviderAuthUrl(uriInfo).build(realmId); - } - -} diff --git a/services/src/main/java/org/keycloak/services/resources/flows/Flows.java b/services/src/main/java/org/keycloak/services/resources/flows/Flows.java new file mode 100644 index 0000000000..67f6a99bb3 --- /dev/null +++ b/services/src/main/java/org/keycloak/services/resources/flows/Flows.java @@ -0,0 +1,28 @@ +package org.keycloak.services.resources.flows; + +import javax.ws.rs.core.UriInfo; + +import org.jboss.resteasy.spi.HttpRequest; +import org.keycloak.services.managers.AuthenticationManager; +import org.keycloak.services.managers.TokenManager; +import org.keycloak.services.models.RealmModel; + +public class Flows { + + private Flows() { + } + + public static PageFlows pages(HttpRequest request) { + return new PageFlows(request); + } + + public static FormFlows forms(RealmModel realm, HttpRequest request) { + return new FormFlows(realm, request); + } + + public static OAuthFlows oauth(RealmModel realm, HttpRequest request, UriInfo uriInfo, AuthenticationManager authManager, + TokenManager tokenManager) { + return new OAuthFlows(realm, request, uriInfo, authManager, tokenManager); + } + +} diff --git a/services/src/main/java/org/keycloak/services/resources/flows/FormFlows.java b/services/src/main/java/org/keycloak/services/resources/flows/FormFlows.java new file mode 100644 index 0000000000..4c65dbb105 --- /dev/null +++ b/services/src/main/java/org/keycloak/services/resources/flows/FormFlows.java @@ -0,0 +1,61 @@ +package org.keycloak.services.resources.flows; + +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Response; + +import org.jboss.resteasy.spi.HttpRequest; +import org.keycloak.services.models.RealmModel; +import org.picketlink.idm.model.sample.Realm; + +public class FormFlows { + + public static final String REALM = Realm.class.getName(); + public static final String ERROR_MESSAGE = "KEYCLOAK_FORMS_ERROR_MESSAGE"; + public static final String DATA = "KEYCLOAK_FORMS_DATA"; + + private MultivaluedMap formData; + private String error; + + private RealmModel realm; + + private HttpRequest request; + + FormFlows(RealmModel realm, HttpRequest request) { + this.realm = realm; + this.request = request; + } + + public FormFlows setFormData(MultivaluedMap formData) { + this.formData = formData; + return this; + } + + public FormFlows setError(String error) { + this.error = error; + return this; + } + + public Response forwardToLogin() { + return forwardToForm(Pages.LOGIN); + } + + public Response forwardToRegistration() { + return forwardToForm(Pages.REGISTER); + } + + private Response forwardToForm(String form) { + request.setAttribute(REALM, realm); + + if (error != null) { + request.setAttribute(ERROR_MESSAGE, error); + } + + if (formData != null) { + request.setAttribute(DATA, formData); + } + + request.forward(form); + return null; + } + +} diff --git a/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java b/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java new file mode 100644 index 0000000000..a49cac182d --- /dev/null +++ b/services/src/main/java/org/keycloak/services/resources/flows/OAuthFlows.java @@ -0,0 +1,89 @@ +package org.keycloak.services.resources.flows; + +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriBuilder; +import javax.ws.rs.core.UriInfo; + +import org.jboss.resteasy.logging.Logger; +import org.jboss.resteasy.spi.HttpRequest; +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; +import org.keycloak.services.models.RoleModel; +import org.keycloak.services.models.UserModel; +import org.keycloak.services.resources.TokenService; + +public class OAuthFlows { + + private static final Logger log = Logger.getLogger(OAuthFlows.class); + + private RealmModel realm; + + private HttpRequest request; + + private UriInfo uriInfo; + + private AuthenticationManager authManager; + + private TokenManager tokenManager; + + OAuthFlows(RealmModel realm, HttpRequest request, UriInfo uriInfo, AuthenticationManager authManager, + TokenManager tokenManager) { + this.realm = realm; + this.request = request; + this.uriInfo = uriInfo; + this.authManager = authManager; + this.tokenManager = tokenManager; + } + + public Response redirectAccessCode(AccessCodeEntry accessCode, String state, String redirect) { + String code = accessCode.getCode(); + UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam("code", code); + log.info("redirectAccessCode: state: " + state); + if (state != null) + redirectUri.queryParam("state", state); + Response.ResponseBuilder location = Response.status(302).location(redirectUri.build()); + if (realm.isCookieLoginAllowed()) { + location.cookie(authManager.createLoginCookie(realm, accessCode.getUser(), uriInfo)); + } + return location.build(); + } + + public Response processAccessCode(String scopeParam, String state, String redirect, UserModel client, UserModel user) { + RoleModel resourceRole = realm.getRole(RealmManager.RESOURCE_ROLE); + RoleModel identityRequestRole = realm.getRole(RealmManager.IDENTITY_REQUESTER_ROLE); + boolean isResource = realm.hasRole(client, resourceRole); + if (!isResource && !realm.hasRole(client, identityRequestRole)) { + return forwardToSecurityFailure("Login requester not allowed to request login."); + } + AccessCodeEntry accessCode = tokenManager.createAccessCode(scopeParam, state, redirect, realm, client, user); + log.info("processAccessCode: isResource: " + isResource); + log.info("processAccessCode: go to oauth page?: " + + (!isResource && (accessCode.getRealmRolesRequested().size() > 0 || accessCode.getResourceRolesRequested() + .size() > 0))); + if (!isResource + && (accessCode.getRealmRolesRequested().size() > 0 || accessCode.getResourceRolesRequested().size() > 0)) { + return oauthGrantPage(accessCode, client); + } + + return redirectAccessCode(accessCode, state, redirect); + } + + public Response oauthGrantPage(AccessCodeEntry accessCode, UserModel client) { + request.setAttribute("realmRolesRequested", accessCode.getRealmRolesRequested()); + request.setAttribute("resourceRolesRequested", accessCode.getResourceRolesRequested()); + request.setAttribute("client", client); + request.setAttribute("action", TokenService.processOAuthUrl(uriInfo).build(realm.getId()).toString()); + request.setAttribute("code", accessCode.getCode()); + + request.forward(Pages.OAUTH_GRANT); + return null; + } + + public Response forwardToSecurityFailure(String message) { + return Flows.pages(request).forwardToSecurityFailure(message); + } + +} diff --git a/services/src/main/java/org/keycloak/services/resources/flows/PageFlows.java b/services/src/main/java/org/keycloak/services/resources/flows/PageFlows.java new file mode 100644 index 0000000000..b63b232c92 --- /dev/null +++ b/services/src/main/java/org/keycloak/services/resources/flows/PageFlows.java @@ -0,0 +1,28 @@ +package org.keycloak.services.resources.flows; + +import javax.ws.rs.core.Response; + +import org.jboss.resteasy.logging.Logger; +import org.jboss.resteasy.spi.HttpRequest; +import org.keycloak.services.JspRequestParameters; + +public class PageFlows { + + private static final Logger log = Logger.getLogger(PageFlows.class); + + private HttpRequest request; + + PageFlows(HttpRequest request) { + this.request = request; + } + + public Response forwardToSecurityFailure(String message) { + log.error(message); + + request.setAttribute(JspRequestParameters.KEYCLOAK_SECURITY_FAILURE_MESSAGE, message); + + request.forward(Pages.SECURITY_FAILURE); + return null; + } + +} diff --git a/services/src/main/java/org/keycloak/services/resources/flows/Pages.java b/services/src/main/java/org/keycloak/services/resources/flows/Pages.java new file mode 100644 index 0000000000..bb41513fa3 --- /dev/null +++ b/services/src/main/java/org/keycloak/services/resources/flows/Pages.java @@ -0,0 +1,13 @@ +package org.keycloak.services.resources.flows; + +public class Pages { + + public final static String LOGIN = "/sdk/login.xhtml"; + + public final static String OAUTH_GRANT = "/saas/oauthGrantForm.jsp"; + + public final static String REGISTER = "/sdk/register.xhtml"; + + public final static String SECURITY_FAILURE = "/saas/securityFailure.jsp"; + +} diff --git a/services/src/main/java/org/keycloak/services/resources/flows/Urls.java b/services/src/main/java/org/keycloak/services/resources/flows/Urls.java new file mode 100644 index 0000000000..e20f974cd7 --- /dev/null +++ b/services/src/main/java/org/keycloak/services/resources/flows/Urls.java @@ -0,0 +1,71 @@ +package org.keycloak.services.resources.flows; + +import java.net.URI; + +import javax.ws.rs.core.UriBuilder; + +import org.keycloak.services.resources.RealmsResource; +import org.keycloak.services.resources.SaasService; +import org.keycloak.services.resources.SocialResource; +import org.keycloak.services.resources.TokenService; + +public class Urls { + + private static UriBuilder realmBase(URI baseUri) { + return UriBuilder.fromUri(baseUri).path(RealmsResource.class); + } + + private static UriBuilder tokenBase(URI baseUri) { + return realmBase(baseUri).path(RealmsResource.class, "getTokenService"); + } + + public static URI realmLoginAction(URI baseUri, String realmId) { + return tokenBase(baseUri).path(TokenService.class, "processLogin").build(realmId); + } + + public static URI realmLoginPage(URI baseUri, String realmId) { + return tokenBase(baseUri).path(TokenService.class, "loginPage").build(realmId); + } + + public static URI realmRegisterAction(URI baseUri, String realmId) { + return tokenBase(baseUri).path(TokenService.class, "processRegister").build(realmId); + } + + public static URI realmRegisterPage(URI baseUri, String realmId) { + return tokenBase(baseUri).path(TokenService.class, "registerPage").build(realmId); + } + + private static UriBuilder saasBase(URI baseUri) { + return UriBuilder.fromUri(baseUri).path(SaasService.class); + } + + public static URI saasLoginAction(URI baseUri) { + return saasBase(baseUri).path(SaasService.class, "processLogin").build(); + } + + public static URI saasLoginPage(URI baseUri) { + return saasBase(baseUri).path(SaasService.class, "loginPage").build(); + } + + public static URI saasRegisterAction(URI baseUri) { + return saasBase(baseUri).path(SaasService.class, "processRegister").build(); + } + + public static URI saasRegisterPage(URI baseUri) { + return saasBase(baseUri).path(SaasService.class, "registerPage").build(); + } + + private static UriBuilder socialBase(URI baseUri) { + return UriBuilder.fromUri(baseUri).path(SocialResource.class); + } + + public static URI socialCallback(URI baseUri) { + return socialBase(baseUri).path(SocialResource.class, "callback").build(); + } + + public static URI socialRedirectToProviderAuth(URI baseUri, String realmId) { + return socialBase(baseUri).path(SocialResource.class, "redirectToProviderAuth") + .build(realmId); + } + +}