diff --git a/forms/common-themes/src/main/resources/theme/base/admin/messages/messages_en.properties b/forms/common-themes/src/main/resources/theme/base/admin/messages/messages_en.properties
new file mode 100644
index 0000000000..ab297dddc5
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/admin/messages/messages_en.properties
@@ -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.
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
index bf5662bbff..9be9349c1a 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
@@ -308,8 +308,12 @@ module.controller('UserCredentialsCtrl', function($scope, realm, user, User, Use
Notifications.success("The password has been reset");
$scope.password = null;
$scope.confirmPassword = null;
- }, function() {
- Notifications.error("Failed to reset user password");
+ }, function(response) {
+ if (response.data && response.data.errorMessage) {
+ Notifications.error(response.data.errorMessage);
+ } else {
+ Notifications.error("Failed to reset user password");
+ }
});
}, function() {
$scope.password = null;
diff --git a/services/src/main/java/org/keycloak/messages/MessagesProvider.java b/services/src/main/java/org/keycloak/messages/MessagesProvider.java
new file mode 100644
index 0000000000..ddb72dd4e2
--- /dev/null
+++ b/services/src/main/java/org/keycloak/messages/MessagesProvider.java
@@ -0,0 +1,12 @@
+package org.keycloak.messages;
+
+import org.keycloak.provider.Provider;
+
+/**
+ * @author Leonardo Zanivan
+ */
+public interface MessagesProvider extends Provider {
+
+ String getMessage(String messageKey, Object... parameters);
+
+}
diff --git a/services/src/main/java/org/keycloak/messages/MessagesProviderFactory.java b/services/src/main/java/org/keycloak/messages/MessagesProviderFactory.java
new file mode 100644
index 0000000000..92c0df8354
--- /dev/null
+++ b/services/src/main/java/org/keycloak/messages/MessagesProviderFactory.java
@@ -0,0 +1,10 @@
+package org.keycloak.messages;
+
+import org.keycloak.provider.ProviderFactory;
+
+/**
+ * @author Leonardo Zanivan
+ */
+public interface MessagesProviderFactory extends ProviderFactory {
+
+}
diff --git a/services/src/main/java/org/keycloak/messages/MessagesSpi.java b/services/src/main/java/org/keycloak/messages/MessagesSpi.java
new file mode 100644
index 0000000000..6e820068c8
--- /dev/null
+++ b/services/src/main/java/org/keycloak/messages/MessagesSpi.java
@@ -0,0 +1,32 @@
+package org.keycloak.messages;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+/**
+ * @author Leonardo Zanivan
+ */
+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;
+ }
+
+}
diff --git a/services/src/main/java/org/keycloak/services/messages/AdminMessagesProvider.java b/services/src/main/java/org/keycloak/services/messages/AdminMessagesProvider.java
new file mode 100644
index 0000000000..1da7bfb9d8
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/messages/AdminMessagesProvider.java
@@ -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 Leonardo Zanivan
+ */
+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;
+ }
+
+}
diff --git a/services/src/main/java/org/keycloak/services/messages/AdminMessagesProviderFactory.java b/services/src/main/java/org/keycloak/services/messages/AdminMessagesProviderFactory.java
new file mode 100644
index 0000000000..7fda4fd97d
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/messages/AdminMessagesProviderFactory.java
@@ -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 Leonardo Zanivan
+ */
+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";
+ }
+
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
index b0f02add10..e4c821cd30 100755
--- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
+++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
@@ -39,7 +39,6 @@ import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.util.HashSet;
-import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
@@ -75,6 +74,7 @@ public class KeycloakApplication extends Application {
singletons.add(new ServerVersionResource());
singletons.add(new RealmsResource());
singletons.add(new AdminRoot());
+ singletons.add(new ModelExceptionMapper());
classes.add(SkeletonKeyContextResolver.class);
classes.add(QRCodeResource.class);
classes.add(ThemeResource.class);
diff --git a/services/src/main/java/org/keycloak/services/resources/ModelExceptionMapper.java b/services/src/main/java/org/keycloak/services/resources/ModelExceptionMapper.java
new file mode 100644
index 0000000000..c5cc88eb8e
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/ModelExceptionMapper.java
@@ -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 Leonardo Zanivan
+ */
+@Provider
+public class ModelExceptionMapper implements ExceptionMapper {
+
+ @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);
+ }
+}
diff --git a/services/src/main/resources/META-INF/services/org.keycloak.messages.MessagesProviderFactory b/services/src/main/resources/META-INF/services/org.keycloak.messages.MessagesProviderFactory
new file mode 100644
index 0000000000..341e768bbb
--- /dev/null
+++ b/services/src/main/resources/META-INF/services/org.keycloak.messages.MessagesProviderFactory
@@ -0,0 +1 @@
+org.keycloak.services.messages.AdminMessagesProviderFactory
\ No newline at end of file
diff --git a/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
index e1c0b91282..a9b5a5438c 100755
--- a/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
+++ b/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
@@ -1,4 +1,5 @@
org.keycloak.protocol.LoginProtocolSpi
org.keycloak.protocol.ProtocolMapperSpi
org.keycloak.exportimport.ClientImportSpi
-org.keycloak.wellknown.WellKnownSpi
\ No newline at end of file
+org.keycloak.wellknown.WellKnownSpi
+org.keycloak.messages.MessagesSpi
\ No newline at end of file