Merge pull request #1261 from panga/master

KEYCLOAK-1306 - Better Admin API error handling (new)
This commit is contained in:
Stian Thorgersen 2015-05-19 08:56:19 +02:00
commit c193ba0c81
11 changed files with 195 additions and 4 deletions

View file

@ -0,0 +1,8 @@
invalidPasswordMinLengthMessage=Invalid password: minimum length {0}.
invalidPasswordMinLowerCaseCharsMessage=Invalid password: must contain at least {0} lower case characters.
invalidPasswordMinDigitsMessage=Invalid password: must contain at least {0} numerical digits.
invalidPasswordMinUpperCaseCharsMessage=Invalid password: must contain at least {0} upper case characters.
invalidPasswordMinSpecialCharsMessage=Invalid password: must contain at least {0} special characters.
invalidPasswordNotUsernameMessage=Invalid password: must not be equal to the username.
invalidPasswordRegexPatternMessage=Invalid password: fails to match regex pattern(s).
invalidPasswordHistoryMessage=Invalid password: must not be equal to any of last {0} passwords.

View file

@ -308,8 +308,12 @@ module.controller('UserCredentialsCtrl', function($scope, realm, user, User, Use
Notifications.success("The password has been reset"); Notifications.success("The password has been reset");
$scope.password = null; $scope.password = null;
$scope.confirmPassword = null; $scope.confirmPassword = null;
}, function() { }, function(response) {
Notifications.error("Failed to reset user password"); if (response.data && response.data.errorMessage) {
Notifications.error(response.data.errorMessage);
} else {
Notifications.error("Failed to reset user password");
}
}); });
}, function() { }, function() {
$scope.password = null; $scope.password = null;

View file

@ -0,0 +1,12 @@
package org.keycloak.messages;
import org.keycloak.provider.Provider;
/**
* @author <a href="mailto:leonardo.zanivan@gmail.com">Leonardo Zanivan</a>
*/
public interface MessagesProvider extends Provider {
String getMessage(String messageKey, Object... parameters);
}

View file

@ -0,0 +1,10 @@
package org.keycloak.messages;
import org.keycloak.provider.ProviderFactory;
/**
* @author <a href="mailto:leonardo.zanivan@gmail.com">Leonardo Zanivan</a>
*/
public interface MessagesProviderFactory extends ProviderFactory<MessagesProvider> {
}

View file

@ -0,0 +1,32 @@
package org.keycloak.messages;
import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.Spi;
/**
* @author <a href="mailto:leonardo.zanivan@gmail.com">Leonardo Zanivan</a>
*/
public class MessagesSpi implements Spi {
@Override
public boolean isPrivate() {
return true;
}
@Override
public String getName() {
return "messages";
}
@Override
public Class<? extends Provider> getProviderClass() {
return MessagesProvider.class;
}
@Override
public Class<? extends ProviderFactory> getProviderFactoryClass() {
return MessagesProviderFactory.class;
}
}

View file

@ -0,0 +1,59 @@
package org.keycloak.services.messages;
import java.io.IOException;
import java.net.URL;
import java.text.MessageFormat;
import java.util.Locale;
import java.util.Properties;
import org.jboss.logging.Logger;
import org.keycloak.models.KeycloakSession;
import org.keycloak.messages.MessagesProvider;
/**
* @author <a href="mailto:leonardo.zanivan@gmail.com">Leonardo Zanivan</a>
*/
public class AdminMessagesProvider implements MessagesProvider {
private static final Logger logger = Logger.getLogger(AdminMessagesProvider.class);
private KeycloakSession session;
private Locale locale;
private Properties messagesBundle;
public AdminMessagesProvider(KeycloakSession session, Locale locale) {
this.session = session;
this.locale = locale;
this.messagesBundle = getMessagesBundle(locale);
}
@Override
public String getMessage(String messageKey, Object... parameters) {
String message = messagesBundle.getProperty(messageKey, messageKey);
return new MessageFormat(message, locale).format(parameters);
}
@Override
public void close() {
}
private Properties getMessagesBundle(Locale locale) {
Properties properties = new Properties();
if (locale == null) {
return properties;
}
URL url = getClass().getClassLoader().getResource(
"theme/base/admin/messages/messages_" + locale.toString() + ".properties");
if (url != null) {
try {
properties.load(url.openStream());
} catch (IOException ex) {
logger.warn("Failed to load messages", ex);
}
}
return properties;
}
}

View file

@ -0,0 +1,37 @@
package org.keycloak.services.messages;
import java.util.Locale;
import org.keycloak.Config;
import org.keycloak.messages.MessagesProvider;
import org.keycloak.messages.MessagesProviderFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
/**
* @author <a href="mailto:leonardo.zanivan@gmail.com">Leonardo Zanivan</a>
*/
public class AdminMessagesProviderFactory implements MessagesProviderFactory {
@Override
public MessagesProvider create(KeycloakSession session) {
return new AdminMessagesProvider(session, Locale.ENGLISH);
}
@Override
public void init(Config.Scope config) {
}
@Override
public void postInit(KeycloakSessionFactory factory) {
}
@Override
public void close() {
}
@Override
public String getId() {
return "admin";
}
}

View file

@ -39,7 +39,6 @@ import java.io.InputStream;
import java.net.URI; import java.net.URI;
import java.net.URL; import java.net.URL;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.Set; import java.util.Set;
import java.util.StringTokenizer; import java.util.StringTokenizer;
@ -75,6 +74,7 @@ public class KeycloakApplication extends Application {
singletons.add(new ServerVersionResource()); singletons.add(new ServerVersionResource());
singletons.add(new RealmsResource()); singletons.add(new RealmsResource());
singletons.add(new AdminRoot()); singletons.add(new AdminRoot());
singletons.add(new ModelExceptionMapper());
classes.add(SkeletonKeyContextResolver.class); classes.add(SkeletonKeyContextResolver.class);
classes.add(QRCodeResource.class); classes.add(QRCodeResource.class);
classes.add(ThemeResource.class); classes.add(ThemeResource.class);

View file

@ -0,0 +1,27 @@
package org.keycloak.services.resources;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
import org.keycloak.messages.MessagesProvider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelException;
import org.keycloak.services.ErrorResponse;
/**
* @author <a href="mailto:leonardo.zanivan@gmail.com">Leonardo Zanivan</a>
*/
@Provider
public class ModelExceptionMapper implements ExceptionMapper<ModelException> {
@Context
private KeycloakSession session;
@Override
public Response toResponse(ModelException ex) {
String message = session.getProvider(MessagesProvider.class, "admin")
.getMessage(ex.getMessage(), ex.getParameters());
return ErrorResponse.error(message, Response.Status.BAD_REQUEST);
}
}

View file

@ -0,0 +1 @@
org.keycloak.services.messages.AdminMessagesProviderFactory

View file

@ -1,4 +1,5 @@
org.keycloak.protocol.LoginProtocolSpi org.keycloak.protocol.LoginProtocolSpi
org.keycloak.protocol.ProtocolMapperSpi org.keycloak.protocol.ProtocolMapperSpi
org.keycloak.exportimport.ClientImportSpi org.keycloak.exportimport.ClientImportSpi
org.keycloak.wellknown.WellKnownSpi org.keycloak.wellknown.WellKnownSpi
org.keycloak.messages.MessagesSpi