i18n for error message in login

This commit is contained in:
Michael Gerber
2015-02-28 19:36:44 +01:00
parent 12926f7f33
commit 8b60c9633d
15 changed files with 257 additions and 115 deletions

View File

@@ -117,8 +117,46 @@ emailInstruction=Enter your username or email address and we will send you instr
accountUpdated=Your account has been updated
accountPasswordUpdated=Your password has been updated
identityProviderUnexpectedError=Unexpected error when authenticating with identity provider
identityProviderNotFound="Could not find an identity provider with the identifier [{0}]."
identityProviderNoToken=Could not obtain token from identity provider [{0}].
realmSupportsNoCredentials=Realm [{0}] does not support any credential type.
identityProviderNotUnique = Realm [{0}] supports multiple identity providers. Could not determine which identity provider should be used to authenticate with.
identityProviderNotUnique=Realm [{0}] supports multiple identity providers. Could not determine which identity provider should be used to authenticate with.
noAccess=No access
noAccess=No access
invalidPasswordMinLength=Invalid password: minimum length {0}
invalidPasswordMinDigits=Invalid password: must contain at least {0} numerical digits
invalidPasswordMinLowerCaseChars=Invalid password: must contain at least {0} lower case characters
invalidPasswordMinUpperCaseChars=Invalid password: must contain at least {0} upper case characters
invalidPasswordMinSpecialChars=Invalid password: must contain at least {0} special characters
failedToProcessResponse=Failed to process response
httpsRequired=HTTPS required
realmNotEnabled=Realm not enabled
invalidRequest=Invalid Request
unknownLoginRequester=Unknown login requester
loginRequesterNotEnabled=Login requester not enabled
bearerOnly=Bearer-only applications are not allowed to initiate browser login
directGrantsOnly=Direct-grants-only clients are not allowed to initiate browser login
invalidRedirectUri=Invalid redirect uri
unsupportedNameIdFormat=Unsupported NameIDFormat
invlidRequester=Invalid requester
registrationNotAllowed=Registration not allowed
permissionNotApproved=Permission not approved.
noRelayStateInResponse=No relay state in response from identity provider [{0}].
identityProviderAlreadyLinked=The identity returned by the identity provider [{0}] is already linked to another user.
userDisabled="User is disabled."
insufficientPermission=Insufficient permissions to link identities.
couldNotProceedWithAuthenticationRequest=Could not proceed with authentication request to identity provider.
couldNotObtainToken=Could not obtain token from identity provider [{0}].
unexpectedErrorRetrievingToken=Unexpected error when retrieving token from identity provider [{0}].
unexpectedErrorHandlingResponse=Unexpected error when handling response from identity provider [{0}].
identityProviderAuthenticationFailed=Authentication failed. Could not authenticate with identity provider [{0}].
couldNotSendAuthenticationRequest=Could not send authentication request to identity provider [{0}].
unexpectedErrorHandlingRequest=Unexpected error when handling authentication request to identity provider [{0}].
invalidAccessCode=Invalid access code.
sessionNotActive=Session not active.
unknownCode=Unknown code, please login again through your application.
invalidCode=Invalid code, please login again through your application.

View File

@@ -119,7 +119,7 @@ import java.util.concurrent.TimeUnit;
session.getProvider(EmailProvider.class).setRealm(realm).setUser(user).sendVerifyEmail(link, expiration);
} catch (EmailException e) {
logger.error("Failed to send verification email", e);
return setError("emailSendError").createErrorPage();
return setError(Messages.EMAIL_SENT_ERROR).createErrorPage();
}
actionMessage = Messages.ACTION_WARN_EMAIL;

View File

@@ -5,6 +5,8 @@ package org.keycloak.models;
*/
public class ModelException extends RuntimeException {
private Object[] parameters;
public ModelException() {
}
@@ -12,6 +14,11 @@ public class ModelException extends RuntimeException {
super(message);
}
public ModelException(String message, Object ... parameters) {
super(message);
this.parameters = parameters;
}
public ModelException(String message, Throwable cause) {
super(message, cause);
}
@@ -20,4 +27,11 @@ public class ModelException extends RuntimeException {
super(cause);
}
public Object[] getParameters() {
return parameters;
}
public void setParameters(Object[] parameters) {
this.parameters = parameters;
}
}

View File

