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> &nbsp;
+			<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> &nbsp;
+              <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> &nbsp;
+	              <a href="$link.setPage("role,FluxRoleForm.vm").addPathInfo("role",$role.Name).addQueryData("mode","modify")">Details</a> &nbsp;
+	              <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>
+			&nbsp; <a href="$link.setPage("user,FluxUserRoleForm.vm").addQueryData("username","$user.Name").addQueryData("mode","modify")">Roles</a>
+			&nbsp; <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> &nbsp; </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>