diff --git a/ccm-bundle-devel-wildfly-web/src/main/webapp/WEB-INF/systeminformation.properties b/ccm-bundle-devel-wildfly-web/src/main/webapp/WEB-INF/systeminformation.properties new file mode 100644 index 000000000..f34883af8 --- /dev/null +++ b/ccm-bundle-devel-wildfly-web/src/main/webapp/WEB-INF/systeminformation.properties @@ -0,0 +1,3 @@ +version = 7.0.0-SNAPSHOT +appname = LibreCCM +apphomepage = http://www.libreccm.org diff --git a/ccm-core/src/main/java/com/arsdigita/ui/admin/AdminServlet.java b/ccm-core/src/main/java/com/arsdigita/ui/admin/AdminServlet.java index 900351e1e..01e1ededd 100644 --- a/ccm-core/src/main/java/com/arsdigita/ui/admin/AdminServlet.java +++ b/ccm-core/src/main/java/com/arsdigita/ui/admin/AdminServlet.java @@ -18,6 +18,7 @@ */ package com.arsdigita.ui.admin; +import com.arsdigita.bebop.Label; import com.arsdigita.bebop.Page; import com.arsdigita.bebop.PageFactory; import com.arsdigita.bebop.TabbedPane; @@ -31,6 +32,7 @@ import com.arsdigita.xml.Document; import org.apache.shiro.subject.Subject; import org.libreccm.cdi.utils.CdiUtil; +import org.libreccm.configuration.ConfigurationManager; import org.libreccm.security.PermissionChecker; import org.libreccm.web.CcmApplication; @@ -38,6 +40,7 @@ import java.io.IOException; import java.util.HashMap; import java.util.Map; +import javax.inject.Inject; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServletRequest; @@ -61,10 +64,12 @@ import static com.arsdigita.ui.admin.AdminConstants.*; * @author pb */ @WebServlet(urlPatterns = {ADMIN_SERVLET_PATH}) -public class AdminServlet extends BaseApplicationServlet implements - AdminConstants { +public class AdminServlet + extends BaseApplicationServlet + implements AdminConstants { private static final long serialVersionUID = -3912367600768871630L; + /** * Logger instance for debugging */ @@ -82,7 +87,7 @@ public class AdminServlet extends BaseApplicationServlet implements */ @Override public void doInit() throws ServletException { - addPage("/", buildAdminIndexPage()); // index page at address ~/ds + //addPage("/", buildAdminIndexPage()); // index page at address ~/ds // addPage("/index.jsp", buildIndexPage()); // index page at address ~/ds } @@ -107,12 +112,13 @@ public class AdminServlet extends BaseApplicationServlet implements /* Determine access privilege: only logged in users may access */ final CdiUtil cdiUtil = new CdiUtil(); final Subject subject = cdiUtil.findBean(Subject.class); - final PermissionChecker permissionChecker = cdiUtil.findBean(PermissionChecker.class); - + final PermissionChecker permissionChecker = cdiUtil.findBean( + PermissionChecker.class); + if (!subject.isAuthenticated()) { throw new LoginSignal(sreq); } - + /* Determine access privilege: Admin privileges must be granted */ if (!permissionChecker.isPermitted("admin")) { throw new AccessDeniedException("User is not an administrator"); @@ -133,13 +139,18 @@ public class AdminServlet extends BaseApplicationServlet implements pathInfo = pathInfo.substring(0, pathInfo.length() - 1); } - final Page page = pages.get(pathInfo); - if (page == null) { - sresp.sendError(404, "No such page for path " + pathInfo); - } else { - final Document doc = page.buildDocument(sreq, sresp); - Templating.getPresentationManager().servePage(doc, sreq, sresp); - } +// final Page page = pages.get(pathInfo); +// if (page == null) { +// sresp.sendError(404, "No such page for path " + pathInfo); +// } else { +// final Document doc = page.buildDocument(sreq, sresp); +// Templating.getPresentationManager().servePage(doc, sreq, sresp); +// } + final Page page = new Page(); + page.add(new Label("admin")); + + final Document doc = page.buildDocument(sreq, sresp); + Templating.getPresentationManager().servePage(doc, sreq, sresp); } /** diff --git a/ccm-core/src/main/java/com/arsdigita/ui/login/ChangePasswordForm.java b/ccm-core/src/main/java/com/arsdigita/ui/login/ChangePasswordForm.java index 0c948b2c9..fe1772ad8 100644 --- a/ccm-core/src/main/java/com/arsdigita/ui/login/ChangePasswordForm.java +++ b/ccm-core/src/main/java/com/arsdigita/ui/login/ChangePasswordForm.java @@ -66,19 +66,19 @@ import org.libreccm.security.UserManager; * */ public class ChangePasswordForm extends Form - implements FormProcessListener, - FormValidationListener { + implements FormProcessListener, + FormValidationListener { private static final Logger s_log = Logger.getLogger( - ChangePasswordForm.class.getName()); + ChangePasswordForm.class.getName()); final static String CHANGE_PASSWORD_FORM_NAME = "change-password"; final static String OLD_PASSWORD_PARAM_NAME = "old-password"; final static String NEW_PASSWORD_PARAM_NAME = "new-password"; final static String CONFIRM_PASSWORD_PARAM_NAME = "confirm-password"; final static String RETURN_URL_PARAM_NAME - = LoginHelper.RETURN_URL_PARAM_NAME; + = LoginHelper.RETURN_URL_PARAM_NAME; private final UserAuthenticationListener m_listener - = new UserAuthenticationListener(); + = new UserAuthenticationListener(); private Hidden m_returnURL; // private Hidden m_recovery; private Label m_oldPasswordLabel; @@ -128,22 +128,31 @@ public class ChangePasswordForm extends Form final KernelConfig kernelConfig = KernelConfig.getConfig(); final User user = shiro.getUser(); - - final Label greeting = new Label(LoginHelper.getMessage( + + final Label greeting; + if (user == null) { + greeting = new Label(LoginHelper.getMessage( "login.changePasswordForm.greeting", - new Object[]{String.format("%s %s", + new Object[]{String.format("%s %s", + "", + "")})); + } else { + greeting = new Label(LoginHelper.getMessage( + "login.changePasswordForm.greeting", + new Object[]{String.format("%s %s", user.getGivenName(), user.getFamilyName())})); + } greeting.setFontWeight(Label.BOLD); greeting.setClassAttr("greeting"); add(greeting); add(new Label(LoginHelper.getMessage( - "login.changePasswortForm.introText"))); + "login.changePasswortForm.introText"))); // old password m_oldPasswordLabel = new Label(LoginHelper.getMessage( - "login.changePasswordForm.oldPasswordLabel")); + "login.changePasswordForm.oldPasswordLabel")); add(m_oldPasswordLabel); m_oldPassword = new Password(OLD_PASSWORD_PARAM_NAME); // don't use NotNullValidationListener because @@ -153,14 +162,14 @@ public class ChangePasswordForm extends Form // new password Object[] params = new Object[]{PasswordValidationListener.MIN_LENGTH}; add(new Label(LoginHelper.getMessage( - "login.changePasswordForm.newPasswordLabel", params))); + "login.changePasswordForm.newPasswordLabel", params))); m_newPassword = new Password(NEW_PASSWORD_PARAM_NAME); m_newPassword.addValidationListener(new PasswordValidationListener()); add(m_newPassword); // confirm new password add(new Label(LoginHelper.getMessage( - "login.changePasswordForm.confirmPasswordLabel"))); + "login.changePasswordForm.confirmPasswordLabel"))); m_confirmPassword = new Password(CONFIRM_PASSWORD_PARAM_NAME); // don't use PasswordValidationListener to avoid duplicate errors m_confirmPassword.addValidationListener(new NotNullValidationListener()); @@ -173,7 +182,7 @@ public class ChangePasswordForm extends Form @Override public void validate(final FormSectionEvent event) - throws FormProcessException { + throws FormProcessException { PageState state = event.getPageState(); FormData data = event.getFormData(); try { @@ -181,8 +190,8 @@ public class ChangePasswordForm extends Form if (!m_listener.isLoggedIn(state)) { // this error should never appear data.addError(LoginHelper.localize( - "login.changePasswordForm.noUserError", - state.getRequest())); + "login.changePasswordForm.noUserError", + state.getRequest())); return; } // User user = m_listener.getUser(state); @@ -191,18 +200,18 @@ public class ChangePasswordForm extends Form String oldPassword = (String) m_oldPassword.getValue(state); String newPassword = (String) m_newPassword.getValue(state); String confirmPassword = (String) m_confirmPassword.getValue(state); - + //check oldPassword final CdiUtil cdiUtil = new CdiUtil(); final Shiro shiro = cdiUtil.findBean(Shiro.class); final UserManager userManager = cdiUtil.findBean(UserManager.class); - + final User user = shiro.getUser(); if (!userManager.verifyPassword(user, oldPassword)) { data.addError(OLD_PASSWORD_PARAM_NAME, LoginHelper.getMessage( "login.changePasswordForm.badPasswordError")); } - + // check new password if (newPassword.equals(oldPassword)) { data.addError(NEW_PASSWORD_PARAM_NAME, LoginHelper.localize( @@ -228,7 +237,7 @@ public class ChangePasswordForm extends Form @Override public void process(final FormSectionEvent event) - throws FormProcessException { + throws FormProcessException { PageState state = event.getPageState(); FormData data = event.getFormData(); @@ -236,19 +245,19 @@ public class ChangePasswordForm extends Form if (!m_listener.isLoggedIn(state)) { // this error should never appear (checked in validate) data.addError(LoginHelper.localize( - "login.changePasswordForm.noUserError", - state.getRequest())); + "login.changePasswordForm.noUserError", + state.getRequest())); return; } - + final CdiUtil cdiUtil = new CdiUtil(); final UserManager userManager = cdiUtil.findBean(UserManager.class); - final Shiro shiro = cdiUtil.findBean(Shiro.class); + final Shiro shiro = cdiUtil.findBean(Shiro.class); final User user = shiro.getUser(); - + final String newPassword = (String) m_newPassword.getValue(state); userManager.updatePassword(user, newPassword); - + final HttpServletRequest req = state.getRequest(); final String path = UI.getWorkspaceURL(req); diff --git a/ccm-core/src/main/java/com/arsdigita/ui/login/LoginServlet.java b/ccm-core/src/main/java/com/arsdigita/ui/login/LoginServlet.java index e04a5dd51..493831c01 100644 --- a/ccm-core/src/main/java/com/arsdigita/ui/login/LoginServlet.java +++ b/ccm-core/src/main/java/com/arsdigita/ui/login/LoginServlet.java @@ -35,7 +35,9 @@ import com.arsdigita.web.ReturnSignal; import com.arsdigita.web.URL; import org.apache.log4j.Logger; +import org.libreccm.configuration.ConfigurationManager; +import javax.inject.Inject; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServletRequest; @@ -125,6 +127,9 @@ public class LoginServlet extends BebopApplicationServlet { public static final String APPLICATION_NAME = "login"; + @Inject + private ConfigurationManager confManager; + /** * User extension point used to create the pages to server and setup a * URL_MSG - page mapping. @@ -133,6 +138,9 @@ public class LoginServlet extends BebopApplicationServlet { */ @Override public void doInit() throws ServletException { + final SecurityConfig securityConfig = confManager.findConfiguration( + SecurityConfig.class); + // Allow world caching for pages without authentication, // ie, /register, /register/explain-persistent-cookies, // /register/login-expired, /register/recover-password @@ -145,8 +153,7 @@ public class LoginServlet extends BebopApplicationServlet { put("/", buildSimplePage( "login.userRegistrationForm.title", - new UserLoginForm(SecurityConfig.getConfig() - .isAutoRegistrationEnabled()), + new UserLoginForm(securityConfig.isAutoRegistrationEnabled()), "login")); disableClientCaching("/"); diff --git a/ccm-core/src/main/java/com/arsdigita/ui/login/UserLoginForm.java b/ccm-core/src/main/java/com/arsdigita/ui/login/UserLoginForm.java index 9f6725b73..feb7a30ee 100644 --- a/ccm-core/src/main/java/com/arsdigita/ui/login/UserLoginForm.java +++ b/ccm-core/src/main/java/com/arsdigita/ui/login/UserLoginForm.java @@ -100,7 +100,7 @@ public class UserLoginForm extends Form implements LoginConstants, private TextField m_loginName; private final Password m_password; private final boolean m_autoRegistrationOn; - private final SecurityConfig securityConfig = SecurityConfig.getConfig(); + private final SecurityConfig securityConfig;// = SecurityConfig.getConfig(); /** * Default constructor delegates to a constructor which creates a LoginForm @@ -128,6 +128,8 @@ public class UserLoginForm extends Form implements LoginConstants, final boolean autoRegistrationOn) { super(FORM_NAME, panel); + securityConfig = SecurityConfig.getConfig(); + setMethod(Form.POST); addInitListener(this); addValidationListener(this); diff --git a/ccm-core/src/main/java/com/arsdigita/web/BaseApplicationServlet.java b/ccm-core/src/main/java/com/arsdigita/web/BaseApplicationServlet.java index a2eb2fad5..291a2198f 100644 --- a/ccm-core/src/main/java/com/arsdigita/web/BaseApplicationServlet.java +++ b/ccm-core/src/main/java/com/arsdigita/web/BaseApplicationServlet.java @@ -25,6 +25,7 @@ import org.libreccm.web.CcmApplication; import java.io.IOException; +import javax.inject.Inject; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -86,6 +87,12 @@ public abstract class BaseApplicationServlet extends BaseServlet { */ public static final String APPLICATION_ID_PARAMETER = "app-id"; + /** + * {@link ApplicationRepository} provided by CDI. + */ + @Inject + private ApplicationRepository appRepository; + /** *