@@ -1,5 +1,6 @@
package org.keycloak.models;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
@@ -74,9 +75,9 @@ public class PasswordPolicy {
return -1;
}
public String validate(String password) {
public Error validate(String password) {
for (Policy p : policies) {
String error = p.validate(password);
Error error = p.validate(password);
if (error != null) {
return error;
}
@@ -85,7 +86,25 @@ public class PasswordPolicy {
}
private static interface Policy {
public String validate(String password);
public Error validate(String password);
}
public static class Error{
private String message;
private Object[] parameters;
private Error(String message, Object ... parameters){
this.message = message;
this.parameters = parameters;
}
public String getMessage() {
return message;
}
public Object[] getParameters() {
return parameters;
}
}
private static class HashIterations implements Policy {
@@ -97,7 +116,7 @@ public class PasswordPolicy {
}
@Override
public String validate(String password) {
public Error validate(String password) {
return null;
}
}
@@ -111,8 +130,8 @@ public class PasswordPolicy {
}
@Override
public String validate(String password) {
return password.length() < min ? "Invalid password: minimum length " + min : null;
public Error validate(String password) {
return password.length() < min ? new Error("invalidPasswordMinLength", min) : null;
}
}
@@ -125,14 +144,14 @@ public class PasswordPolicy {
}
@Override
public String validate(String password) {
public Error validate(String password) {
int count = 0;
for (char c : password.toCharArray()) {
if (Character.isDigit(c)) {
count++;
}
}
return count < min ? "Invalid password: must contain at least " + min + " numerical digits" : null;
return count < min ? new Error("invalidPasswordMinDigits", min) : null;
}
}
@@ -145,14 +164,14 @@ public class PasswordPolicy {
}
@Override
public String validate(String password) {
public Error validate(String password) {
int count = 0;
for (char c : password.toCharArray()) {
if (Character.isLowerCase(c)) {
count++;
}
}
return count < min ? "Invalid password: must contain at least " + min + " lower case characters": null;
return count < min ? new Error("invalidPasswordMinLowerCaseChars", min): null;
}
}
@@ -165,14 +184,14 @@ public class PasswordPolicy {
}
@Override
public String validate(String password) {
public Error validate(String password) {
int count = 0;
for (char c : password.toCharArray()) {
if (Character.isUpperCase(c)) {
count++;
}
}
return count < min ? "Invalid password: must contain at least " + min + " upper case characters" : null;
return count < min ? new Error("invalidPasswordMinUpperCaseChars", min) : null;
}
}
@@ -185,14 +204,14 @@ public class PasswordPolicy {
}
@Override
public String validate(String password) {
public Error validate(String password) {
int count = 0;
for (char c : password.toCharArray()) {
if (!Character.isLetterOrDigit(c)) {
count++;
}
}
return count < min ? "Invalid password: must contain at least " + min + " special characters" : null;
return count < min ? new Error("invalidPasswordMinSpecialChars", min) : null;
}
}

View File

@@ -322,8 +322,8 @@ public class UserFederationManager implements UserProvider {
public void updateCredential(RealmModel realm, UserModel user, UserCredentialModel credential) {
if (credential.getType().equals(UserCredentialModel.PASSWORD)) {
if (realm.getPasswordPolicy() != null) {
String error = realm.getPasswordPolicy().validate(credential.getValue());
if (error != null) throw new ModelException(error);
PasswordPolicy.Error error = realm.getPasswordPolicy().validate(credential.getValue());
if (error != null) throw new ModelException(error.getMessage(), error.getParameters());
}
}
user.updateCredential(credential);

View File

@@ -20,6 +20,7 @@ import org.keycloak.protocol.ProtocolMapper;
import org.keycloak.protocol.saml.mappers.SAMLLoginResponseMapper;
import org.keycloak.services.managers.ClientSessionCode;
import org.keycloak.services.managers.ResourceAdminManager;
import org.keycloak.services.messages.Messages;
import org.keycloak.services.resources.RealmsResource;
import org.keycloak.services.resources.admin.ClientAttributeCertificateResource;
import org.keycloak.services.resources.flows.Flows;
@@ -143,7 +144,7 @@ public class SamlProtocol implements LoginProtocol {
return builder.redirectBinding().response();
}
} catch (Exception e) {
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Failed to process response", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.FAILED_TO_PROCESS_RESPONSE );
}
}
@@ -284,7 +285,7 @@ public class SamlProtocol implements LoginProtocol {
samlDocument = builder.buildDocument(samlModel);
} catch (Exception e) {
logger.error("failed", e);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Failed to process response");
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.FAILED_TO_PROCESS_RESPONSE);
}
SAML2BindingBuilder2 bindingBuilder = new SAML2BindingBuilder2();
@@ -306,7 +307,7 @@ public class SamlProtocol implements LoginProtocol {
publicKey = SamlProtocolUtils.getEncryptionValidationKey(client);
} catch (Exception e) {
logger.error("failed", e);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Failed to process response", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.FAILED_TO_PROCESS_RESPONSE);
}
bindingBuilder.encrypt(publicKey);
}
@@ -318,7 +319,7 @@ public class SamlProtocol implements LoginProtocol {
}
} catch (Exception e) {
logger.error("failed", e);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Failed to process response", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.FAILED_TO_PROCESS_RESPONSE );
}
}

