Integrating the turbine-flux library into the archetype builder
git-svn-id: https://svn.apache.org/repos/asf/turbine/maven/archetypes/trunk/turbine-webapp-4.0@1821946 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/main/resources/archetype-resources/pom.xml b/src/main/resources/archetype-resources/pom.xml
index a6b95dc..53efaad 100644
--- a/src/main/resources/archetype-resources/pom.xml
+++ b/src/main/resources/archetype-resources/pom.xml
@@ -62,7 +62,7 @@
</testResource>
</testResources>
<plugins>
- <!-- test will not fail in turbien 4.0.1 -->
+ <!-- test will not fail in turbine 4.0.1 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
diff --git a/src/main/resources/archetype-resources/src/main/java/flux/modules/actions/FluxAction.java b/src/main/resources/archetype-resources/src/main/java/flux/modules/actions/FluxAction.java
new file mode 100644
index 0000000..3ba889e
--- /dev/null
+++ b/src/main/resources/archetype-resources/src/main/java/flux/modules/actions/FluxAction.java
@@ -0,0 +1,97 @@
+package ${package}.flux.modules.actions;
+
+/*
+ * Copyright 2001-2017 The Apache Software Foundation.
+ *
+ * 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.
+ */
+
+import org.apache.fulcrum.localization.LocalizationService;
+import org.apache.fulcrum.security.model.turbine.TurbineAccessControlList;
+import org.apache.turbine.Turbine;
+import org.apache.turbine.annotation.TurbineService;
+import org.apache.turbine.modules.actions.VelocitySecureAction;
+import org.apache.turbine.om.security.User;
+import org.apache.turbine.pipeline.PipelineData;
+import org.apache.turbine.util.RunData;
+import org.apache.velocity.context.Context;
+
+/**
+ * Velocity Secure action.
+ *
+ * Always performs a Security Check that you've defined before executing the
+ * doPerform().
+ */
+public class FluxAction extends VelocitySecureAction {
+
+ @TurbineService
+ private LocalizationService localizationService;
+
+ /**
+ * This currently only checks to make sure that user is allowed to view the
+ * storage area. If you create an action that requires more security then
+ * override this method.
+ *
+ * @param data
+ * Turbine information.
+ * @return True if the user is authorized to access the screen.
+ * @exception Exception,
+ * a generic exception.
+ */
+ /**
+ * This checks if the user has the role mapped in the flux.properties file for
+ * flux.admin.role which you should define
+ */
+ @Override
+ protected boolean isAuthorized(PipelineData data) throws Exception {
+ boolean isAuthorized = false;
+
+ /*
+ * Grab the Flux Admin role listed in the Flux.properties file that is included
+ * in the the standard TurbineResources.properties file.
+ */
+ String fluxAdminRole = Turbine.getConfiguration().getString("flux.admin.role");
+
+ // Get the Turbine ACL implementation
+ TurbineAccessControlList acl = getRunData(data).getACL();
+
+ if (acl == null || !(acl.hasRole(fluxAdminRole))) {
+ String msg = localizationService.getString(localizationService.getDefaultBundleName(),
+ localizationService.getLocale(((RunData) data).getRequest()), "no_permission");
+
+ getRunData(data).setMessage(msg);
+
+ getRunData(data).setScreenTemplate("Login.vm");
+ isAuthorized = false;
+ } else if (acl.hasRole(fluxAdminRole)) {
+ isAuthorized = true;
+ }
+
+ return isAuthorized;
+ }
+
+ /**
+ * Implement this to add information to the context.
+ *
+ * @param data
+ * Turbine information.
+ * @param context
+ * Context for web pages.
+ * @exception Exception,
+ * a generic exception.
+ */
+ public void doPerform(PipelineData data, Context context) throws Exception {
+ User user = getRunData(data).getUser();
+ context.put("user", user);
+ }
+}
diff --git a/src/main/resources/archetype-resources/src/main/java/flux/modules/actions/FluxLogin.java b/src/main/resources/archetype-resources/src/main/java/flux/modules/actions/FluxLogin.java
new file mode 100644
index 0000000..09cba49
--- /dev/null
+++ b/src/main/resources/archetype-resources/src/main/java/flux/modules/actions/FluxLogin.java
@@ -0,0 +1,97 @@
+package ${package}.flux.modules.actions;
+
+/*
+ * Copyright 2001-2017 The Apache Software Foundation.
+ *
+ * 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.
+ */
+
+/**
+ * Login for Flux in stand-alone mode.
+ *
+ */
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.lang.StringUtils;
+import org.apache.fulcrum.security.util.FulcrumSecurityException;
+import org.apache.fulcrum.security.util.UnknownEntityException;
+import org.apache.turbine.TurbineConstants;
+import org.apache.turbine.annotation.TurbineConfiguration;
+import org.apache.turbine.annotation.TurbineService;
+import org.apache.turbine.om.security.User;
+import org.apache.turbine.pipeline.PipelineData;
+import org.apache.turbine.services.security.SecurityService;
+import org.apache.turbine.util.RunData;
+
+/**
+ * This is where we authenticate the user logging into the system against a user
+ * in the database. If the user exists in the database that users last login
+ * time will be updated.
+ *
+ * @author <a href="mailto:mbryson@mont.mindspring.com">Dave Bryson</a>
+ * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
+ * @author <a href="mailto:quintonm@bellsouth.net">Quinton McCombs</a>
+ * @author <a href="mailto:peter@courcoux.biz">Peter Courcoux</a>
+ * @author <a href="mailto:jeff@jivecast.com">Jeffery Painter</a>
+ * @version $Id: LoginUser.java 1725012 2017-11-16 11:38:47Z painter $
+ */
+public class FluxLogin extends org.apache.turbine.modules.actions.LoginUser {
+
+ /** Injected service instance */
+ @TurbineService
+ private SecurityService security;
+
+ /** Injected configuration instance */
+ @TurbineConfiguration
+ private Configuration conf;
+
+ /**
+ * Checks for anonymous user, else calls parent method.
+ *
+ * @param pipelineData
+ * Turbine information.
+ * @exception FulcrumSecurityException
+ * could not get instance of the anonymous user
+ */
+ @Override
+ public void doPerform(PipelineData pipelineData) throws FulcrumSecurityException {
+
+ RunData data = getRunData(pipelineData);
+ String username = data.getParameters().getString(FluxLogin.CGI_USERNAME, "");
+
+ if (StringUtils.isEmpty(username)) {
+ return;
+ }
+
+ if (username.equals(security.getAnonymousUser().getName())) {
+ data.setMessage("Anonymous user cannot login");
+ reset(data);
+ return;
+ }
+
+ super.doPerform(pipelineData);
+ }
+
+ private void reset(RunData data) throws UnknownEntityException {
+ User anonymousUser = security.getAnonymousUser();
+ data.setUser(anonymousUser);
+
+ if (StringUtils.isNotEmpty(conf.getString(TurbineConstants.TEMPLATE_LOGIN, ""))) {
+ // We're running in a templating solution
+ data.setScreenTemplate(conf.getString(TurbineConstants.TEMPLATE_LOGIN));
+ } else {
+ data.setScreen(conf.getString(TurbineConstants.SCREEN_LOGIN));
+ }
+ }
+
+}
diff --git a/src/main/resources/archetype-resources/src/main/java/flux/modules/actions/FluxLogout.java b/src/main/resources/archetype-resources/src/main/java/flux/modules/actions/FluxLogout.java
new file mode 100644
index 0000000..a47af7c
--- /dev/null
+++ b/src/main/resources/archetype-resources/src/main/java/flux/modules/actions/FluxLogout.java
@@ -0,0 +1,107 @@
+package ${package}.flux.modules.actions;
+
+/*
+ * Copyright 2001-2017 The Apache Software Foundation.
+ *
+ * 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.
+ */
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.fulcrum.security.util.FulcrumSecurityException;
+import org.apache.turbine.TurbineConstants;
+import org.apache.turbine.annotation.TurbineConfiguration;
+import org.apache.turbine.annotation.TurbineService;
+import org.apache.turbine.modules.Action;
+import org.apache.turbine.om.security.User;
+import org.apache.turbine.pipeline.PipelineData;
+import org.apache.turbine.services.security.SecurityService;
+import org.apache.turbine.util.RunData;
+
+/**
+ * Removes the user from the session and sends them to the screen.homepage
+ * specified in the resources file
+ *
+ */
+public class FluxLogout extends Action {
+
+ /** Injected service instance */
+ @TurbineService
+ private SecurityService security;
+
+ /** Injected configuration instance */
+ @TurbineConfiguration
+ private Configuration conf;
+
+ /**
+ * Clears the PipelineData user object back to an anonymous status not logged
+ * in, and with a null ACL. If the tr.props ACTION_LOGIN is anything except
+ * "LogoutUser", flow is transfered to the SCREEN_HOMEPAGE
+ *
+ * If this action name is the value of action.logout then we are being run
+ * before the session validator, so we don't need to set the screen (we assume
+ * that the session validator will handle that). This is basically still here
+ * simply to preserve old behaviour - it is recommended that action.logout is
+ * set to "LogoutUser" and that the session validator does handle setting the
+ * screen/template for a logged out (read not-logged-in) user.
+ *
+ * @param pipelineData
+ * Turbine information.
+ * @exception FulcrumSecurityException
+ * a problem occurred in the security service.
+ */
+ @Override
+ public void doPerform(PipelineData pipelineData) throws FulcrumSecurityException {
+
+ RunData data = getRunData(pipelineData);
+ // Session validator did not run, so RunData is not populated
+ User user = data.getUserFromSession();
+
+ if (user != null && !security.isAnonymousUser(user)) {
+ // Make sure that the user has really logged in...
+ if (!user.hasLoggedIn()) {
+ return;
+ }
+
+ user.setHasLoggedIn(Boolean.FALSE);
+ security.saveUser(user);
+ }
+
+ data.setMessage(conf.getString(TurbineConstants.LOGOUT_MESSAGE));
+
+ // This will cause the acl to be removed from the session in
+ // the Turbine servlet code.
+ data.setACL(null);
+
+ // Retrieve an anonymous user.
+ User anonymousUser = security.getAnonymousUser();
+ data.setUser(anonymousUser);
+ data.save();
+
+ // In the event that the current screen or related navigations
+ // require acl info, we cannot wait for Turbine to handle
+ // regenerating acl.
+ data.getSession().removeAttribute(TurbineConstants.ACL_SESSION_KEY);
+
+ // If this action name is the value of action.logout then we are
+ // being run before the session validator, so we don't need to
+ // set the screen (we assume that the session validator will handle
+ // that). This is basically still here simply to preserve old behavior
+ // - it is recommended that action.logout is set to "LogoutUser" and
+ // that the session validator does handle setting the screen/template
+ // for a logged out (read not-logged-in) user.
+ if (!conf.getString(TurbineConstants.ACTION_LOGOUT_KEY, TurbineConstants.ACTION_LOGOUT_DEFAULT)
+ .equals(TurbineConstants.ACTION_LOGOUT_DEFAULT)) {
+ data.setScreen(conf.getString(TurbineConstants.SCREEN_HOMEPAGE));
+ }
+ }
+}
diff --git a/src/main/resources/archetype-resources/src/main/java/flux/modules/actions/SecureAction.java b/src/main/resources/archetype-resources/src/main/java/flux/modules/actions/SecureAction.java
new file mode 100644
index 0000000..7e35060
--- /dev/null
+++ b/src/main/resources/archetype-resources/src/main/java/flux/modules/actions/SecureAction.java
@@ -0,0 +1,116 @@
+package ${package}.flux.modules.actions;
+
+/*
+ * Copyright 2001-2017 The Apache Software Foundation.
+ *
+ * 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.
+ */
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.fulcrum.localization.LocalizationService;
+import org.apache.fulcrum.security.model.turbine.TurbineAccessControlList;
+import org.apache.turbine.Turbine;
+import org.apache.turbine.TurbineConstants;
+import org.apache.turbine.annotation.TurbineConfiguration;
+import org.apache.turbine.annotation.TurbineService;
+import org.apache.turbine.modules.actions.VelocitySecureAction;
+import org.apache.turbine.om.security.User;
+import org.apache.turbine.pipeline.PipelineData;
+import org.apache.turbine.util.RunData;
+import org.apache.velocity.context.Context;
+
+/**
+ * Velocity Secure action.
+ *
+ * Always performs a Security Check that you've defined before executing the
+ * doPerform().
+ */
+public class SecureAction extends VelocitySecureAction {
+
+ @TurbineService
+ private LocalizationService localizationService;
+
+ /** Injected configuration instance */
+ @TurbineConfiguration
+ private Configuration conf;
+
+ @TurbineConfiguration(TurbineConstants.TEMPLATE_LOGIN)
+ private Configuration templateLogin;
+
+ @TurbineConfiguration(TurbineConstants.TEMPLATE_HOMEPAGE)
+ private Configuration templateHomepage;
+
+ /**
+ * This currently only checks to make sure that user is allowed to view the
+ * storage area. If you create an action that requires more security then
+ * override this method.
+ *
+ * @param data
+ * Turbine information.
+ * @return True if the user is authorized to access the screen.
+ * @exception Exception,
+ * a generic exception.
+ */
+ @Override
+ protected boolean isAuthorized(PipelineData pipelineData) throws Exception {
+
+ RunData data = getRunData(pipelineData);
+
+ boolean isAuthorized = false;
+
+ /*
+ * Grab the Flux Admin role listed in the Flux.properties file that is included
+ * in the the standard TurbineResources.properties file.
+ */
+ String fluxAdminRole = Turbine.getConfiguration().getString("flux.admin.role");
+
+ // Get the Turbine ACL implementation
+ TurbineAccessControlList acl = data.getACL();
+
+ if (acl == null) {
+ String msg = localizationService.getString(localizationService.getDefaultBundleName(),
+ localizationService.getLocale(((RunData) data).getRequest()), "no_permission");
+
+ data.setMessage(msg);
+
+ log.debug(
+ "call not permitted for template: " + data.getScreenTemplate() + " and action " + data.getAction());
+ data.setScreenTemplate((String) templateHomepage.getProperty(""));
+ isAuthorized = false;
+
+ } else if (acl.hasRole(fluxAdminRole)) {
+ isAuthorized = true;
+ } else {
+ data.setScreenTemplate((String) templateHomepage.getProperty(""));
+ data.setMessage("You do not have access to this part of the site.");
+ isAuthorized = false;
+ }
+ return isAuthorized;
+
+ }
+
+ /**
+ * Implement this to add information to the context.
+ *
+ * @param data
+ * Turbine information.
+ * @param context
+ * Context for web pages.
+ * @exception Exception,
+ * a generic exception.
+ */
+ public void doPerform(PipelineData data, Context context) throws Exception {
+ User user = getRunData(data).getUser();
+ context.put("user", user);
+ }
+}
diff --git a/src/main/resources/archetype-resources/src/main/java/flux/modules/actions/group/FluxGroupAction.java b/src/main/resources/archetype-resources/src/main/java/flux/modules/actions/group/FluxGroupAction.java
new file mode 100644
index 0000000..f04bfe0
--- /dev/null
+++ b/src/main/resources/archetype-resources/src/main/java/flux/modules/actions/group/FluxGroupAction.java
@@ -0,0 +1,171 @@
+package ${package}.flux.modules.actions.group;
+
+/*
+ * Copyright 2001-2017 The Apache Software Foundation.
+ *
+ * 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.
+ */
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.fulcrum.security.entity.Group;
+import org.apache.fulcrum.security.util.EntityExistsException;
+import org.apache.fulcrum.security.util.UnknownEntityException;
+import org.apache.fulcrum.yaafi.framework.util.StringUtils;
+import org.apache.turbine.annotation.TurbineConfiguration;
+import org.apache.turbine.annotation.TurbineService;
+import org.apache.turbine.pipeline.PipelineData;
+import org.apache.turbine.services.security.SecurityService;
+import org.apache.turbine.util.RunData;
+import org.apache.velocity.context.Context;
+
+import ${package}.flux.modules.actions.FluxAction;
+
+/**
+ * Action to manage groups in Turbine.
+ *
+ */
+public class FluxGroupAction extends FluxAction {
+
+ private static Log log = LogFactory.getLog(FluxGroupAction.class);
+ private static String GROUP_ID = "group";
+
+ /** Injected service instance */
+ @TurbineService
+ private SecurityService security;
+
+ /** Injected configuration instance */
+ @TurbineConfiguration
+ private Configuration conf;
+
+ /**
+ * ActionEvent responsible for inserting a new user into the Turbine security
+ * system.
+ *
+ * @param data
+ * Turbine information.
+ * @param context
+ * Context for web pages.
+ * @exception Exception
+ * a generic exception.
+ */
+ public void doInsert(PipelineData pipelineData, Context context) throws Exception {
+ RunData data = getRunData(pipelineData);
+
+ String name = data.getParameters().getString(GROUP_ID);
+ if (!StringUtils.isEmpty(name)) {
+
+ try {
+ // create the group
+ Group group = security.getGroupInstance();
+ group.setName(name);
+ security.addGroup(group);
+ } catch (EntityExistsException eee) {
+
+ context.put("name", name);
+ context.put("errorTemplate", "group,GroupAlreadyExists.vm");
+
+ /*
+ * We are still in insert mode. So keep this value alive.
+ */
+ data.getParameters().add("mode", "insert");
+ setTemplate(data, "group,GroupForm.vm");
+ } catch (Exception e) {
+ log.error("Something bad happened");
+ }
+ } else {
+ log.error("Cannot add empty group name");
+ }
+
+ }
+
+ /**
+ * Update a group name
+ *
+ * @param data
+ * Turbine information.
+ * @param context
+ * Context for web pages.
+ * @exception Exception
+ * a generic exception.
+ */
+ public void doUpdate(PipelineData pipelineData, Context context) throws Exception {
+ RunData data = getRunData(pipelineData);
+ String groupName = data.getParameters().getString("oldName");
+ if (!StringUtils.isEmpty(groupName)) {
+ Group group = security.getGroupByName(groupName);
+ String name = data.getParameters().getString(GROUP_ID);
+ if (group != null && !StringUtils.isEmpty(name)) {
+ try {
+ security.renameGroup(group, name);
+ } catch (UnknownEntityException uee) {
+ /*
+ * Should do something here but I still think we should use the an id so that
+ * this can't happen.
+ */
+ }
+ } else {
+ log.error("Cannot update group to empty name");
+ }
+ } else {
+ log.error("Cannot update group to empty name");
+ }
+ }
+
+ /**
+ * ActionEvent responsible for removing a user.
+ *
+ * @param data
+ * Turbine information.
+ * @param context
+ * Context for web pages.
+ * @exception Exception
+ * a generic exception.
+ */
+ public void doDelete(PipelineData pipelineData, Context context) throws Exception {
+
+ RunData data = getRunData(pipelineData);
+
+ try {
+ Group group = security.getGroupByName(data.getParameters().getString(GROUP_ID));
+ if ( group != null ) {
+ security.removeGroup(group);
+ } else {
+ data.setMessage("Could not find group to remove");
+ }
+ } catch (UnknownEntityException uee) {
+ /*
+ * Should do something here but I still think we should use the an id so that
+ * this can't happen.
+ */
+ log.error(uee);
+ }
+ }
+
+ /**
+ * Implement this to add information to the context.
+ *
+ * @param data
+ * Turbine information.
+ * @param context
+ * Context for web pages.
+ * @exception Exception
+ * a generic exception.
+ */
+ public void doPerform(PipelineData pipelineData, Context context) throws Exception {
+ RunData data = getRunData(pipelineData);
+ log.info("Running do perform!");
+ data.setMessage("Can't find the requested action!");
+ }
+}
diff --git a/src/main/resources/archetype-resources/src/main/java/flux/modules/actions/permission/FluxPermissionAction.java b/src/main/resources/archetype-resources/src/main/java/flux/modules/actions/permission/FluxPermissionAction.java
new file mode 100644
index 0000000..d9ada4f
--- /dev/null
+++ b/src/main/resources/archetype-resources/src/main/java/flux/modules/actions/permission/FluxPermissionAction.java
@@ -0,0 +1,176 @@
+package ${package}.flux.modules.actions.permission;
+
+/*
+ * Copyright 2001-2017 The Apache Software Foundation.
+ *
+ * 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.
+ */
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.fulcrum.security.entity.Role;
+import org.apache.fulcrum.security.torque.om.TurbinePermission;
+import org.apache.fulcrum.security.torque.om.TurbinePermissionPeer;
+import org.apache.fulcrum.security.util.RoleSet;
+import org.apache.fulcrum.yaafi.framework.util.StringUtils;
+import org.apache.torque.criteria.Criteria;
+import org.apache.turbine.annotation.TurbineConfiguration;
+import org.apache.turbine.annotation.TurbineService;
+import org.apache.turbine.pipeline.PipelineData;
+import org.apache.turbine.services.security.SecurityService;
+import org.apache.turbine.util.RunData;
+import org.apache.velocity.context.Context;
+
+import ${package}.flux.modules.actions.FluxAction;
+
+/**
+ * Action to manage permissions in Turbine.
+ *
+ * @version $Id: FluxPermissionAction.java,v 1.11 2017/11/16 10:24:41 painter
+ * Exp $
+ */
+public class FluxPermissionAction extends FluxAction {
+ private static Log log = LogFactory.getLog(FluxPermissionAction.class);
+ private static String PERM_ID = "permission";
+
+ /** Injected service instance */
+ @TurbineService
+ private SecurityService security;
+
+ /** Injected configuration instance */
+ @TurbineConfiguration
+ private Configuration conf;
+
+ /**
+ * ActionEvent responsible for inserting a new permission into the Turbine
+ * security system.
+ *
+ * @param data
+ * Turbine information.
+ * @param context
+ * Context for web pages.
+ * @exception Exception
+ * a generic exception.
+ */
+ public void doInsert(PipelineData pipelineData, Context context) throws Exception {
+
+ RunData data = getRunData(pipelineData);
+ String name = data.getParameters().getString(PERM_ID);
+ if (!StringUtils.isEmpty(name)) {
+ // create the permission
+ TurbinePermission tp = new TurbinePermission();
+ tp.setName(name);
+ tp.setNew(true);
+ tp.save();
+ } else {
+ data.setMessage("Cannot add permission without a name");
+ data.getParameters().add("mode", "insert");
+ data.setScreen("permission,FluxPermissionForm.vm");
+ }
+
+ }
+
+ /**
+ * ActionEvent responsible updating a permission. Must check the input for
+ * integrity before allowing the user info to be update in the database.
+ *
+ * @param data
+ * Turbine information.
+ * @param context
+ * Context for web pages.
+ * @exception Exception
+ * a generic exception.
+ */
+ public void doUpdate(PipelineData pipelineData, Context context) throws Exception {
+ RunData data = getRunData(pipelineData);
+
+ String old_name = data.getParameters().getString("oldName");
+ String name = data.getParameters().getString(PERM_ID);
+ if (!StringUtils.isEmpty(name)) {
+
+ /*
+ * broken Permission perm = security.getPermissionByName(old_name);
+ * security.renamePermission(perm, name);
+ */
+
+ // manual rename
+ Criteria criteria = new Criteria();
+ criteria.where(TurbinePermissionPeer.PERMISSION_NAME, old_name);
+ TurbinePermission tp = TurbinePermissionPeer.doSelectSingleRecord(criteria);
+ if (tp != null) {
+ tp.setName(name);
+ tp.save();
+ }
+
+ } else {
+ data.setMessage("Cannot find permission to update");
+ data.setScreen("permission,FluxPermissionList.vm");
+ }
+ }
+
+ /**
+ * ActionEvent responsible for removing a permission.
+ *
+ * @param data
+ * Turbine information.
+ * @param context
+ * Context for web pages.
+ * @exception Exception
+ * a generic exception.
+ */
+ public void doDelete(PipelineData pipelineData, Context context) throws Exception {
+ RunData data = getRunData(pipelineData);
+
+ try {
+ String permName = data.getParameters().getString(PERM_ID);
+
+ if (!StringUtils.isEmpty(permName)) {
+ // just
+ org.apache.fulcrum.security.model.turbine.entity.TurbinePermission perm = security.<org.apache.fulcrum.security.model.turbine.entity.TurbinePermission>getPermissionByName(
+ permName);
+ // remove the role-permission links
+ RoleSet roles = perm.getRoles();
+ for (Role role : roles) {
+ security.revoke(role, perm);
+ }
+
+ // Remove the permission
+ security.removePermission(perm);
+
+ } else {
+ data.setMessage("Cannot find permission to delete");
+ data.setScreen("permission,FluxPermissionList.vm");
+ }
+ } catch (Exception e) {
+ data.setMessage("An error occured while trying to remove a permission");
+ }
+ }
+
+ /**
+ * Implement this to add information to the context.
+ *
+ * @param data
+ * Turbine information.
+ * @param context
+ * Context for web pages.
+ * @exception Exception
+ * a generic exception.
+ */
+ public void doPerform(PipelineData pipelineData, Context context) throws Exception {
+ RunData data = getRunData(pipelineData);
+ log.info("Running do perform!");
+ data.setMessage("Can't find the requested action!");
+ }
+
+}
diff --git a/src/main/resources/archetype-resources/src/main/java/flux/modules/actions/role/FluxRoleAction.java b/src/main/resources/archetype-resources/src/main/java/flux/modules/actions/role/FluxRoleAction.java
new file mode 100644
index 0000000..a29471e
--- /dev/null
+++ b/src/main/resources/archetype-resources/src/main/java/flux/modules/actions/role/FluxRoleAction.java
@@ -0,0 +1,234 @@
+package ${package}.flux.modules.actions.role;
+
+/*
+ * Copyright 2001-2017 The Apache Software Foundation.
+ *
+ * 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.
+ */
+
+import java.util.Iterator;
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.fulcrum.security.entity.Permission;
+import org.apache.fulcrum.security.entity.Role;
+import org.apache.fulcrum.security.model.turbine.entity.TurbineRole;
+import org.apache.fulcrum.security.torque.om.TurbineUserGroupRolePeer;
+import org.apache.fulcrum.security.util.EntityExistsException;
+import org.apache.fulcrum.security.util.PermissionSet;
+import org.apache.fulcrum.security.util.UnknownEntityException;
+import org.apache.fulcrum.yaafi.framework.util.StringUtils;
+import org.apache.torque.criteria.Criteria;
+import org.apache.turbine.annotation.TurbineConfiguration;
+import org.apache.turbine.annotation.TurbineService;
+import org.apache.turbine.pipeline.PipelineData;
+import org.apache.turbine.services.security.SecurityService;
+import org.apache.turbine.util.RunData;
+import org.apache.velocity.context.Context;
+
+import ${package}.flux.modules.actions.FluxAction;
+
+/**
+ * Action to manager roles in Turbine.
+ *
+ */
+public class FluxRoleAction extends FluxAction {
+
+ private static Log log = LogFactory.getLog(FluxRoleAction.class);
+ private static String ROLE_ID = "role";
+
+ /** Injected service instance */
+ @TurbineService
+ private SecurityService security;
+
+ /** Injected configuration instance */
+ @TurbineConfiguration
+ private Configuration conf;
+
+ public void doInsert(PipelineData pipelineData, Context context) throws Exception {
+ RunData data = getRunData(pipelineData);
+ Role role = security.getRoleInstance();
+ data.getParameters().setProperties(role);
+
+ String name = data.getParameters().getString(ROLE_ID);
+ role.setName(name);
+
+ try {
+ security.addRole(role);
+ } catch (EntityExistsException eee) {
+ context.put("name", name);
+ context.put("errorTemplate", "role,FluxRoleAlreadyExists.vm");
+ context.put("role", role);
+ /*
+ * We are still in insert mode. So keep this value alive.
+ */
+ data.getParameters().add("mode", "insert");
+ setTemplate(data, "role,FluxRoleForm.vm");
+ }
+
+ }
+
+ /**
+ * ActionEvent responsible updating a role. Must check the input for integrity
+ * before allowing the user info to be update in the database.
+ *
+ * @param data
+ * Turbine information.
+ * @param context
+ * Context for web pages.
+ * @exception Exception
+ * a generic exception.
+ */
+ public void doUpdate(PipelineData pipelineData, Context context) throws Exception {
+ RunData data = getRunData(pipelineData);
+ Role role = security.getRoleByName(data.getParameters().getString("oldName"));
+ String name = data.getParameters().getString(ROLE_ID);
+ if (role != null && !StringUtils.isEmpty(name)) {
+ try {
+ security.renameRole(role, name);
+ } catch (UnknownEntityException uee) {
+ log.error("Could not rename role: " + uee);
+ }
+ } else {
+ data.setMessage("Cannot update a role to an empty name");
+ log.error("Cannot update role to empty name");
+ }
+ }
+
+ /**
+ * ActionEvent responsible for removing a role.
+ *
+ * @param data
+ * Turbine information.
+ * @param context
+ * Context for web pages.
+ * @exception Exception
+ * a generic exception.
+ */
+ public void doDelete(PipelineData pipelineData, Context context) throws Exception {
+ RunData data = getRunData(pipelineData);
+
+ try {
+ // find the role
+ Role role = security.getRoleByName(data.getParameters().getString(ROLE_ID));
+
+ if (role != null) {
+ // remove dependencies to users with the role
+ removeRoleFromAllUsers(role);
+
+ // remove all permissions
+ security.revokeAll(role);
+
+ // now remove the role
+ security.removeRole(role);
+ } else {
+ data.setMessage("Role was not found");
+ }
+ } catch (UnknownEntityException uee) {
+ /*
+ * Should do something here but I still think we should use the an id so that
+ * this can't happen.
+ */
+ log.error(uee);
+ } catch (Exception e) {
+ log.error("Could not remove role: " + e);
+ }
+ }
+
+ /**
+ * Update the roles that are to assigned to a user for a project.
+ *
+ * @param data
+ * Turbine information.
+ * @param context
+ * Context for web pages.
+ * @exception Exception
+ * a generic exception.
+ */
+ public void doPermissions(PipelineData pipelineData, Context context) throws Exception {
+
+ RunData data = getRunData(pipelineData);
+ /*
+ * Grab the role we are trying to update. Always not null
+ */
+ TurbineRole role = security.<TurbineRole>getRoleByName(data.getParameters().getString(ROLE_ID));
+
+ /*
+ * Grab the permissions for the role we are dealing with.
+ */
+ PermissionSet rolePermissions = role.getPermissions();
+
+ /*
+ * Grab all the permissions.
+ */
+ PermissionSet permissions = security.getAllPermissions();
+
+ // id part one
+ String roleName = role.getName();
+ for (Iterator<Permission> iterator = permissions.iterator(); iterator.hasNext();) {
+ Permission permission = iterator.next();
+ String permissionName = permission.getName();
+ String rolePermission = roleName + permissionName;
+
+ String formRolePermission = data.getParameters().getString(rolePermission);
+ if (formRolePermission != null && !rolePermissions.contains(permission)) {
+ /*
+ * Checkbox has been checked AND the role doesn't already contain this
+ * permission. So assign the permission to the role.
+ */
+ log.debug("adding " + permissionName + " to " + roleName);
+ security.grant(role, permission);
+ // this might also be done with role.addPermission(permission);
+ } else if (formRolePermission == null && rolePermissions.contains(permission)) {
+ /*
+ * Checkbox has not been checked AND the role contains this permission. So
+ * remove this permission from the role.
+ */
+ log.debug("removing " + permissionName + " from " + roleName);
+ security.revoke(role, permission);
+ // this might also be done with role.removePermission(permission);
+ }
+
+ }
+ }
+
+ /**
+ * Implement this to add information to the context.
+ *
+ * @param data
+ * Turbine information.
+ * @param context
+ * Context for web pages.
+ * @exception Exception
+ * a generic exception.
+ */
+ public void doPerform(PipelineData pipelineData, Context context) throws Exception {
+ log.info("Running do perform!");
+ getRunData(pipelineData).setMessage("Can't find the requested action!");
+ }
+
+ /**
+ * Helper method for removing roles, must clear associated users with the role
+ */
+ private void removeRoleFromAllUsers(Role role) {
+ try {
+ Criteria criteria = new Criteria();
+ criteria.where(TurbineUserGroupRolePeer.ROLE_ID, role.getId());
+ TurbineUserGroupRolePeer.doDelete(criteria);
+ } catch (Exception e) {
+ log.error("Error removing user, role associations: " + e.toString());
+ }
+ }
+
+}
diff --git a/src/main/resources/archetype-resources/src/main/java/flux/modules/actions/user/FluxUserAction.java b/src/main/resources/archetype-resources/src/main/java/flux/modules/actions/user/FluxUserAction.java
new file mode 100644
index 0000000..6252f9e
--- /dev/null
+++ b/src/main/resources/archetype-resources/src/main/java/flux/modules/actions/user/FluxUserAction.java
@@ -0,0 +1,261 @@
+package ${package}.flux.modules.actions.user;
+
+/*
+ * Copyright 2001-2017 The Apache Software Foundation.
+ *
+ * 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.
+ */
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.fulcrum.security.entity.Group;
+import org.apache.fulcrum.security.entity.Role;
+import org.apache.fulcrum.security.model.turbine.TurbineAccessControlList;
+import org.apache.fulcrum.security.util.GroupSet;
+import org.apache.fulcrum.security.util.RoleSet;
+import org.apache.turbine.annotation.TurbineService;
+import org.apache.turbine.om.security.User;
+import org.apache.turbine.pipeline.PipelineData;
+import org.apache.turbine.services.security.SecurityService;
+import org.apache.turbine.util.RunData;
+import org.apache.velocity.context.Context;
+
+import ${package}.flux.modules.actions.FluxAction;
+
+/**
+ * Change Password action.
+ *
+ */
+public class FluxUserAction extends FluxAction {
+ private static Log log = LogFactory.getLog(FluxUserAction.class);
+
+ /** Injected service instance */
+ @TurbineService
+ private SecurityService security;
+
+ /**
+ * ActionEvent responsible for inserting a new user into the Turbine security
+ * system.
+ */
+ public void doInsert(PipelineData pipelineData, Context context) throws Exception {
+ RunData data = getRunData(pipelineData);
+
+ /*
+ * Grab the username entered in the form.
+ */
+ String username = data.getParameters().getString("username");
+ String password = data.getParameters().getString("password");
+
+ if (!StringUtils.isEmpty(username) && !StringUtils.isEmpty(password)) {
+ /*
+ * Make sure this account doesn't already exist. If the account already exists
+ * then alert the user and make them change the username.
+ */
+ if (security.accountExists(username)) {
+ context.put("username", username);
+ context.put("errorTemplate", "user,FluxUserAlreadyExists.vm");
+
+ data.setMessage("The user already exists");
+ data.getParameters().add("mode", "insert");
+ data.setScreen("user,FluxUserForm.vm");
+ return;
+ } else {
+
+ try {
+ /*
+ * Create a new user modeled directly from the SecurityServiceTest method
+ */
+ User user = security.getUserInstance(username);
+ data.getParameters().setProperties(user);
+ security.addUser(user, password);
+
+ // Use security to force the password
+ security.forcePassword(user, password);
+
+ } catch (Exception e) {
+ log.error("Error adding new user: " + e);
+
+ context.put("username", username);
+ context.put("errorTemplate", "user,FluxUserAlreadyExists.vm");
+
+ data.setMessage("Could not add the user");
+ data.getParameters().add("mode", "insert");
+ data.setScreen("user,FluxUserForm.vm");
+ return;
+ }
+ }
+
+ } else {
+ String msg = "Cannot add user without username or password";
+ log.error(msg);
+ data.setMessage(msg);
+ data.getParameters().add("mode", "insert");
+ data.setScreen("user,FluxUserForm.vm");
+ }
+ }
+
+ /**
+ * ActionEvent responsible updating a user. Must check the input for integrity
+ * before allowing the user info to be update in the database.
+ */
+ public void doUpdate(PipelineData pipelineData, Context context) throws Exception {
+ RunData data = getRunData(pipelineData);
+ String username = data.getParameters().getString("username");
+ if (!StringUtils.isEmpty(username)) {
+ if (security.accountExists(username)) {
+
+ // This wrapped user does work for change password though... see below
+ User user = security.getUser(username);
+ if (user != null) {
+
+ // update all properties from form
+ data.getParameters().setProperties(user);
+
+ // save the changes to the user account
+ security.saveUser(user);
+
+ // get the new password from form submit
+ String password = data.getParameters().getString("password");
+
+ // Only update if we received a new (non-empty) password
+ if (!StringUtils.isEmpty(password)) {
+
+ // Change user password
+ security.changePassword(user, user.getPassword(), password);
+
+ // this still works
+ security.forcePassword(user, password);
+ } else {
+ data.setMessage("Cannot provide an empty password");
+ return;
+ }
+
+ }
+
+ } else {
+ log.error("User does not exist!");
+ }
+ }
+ }
+
+ /**
+ * ActionEvent responsible for removing a user from the Tambora system.
+ */
+ public void doDelete(PipelineData pipelineData, Context context) throws Exception {
+
+ try {
+ RunData data = getRunData(pipelineData);
+ String username = data.getParameters().getString("username");
+ if (!StringUtils.isEmpty(username)) {
+ if (security.accountExists(username)) {
+
+ // find the user object and remove using security mgr
+ User user = security.getUser(username);
+ security.removeUser(user);
+
+ } else {
+ log.error("User does not exist!");
+ data.setMessage("User not found!");
+ }
+ }
+ } catch (Exception e) {
+ log.error("Could not remove user: " + e);
+ }
+ }
+
+ /**
+ * Update the roles that are to assigned to a user for a project.
+ */
+ public void doRoles(PipelineData pipelineData, Context context) throws Exception {
+ RunData data = getRunData(pipelineData);
+
+ try {
+ /*
+ * Get the user we are trying to update. The username has been hidden in the
+ * form so we will grab the hidden username and use that to retrieve the user.
+ */
+ String username = data.getParameters().getString("username");
+ if (!StringUtils.isEmpty(username)) {
+ if (security.accountExists(username)) {
+ User user = security.getUser(username);
+
+ // Get the Turbine ACL implementation
+ TurbineAccessControlList acl = security.getUserManager().getACL(user);
+
+ /*
+ * Grab all the Groups and Roles in the system.
+ */
+ GroupSet groups = security.getAllGroups();
+ RoleSet roles = security.getAllRoles();
+
+ for (Group group : groups) {
+ String groupName = group.getName();
+ for (Role role : roles) {
+ String roleName = role.getName();
+
+ /*
+ * In the UserRoleForm.vm we made a checkbox for every possible Group/Role
+ * combination so we will compare every possible combination with the values
+ * that were checked off in the form. If we have a match then we will grant the
+ * user the role in the group.
+ */
+ String groupRole = groupName + roleName;
+ String formGroupRole = data.getParameters().getString(groupRole);
+ boolean addGroupRole = false;
+
+ // signal to add group
+ if (!StringUtils.isEmpty(formGroupRole))
+ addGroupRole = true;
+
+ if (addGroupRole) {
+ // only add if new
+ if (!acl.hasRole(role, group)) {
+ security.grant(user, group, role);
+ }
+
+ } else {
+
+ // only remove if it was previously assigned
+ if (acl.hasRole(role, group)) {
+
+ // revoke the role for this user
+ acl.getRoles(group).remove(role);
+
+ // revoke the user/group/role entry
+ security.revoke(user, group, role);
+ }
+ }
+
+ }
+ }
+
+ } else {
+ log.error("User does not exist!");
+ }
+ }
+
+ } catch (Exception e) {
+ log.error("Error on role assignment: " + e);
+ }
+ }
+
+ /**
+ * Implement this to add information to the context.
+ */
+ public void doPerform(PipelineData pipelineData, Context context) throws Exception {
+ log.info("Running do perform!");
+ getRunData(pipelineData).setMessage("Can't find the requested action!");
+ }
+
+}
diff --git a/src/main/resources/archetype-resources/src/main/java/flux/modules/screens/FluxError.java b/src/main/resources/archetype-resources/src/main/java/flux/modules/screens/FluxError.java
new file mode 100644
index 0000000..dca782d
--- /dev/null
+++ b/src/main/resources/archetype-resources/src/main/java/flux/modules/screens/FluxError.java
@@ -0,0 +1,27 @@
+package ${package}.flux.modules.screens;
+
+/*
+ * Copyright 2001-2017 The Apache Software Foundation.
+ *
+ * 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.
+ */
+
+import org.apache.turbine.modules.screens.VelocityErrorScreen;
+
+/**
+ * Base screen for secure web acces to the storage side of Tambora.
+ *
+ */
+public class FluxError extends VelocityErrorScreen
+{
+}
diff --git a/src/main/resources/archetype-resources/src/main/java/flux/modules/screens/FluxIndex.java b/src/main/resources/archetype-resources/src/main/java/flux/modules/screens/FluxIndex.java
new file mode 100644
index 0000000..115252c
--- /dev/null
+++ b/src/main/resources/archetype-resources/src/main/java/flux/modules/screens/FluxIndex.java
@@ -0,0 +1,92 @@
+package ${package}.flux.modules.screens;
+
+/*
+ * Copyright 2001-2017 The Apache Software Foundation.
+ *
+ * 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.
+ */
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.fulcrum.security.model.turbine.TurbineAccessControlList;
+import org.apache.turbine.Turbine;
+import org.apache.turbine.TurbineConstants;
+import org.apache.turbine.annotation.TurbineConfiguration;
+import org.apache.turbine.annotation.TurbineService;
+import org.apache.turbine.modules.screens.VelocitySecureScreen;
+import org.apache.turbine.pipeline.PipelineData;
+import org.apache.turbine.services.security.SecurityService;
+import org.apache.turbine.util.RunData;
+import org.apache.velocity.context.Context;
+
+/**
+ * Base screen for secure web access to the Flux user manager
+ *
+ */
+public class FluxIndex extends VelocitySecureScreen {
+
+ @TurbineService
+ protected SecurityService securityService;
+
+ @TurbineConfiguration(TurbineConstants.TEMPLATE_LOGIN)
+ private Configuration templateLogin;
+
+ @TurbineConfiguration(TurbineConstants.TEMPLATE_HOMEPAGE)
+ private Configuration templateHomepage;
+
+ /**
+ * This method is called by the Turbine framework when the associated Velocity
+ * template, Index.vm is requested
+ *
+ * @param data
+ * the Turbine request data
+ * @param context
+ * the Velocity context
+ * @throws Exception
+ * a generic Exception
+ */
+ @Override
+ protected void doBuildTemplate(PipelineData data, Context context) throws Exception {
+ }
+
+ @Override
+ protected boolean isAuthorized(PipelineData pipelineData) throws Exception {
+
+ RunData data = getRunData(pipelineData);
+
+ boolean isAuthorized = false;
+
+ /*
+ * Grab the Flux Admin role listed in the Flux.properties file that is included
+ * in the the standard TurbineResources.properties file.
+ */
+ String fluxAdminRole = Turbine.getConfiguration().getString("flux.admin.role");
+
+ // Get the Turbine ACL implementation
+ TurbineAccessControlList acl = data.getACL();
+
+ if (acl == null) {
+ // commons configuration getProperty: prefix removed, the key for the value ..
+ // is an empty string, the result an object
+ data.setScreenTemplate((String) templateLogin.getProperty(""));
+ isAuthorized = false;
+ } else if (acl.hasRole(fluxAdminRole)) {
+ isAuthorized = true;
+ } else {
+ data.setScreenTemplate((String) templateHomepage.getProperty(""));
+ data.setMessage("You do not have access to this part of the site.");
+ isAuthorized = false;
+ }
+ return isAuthorized;
+ }
+
+}
diff --git a/src/main/resources/archetype-resources/src/main/java/flux/modules/screens/FluxScreen.java b/src/main/resources/archetype-resources/src/main/java/flux/modules/screens/FluxScreen.java
new file mode 100644
index 0000000..772eaeb
--- /dev/null
+++ b/src/main/resources/archetype-resources/src/main/java/flux/modules/screens/FluxScreen.java
@@ -0,0 +1,94 @@
+package ${package}.flux.modules.screens;
+
+/*
+ * Copyright 2001-2017 The Apache Software Foundation.
+ *
+ * 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.
+ */
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.fulcrum.security.model.turbine.TurbineAccessControlList;
+import org.apache.turbine.Turbine;
+import org.apache.turbine.TurbineConstants;
+import org.apache.turbine.annotation.TurbineConfiguration;
+import org.apache.turbine.annotation.TurbineService;
+import org.apache.turbine.modules.screens.VelocitySecureScreen;
+import org.apache.turbine.pipeline.PipelineData;
+import org.apache.turbine.services.security.SecurityService;
+import org.apache.velocity.context.Context;
+
+/**
+ * Base screen for secure web acces to the storage side of Tambora.
+ *
+ */
+public abstract class FluxScreen extends VelocitySecureScreen {
+
+ @TurbineService
+ protected SecurityService securityService;
+
+ @TurbineConfiguration(TurbineConstants.TEMPLATE_LOGIN)
+ private Configuration templateLogin;
+
+ @TurbineConfiguration(TurbineConstants.TEMPLATE_HOMEPAGE)
+ private Configuration templateHomepage;
+
+ /**
+ * This method is called by Turbine
+ */
+ @Override
+ protected void doBuildTemplate(PipelineData data, Context context) throws Exception {
+
+ /*
+ * Check to see if the embedded menu should be displayed in the templates.
+ */
+ if (Turbine.getConfiguration().getBoolean("flux.embedded.show.menu", false)) {
+ context.put("showEmbeddedMenu", true);
+ }
+
+ /*
+ * Check to see if we will display the finders on the forms used in Flux.
+ */
+ if (Turbine.getConfiguration().getBoolean("flux.ui.show.finder", false)) {
+ context.put("showFinder", true);
+ }
+
+ }
+
+ @Override
+ protected boolean isAuthorized(PipelineData data) throws Exception {
+ boolean isAuthorized = false;
+
+ /*
+ * Grab the Flux Admin role listed in the Flux.properties file that is included
+ * in the the standard TurbineResources.properties file.
+ */
+ String fluxAdminRole = Turbine.getConfiguration().getString("flux.admin.role");
+
+ // Get the Turbine ACL implementation
+ TurbineAccessControlList acl = getRunData(data).getACL();
+
+ if (acl == null) {
+ // commons configuration getProperty: prefix removed, the key for the value ..
+ // is an empty string, the result an object
+ getRunData(data).setScreenTemplate((String) templateLogin.getProperty(""));
+ isAuthorized = false;
+ } else if (acl.hasRole(fluxAdminRole)) {
+ isAuthorized = true;
+ } else {
+ getRunData(data).setScreenTemplate((String) templateHomepage.getProperty(""));
+ getRunData(data).setMessage("You do not have access to this part of the site.");
+ isAuthorized = false;
+ }
+ return isAuthorized;
+ }
+}
diff --git a/src/main/resources/archetype-resources/src/main/java/flux/modules/screens/group/Default.java b/src/main/resources/archetype-resources/src/main/java/flux/modules/screens/group/Default.java
new file mode 100644
index 0000000..5360e93
--- /dev/null
+++ b/src/main/resources/archetype-resources/src/main/java/flux/modules/screens/group/Default.java
@@ -0,0 +1,25 @@
+package ${package}.flux.modules.screens.group;
+
+/*
+ * Copyright 2001-2017 The Apache Software Foundation.
+ *
+ * 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.
+ */
+
+import ${package}.flux.modules.screens.FluxScreen;
+
+/**
+ */
+public class Default extends FluxScreen
+{
+}
diff --git a/src/main/resources/archetype-resources/src/main/java/flux/modules/screens/permission/Default.java b/src/main/resources/archetype-resources/src/main/java/flux/modules/screens/permission/Default.java
new file mode 100644
index 0000000..4d27c70
--- /dev/null
+++ b/src/main/resources/archetype-resources/src/main/java/flux/modules/screens/permission/Default.java
@@ -0,0 +1,25 @@
+package ${package}.flux.modules.screens.permission;
+
+/*
+ * Copyright 2001-2017 The Apache Software Foundation.
+ *
+ * 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.
+ */
+
+import ${package}.flux.modules.screens.FluxScreen;
+
+/**
+ */
+public class Default extends FluxScreen
+{
+}
diff --git a/src/main/resources/archetype-resources/src/main/java/flux/modules/screens/role/Default.java b/src/main/resources/archetype-resources/src/main/java/flux/modules/screens/role/Default.java
new file mode 100644
index 0000000..d4d8962
--- /dev/null
+++ b/src/main/resources/archetype-resources/src/main/java/flux/modules/screens/role/Default.java
@@ -0,0 +1,25 @@
+package ${package}.flux.modules.screens.role;
+
+/*
+ * Copyright 2001-2017 The Apache Software Foundation.
+ *
+ * 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.
+ */
+
+import ${package}.flux.modules.screens.FluxScreen;
+
+/**
+ */
+public class Default extends FluxScreen
+{
+}
diff --git a/src/main/resources/archetype-resources/src/main/java/flux/modules/screens/user/Default.java b/src/main/resources/archetype-resources/src/main/java/flux/modules/screens/user/Default.java
new file mode 100644
index 0000000..436ff17
--- /dev/null
+++ b/src/main/resources/archetype-resources/src/main/java/flux/modules/screens/user/Default.java
@@ -0,0 +1,25 @@
+package ${package}.flux.modules.screens.user;
+
+/*
+ * Copyright 2001-2017 The Apache Software Foundation.
+ *
+ * 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.
+ */
+
+import ${package}.flux.modules.screens.FluxScreen;
+
+/**
+ */
+public class Default extends FluxScreen
+{
+}
diff --git a/src/main/resources/archetype-resources/src/main/java/flux/tools/FluxTool.java b/src/main/resources/archetype-resources/src/main/java/flux/tools/FluxTool.java
new file mode 100644
index 0000000..674f7a0
--- /dev/null
+++ b/src/main/resources/archetype-resources/src/main/java/flux/tools/FluxTool.java
@@ -0,0 +1,249 @@
+package ${package}.flux.tools;
+
+/*
+ * Copyright 2001-2017 The Apache Software Foundation.
+ *
+ * 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.
+ */
+
+import java.util.List;
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.lang.StringUtils;
+import org.apache.fulcrum.security.acl.AccessControlList;
+import org.apache.fulcrum.security.entity.Group;
+import org.apache.fulcrum.security.entity.Permission;
+import org.apache.fulcrum.security.entity.Role;
+import org.apache.fulcrum.security.torque.om.TurbinePermission;
+import org.apache.fulcrum.security.torque.om.TurbinePermissionPeer;
+import org.apache.fulcrum.security.torque.om.TurbineRolePermissionPeer;
+import org.apache.fulcrum.security.torque.om.TurbineUserPeer;
+import org.apache.fulcrum.security.util.GroupSet;
+import org.apache.fulcrum.security.util.RoleSet;
+import org.apache.torque.criteria.Criteria;
+import org.apache.turbine.annotation.TurbineConfiguration;
+import org.apache.turbine.annotation.TurbineService;
+import org.apache.turbine.om.security.User;
+import org.apache.turbine.services.pull.ApplicationTool;
+import org.apache.turbine.services.pull.RunDataApplicationTool;
+import org.apache.turbine.services.security.SecurityService;
+import org.apache.turbine.util.RunData;
+import org.apache.turbine.util.template.SelectorBox;
+
+/**
+ * The pull api for flux templates
+ *
+ * @version $Id: FluxTool.java,v 1.13 2017/11/16 11:24:41 painter Exp $
+ */
+public class FluxTool implements ApplicationTool, RunDataApplicationTool {
+
+ /** Injected service instance */
+ @TurbineService
+ private SecurityService security;
+
+ /** Injected configuration instance */
+ @TurbineConfiguration
+ private Configuration conf;
+
+ /** The object containing request specific data */
+ private RunData data;
+
+ /** A Group object for use within the Flux API. */
+ private Group group = null;
+
+ /** A Issue object for use within the Flux API. */
+ private Role role = null;
+
+ /** A Permission object for use within the Flux API. */
+ private Permission permission = null;
+
+ /** A User object for use within the Flux API. */
+ private User user = null;
+
+ public void init(Object data) {
+ this.data = (RunData) data;
+ }
+
+ /**
+ * Constructor does initialization stuff
+ */
+ public FluxTool() {
+
+ }
+
+ public Group getGroup() throws Exception {
+ String groupName = data.getParameters().getString("group");
+ if (StringUtils.isEmpty(groupName)) {
+ group = security.getGroupInstance();
+ } else {
+ group = security.getGroupByName(groupName);
+ }
+ return group;
+ }
+
+ public String getMode() {
+ return data.getParameters().getString("mode");
+ }
+
+ public GroupSet getGroups() throws Exception {
+ return security.getAllGroups();
+ }
+
+ public Role getRole() throws Exception {
+ String roleName = data.getParameters().getString("role");
+ if (StringUtils.isEmpty(roleName)) {
+ role = security.getRoleInstance();
+ } else {
+ role = security.getRoleByName(roleName);
+ }
+ return role;
+ }
+
+ /**
+ */
+ public RoleSet getRoles() throws Exception {
+ return security.getAllRoles();
+ }
+
+ public TurbinePermission getPermission() throws Exception {
+
+ // make sure that the get permission returns the one linked to the role
+ String permissionName = data.getParameters().getString("permission");
+ if (StringUtils.isEmpty(permissionName)) {
+ return null;
+ } else {
+ try {
+ Criteria criteria = new Criteria();
+ criteria.where(TurbinePermissionPeer.PERMISSION_NAME, permissionName);
+ return TurbinePermissionPeer.doSelectSingleRecord(criteria);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+ }
+
+ /**
+ * Get last cached role - useful after calling getPermissions()
+ *
+ * @return
+ */
+ public Role getCachedRole() {
+ return role;
+ }
+
+ /**
+ * Return all permissions for a role
+ */
+ public List<TurbinePermission> getRolePermissions() throws Exception {
+
+ String roleName = data.getParameters().getString("role");
+ if (StringUtils.isEmpty(roleName)) {
+ role = security.getRoleInstance();
+ } else {
+ role = security.getRoleByName(roleName);
+ }
+
+ if (role != null) {
+ try {
+ Criteria criteria = new Criteria();
+ criteria.where(TurbineRolePermissionPeer.ROLE_ID, role.getId());
+ criteria.addJoin(TurbineRolePermissionPeer.PERMISSION_ID, TurbinePermissionPeer.PERMISSION_ID);
+ criteria.addAscendingOrderByColumn(TurbinePermissionPeer.PERMISSION_NAME);
+ return TurbinePermissionPeer.doSelect(criteria);
+ } catch (Exception e) {
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Return all permissions
+ */
+ public List<TurbinePermission> getPermissions() throws Exception {
+
+ try {
+ Criteria criteria = new Criteria();
+ criteria.addAscendingOrderByColumn(TurbinePermissionPeer.PERMISSION_NAME);
+ return TurbinePermissionPeer.doSelect(criteria);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ public User getUser() throws Exception {
+ String name = data.getParameters().getString("username");
+ if (StringUtils.isEmpty(name)) {
+ user = security.getUserInstance();
+ } else {
+ user = security.getUser(name);
+ }
+ return user;
+ }
+
+ public AccessControlList getACL() throws Exception {
+ // Get the Turbine ACL implementation
+ return security.getUserManager().getACL(getUser());
+ }
+
+ /**
+ */
+ public SelectorBox getFieldList() throws Exception {
+ Object[] names = { "username", "firstname", "middlename", "lastname" };
+ Object[] values = { "Username", "First Name", "Middle Name", "Last Name" };
+ return new SelectorBox("fieldList", names, values);
+ }
+
+ /**
+ */
+ public SelectorBox getUserFieldList() throws Exception {
+ /**
+ * This is a tie to the DB implementation something should be added the
+ * pluggable pieces to allow decent parameterized searching.
+ */
+
+ Object[] names = { TurbineUserPeer.LOGIN_NAME, TurbineUserPeer.FIRST_NAME, TurbineUserPeer.LAST_NAME };
+
+ Object[] values = { "User Name", "First Name", "Last Name" };
+
+ return new SelectorBox("fieldList", names, values);
+ }
+
+ /**
+ * Select all the users and place them in an array that can be used within the
+ * UserList.vm template.
+ */
+ @SuppressWarnings("unchecked")
+ public List<User> getUsers() throws Exception {
+ Criteria criteria = new Criteria();
+ String fieldList = data.getParameters().getString("fieldList");
+
+ if (fieldList != null) {
+ // This is completely database centric.
+ String searchField = data.getParameters().getString("searchField");
+ criteria.where(fieldList, searchField, Criteria.LIKE);
+ }
+
+ return (List<User>) security.getUserManager().retrieveList(criteria);
+ }
+
+ public void refresh(RunData data) {
+ this.data = data;
+ }
+
+ public void refresh() {
+ // nothing to do here
+ }
+
+}
diff --git a/src/main/resources/archetype-resources/src/main/webapp/WEB-INF/conf/TurbineResources.properties b/src/main/resources/archetype-resources/src/main/webapp/WEB-INF/conf/TurbineResources.properties
index a883250..4e954dc 100644
--- a/src/main/resources/archetype-resources/src/main/webapp/WEB-INF/conf/TurbineResources.properties
+++ b/src/main/resources/archetype-resources/src/main/webapp/WEB-INF/conf/TurbineResources.properties
@@ -131,7 +131,7 @@
# Default: org.apache.turbine.modules
# -------------------------------------------------------------------
-module.packages=${package}.modules,org.apache.turbine.modules
+module.packages=${package}.modules,${package}.flux.modules,org.apache.turbine.modules
# -------------------------------------------------------------------
#
@@ -541,6 +541,9 @@
tool.content.want.relative = true
tool.link.want.relative = true
+# Flux tool
+tool.request.flux=${package}.flux.tools.FluxTool
+
# -------------------------------------------------------------------
#
# V E L O C I T Y S E R V I C E
@@ -577,7 +580,7 @@
services.VelocityService.resource.loader = file
services.VelocityService.file.resource.loader.description = Velocity File Resource Loader
services.VelocityService.file.resource.loader.class = org.apache.velocity.runtime.resource.loader.FileResourceLoader
-services.VelocityService.file.resource.loader.path = /templates
+services.VelocityService.file.resource.loader.path = /templates/app,/templates/flux
services.VelocityService.file.resource.loader.cache = false
services.VelocityService.file.resource.loader.modificationCheckInterval = 2
@@ -653,3 +656,6 @@
#
# Default: none
# -------------------------------------------------------------------
+
+# Required to enable the flux library
+include=flux.properties
\ No newline at end of file
diff --git a/src/main/resources/archetype-resources/src/main/webapp/WEB-INF/conf/flux.properties b/src/main/resources/archetype-resources/src/main/webapp/WEB-INF/conf/flux.properties
new file mode 100644
index 0000000..687afc8
--- /dev/null
+++ b/src/main/resources/archetype-resources/src/main/webapp/WEB-INF/conf/flux.properties
@@ -0,0 +1,37 @@
+# -------------------------------------------------------------------
+#
+# F L U X S E R V I C E
+#
+# -------------------------------------------------------------------
+
+# ------------------------------------------------------------------------
+# Security Properties
+# ------------------------------------------------------------------------
+flux.admin.role = turbineadmin
+
+# ------------------------------------------------------------------------
+# Mode Properties
+# ------------------------------------------------------------------------
+# Flux can work in stand-alone mode and in embedded mode.
+# ------------------------------------------------------------------------
+
+flux.mode = embedded
+
+# ------------------------------------------------------------------------
+# Embedded Properties
+# ------------------------------------------------------------------------
+
+flux.embedded.layout = /Default.vm
+flux.embedded.show.menu = true
+
+# ------------------------------------------------------------------------
+# Stand-Alone Properties
+# ------------------------------------------------------------------------
+
+flux.stand.alone.layout = /FluxDefault.vm
+
+# ------------------------------------------------------------------------
+# UI Properties
+# ------------------------------------------------------------------------
+
+flux.ui.show.finder = false
diff --git a/src/main/resources/archetype-resources/src/main/webapp/templates/layouts/Default.vm b/src/main/resources/archetype-resources/src/main/webapp/templates/app/layouts/Default.vm
similarity index 100%
rename from src/main/resources/archetype-resources/src/main/webapp/templates/layouts/Default.vm
rename to src/main/resources/archetype-resources/src/main/webapp/templates/app/layouts/Default.vm
diff --git a/src/main/resources/archetype-resources/src/main/webapp/templates/macros/applicationMacros.vm b/src/main/resources/archetype-resources/src/main/webapp/templates/app/macros/applicationMacros.vm
similarity index 100%
rename from src/main/resources/archetype-resources/src/main/webapp/templates/macros/applicationMacros.vm
rename to src/main/resources/archetype-resources/src/main/webapp/templates/app/macros/applicationMacros.vm
diff --git a/src/main/resources/archetype-resources/src/main/webapp/templates/navigations/Menu.vm b/src/main/resources/archetype-resources/src/main/webapp/templates/app/navigations/Menu.vm
similarity index 100%
rename from src/main/resources/archetype-resources/src/main/webapp/templates/navigations/Menu.vm
rename to src/main/resources/archetype-resources/src/main/webapp/templates/app/navigations/Menu.vm
diff --git a/src/main/resources/archetype-resources/src/main/webapp/templates/screens/Error.vm b/src/main/resources/archetype-resources/src/main/webapp/templates/app/screens/Error.vm
similarity index 100%
rename from src/main/resources/archetype-resources/src/main/webapp/templates/screens/Error.vm
rename to src/main/resources/archetype-resources/src/main/webapp/templates/app/screens/Error.vm
diff --git a/src/main/resources/archetype-resources/src/main/webapp/templates/screens/Index.vm b/src/main/resources/archetype-resources/src/main/webapp/templates/app/screens/Index.vm
similarity index 100%
rename from src/main/resources/archetype-resources/src/main/webapp/templates/screens/Index.vm
rename to src/main/resources/archetype-resources/src/main/webapp/templates/app/screens/Index.vm
diff --git a/src/main/resources/archetype-resources/src/main/webapp/templates/screens/Login.vm b/src/main/resources/archetype-resources/src/main/webapp/templates/app/screens/Login.vm
similarity index 100%
rename from src/main/resources/archetype-resources/src/main/webapp/templates/screens/Login.vm
rename to src/main/resources/archetype-resources/src/main/webapp/templates/app/screens/Login.vm
diff --git a/src/main/resources/archetype-resources/src/main/webapp/templates/screens/Password.vm b/src/main/resources/archetype-resources/src/main/webapp/templates/app/screens/Password.vm
similarity index 100%
rename from src/main/resources/archetype-resources/src/main/webapp/templates/screens/Password.vm
rename to src/main/resources/archetype-resources/src/main/webapp/templates/app/screens/Password.vm
diff --git a/src/main/resources/archetype-resources/src/main/webapp/templates/screens/TestSecure.vm b/src/main/resources/archetype-resources/src/main/webapp/templates/app/screens/TestSecure.vm
similarity index 100%
rename from src/main/resources/archetype-resources/src/main/webapp/templates/screens/TestSecure.vm
rename to src/main/resources/archetype-resources/src/main/webapp/templates/app/screens/TestSecure.vm
diff --git a/src/main/resources/archetype-resources/src/main/webapp/templates/screens/showRecords.vm b/src/main/resources/archetype-resources/src/main/webapp/templates/app/screens/showRecords.vm
similarity index 100%
rename from src/main/resources/archetype-resources/src/main/webapp/templates/screens/showRecords.vm
rename to src/main/resources/archetype-resources/src/main/webapp/templates/app/screens/showRecords.vm
diff --git a/src/main/resources/archetype-resources/src/main/webapp/templates/flux/layouts/FluxDefault.vm b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/layouts/FluxDefault.vm
new file mode 100644
index 0000000..32f5bef
--- /dev/null
+++ b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/layouts/FluxDefault.vm
@@ -0,0 +1,30 @@
+<table class="w3-table">
+ <tr>
+ <td colspan="2" align="left">
+ $navigation.setTemplate("/FluxTop.vm")
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <hr noshade size="1">
+ </td>
+ </tr>
+ <tr>
+ <td width="10%" valign="top" align="left">
+ $navigation.setTemplate("/FluxMenu.vm")
+ </td>
+ <td width="*" valign="center">
+ $screen_placeholder
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <hr noshade size="1">
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ $navigation.setTemplate("/FluxBottom.vm")
+ </td>
+ </tr>
+</table>
diff --git a/src/main/resources/archetype-resources/src/main/webapp/templates/macros/applicationMacros.vm b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/macros/fluxMacros.vm
similarity index 100%
copy from src/main/resources/archetype-resources/src/main/webapp/templates/macros/applicationMacros.vm
copy to src/main/resources/archetype-resources/src/main/webapp/templates/flux/macros/fluxMacros.vm
diff --git a/src/main/resources/archetype-resources/src/main/webapp/templates/flux/navigations/FluxBottom.vm b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/navigations/FluxBottom.vm
new file mode 100644
index 0000000..2e988c3
--- /dev/null
+++ b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/navigations/FluxBottom.vm
@@ -0,0 +1,2 @@
+<font face="verdana,geneva,helvetica">
+</font>
diff --git a/src/main/resources/archetype-resources/src/main/webapp/templates/flux/navigations/FluxMenu.vm b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/navigations/FluxMenu.vm
new file mode 100644
index 0000000..2c4f800
--- /dev/null
+++ b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/navigations/FluxMenu.vm
@@ -0,0 +1,16 @@
+## -----------------------------------------------------------------------
+## Menu.vm
+##
+## Main menu for the Turbine administration application.
+## -----------------------------------------------------------------------
+<font size="-1" face="$ui.sansSerifFonts">
+ <a href="$link.setPage("user,FluxUserList.vm")">Users</a>
+ <br>
+ <a href="$link.setPage("group,FluxGroupList.vm")">Groups</a>
+ <br>
+ <a href="$link.setPage("role,FluxRoleList.vm")">Roles</a>
+ <br>
+ <a href="$link.setPage("permission,FluxPermissionList.vm")">Permissions</a>
+ <p>
+ <a href="$link.setAction("FluxLogout").setPage("FluxLogin.vm")">Logout</a>
+</font>
diff --git a/src/main/resources/archetype-resources/src/main/webapp/templates/flux/navigations/FluxTop.vm b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/navigations/FluxTop.vm
new file mode 100644
index 0000000..ff1450c
--- /dev/null
+++ b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/navigations/FluxTop.vm
@@ -0,0 +1,3 @@
+<img src="$ui.image($ui.logo)">
+<font face="verdana,geneva,helvetica">
+</font>
diff --git a/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/FluxEmbeddedMenu.vm b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/FluxEmbeddedMenu.vm
new file mode 100644
index 0000000..3abf426
--- /dev/null
+++ b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/FluxEmbeddedMenu.vm
@@ -0,0 +1,12 @@
+## -----------------------------------------------------------------------
+## Menu.vm
+##
+## Main menu for the Turbine administration application.
+## -----------------------------------------------------------------------
+<div class="w3-container">
+ <a href="$link.setPage("user,FluxUserList.vm")" class="w3-btn w3-round w3-white w3-border w3-border-blue">Users</a>
+ <a href="$link.setPage("group,FluxGroupList.vm")" class="w3-btn w3-round w3-white w3-border w3-border-blue">Groups</a>
+ <a href="$link.setPage("role,FluxRoleList.vm")" class="w3-btn w3-round w3-white w3-border w3-border-blue">Roles</a>
+ <a href="$link.setPage("permission,FluxPermissionList.vm")" class="w3-btn w3-round w3-white w3-border w3-border-blue">Permissions</a>
+</div>
+
diff --git a/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/FluxError.vm b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/FluxError.vm
new file mode 100644
index 0000000..782f192
--- /dev/null
+++ b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/FluxError.vm
@@ -0,0 +1,26 @@
+#**
+
+@author <a href="mailto:jvanzyl@periapt.com">Jason van Zyl</a>
+@version $Id: FluxError.vm,v 1.1.1.1 2003-06-19 23:07:21 painter Exp $
+
+*#
+
+$page.setBgColor($ui.bgcolor)
+$page.setTitle("")
+
+<font face="$ui.sansSerifFonts">
+
+The reason for the error was:
+<p>
+<pre>
+$processingException
+
+</pre>
+
+<P>
+The stacktrace is as follows:
+<pre>
+$stackTrace
+</pre>
+
+</font>
diff --git a/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/FluxIndex.vm b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/FluxIndex.vm
new file mode 100644
index 0000000..666a155
--- /dev/null
+++ b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/FluxIndex.vm
@@ -0,0 +1,15 @@
+#**
+
+@author <a href="mailto:jvanzyl@periapt.com">Jason van Zyl</a>
+@version $Id: FluxIndex.vm,v 1.1.1.1 2003-06-19 23:07:21 painter Exp $
+
+*#
+
+$page.setBgColor($ui.bgcolor)
+$page.setTitle("Home Page")
+
+<font face="$ui.sansSerifFonts">
+
+This is the administration application.
+
+</font>
diff --git a/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/FluxLogin.vm b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/FluxLogin.vm
new file mode 100644
index 0000000..9d4e80a
--- /dev/null
+++ b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/FluxLogin.vm
@@ -0,0 +1,28 @@
+#**
+
+@author <a href="mailto:jvanzyl@periapt.com">Jason van Zyl</a>
+@version $Id: FluxLogin.vm,v 1.1.1.1 2003-06-19 23:07:21 painter Exp $
+
+*#
+
+$page.setTitle("Please Login")
+$page.setBgColor($ui.bgcolor)
+
+<form method="post" action="$link.setAction("FluxLogin").setPage("FluxIndex.vm")">
+ <table>
+ <tr>
+ #formCell ("User" "username" "")
+ </tr>
+ <tr>
+ #formPasswordCell ("Password" "password" "")
+ </tr>
+ <tr>
+ <td>
+ <font face="$ui.sansSerifFonts">
+ <input type="submit" value="Login">
+ </font>
+ </td>
+ </tr>
+ </table>
+</form>
+
diff --git a/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/group/FluxGroupAlreadyExists.vm b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/group/FluxGroupAlreadyExists.vm
new file mode 100644
index 0000000..c8b7c55
--- /dev/null
+++ b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/group/FluxGroupAlreadyExists.vm
@@ -0,0 +1,13 @@
+#**
+
+@author <a href="mailto:jvanzyl@periapt.com">Jason van Zyl</a>
+@author <a href="mailto:jeff@jivecast.com">Jeffery Painter</a>
+@version $Id: FluxGroupAlreadyExists.vm,v 1.1.1.1 2017-11-26 23:07:21 painter Exp $
+
+*#
+
+<div class="w3-panel w3-red">
+The group name <b>$name</b> is currently is use. Group names
+must be unique, please choose another group name.
+</div>
+
diff --git a/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/group/FluxGroupForm.vm b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/group/FluxGroupForm.vm
new file mode 100644
index 0000000..ec00606
--- /dev/null
+++ b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/group/FluxGroupForm.vm
@@ -0,0 +1,50 @@
+#**
+
+@author <a href="mailto:jeff@jivecast.com">Jeffery Painter</a>
+@author <a href="mailto:jvanzyl@periapt.com">Jason van Zyl</a>
+@version $Id: FluxGroupForm.vm,v 1.1.1.1 2017-11-26 23:07:21 painter Exp $
+
+*#
+
+
+#if ($errorTemplate)
+ #parse ($errorTemplate)
+#end
+
+
+<form
+ method="post"
+ action="$link.setPage("group,FluxGroupList.vm").setAction("group.FluxGroupAction")">
+
+ <table class="w3-table w3-striped w3-bordered">
+ <tr>
+ #formCell ("Group Name" "group" $!flux.Group.Name)
+ </tr>
+ </table>
+
+ #*
+ Check for a mode, the update and delete buttons
+ shouldn't appear when inserting a new user.
+ *#
+
+ #if ($flux.Mode == "modify")
+ <input type="hidden" name="oldName" value="$!flux.Group.Name">
+ <div class="w3-container w3-padding w3-center">
+ <input class="w3-btn w3-blue w3-round" style="width: 180px;" type="submit" name="eventSubmit_doUpdate" value="Update Group"/>
+ </div>
+ #elseif ($flux.Mode == "delete")
+ <div class="w3-container w3-padding w3-center">
+ <input class="w3-btn w3-blue w3-round" style="width: 180px;" type="submit" name="eventSubmit_doDelete" value="Confirm Deletion"/>
+ </div>
+ #else
+ <div class="w3-container w3-padding w3-center">
+ <input class="w3-btn w3-blue w3-round" style="width: 180px;" type="submit" name="eventSubmit_doInsert" value="Add Group"/>
+ </div>
+ #end
+
+</form>
+
+#if ($showEmbeddedMenu)
+ <hr size="1" noshade>
+ #parse ("screens/FluxEmbeddedMenu.vm")
+#end
diff --git a/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/group/FluxGroupList.vm b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/group/FluxGroupList.vm
new file mode 100644
index 0000000..3b96ff8
--- /dev/null
+++ b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/group/FluxGroupList.vm
@@ -0,0 +1,43 @@
+#**
+
+@author <a href="mailto:jeff@jivecast.com">Jeffery Painter</a>
+@author <a href="mailto:jvanzyl@periapt.com">Jason van Zyl</a>
+@version $Id: FluxGroupList.vm,v 1.1.1.1 2017-11-26 23:07:21 painter Exp $
+
+*#
+
+$page.setTitle("Group Adminstration")
+$page.setBgColor($ui.bgcolor)
+
+#set ( $headings = ["Group Name"] )
+
+<div class="w3-container w3-padding"> <h2>Current Groups</h2> </div>
+
+<table class="w3-table w3-striped w3-bordered">
+ <tr>
+ #foreach ($heading in $headings)
+ <th>$heading</th>
+ #end
+ </tr>
+
+ #foreach ($group in $flux.Groups)
+ <tr>
+ <td>$group.Name</td>
+ <td>
+ <a href="$link.setPage("group,FluxGroupForm.vm").addPathInfo("group",$group.Name).addQueryData("mode","modify")">Details</a>
+ <a href="$link.setPage("group,FluxGroupForm.vm").addPathInfo("group",$group.Name).addQueryData("mode","delete")">Remove</a>
+ </td>
+ </tr>
+ #end
+</table>
+
+
+<div class="w3-container w3-padding w3-center">
+ <a class="w3-btn w3-blue w3-round" style="width: 180px;"
+ href="$link.setPage("group,FluxGroupForm.vm").addQueryData("mode","insert")">Add Group</a>
+</div>
+
+#if ($showEmbeddedMenu)
+ <hr size="1" noshade>
+ #parse ("screens/FluxEmbeddedMenu.vm")
+#end
diff --git a/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/permission/FluxPermissionAlreadyExists.vm b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/permission/FluxPermissionAlreadyExists.vm
new file mode 100644
index 0000000..48f0f79
--- /dev/null
+++ b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/permission/FluxPermissionAlreadyExists.vm
@@ -0,0 +1,11 @@
+#**
+
+@author <a href="mailto:jvanzyl@periapt.com">Jason van Zyl</a>
+@version $Id: FluxPermissionAlreadyExists.vm,v 1.1.1.1 2017-11-26 23:07:21 painter Exp $
+
+*#
+
+<div class="w3-panel w3-red">
+The permission name <b>$name</b> is currently is use. Permission
+names must be unique, please choose another name.
+</div>
\ No newline at end of file
diff --git a/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/permission/FluxPermissionForm.vm b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/permission/FluxPermissionForm.vm
new file mode 100644
index 0000000..5462737
--- /dev/null
+++ b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/permission/FluxPermissionForm.vm
@@ -0,0 +1,48 @@
+#**
+
+@author <a href="mailto:jvanzyl@periapt.com">Jason van Zyl</a>
+@version $Id: FluxPermissionForm.vm,v 1.1.1.1 2003-06-19 23:07:21 painter Exp $
+
+*#
+
+#if ($errorTemplate)
+ #parse ($errorTemplate)
+#end
+
+<form
+ method="post"
+ action="$link.setPage("permission,FluxPermissionList.vm").setAction("permission.FluxPermissionAction")">
+
+ <table class="w3-table w3-striped w3-bordered">
+ <tr>
+ #formCell ("Permission Name" "permission" $!flux.Permission.Name)
+ </tr>
+ </table>
+
+
+ #*
+ Check for a mode, the update and delete buttons
+ shouldn't appear when inserting a new user.
+ *#
+
+ #if ($flux.Mode == "modify")
+ <input type="hidden" name="oldName" value="$!flux.Permission.Name">
+ <div class="w3-container w3-padding w3-center">
+ <input class="w3-btn w3-blue w3-round" style="width: 180px;" type="submit" name="eventSubmit_doUpdate" value="Update Permission"/>
+ </div>
+ #elseif ($flux.Mode == "delete")
+ <div class="w3-container w3-padding w3-center">
+ <input class="w3-btn w3-blue w3-round" style="width: 180px;" type="submit" name="eventSubmit_doDelete" value="Confirm Deletion"/>
+ </div>
+ #else
+ <div class="w3-container w3-padding w3-center">
+ <input class="w3-btn w3-blue w3-round" style="width: 180px;" type="submit" name="eventSubmit_doInsert" value="Add Permission"/>
+ </div>
+ #end
+
+</form>
+
+#if ($showEmbeddedMenu)
+ <hr size="1" noshade>
+ #parse ("screens/FluxEmbeddedMenu.vm")
+#end
diff --git a/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/permission/FluxPermissionList.vm b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/permission/FluxPermissionList.vm
new file mode 100644
index 0000000..72266eb
--- /dev/null
+++ b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/permission/FluxPermissionList.vm
@@ -0,0 +1,42 @@
+#**
+
+@author <a href="mailto:jeff@jivecast.com">Jeffery Painter</a>
+@author <a href="mailto:jvanzyl@periapt.com">Jason van Zyl</a>
+@version $Id: FluxPermissionList.vm,v 1.1.1.1 2017-11-26 23:07:21 painter Exp $
+
+*#
+
+## Load permissions
+#set ( $permissions = $flux.Permissions )
+
+<h2>All Permissions</h2>
+
+#set ( $headings = ["Permission Name"] )
+
+<table class="w3-table w3-bordered">
+ <tr>
+ #foreach ($heading in $headings)
+ <th>$heading</th>
+ #end
+ </tr>
+
+ #foreach ($permission in $permissions )
+ <tr>
+ <td> $permission.Name </td>
+ <td>
+ <a href="$link.setPage("permission,FluxPermissionForm.vm").addPathInfo("permission",$permission.Name).addQueryData("mode","modify")">Details</a>
+ <a href="$link.setPage("permission,FluxPermissionForm.vm").addPathInfo("permission",$permission.Name).addQueryData("mode","delete")">Remove</a>
+ </td>
+ </tr>
+ #end
+</table>
+
+<div class="w3-container w3-padding w3-center">
+ <a class="w3-btn w3-blue w3-round" style="width: 180px;"
+ href="$link.setPage("permission,FluxPermissionForm.vm").addQueryData("mode","insert")">Add New Permission</a>
+</div>
+
+#if ($showEmbeddedMenu)
+ <hr size="1" noshade>
+ #parse ("screens/FluxEmbeddedMenu.vm")
+#end
diff --git a/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/permission/FluxRolePermissionList.vm b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/permission/FluxRolePermissionList.vm
new file mode 100644
index 0000000..aa2c8aa
--- /dev/null
+++ b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/permission/FluxRolePermissionList.vm
@@ -0,0 +1,56 @@
+#**
+
+This template is used to display the permissions
+for a role.
+
+@author <a href="mailto:jeff@jivecast.com">Jeffery Painter</a>
+@author <a href="mailto:jvanzyl@periapt.com">Jason van Zyl</a>
+@version $Id: FluxRolePermissionForm.vm,v 1.1.1.1 2017-11-26 23:07:21 painter Exp $
+
+*#
+
+#set ( $role = $flux.Role )
+
+## Load permissions
+#set ( $permissions = $flux.Permissions )
+<h2>Permissions for the $role.Name role</h2>
+
+<form method="post" action="$link.setAction("role.FluxRoleAction").setPage("role,FluxRoleList.vm")">
+
+ <input type="hidden" name="role" value="$role.Name">
+
+ <table class="w3-table w3-bordered">
+ <tr>
+ <th> Permission </th>
+ <th> Assign to Role </th>
+ </tr>
+
+ #foreach ($permission in $permissions)
+ <tr>
+ <td align="right"> <b>$!permission.Name</b> </td>
+ <td align="center">
+ #if ($role.Permissions.contains($permission))
+ #set ($checked = "checked")
+ #else
+ #set ($checked = "")
+ #end
+ <input type="checkbox" name="${role.Name}${permission.Name}" $checked>
+ </td>
+ </tr>
+ #end
+
+ <tr>
+ <td>
+ <input class="w3-button w3-blue" type="submit" name="eventSubmit_doPermissions" value="Update Permissions">
+ </td>
+ </tr>
+ </table>
+
+</form>
+
+#if ($showEmbeddedMenu)
+ <hr size="1" noshade>
+ #parse ("screens/FluxEmbeddedMenu.vm")
+#end
+
+
diff --git a/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/role/FluxRoleAlreadyExists.vm b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/role/FluxRoleAlreadyExists.vm
new file mode 100644
index 0000000..775ce2e
--- /dev/null
+++ b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/role/FluxRoleAlreadyExists.vm
@@ -0,0 +1,16 @@
+#**
+
+Display this template when the administrator
+tries to add a user with a role that is
+already being used by the security system.
+
+@author <a href="mailto:jvanzyl@periapt.com">Jason van Zyl</a>
+@version $Id: FluxRoleAlreadyExists.vm,v 1.1.1.1 2017-11-26 23:07:21 painter Exp $
+
+*#
+
+<div class="w3-panel w3-red">
+The role name <b>$name</b> is currently in use. Role names
+must be unique, please choose another role name.
+</div>
+
diff --git a/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/role/FluxRoleForm.vm b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/role/FluxRoleForm.vm
new file mode 100644
index 0000000..21ff042
--- /dev/null
+++ b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/role/FluxRoleForm.vm
@@ -0,0 +1,51 @@
+#**
+
+This template is used to display the details of
+a role.
+
+@author <a href="mailto:jeff@jivecast.com">Jeffery Painter</a>
+@author <a href="mailto:jvanzyl@periapt.com">Jason van Zyl</a>
+@version $Id: FluxRoleForm.vm,v 1.1.1.1 2017-11-26 23:07:21 painter Exp $
+
+*#
+
+#if ($errorTemplate)
+ #parse ($errorTemplate)
+#end
+
+<form
+ method="post"
+ action="$link.setPage("role,FluxRoleList.vm").setAction("role.FluxRoleAction")">
+
+ <table class="w3-table w3-striped w3-bordered">
+ <tr>
+ #formCell ("Role Name" "role" $!flux.Role.Name)
+ </tr>
+ </table>
+
+ #*
+ Check for a mode, the update and delete buttons
+ shouldn't appear when inserting a new user.
+ *#
+
+ #if ($flux.Mode == "modify")
+ <input type="hidden" name="oldName" value="$!flux.Role.Name">
+ <div class="w3-container w3-padding w3-center">
+ <input class="w3-btn w3-blue w3-round" style="width: 180px;" type="submit" name="eventSubmit_doUpdate" value="Update Role"/>
+ </div>
+ #elseif ($flux.Mode == "delete")
+ <div class="w3-container w3-padding w3-center">
+ <input class="w3-btn w3-blue w3-round" style="width: 180px;" type="submit" name="eventSubmit_doDelete" value="Confirm Deletion"/>
+ </div>
+ #else
+ <div class="w3-container w3-padding w3-center">
+ <input class="w3-btn w3-blue w3-round" style="width: 180px;" type="submit" name="eventSubmit_doInsert" value="Add Role"/>
+ </div>
+ #end
+
+</form>
+
+#if ($showEmbeddedMenu)
+ <hr size="1" noshade>
+ #parse ("screens/FluxEmbeddedMenu.vm")
+#end
diff --git a/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/role/FluxRoleList.vm b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/role/FluxRoleList.vm
new file mode 100644
index 0000000..99da13b
--- /dev/null
+++ b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/role/FluxRoleList.vm
@@ -0,0 +1,42 @@
+#**
+
+Used to display the roles currently being used in
+the application.
+
+*#
+#set ( $headings = ["Role Name"] )
+
+<div class="w3-container w3-padding"> <h2>Current Roles</h2> </div>
+
+<table class="w3-table w3-striped w3-bordered">
+ <tr>
+ #foreach ($heading in $headings)
+ <th>$heading</th>
+ #end
+ </tr>
+
+ #foreach ($role in $flux.Roles)
+ #if ( $role.Name != "super_admin" )
+ <tr>
+ <td> $role.Name </td>
+ <td>
+ <a href="$link.setPage("permission,FluxRolePermissionList.vm").addPathInfo("role",$role.Name)">Permissions</a>
+ <a href="$link.setPage("role,FluxRoleForm.vm").addPathInfo("role",$role.Name).addQueryData("mode","modify")">Details</a>
+ <a href="$link.setPage("role,FluxRoleForm.vm").addPathInfo("role",$role.Name).addQueryData("mode","delete")">Remove</a>
+ </td>
+ </tr>
+ #end
+ #end
+
+</table>
+
+ <div class="w3-container w3-padding w3-center">
+ <a class="w3-btn w3-blue w3-round" style="width: 180px;"
+ href="$link.setPage("role,FluxRoleForm.vm").addQueryData("mode","insert")">Add New Role</a>
+ </div>
+
+
+#if ($showEmbeddedMenu)
+ <hr size="1" noshade>
+ #parse ("screens/FluxEmbeddedMenu.vm")
+#end
diff --git a/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/user/FluxMissingRequiredInputs.vm b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/user/FluxMissingRequiredInputs.vm
new file mode 100644
index 0000000..534c640
--- /dev/null
+++ b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/user/FluxMissingRequiredInputs.vm
@@ -0,0 +1,15 @@
+#*
+@author <a href="mailto:jvanzyl@periapt.com">Jason van Zyl</a>
+@author <a href="mailto:jeff@jivecast.com">Jeffery Painter</a>
+@version $Id: FluxMissingRequiredInputs.vm,v 1.1.1.1 2017-11-26 23:07:21 painter Exp $
+*#
+
+<div class="w3-panel w3-red">
+The following fields are required and must be entered before
+this request can be processed:
+<ul>
+#foreach ($missingRequiredInput in $missingRequiredInputs)
+ <li>$missingRequiredInput</li>
+#end
+</ul>
+</div>
\ No newline at end of file
diff --git a/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/user/FluxUserAlreadyExists.vm b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/user/FluxUserAlreadyExists.vm
new file mode 100644
index 0000000..a2b3572
--- /dev/null
+++ b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/user/FluxUserAlreadyExists.vm
@@ -0,0 +1,13 @@
+#*
+
+@author <a href="mailto:jvanzyl@periapt.com">Jason van Zyl</a>
+@author <a href="mailto:jeff@jivecast.com">Jeffery Painter</a>
+@version $Id: FluxUserAlreadyExists.vm,v 1.1.1.1 2017-11-26 23:07:21 painter Exp $
+
+*#
+
+<div class="w3-panel w3-red">
+The username <b>$username</b> is currently is use. Usernames
+must be unique for all users, please choose another username.
+</div>
+
diff --git a/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/user/FluxUserForm.vm b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/user/FluxUserForm.vm
new file mode 100644
index 0000000..5858b11
--- /dev/null
+++ b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/user/FluxUserForm.vm
@@ -0,0 +1,66 @@
+#**
+
+Display the details of a user.
+
+@author <a href="mailto:jeff@jivecast.com">Jeffery Painter</a>
+@author <a href="mailto:jvanzyl@periapt.com">Jason van Zyl</a>
+@version $Id: FluxUserForm.vm,v 1.10 2017-11-17 19:21:40 painter Exp $
+
+*#
+
+<!-- Primary edit screen -->
+<form
+ name="user"
+ method="post"
+ action="$link.setAction("user.FluxUserAction").setPage("user,FluxUserList.vm")">
+
+ #set ( $user = $flux.User )
+
+ <div class="w3-container w3-padding w3-gray">
+
+ <div class="w3-row-padding">
+
+ <!-- User Info -->
+ <div class="w3-half">
+ <div class="w3-card-8 w3-padding w3-white">
+ <div id="newUser" style="display: block;">
+ <h2>User Details</h2>
+ #formCell( "Username" "username" $!user.Name )
+ #formCell( "First Name" "firstName" $!user.FirstName )
+ #formCell( "Last Name" "lastName" $!user.LastName )
+ #formCell( "Email" "email" $!user.Email )
+ </div>
+ </div>
+ </div>
+ <div class="w3-half">
+ <div id="userPassword" class="w3-card-8 w3-padding w3-white">
+ <h2>Password</h2>
+
+ <p>
+ Password required for user to login. If you are just
+ updating a user's details, leave the password field
+ blank, otherwise, new password will be set
+
+ #formPasswordCell( "New Password" "password" "" )
+
+ </div>
+ </div>
+ </div>
+ </div>
+ <p>
+ #if ($flux.Mode == "modify")
+ <input type="submit" name="eventSubmit_doUpdate" value="Update User">
+ #elseif ($flux.Mode == "delete")
+ <input type="submit" name="eventSubmit_doDelete" value="Delete User">
+ #else
+ <input type="submit" name="eventSubmit_doInsert" value="Add User">
+ #end
+
+ <p>
+
+ #if ($showEmbeddedMenu)
+ <hr size="1" noshade>
+ #parse ("screens/FluxEmbeddedMenu.vm")
+ #end
+
+</form>
diff --git a/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/user/FluxUserList.vm b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/user/FluxUserList.vm
new file mode 100644
index 0000000..cdc0d43
--- /dev/null
+++ b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/user/FluxUserList.vm
@@ -0,0 +1,38 @@
+#set ( $headings = ["Username","First Name", "Last Name", "Email", "Admin" ] )
+
+
+<div class="w3-container w3-padding"> <h2>Current Users</h2> </div>
+
+<table class="w3-table w3-striped w3-bordered">
+ <tr>
+ #foreach ($heading in $headings)
+ <th>$heading</th>
+ #end
+ </tr>
+
+ #foreach ($user in $flux.getUsers() )
+ <tr>
+ <td>$!user.Name</td>
+ <td>$!user.FirstName</td>
+ <td>$!user.LastName</td>
+ <td><a href=mailto:$!user.Email>$!user.Email</a></td>
+ <td><a href="$link.setPage("user,FluxUserForm.vm").addQueryData("username","$user.Name").addQueryData("mode","modify")">Details</a>
+ <a href="$link.setPage("user,FluxUserRoleForm.vm").addQueryData("username","$user.Name").addQueryData("mode","modify")">Roles</a>
+ <a href="$link.setPage("user,FluxUserForm.vm").addQueryData("username","$user.Name").addQueryData("mode","delete")">Remove</a>
+ </td>
+ </tr>
+ #end
+
+</table>
+<p>
+ <div class="w3-container w3-padding w3-center">
+ <a class="w3-btn w3-blue w3-round" style="width: 180px;"
+ href="$link.setPage("user,FluxUserForm.vm").addQueryData("mode","insert")">Add
+ New User</a>
+ </div>
+
+#if ($showEmbeddedMenu)
+ <hr size="1" noshade>
+ #parse ("screens/FluxEmbeddedMenu.vm")
+#end
+
\ No newline at end of file
diff --git a/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/user/FluxUserRoleForm.vm b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/user/FluxUserRoleForm.vm
new file mode 100644
index 0000000..e11ad78
--- /dev/null
+++ b/src/main/resources/archetype-resources/src/main/webapp/templates/flux/screens/user/FluxUserRoleForm.vm
@@ -0,0 +1,63 @@
+#**
+
+@author <a href="mailto:jvanzyl@periapt.com">Jason van Zyl</a>
+@author <a href="mailto:jeff@jivecast.com">Jeffery Painter</a>
+@version $Id: FluxUserRoleForm.vm,v 1.1.1.1 2017-11-26 23:07:21 painter Exp $
+
+*#
+
+#set ($user = $flux.User)
+#set ($acl = $flux.ACL)
+
+<h2>Roles for $user.FirstName $user.LastName</h2>
+
+<form method="post" action="$link.setAction("user.FluxUserAction").setPage("user,FluxUserList.vm")">
+
+<input type="hidden" name="username" value="$user.Name">
+<table class="w3-table w3-bordered">
+ <tr>
+ <th> </th>
+
+ #foreach ($role in $flux.Roles)
+ #if ( $role.Name != "super_admin" )
+ <th>
+ <b>$role.Name</b>
+ </th>
+ #end
+ #end
+
+ </tr>
+
+
+ #foreach ($group in $flux.Groups)
+ <tr>
+ <td>
+ <b>$group.Name</b>
+ </td>
+
+ #foreach ($role in $flux.Roles)
+ #if ( $role.Name != "super_admin" )
+ #if ($acl.hasRole($role, $group))
+ #set ($checked = "checked")
+ #else
+ #set ($checked = "")
+ #end
+
+ <td align="center">
+ <input type="checkbox" class="w3-checkbox" name="${group.Name}${role.Name}" $checked>
+ </td>
+ #end
+ #end
+
+ </tr>
+ #end
+
+ <tr>
+ <td>
+ <input type="submit" name="eventSubmit_doRoles" value="Update Roles">
+ </td>
+ </tr>
+
+</table>
+
+</form>