From bdfc9b8efc0f32d01082675ffd45302e543d44fa Mon Sep 17 00:00:00 2001 From: Stian Thorgersen Date: Fri, 11 Mar 2016 12:08:28 +0100 Subject: [PATCH] KEYCLOAK-2637 ModelExceptionMapper uses AdminMessagesProvider which loads messages outside of themes --- .../keycloak/messages/MessagesProvider.java | 29 ------- .../messages/MessagesProviderFactory.java | 27 ------- .../org/keycloak/messages/MessagesSpi.java | 49 ----------- .../messages/AdminMessagesProvider.java | 81 ------------------- .../AdminMessagesProviderFactory.java | 54 ------------- .../resources/KeycloakApplication.java | 1 - .../resources/ModelExceptionMapper.java | 49 ----------- .../resources/admin/AdminConsole.java | 16 +--- .../services/resources/admin/AdminRoot.java | 32 ++++++++ .../resources/admin/UsersResource.java | 13 +++ ....keycloak.messages.MessagesProviderFactory | 18 ----- .../services/org.keycloak.provider.Spi | 1 - .../admin/resources/js/controllers/users.js | 4 +- 13 files changed, 50 insertions(+), 324 deletions(-) delete mode 100644 services/src/main/java/org/keycloak/messages/MessagesProvider.java delete mode 100644 services/src/main/java/org/keycloak/messages/MessagesProviderFactory.java delete mode 100644 services/src/main/java/org/keycloak/messages/MessagesSpi.java delete mode 100644 services/src/main/java/org/keycloak/services/messages/AdminMessagesProvider.java delete mode 100644 services/src/main/java/org/keycloak/services/messages/AdminMessagesProviderFactory.java delete mode 100644 services/src/main/java/org/keycloak/services/resources/ModelExceptionMapper.java delete mode 100644 services/src/main/resources/META-INF/services/org.keycloak.messages.MessagesProviderFactory diff --git a/services/src/main/java/org/keycloak/messages/MessagesProvider.java b/services/src/main/java/org/keycloak/messages/MessagesProvider.java deleted file mode 100644 index 4243e1e515..0000000000 --- a/services/src/main/java/org/keycloak/messages/MessagesProvider.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -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 deleted file mode 100644 index 10d2a2e1f0..0000000000 --- a/services/src/main/java/org/keycloak/messages/MessagesProviderFactory.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -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 deleted file mode 100644 index f9d58ff5e4..0000000000 --- a/services/src/main/java/org/keycloak/messages/MessagesSpi.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -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 isInternal() { - return true; - } - - @Override - public String getName() { - return "messages"; - } - - @Override - public Class getProviderClass() { - return MessagesProvider.class; - } - - @Override - public Class 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 deleted file mode 100644 index a37c850858..0000000000 --- a/services/src/main/java/org/keycloak/services/messages/AdminMessagesProvider.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -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.keycloak.models.KeycloakSession; -import org.keycloak.messages.MessagesProvider; -import org.keycloak.services.ServicesLogger; - -/** - * @author Leonardo Zanivan - */ -public class AdminMessagesProvider implements MessagesProvider { - - private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; - - 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); - - try { - return new MessageFormat(message, locale).format(parameters); - } catch (Exception e) { - logger.failedToFormatMessage(e.getMessage()); - return message; - } - } - - @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.failedToloadMessages(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 deleted file mode 100644 index 205d437b8f..0000000000 --- a/services/src/main/java/org/keycloak/services/messages/AdminMessagesProviderFactory.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -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 5232588cfe..4de67ed3ad 100644 --- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java +++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java @@ -82,7 +82,6 @@ public class KeycloakApplication extends Application { singletons.add(new ServerVersionResource()); singletons.add(new RealmsResource()); singletons.add(new AdminRoot()); - singletons.add(new ModelExceptionMapper()); classes.add(QRCodeResource.class); classes.add(ThemeResource.class); classes.add(JsResource.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 deleted file mode 100644 index e71fa670d2..0000000000 --- a/services/src/main/java/org/keycloak/services/resources/ModelExceptionMapper.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -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; -import org.keycloak.services.ServicesLogger; - -/** - * @author Leonardo Zanivan - */ -@Provider -public class ModelExceptionMapper implements ExceptionMapper { - - private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER; - - @Context - private KeycloakSession session; - - @Override - public Response toResponse(ModelException ex) { - String message = session.getProvider(MessagesProvider.class, "admin") - .getMessage(ex.getMessage(), ex.getParameters()); - - logger.error(message, ex); - return ErrorResponse.error(message, Response.Status.BAD_REQUEST); - } -} diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java b/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java index 044c7d506c..ba7c83af78 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java @@ -27,7 +27,6 @@ import org.keycloak.theme.BrowserSecurityHeaderSetup; import org.keycloak.theme.FreeMarkerException; import org.keycloak.theme.FreeMarkerUtil; import org.keycloak.theme.Theme; -import org.keycloak.theme.ThemeProvider; import org.keycloak.models.AdminRoles; import org.keycloak.models.ClientModel; import org.keycloak.models.Constants; @@ -281,7 +280,7 @@ public class AdminConsole { if (!uriInfo.getRequestUri().getPath().endsWith("/")) { return Response.status(302).location(uriInfo.getRequestUriBuilder().path("/").build()).build(); } else { - Theme theme = getTheme(); + Theme theme = AdminRoot.getTheme(session, realm); Map map = new HashMap<>(); @@ -303,11 +302,6 @@ public class AdminConsole { } } - private Theme getTheme() throws IOException { - ThemeProvider themeProvider = session.getProvider(ThemeProvider.class, "extending"); - return themeProvider.getTheme(realm.getAdminTheme(), Theme.Type.ADMIN); - } - @GET @Path("{indexhtml: index.html}") // this expression is a hack to get around jaxdoclet generation bug. Doesn't like index.html public Response getIndexHtmlRedirect() { @@ -318,11 +312,7 @@ public class AdminConsole { @Path("messages.json") @Produces(MediaType.APPLICATION_JSON) public Properties getMessages(@QueryParam("lang") String lang) { - try { - Locale locale = lang != null ? Locale.forLanguageTag(lang) : Locale.ENGLISH; - return getTheme().getMessages("admin-messages", locale); - } catch (IOException e) { - throw new WebApplicationException("Failed to load message bundle", e); - } + return AdminRoot.getMessages(session, realm, "admin-messages", lang); } + } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AdminRoot.java b/services/src/main/java/org/keycloak/services/resources/admin/AdminRoot.java index 10b636b2db..0649b25fff 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/AdminRoot.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/AdminRoot.java @@ -38,6 +38,8 @@ import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.managers.RealmManager; import org.keycloak.services.resources.Cors; import org.keycloak.services.resources.admin.info.ServerInfoAdminResource; +import org.keycloak.theme.Theme; +import org.keycloak.theme.ThemeProvider; import javax.ws.rs.GET; import javax.ws.rs.Path; @@ -47,6 +49,9 @@ import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriInfo; +import java.io.IOException; +import java.util.Locale; +import java.util.Properties; /** * Root resource for admin console and admin REST API @@ -265,4 +270,31 @@ public class AdminRoot { } } + public static Theme getTheme(KeycloakSession session, RealmModel realm) throws IOException { + ThemeProvider themeProvider = session.getProvider(ThemeProvider.class, "extending"); + return themeProvider.getTheme(realm.getAdminTheme(), Theme.Type.ADMIN); + } + + public static Properties getMessages(KeycloakSession session, RealmModel realm, String lang) { + try { + Theme theme = getTheme(session, realm); + Locale locale = lang != null ? Locale.forLanguageTag(lang) : Locale.ENGLISH; + return theme.getMessages(locale); + } catch (IOException e) { + logger.error("Failed to load messages from theme", e); + return new Properties(); + } + } + + public static Properties getMessages(KeycloakSession session, RealmModel realm, String bundle, String lang) { + try { + Theme theme = getTheme(session, realm); + Locale locale = lang != null ? Locale.forLanguageTag(lang) : Locale.ENGLISH; + return theme.getMessages(bundle, locale); + } catch (IOException e) { + logger.error("Failed to load messages from theme", e); + return new Properties(); + } + } + } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java index dea9daf87c..9b035356ed 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java @@ -36,6 +36,7 @@ import org.keycloak.models.GroupModel; import org.keycloak.models.IdentityProviderModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.ModelDuplicateException; +import org.keycloak.models.ModelException; import org.keycloak.models.ModelReadOnlyException; import org.keycloak.models.RealmModel; import org.keycloak.models.UserConsentModel; @@ -54,6 +55,7 @@ import org.keycloak.representations.idm.GroupRepresentation; import org.keycloak.representations.idm.UserConsentRepresentation; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.idm.UserSessionRepresentation; +import org.keycloak.services.ErrorResponseException; import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.managers.ClientSessionCode; import org.keycloak.services.managers.UserManager; @@ -74,11 +76,14 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; 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 javax.ws.rs.WebApplicationException; +import java.io.IOException; import java.net.URI; +import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -86,6 +91,7 @@ import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Properties; import java.util.Set; import java.util.concurrent.TimeUnit; import org.keycloak.models.UsernameLoginFailureModel; @@ -94,6 +100,9 @@ import org.keycloak.services.managers.UserSessionManager; import org.keycloak.services.resources.AccountService; import org.keycloak.common.util.Time; import org.keycloak.services.validation.Validation; +import org.keycloak.theme.Theme; +import org.keycloak.theme.Theme.Type; +import org.keycloak.theme.ThemeProvider; /** * Base resource for managing users @@ -719,6 +728,10 @@ public class UsersResource { throw new BadRequestException("Resetting to N old passwords is not allowed."); } catch (ModelReadOnlyException mre) { throw new BadRequestException("Can't reset password as account is read only"); + } catch (ModelException e) { + Properties messages = AdminRoot.getMessages(session, realm, auth.getAuth().getToken().getLocale()); + throw new ErrorResponseException(e.getMessage(), MessageFormat.format(messages.getProperty(e.getMessage(), e.getMessage()), e.getParameters()), + Status.BAD_REQUEST); } if (pass.isTemporary() != null && pass.isTemporary()) user.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD); 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 deleted file mode 100644 index 617bd16097..0000000000 --- a/services/src/main/resources/META-INF/services/org.keycloak.messages.MessagesProviderFactory +++ /dev/null @@ -1,18 +0,0 @@ -# -# Copyright 2016 Red Hat, Inc. and/or its affiliates -# and other contributors as indicated by the @author tags. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -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 7143e6c20b..50bb346ff5 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 @@ -17,5 +17,4 @@ org.keycloak.exportimport.ClientDescriptionConverterSpi org.keycloak.wellknown.WellKnownSpi -org.keycloak.messages.MessagesSpi org.keycloak.services.clientregistration.ClientRegistrationSpi diff --git a/themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js b/themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js index 0fc333aaf8..eb896faeab 100755 --- a/themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js +++ b/themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js @@ -506,8 +506,8 @@ module.controller('UserCredentialsCtrl', function($scope, realm, user, RequiredA $scope.password = null; $scope.confirmPassword = null; }, function(response) { - if (response.data && response.data.errorMessage) { - Notifications.error(response.data.errorMessage); + if (response.data && response.data['error_description']) { + Notifications.error(response.data['error_description']); } else { Notifications.error("Failed to reset user password"); }