View File

@@ -20,6 +20,7 @@ import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.ClientSessionCode;
import org.keycloak.services.messages.Messages;
import org.keycloak.services.resources.RealmsResource;
import org.keycloak.services.resources.flows.Flows;
import org.keycloak.util.StreamUtil;
@@ -101,18 +102,18 @@ public class SamlService {
if (!checkSsl()) {
event.event(EventType.LOGIN);
event.error(Errors.SSL_REQUIRED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "HTTPS required", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.HTTPS_REQUIRED );
}
if (!realm.isEnabled()) {
event.event(EventType.LOGIN_ERROR);
event.error(Errors.REALM_DISABLED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Realm not enabled", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.REALM_NOT_ENABLED);
}
if (samlRequest == null && samlResponse == null) {
event.event(EventType.LOGIN);
event.error(Errors.INVALID_TOKEN);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid Request", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUEST );
}
return null;
@@ -124,7 +125,7 @@ public class SamlService {
logger.warn("Unknown saml response.");
event.event(EventType.LOGIN);
event.error(Errors.INVALID_TOKEN);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid Request", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUEST);
}
// assume this is a logout response
UserSessionModel userSession = authResult.getSession();
@@ -133,7 +134,7 @@ public class SamlService {
logger.warn("UserSession is not tagged as logging out.");
event.event(EventType.LOGIN);
event.error(Errors.INVALID_TOKEN);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid Request", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUEST);
}
logger.debug("logout response");
return authManager.browserLogout(session, realm, userSession, uriInfo, clientConnection, headers);
@@ -144,7 +145,7 @@ public class SamlService {
if (documentHolder == null) {
event.event(EventType.LOGIN);
event.error(Errors.INVALID_TOKEN);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid Request", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUEST);
}
SAML2Object samlObject = documentHolder.getSamlObject();
@@ -156,23 +157,23 @@ public class SamlService {
if (client == null) {
event.event(EventType.LOGIN);
event.error(Errors.CLIENT_NOT_FOUND);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown login requester.", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.UNKNOWN_LOGIN_REQUESTER);
}
if (!client.isEnabled()) {
event.event(EventType.LOGIN);
event.error(Errors.CLIENT_DISABLED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Login requester not enabled.", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.LOGIN_REQUESTER_NOT_ENABLED);
}
if ((client instanceof ApplicationModel) && ((ApplicationModel)client).isBearerOnly()) {
event.event(EventType.LOGIN);
event.error(Errors.NOT_ALLOWED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Bearer-only applications are not allowed to initiate browser login", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.BEARER_ONLY);
}
if (client.isDirectGrantsOnly()) {
event.event(EventType.LOGIN);
event.error(Errors.NOT_ALLOWED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "direct-grants-only clients are not allowed to initiate browser login", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.DIRECT_GRANTS_ONLY );
}
try {
@@ -181,7 +182,7 @@ public class SamlService {
SamlService.logger.error("request validation failed", e);
event.event(EventType.LOGIN);
event.error(Errors.INVALID_SIGNATURE);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid requester.", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUESTER);
}
logger.debug("verified request");
if (samlObject instanceof AuthnRequestType) {
@@ -199,7 +200,7 @@ public class SamlService {
} else {
event.event(EventType.LOGIN);
event.error(Errors.INVALID_TOKEN);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid Request", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUEST);
}
}
@@ -229,7 +230,7 @@ public class SamlService {
if (redirect == null) {
event.error(Errors.INVALID_REDIRECT_URI);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid redirect_uri.", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REDIRECT_URI );
}
@@ -251,7 +252,7 @@ public class SamlService {
clientSession.setNote(GeneralConstants.NAMEID_FORMAT, nameIdFormat);
} else {
event.error(Errors.INVALID_TOKEN);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unsupported NameIDFormat.", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.UNSUPPORTED_NAME_ID_FORMAT);
}
}
@@ -340,7 +341,7 @@ public class SamlService {
if (redirectUri != null) {
redirectUri = OIDCLoginProtocolService.verifyRedirectUri(uriInfo, redirectUri, realm, client);
if (redirectUri == null) {
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid redirect uri.", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REDIRECT_URI );
}
}
if (redirectUri != null) {

View File

@@ -72,7 +72,6 @@ import javax.ws.rs.ext.Providers;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -787,37 +786,37 @@ public class OIDCLoginProtocolService {
event.client(clientId).detail(Details.REDIRECT_URI, redirect).detail(Details.RESPONSE_TYPE, "code");
if (!checkSsl()) {
event.error(Errors.SSL_REQUIRED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "HTTPS required", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.HTTPS_REQUIRED);
}
if (!realm.isEnabled()) {
event.error(Errors.REALM_DISABLED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Realm not enabled", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.REALM_NOT_ENABLED);
}
clientSession = null;
ClientModel client = realm.findClient(clientId);
if (client == null) {
event.error(Errors.CLIENT_NOT_FOUND);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown login requester.", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.UNKNOWN_LOGIN_REQUESTER);
}
if (!client.isEnabled()) {
event.error(Errors.CLIENT_DISABLED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Login requester not enabled.", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.LOGIN_REQUESTER_NOT_ENABLED);
}
if ((client instanceof ApplicationModel) && ((ApplicationModel)client).isBearerOnly()) {
event.error(Errors.NOT_ALLOWED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Bearer-only applications are not allowed to initiate browser login", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.BEARER_ONLY);
}
if (client.isDirectGrantsOnly()) {
event.error(Errors.NOT_ALLOWED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "direct-grants-only clients are not allowed to initiate browser login", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.DIRECT_GRANTS_ONLY);
}
String redirectUriParam = redirect;
redirect = verifyRedirectUri(uriInfo, redirect, realm, client);
if (redirect == null) {
event.error(Errors.INVALID_REDIRECT_URI);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid redirect_uri.", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REDIRECT_URI);
}
clientSession = session.sessions().createClientSession(realm, client);
clientSession.setAuthMethod(OIDCLoginProtocol.LOGIN_PROTOCOL);
@@ -962,7 +961,7 @@ public class OIDCLoginProtocolService {
event.event(EventType.REGISTER);
if (!realm.isRegistrationAllowed()) {
event.error(Errors.REGISTRATION_DISABLED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Registration not allowed", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.REGISTRATION_NOT_ALLOWED );
}
FrontPageInitializer pageInitializer = new FrontPageInitializer();
@@ -1006,7 +1005,7 @@ public class OIDCLoginProtocolService {
if (redirectUri != null) {
String validatedRedirect = verifyRealmRedirectUri(uriInfo, redirectUri, realm);
if (validatedRedirect == null) {
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid redirect uri.", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REDIRECT_URI);
}
return Response.status(302).location(UriBuilder.fromUri(validatedRedirect).build()).build();
} else {

View File

@@ -112,7 +112,7 @@ public class HttpAuthenticationManager {
Response response;
if (!user.isEnabled()) {
event.error(Errors.USER_DISABLED);
response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, Messages.ACCOUNT_DISABLED, headers);
response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.ACCOUNT_DISABLED);
} else {
UserSessionModel userSession = session.sessions().createUserSession(realm, user, user.getUsername(), clientConnection.getRemoteAddr(), authMethod, false);
TokenManager.attachClientSession(userSession, clientSession);

View File

@@ -27,6 +27,7 @@ package org.keycloak.services.messages;
public class Messages {
public static final String ACCOUNT_DISABLED = "accountDisabled";
public static final String ACCOUNT_TEMPORARILY_DISABLED = "accountTemporarilyDisabled";
public static final String INVALID_PASSWORD = "invalidPassword";
@@ -87,6 +88,10 @@ public class Messages {
public static final String IDENTITY_PROVIDER_REMOVED = "identityProviderRemoved";
public static final String IDENTITY_PROVIDER_UNEXPECTED_ERROR = "identityProviderUnexpectedError";
public static final String IDENTITY_PROVIDER_NO_TOKEN = "identityProviderNoToken";
public static final String ERROR = "error";
public static final String REALM_SUPPORTS_NO_CREDENTIALS = "realmSupportsNoCredentials";
@@ -94,4 +99,65 @@ public class Messages {
public static final String IDENTITY_PROVIDER_NOT_UNIQUE = "identityProviderNotUnique";
public static final String NO_ACCESS = "noAccess";
public static final String EMAIL_SENT = "emailSent";
public static final String EMAIL_SENT_ERROR = "emailSendError";
public static final String FAILED_TO_PROCESS_RESPONSE = "failedToProcessResponse";
public static final String HTTPS_REQUIRED = "httpsRequired";
public static final String REALM_NOT_ENABLED = "realmNotEnabled";
public static final String INVALID_REQUEST = "invalidRequest";
public static final String INVALID_REQUESTER = "invalidRequester";
public static final String UNKNOWN_LOGIN_REQUESTER = "unknownLoginRequester";
public static final String LOGIN_REQUESTER_NOT_ENABLED = "loginRequesterNotEnabled";
public static final String BEARER_ONLY = "bearerOnly";
public static final String DIRECT_GRANTS_ONLY = "directGrantsOnly";
public static final String INVALID_REDIRECT_URI = "invalidRedirectUri";
public static final String UNSUPPORTED_NAME_ID_FORMAT = "unsupportedNameIdFormat";
public static final String REGISTRATION_NOT_ALLOWED = "registrationNotAllowed";
public static final String PERMISSION_NOT_APPROVED = "permissionNotApproved";
public static final String NO_RELAY_STATE_IN_RESPONSE = "noRelayStateInResponse";
public static final String IDENTITY_PROVIDER_ALREADY_LINKED = "identityProviderAlreadyLinked";
public static final String USER_DISABLED = "userDisabled";
public static final String INSUFFICIENT_PERMISSION = "insufficientPermission";
public static final String COULD_NOT_PROCEED_WITH_AUTHENTICATION_REQUEST = "couldNotProceedWithAuthenticationRequest";
public static final String COULD_NOT_OBTAIN_TOKEN = "couldNotObtainToken";
public static final String UNEXPECTED_ERROR_RETRIEVING_TOKEN = "unexpectedErrorRetrievingToken";
public static final String IDENTITY_PROVIDER_AUTHENTICATION_FAILED = "identityProviderAuthenticationFailed";
public static final String UNEXPECTED_ERROR_HANDLING_RESPONSE = "unexpectedErrorHandlingResponse";
public static final String COULD_NOT_SEND_AUTHENTICATION_REQUEST = "couldNotSendAuthenticationRequest";
public static final String UNEXPECTED_ERROR_HANDLING_REQUEST = "unexpectedErrorHandlingRequest";
public static final String INVALID_ACCESS_CODE = "invalidAccessCode";
public static final String SESSION_NOT_ACTIVE = "sessionNotActive";
public static final String UNKNOWN_CODE = "unknownCode";
public static final String INVALID_CODE = "invalidCode";
}

View File

@@ -611,7 +611,7 @@ public class AccountService {
} catch (ModelReadOnlyException mre) {
setReferrerOnPage();
return account.setError(Messages.READ_ONLY_PASSWORD).createResponse(AccountPages.PASSWORD);
} catch (Exception ape) {
}catch (Exception ape) {
logger.error("Failed to update password", ape);
setReferrerOnPage();
return account.setError(ape.getMessage()).createResponse(AccountPages.PASSWORD);

View File

@@ -46,6 +46,7 @@ import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.AuthenticationManager.AuthResult;
import org.keycloak.services.managers.ClientSessionCode;
import org.keycloak.services.managers.EventsManager;
import org.keycloak.services.messages.Messages;
import org.keycloak.services.resources.flows.Flows;
import org.keycloak.services.resources.flows.Urls;
import org.keycloak.social.SocialIdentityProvider;
@@ -134,12 +135,12 @@ public class IdentityBrokerService {
return response;
}
} catch (IdentityBrokerException e) {
return redirectToErrorPage("Could not send authentication request to identity provider [" + providerId + "].", e);
return redirectToErrorPage(Messages.COULD_NOT_SEND_AUTHENTICATION_REQUEST, e, providerId);
} catch (Exception e) {
return redirectToErrorPage("Unexpected error when handling authentication request to identity provider [" + providerId + "].", e);
return redirectToErrorPage(Messages.UNEXPECTED_ERROR_HANDLING_REQUEST, e, providerId);
}
return redirectToErrorPage("Could not proceed with authentication request to identity provider.");
return redirectToErrorPage(Messages.COULD_NOT_PROCEED_WITH_AUTHENTICATION_REQUEST);
}
@GET
@@ -218,9 +219,9 @@ public class IdentityBrokerService {
return badRequest("Invalid token.");
} catch (IdentityBrokerException e) {
return redirectToErrorPage("Could not obtain token fron identity provider [" + providerId + "].", e);
return redirectToErrorPage(Messages.COULD_NOT_OBTAIN_TOKEN, e, providerId);
} catch (Exception e) {
return redirectToErrorPage("Unexpected error when retrieving token from identity provider [" + providerId + "].", e);
return redirectToErrorPage(Messages.UNEXPECTED_ERROR_RETRIEVING_TOKEN, e, providerId);
}
}
@@ -230,7 +231,7 @@ public class IdentityBrokerService {
public Response consentTokenRetrieval(@PathParam("provider_id") String providerId,
MultivaluedMap<String, String> formData) {
if (formData.containsKey("cancel")) {
return redirectToErrorPage("Permission not approved.");
return redirectToErrorPage(Messages.PERMISSION_NOT_APPROVED);
}
return getToken(providerId, true);
@@ -249,7 +250,7 @@ public class IdentityBrokerService {
String relayState = identityProvider.getRelayState(createAuthenticationRequest(providerId, null));
if (relayState == null) {
return redirectToErrorPage("No relay state in response from identity identity [" + providerId + ".");
return redirectToErrorPage(Messages.NO_RELAY_STATE_IN_RESPONSE, providerId);
}
if (isDebugEnabled()) {
@@ -285,10 +286,10 @@ public class IdentityBrokerService {
return performLocalAuthentication(identity, clientSessionCode);
} catch (IdentityBrokerException e) {
rollback();
return redirectToErrorPage("Authentication failed. Could not authenticate with identity provider [" + providerId + "].", e);
return redirectToErrorPage(Messages.IDENTITY_PROVIDER_AUTHENTICATION_FAILED, e, providerId);
} catch (Exception e) {
rollback();
return redirectToErrorPage("Unexpected error when handling response from identity provider [" + providerId + "].", e);
return redirectToErrorPage(Messages.UNEXPECTED_ERROR_HANDLING_RESPONSE, e, providerId);
} finally {
if (this.session.getTransaction().isActive()) {
this.session.getTransaction().commit();
@@ -351,7 +352,7 @@ public class IdentityBrokerService {
this.event.event(EventType.IDENTITY_PROVIDER_ACCCOUNT_LINKING);
if (federatedUser != null) {
return redirectToErrorPage("The identity returned by the identity provider [" + providerId + "] is already linked to other user.");
return redirectToErrorPage(Messages.IDENTITY_PROVIDER_ALREADY_LINKED, providerId);
}
UserModel authenticatedUser = clientSession.getUserSession().getUser();
@@ -362,12 +363,12 @@ public class IdentityBrokerService {
if (!authenticatedUser.isEnabled()) {
fireErrorEvent(Errors.USER_DISABLED);
return redirectToErrorPage("User is disabled.");
return redirectToErrorPage(Messages.USER_DISABLED);
}
if (!authenticatedUser.hasRole(this.realmModel.getApplicationByName(ACCOUNT_MANAGEMENT_APP).getRole(MANAGE_ACCOUNT))) {
fireErrorEvent(Errors.NOT_ALLOWED);
return redirectToErrorPage("Insufficient permissions to link identities.");
return redirectToErrorPage(Messages.INSUFFICIENT_PERMISSION);
}
this.session.users().addFederatedIdentity(this.realmModel, authenticatedUser, federatedIdentityModel);
@@ -436,24 +437,24 @@ public class IdentityBrokerService {
return Urls.identityProviderAuthnResponse(this.uriInfo.getBaseUri(), providerId, this.realmModel.getName()).toString();
}
private Response redirectToErrorPage(String message) {
return redirectToErrorPage(message, null);
private Response redirectToErrorPage(String message, Object ... parameters) {
return redirectToErrorPage(message, null, parameters);
}
private Response redirectToErrorPage(String message, Throwable throwable) {
private Response redirectToErrorPage(String message, Throwable throwable, Object ... parameters) {
if (message == null) {
message = "Unexpected error when authenticating with identity provider";
message = Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR;
}
fireErrorEvent(message, throwable);
return Flows.forwardToSecurityFailurePage(this.session, this.realmModel, this.uriInfo, message, headers);
return Flows.forwardToSecurityFailurePage(this.session, this.realmModel, this.uriInfo, headers, message, parameters);
}
private Response redirectToLoginPage(Throwable t, ClientSessionCode clientCode) {
String message = t.getMessage();
if (message == null) {
message = "Unexpected error when authenticating with identity provider";
message = Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR;
}
fireErrorEvent(message);

View File

@@ -32,15 +32,8 @@ import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventType;
import org.keycloak.jose.jws.JWSBuilder;
import org.keycloak.login.LoginFormsProvider;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredCredentialModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.*;
import org.keycloak.models.UserModel.RequiredAction;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.TimeBasedOTP;
import org.keycloak.protocol.LoginProtocol;
@@ -156,7 +149,7 @@ public class LoginActionsService {
return false;
} else if (!clientCode.isValid(requiredAction)) {
event.error(Errors.INVALID_CODE);
response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid code, please login again through your application.", headers);
response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_CODE);
return false;
} else {
return true;
@@ -166,18 +159,18 @@ public class LoginActionsService {
public boolean check(String code) {
if (!checkSsl()) {
event.error(Errors.SSL_REQUIRED);
response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "HTTPS required", headers);
response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.HTTPS_REQUIRED );
return false;
}
if (!realm.isEnabled()) {
event.error(Errors.REALM_DISABLED);
response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Realm not enabled.", headers);
response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.REALM_NOT_ENABLED);
return false;
}
clientCode = ClientSessionCode.parse(code, session, realm);
if (clientCode == null) {
event.error(Errors.INVALID_CODE);
response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown code, please login again through your application.", headers);
response = Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.UNKNOWN_CODE );
return false;
}
return true;
@@ -226,7 +219,7 @@ public class LoginActionsService {
event.event(EventType.REGISTER);
if (!realm.isRegistrationAllowed()) {
event.error(Errors.REGISTRATION_DISABLED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Registration not allowed", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.REGISTRATION_NOT_ALLOWED );
}
Checks checks = new Checks();
@@ -260,17 +253,17 @@ public class LoginActionsService {
event.event(EventType.LOGIN);
if (!checkSsl()) {
event.error(Errors.SSL_REQUIRED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "HTTPS required", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.HTTPS_REQUIRED );
}
if (!realm.isEnabled()) {
event.error(Errors.REALM_DISABLED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Realm not enabled.", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.REALM_NOT_ENABLED);
}
ClientSessionCode clientCode = ClientSessionCode.parse(code, session, realm);
if (clientCode == null) {
event.error(Errors.INVALID_CODE);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown code, please login again through your application.", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.UNKNOWN_CODE );
}
ClientSessionModel clientSession = clientCode.getClientSession();
if (!clientCode.isValid(ClientSessionModel.Action.AUTHENTICATE) || clientSession.getUserSession() != null) {
@@ -300,11 +293,11 @@ public class LoginActionsService {
ClientModel client = clientSession.getClient();
if (client == null) {
event.error(Errors.CLIENT_NOT_FOUND);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown login requester.", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.UNKNOWN_LOGIN_REQUESTER );
}
if (!client.isEnabled()) {
event.error(Errors.CLIENT_NOT_FOUND);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Login requester not enabled.", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.LOGIN_REQUESTER_NOT_ENABLED );
}
if (formData.containsKey("cancel")) {
@@ -389,25 +382,25 @@ public class LoginActionsService {
event.event(EventType.REGISTER);
if (!checkSsl()) {
event.error(Errors.SSL_REQUIRED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "HTTPS required", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.HTTPS_REQUIRED);
}
if (!realm.isEnabled()) {
event.error(Errors.REALM_DISABLED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Realm not enabled.", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.REALM_NOT_ENABLED);
}
if (!realm.isRegistrationAllowed()) {
event.error(Errors.REGISTRATION_DISABLED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Registration not allowed", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.REGISTRATION_NOT_ALLOWED );
}
ClientSessionCode clientCode = ClientSessionCode.parse(code, session, realm);
if (clientCode == null) {
event.error(Errors.INVALID_CODE);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown code, please login again through your application.", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.UNKNOWN_CODE );
}
if (!clientCode.isValid(ClientSessionModel.Action.AUTHENTICATE)) {
event.error(Errors.INVALID_CODE);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid code, please login again through your application.", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_CODE );
}
String username = formData.getFirst("username");
@@ -422,17 +415,17 @@ public class LoginActionsService {
if (!realm.isEnabled()) {
event.error(Errors.REALM_DISABLED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Realm not enabled", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.REALM_NOT_ENABLED);
}
ClientModel client = clientSession.getClient();
if (client == null) {
event.error(Errors.CLIENT_NOT_FOUND);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown login requester.", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.UNKNOWN_LOGIN_REQUESTER );
}
if (!client.isEnabled()) {
event.error(Errors.CLIENT_DISABLED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Login requester not enabled.", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.LOGIN_REQUESTER_NOT_ENABLED );
}
@@ -442,15 +435,18 @@ public class LoginActionsService {
}
// Validate here, so user is not created if password doesn't validate to passwordPolicy of current realm
String error = Validation.validateRegistrationForm(formData, requiredCredentialTypes);
if (error == null) {
error = Validation.validatePassword(formData, realm.getPasswordPolicy());
String errorMessage = Validation.validateRegistrationForm(formData, requiredCredentialTypes);
Object[] parameters = new Object[0];
if (errorMessage == null) {
PasswordPolicy.Error error = Validation.validatePassword(formData, realm.getPasswordPolicy());
errorMessage = error.getMessage();
parameters = error.getParameters();
}
if (error != null) {
if (errorMessage != null) {
event.error(Errors.INVALID_REGISTRATION);
return Flows.forms(session, realm, client, uriInfo, headers)
.setError(error)
.setError(errorMessage, parameters)
.setFormData(formData)
.setClientSessionCode(clientCode.getCode())
.createRegistration();
@@ -490,9 +486,14 @@ public class LoginActionsService {
boolean passwordUpdateSuccessful;
String passwordUpdateError = null;
Object[] passwordUpdateErrorParameters = null;
try {
session.users().updateCredential(realm, user, UserCredentialModel.password(formData.getFirst("password")));
passwordUpdateSuccessful = true;
} catch (ModelException me) {
passwordUpdateSuccessful = false;
passwordUpdateError = me.getMessage();
passwordUpdateErrorParameters = me.getParameters();
} catch (Exception ape) {
passwordUpdateSuccessful = false;
passwordUpdateError = ape.getMessage();
@@ -502,7 +503,7 @@ public class LoginActionsService {
if (!passwordUpdateSuccessful) {
user.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
return Flows.forms(session, realm, client, uriInfo, headers)
.setError(passwordUpdateError)
.setError(passwordUpdateError, passwordUpdateErrorParameters)
.setClientSessionCode(clientCode.getCode())
.createResponse(UserModel.RequiredAction.UPDATE_PASSWORD);
}
@@ -528,7 +529,7 @@ public class LoginActionsService {
if (!checkSsl()) {
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "HTTPS required", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.HTTPS_REQUIRED);
}
String code = formData.getFirst("code");
@@ -536,7 +537,7 @@ public class LoginActionsService {
ClientSessionCode accessCode = ClientSessionCode.parse(code, session, realm);
if (accessCode == null || !accessCode.isValid(ClientSessionModel.Action.OAUTH_GRANT)) {
event.error(Errors.INVALID_CODE);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Invalid access code.", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_ACCESS_CODE );
}
ClientSessionModel clientSession = accessCode.getClientSession();
event.detail(Details.CODE_ID, clientSession.getId());
@@ -560,7 +561,7 @@ public class LoginActionsService {
if (!AuthenticationManager.isSessionValid(realm, userSession)) {
AuthenticationManager.logout(session, realm, userSession, uriInfo, clientConnection, headers);
event.error(Errors.INVALID_CODE);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Session not active", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.SESSION_NOT_ACTIVE );
}
event.session(userSession);
@@ -715,6 +716,10 @@ public class LoginActionsService {
try {
session.users().updateCredential(realm, user, UserCredentialModel.password(passwordNew));
} catch (ModelException me) {
return loginForms.setError(me.getMessage(), me.getParameters())
.setClientSessionCode(accessCode.getCode())
.createResponse(RequiredAction.UPDATE_PASSWORD);
} catch (Exception ape) {
return loginForms.setError(ape.getMessage())
.setClientSessionCode(accessCode.getCode())
@@ -794,16 +799,16 @@ public class LoginActionsService {
final MultivaluedMap<String, String> formData) {
event.event(EventType.SEND_RESET_PASSWORD);
if (!checkSsl()) {
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "HTTPS required", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.HTTPS_REQUIRED);
}
if (!realm.isEnabled()) {
event.error(Errors.REALM_DISABLED);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Realm not enabled.", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.REALM_NOT_ENABLED);
}
ClientSessionCode accessCode = ClientSessionCode.parse(code, session, realm);
if (accessCode == null) {
event.error(Errors.INVALID_CODE);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, "Unknown code, please login again through your application.", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.UNKNOWN_CODE );
}
ClientSessionModel clientSession = accessCode.getClientSession();
@@ -811,12 +816,10 @@ public class LoginActionsService {
ClientModel client = clientSession.getClient();
if (client == null) {
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo,
"Unknown login requester.", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.UNKNOWN_LOGIN_REQUESTER );
}
if (!client.isEnabled()) {
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo,
"Login requester not enabled.", headers);
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.LOGIN_REQUESTER_NOT_ENABLED );
}
event.client(client.getClientId())
@@ -859,13 +862,13 @@ public class LoginActionsService {
} catch (EmailException e) {
event.error(Errors.EMAIL_SEND_FAILED);
logger.error("Failed to send password reset email", e);
return Flows.forms(this.session, realm, client, uriInfo, headers).setError("emailSendError")
return Flows.forms(this.session, realm, client, uriInfo, headers).setError(Messages.EMAIL_SENT_ERROR)
.setClientSessionCode(accessCode.getCode())
.createErrorPage();
}
}
return Flows.forms(session, realm, client, uriInfo, headers).setSuccess("emailSent").setClientSessionCode(accessCode.getCode()).createPasswordReset();
return Flows.forms(session, realm, client, uriInfo, headers).setSuccess(Messages.EMAIL_SENT).setClientSessionCode(accessCode.getCode()).createPasswordReset();
}
private Response redirectOauth(UserModel user, ClientSessionCode accessCode, ClientSessionModel clientSession, UserSessionModel userSession) {

View File

@@ -46,8 +46,8 @@ public class Flows {
return new ErrorFlows();
}
public static Response forwardToSecurityFailurePage(KeycloakSession session, RealmModel realm, UriInfo uriInfo, String message, HttpHeaders headers) {
return Flows.forms(session, realm, null, uriInfo, headers).setError(message).createErrorPage();
public static Response forwardToSecurityFailurePage(KeycloakSession session, RealmModel realm, UriInfo uriInfo, HttpHeaders headers, String message, Object ... parameters) {
return Flows.forms(session, realm, null, uriInfo, headers).setError(message,parameters).createErrorPage();
}

View File

@@ -47,7 +47,7 @@ public class Validation {
return null;
}
public static String validatePassword(MultivaluedMap<String, String> formData, PasswordPolicy policy) {
public static PasswordPolicy.Error validatePassword(MultivaluedMap<String, String> formData, PasswordPolicy policy) {
return policy.validate(formData.getFirst("password"));
}