* Augments the context of the request and delegates to {@link @@ -174,10 +181,7 @@ public abstract class BaseApplicationServlet extends BaseServlet { + "database"); } - final CdiUtil cdiUtil = new CdiUtil(); - final ApplicationRepository appRepo = cdiUtil.findBean( - ApplicationRepository.class); - return appRepo.findById(appId); + return appRepository.findById(appId); } /** diff --git a/ccm-core/src/main/java/com/arsdigita/web/BaseServlet.java b/ccm-core/src/main/java/com/arsdigita/web/BaseServlet.java index 0790ca923..996133efe 100644 --- a/ccm-core/src/main/java/com/arsdigita/web/BaseServlet.java +++ b/ccm-core/src/main/java/com/arsdigita/web/BaseServlet.java @@ -86,11 +86,23 @@ public abstract class BaseServlet extends HttpServlet { private void internalService(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { - //This method was present in the old implemention and was responsible - //for managing the application managed transactions. Because we now use - //container managed transactions we may don't need this method anymore. - doService(request, response); + Web.init(request, getServletContext()); + Web.getWebContext().setRequestURL(getRequestURL(request)); + + try { + doService(request, response); + } catch (RedirectSignal signal) { + redirect(response, signal); + } catch (ServletException ex) { + final RedirectSignal signal = findRedirectSignal(ex); + + if (signal == null) { + throw ex; + } else { + redirect(response, signal); + } + } } /** @@ -107,15 +119,15 @@ public abstract class BaseServlet extends HttpServlet { * @throws java.io.IOException */ protected abstract void doService(final HttpServletRequest request, - final HttpServletResponse response) + final HttpServletResponse response) throws ServletException, IOException; /** *

* Processes HTTP GET requests.

* - * @param request - * @param response + * @param request + * @param response * * @throws javax.servlet.ServletException * @throws java.io.IOException @@ -130,30 +142,66 @@ public abstract class BaseServlet extends HttpServlet { request.getPathInfo(), getServletConfig().getServletName(), getClass().getName()); - - internalService(request, response); - } - - /** - *

Processes HTTP POST requests.

- * - * @param request - * @param response - * @throws javax.servlet.ServletException - * @throws java.io.IOException - * - * @see javax.servlet.http.HttpServlet#doPost(HttpServletRequest,HttpServletResponse) - */ - @Override - protected final void doPost(final HttpServletRequest request, - final HttpServletResponse response) - throws ServletException, IOException { - LOGGER.info("Serving POST request path %s with servlet %s (class: %s)", - request.getPathInfo(), - getServletConfig().getServletName(), - getClass().getName()); - + internalService(request, response); } + /** + *

+ * Processes HTTP POST requests.

+ * + * @param request + * @param response + * + * @throws javax.servlet.ServletException + * @throws java.io.IOException + * + * @see + * javax.servlet.http.HttpServlet#doPost(HttpServletRequest,HttpServletResponse) + */ + @Override + protected final void doPost(final HttpServletRequest request, + final HttpServletResponse response) + throws ServletException, IOException { + LOGGER.info("Serving POST request path %s with servlet %s (class: %s)", + request.getPathInfo(), + getServletConfig().getServletName(), + getClass().getName()); + + internalService(request, response); + } + + private URL getRequestURL(final HttpServletRequest request) { + URL url = (URL) request.getAttribute(REQUEST_URL_ATTRIBUTE); + + if (url == null) { + url = new URL(request); + } + + return url; + } + + private RedirectSignal findRedirectSignal(final ServletException ex) { + Throwable root = ex.getRootCause(); + + while (root instanceof ServletException) { + root = ((ServletException) root).getRootCause(); + } + + if (root instanceof RedirectSignal) { + return (RedirectSignal) root; + } else { + return null; + } + } + + private void redirect(final HttpServletResponse response, + final RedirectSignal redirectSignal) + throws IOException { + final String url = response.encodeRedirectURL(redirectSignal + .getDestinationURL()); + + response.sendRedirect(url); + } + } diff --git a/ccm-core/src/main/java/com/arsdigita/web/CCMDispatcherServlet.java b/ccm-core/src/main/java/com/arsdigita/web/CCMDispatcherServlet.java index 7bc23ad1a..52a88b6a5 100644 --- a/ccm-core/src/main/java/com/arsdigita/web/CCMDispatcherServlet.java +++ b/ccm-core/src/main/java/com/arsdigita/web/CCMDispatcherServlet.java @@ -40,6 +40,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.shiro.subject.Subject; +import org.libreccm.configuration.ConfigurationManager; import org.libreccm.web.ApplicationManager; import org.libreccm.web.ApplicationType; diff --git a/ccm-core/src/main/java/com/arsdigita/web/Web.java b/ccm-core/src/main/java/com/arsdigita/web/Web.java index 7a54453ea..80777c211 100644 --- a/ccm-core/src/main/java/com/arsdigita/web/Web.java +++ b/ccm-core/src/main/java/com/arsdigita/web/Web.java @@ -47,11 +47,10 @@ public class Web { */ private static final Logger s_log = Logger.getLogger(Web.class); - private static final WebConfig s_config = WebConfig.getConfig(); - +// private static final WebConfig s_config = WebConfig.getConfig(); private static final ThreadLocal s_request = new InternalRequestLocal(); private static final ThreadLocal s_servletContext - = new InternalRequestLocal(); + = new InternalRequestLocal(); private static final ThreadLocal s_userContext = new InternalRequestLocal(); private static ThreadLocal s_context; @@ -93,7 +92,8 @@ public class Web { * @return A WebConfig configuration record; it cannot be null */ public static WebConfig getConfig() { - return s_config; +// return s_config; + return WebConfig.getConfig(); } /** @@ -127,26 +127,25 @@ public class Web { return (ServletContext) s_servletContext.get(); } - /** - * Gets the webapp context path portion of the WEB application where this - * CCM instance is executed. (I.e. where the WEB-INF directory is located - * in the servlet container webapps directory, known as ServletContext in - * the Servlet API) + /** + * Gets the webapp context path portion of the WEB application where this + * CCM instance is executed. (I.e. where the WEB-INF directory is located in + * the servlet container webapps directory, known as ServletContext in the + * Servlet API) * - * @return web context path portion as a String, may be used to construct - * a URL (NOT the RealPath!). The ROOT context returns an empty + * @return web context path portion as a String, may be used to construct a + * URL (NOT the RealPath!). The ROOT context returns an empty * String(""). */ public static String getWebappContextPath() { - return (String) s_contextPath; + return (String) s_contextPath; } /** - * Sets the webapp context path portion of the WEB application where this - * CCM instance is executed. (I.e. where the WEB-INF directory is located - * in the servlet container webapps directory, known as ServletContext in - * the Servlet API) - * Meant to be executed by CCMDispatcherServlet only. + * Sets the webapp context path portion of the WEB application where this + * CCM instance is executed. (I.e. where the WEB-INF directory is located in + * the servlet container webapps directory, known as ServletContext in the + * Servlet API) Meant to be executed by CCMDispatcherServlet only. * * @param contextPath */ @@ -154,7 +153,6 @@ public class Web { s_contextPath = contextPath; } - /** * Processes an URL String trying to identify a corresponding recource which * is mapped to the given path String. The method ensures that the resource @@ -249,10 +247,8 @@ public class Web { s_log.debug("Got URL " + url + " for " + path); } return url; // Return adjusted resourcePath url - } else { - if (s_log.isDebugEnabled()) { - s_log.debug("No URL present for " + path); - } + } else if (s_log.isDebugEnabled()) { + s_log.debug("No URL present for " + path); } } catch (MalformedURLException exc) { if (s_log.isDebugEnabled()) { @@ -282,7 +278,7 @@ public class Web { if (!ctxPath.startsWith("/")) { ctxPath = "/" + ctxPath; } - // No trailing slash allowed by servlet API! + // No trailing slash allowed by servlet API! // if (!ctxPath.endsWith("/")) { // ctxPath = ctxPath + "/"; // } @@ -299,10 +295,8 @@ public class Web { s_log.debug("Got URL " + url + " for " + path); } return url; // Return adjusted resourcePath url - } else { - if (s_log.isDebugEnabled()) { - s_log.debug("No URL present for " + path); - } + } else if (s_log.isDebugEnabled()) { + s_log.debug("No URL present for " + path); } } catch (MalformedURLException ex) { if (s_log.isDebugEnabled()) { diff --git a/ccm-core/src/main/java/org/libreccm/l10n/LocalizedString.java b/ccm-core/src/main/java/org/libreccm/l10n/LocalizedString.java index fdbe9aaaf..be8405ce6 100644 --- a/ccm-core/src/main/java/org/libreccm/l10n/LocalizedString.java +++ b/ccm-core/src/main/java/org/libreccm/l10n/LocalizedString.java @@ -31,6 +31,7 @@ import java.util.Set; import javax.persistence.Column; import javax.persistence.ElementCollection; import javax.persistence.Embeddable; +import javax.persistence.FetchType; import javax.persistence.Lob; import javax.persistence.MapKeyColumn; import javax.xml.bind.annotation.XmlElement; @@ -54,7 +55,7 @@ public class LocalizedString implements Serializable { /** * The localised values of the string. */ - @ElementCollection + @ElementCollection(fetch = FetchType.EAGER) @MapKeyColumn(name = "LOCALE") @Column(name = "LOCALIZED_VALUE") @Lob