diff --git a/source/java/org/alfresco/web/app/servlet/AbstractAuthenticationFilter.java b/source/java/org/alfresco/web/app/servlet/AbstractAuthenticationFilter.java new file mode 100644 index 0000000000..cb229ee27f --- /dev/null +++ b/source/java/org/alfresco/web/app/servlet/AbstractAuthenticationFilter.java @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Alfresco Network License. You may obtain a + * copy of the License at + * + * http://www.alfrescosoftware.com/legal/ + * + * Please view the license relevant to your network subscription. + * + * BY CLICKING THE "I UNDERSTAND AND ACCEPT" BOX, OR INSTALLING, + * READING OR USING ALFRESCO'S Network SOFTWARE (THE "SOFTWARE"), + * YOU ARE AGREEING ON BEHALF OF THE ENTITY LICENSING THE SOFTWARE + * ("COMPANY") THAT COMPANY WILL BE BOUND BY AND IS BECOMING A PARTY TO + * THIS ALFRESCO NETWORK AGREEMENT ("AGREEMENT") AND THAT YOU HAVE THE + * AUTHORITY TO BIND COMPANY. IF COMPANY DOES NOT AGREE TO ALL OF THE + * TERMS OF THIS AGREEMENT, DO NOT SELECT THE "I UNDERSTAND AND AGREE" + * BOX AND DO NOT INSTALL THE SOFTWARE OR VIEW THE SOURCE CODE. COMPANY + * HAS NOT BECOME A LICENSEE OF, AND IS NOT AUTHORIZED TO USE THE + * SOFTWARE UNLESS AND UNTIL IT HAS AGREED TO BE BOUND BY THESE LICENSE + * TERMS. THE "EFFECTIVE DATE" FOR THIS AGREEMENT SHALL BE THE DAY YOU + * CHECK THE "I UNDERSTAND AND ACCEPT" BOX. + */ +package org.alfresco.web.app.servlet; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.StringTokenizer; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +public class AbstractAuthenticationFilter +{ + + private static Log logger = LogFactory.getLog(AbstractAuthenticationFilter.class); + + public AbstractAuthenticationFilter() + { + super(); + } + + + /** + * Parse the Accept-Lanaguage HTTP header value + * + * @param req HttpServletRequest + * @return Locale + */ + protected static final Locale parseAcceptLanguageHeader(HttpServletRequest req, List m_languages) + { + // Default the locale + + Locale locale = Locale.getDefault(); + + // Get the accept language header value + + String acceptHeader = req.getHeader("Accept-Language"); + if ( acceptHeader != null) + { + // Parse the accepted language list + + StringTokenizer tokens = new StringTokenizer(acceptHeader, ","); + List langList = new ArrayList(); + + while ( tokens.hasMoreTokens()) + { + // Get the current language token + + String lang = tokens.nextToken(); + float quality = 1.0f; + + // Check if the optional quality has been specified + + int qpos = lang.indexOf(";"); + if ( qpos != -1) + { + // Parse the quality value + + try + { + quality = Float.parseFloat(lang.substring(qpos+3)); + } + catch (NumberFormatException ex) + { + logger.error("Error parsing Accept-Language value " + lang); + } + + // Strip the quality value from the language token + + lang = lang.substring(0,qpos); + } + + // Add the language to the list + + langList.add(new AcceptLanguage(lang, quality)); + } + + // Debug + + if ( logger.isDebugEnabled()) + logger.debug("Accept-Language list : " + langList); + + // Match the client languages to the available locales + + if ( langList.size() > 0) + { + // Search for the best match locale to use for this client + + AcceptLanguage useLang = null; + String useName = null; + boolean match = false; + + for ( AcceptLanguage curLang : langList) + { + // Match against the available languages + + for(String availLang : m_languages) + { + // The accept language may be in 'cc' or 'cc_cc' format + + match = false; + + if ( curLang.getLanguage().length() == 2) + { + if ( availLang.startsWith(curLang.getLanguage())) + match = true; + } + else if ( availLang.equalsIgnoreCase(curLang.getLanguage())) + match = true; + + // If we found a match check if it is a higher quality than the current match. + // If the quality is the same we stick with the existing match as it was nearer the + // start of the list. + + if ( match == true) + { + if ( useLang == null || + ( curLang.getQuality() > useLang.getQuality())) + { + useLang = curLang; + useName = availLang; + } + } + } + } + + // Debug + + if ( logger.isDebugEnabled()) + logger.debug("Accept-Language using " + (useLang != null ? useLang.toString() : "")); + + // Create the required user locale + + if ( useLang != null) + { + Locale useLocale = AcceptLanguage.createLocale(useName); + if ( useLocale != null) + { + locale = useLocale; + + // Debug + + if ( logger.isDebugEnabled()) + logger.debug("Using language " + useLang + ", locale " + locale); + } + } + } + } + + // Return the selected locale + + return locale; + } + +} diff --git a/source/java/org/alfresco/web/app/servlet/AcceptLanguage.java b/source/java/org/alfresco/web/app/servlet/AcceptLanguage.java new file mode 100644 index 0000000000..2407b1820c --- /dev/null +++ b/source/java/org/alfresco/web/app/servlet/AcceptLanguage.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Alfresco Network License. You may obtain a + * copy of the License at + * + * http://www.alfrescosoftware.com/legal/ + * + * Please view the license relevant to your network subscription. + * + * BY CLICKING THE "I UNDERSTAND AND ACCEPT" BOX, OR INSTALLING, + * READING OR USING ALFRESCO'S Network SOFTWARE (THE "SOFTWARE"), + * YOU ARE AGREEING ON BEHALF OF THE ENTITY LICENSING THE SOFTWARE + * ("COMPANY") THAT COMPANY WILL BE BOUND BY AND IS BECOMING A PARTY TO + * THIS ALFRESCO NETWORK AGREEMENT ("AGREEMENT") AND THAT YOU HAVE THE + * AUTHORITY TO BIND COMPANY. IF COMPANY DOES NOT AGREE TO ALL OF THE + * TERMS OF THIS AGREEMENT, DO NOT SELECT THE "I UNDERSTAND AND AGREE" + * BOX AND DO NOT INSTALL THE SOFTWARE OR VIEW THE SOURCE CODE. COMPANY + * HAS NOT BECOME A LICENSEE OF, AND IS NOT AUTHORIZED TO USE THE + * SOFTWARE UNLESS AND UNTIL IT HAS AGREED TO BE BOUND BY THESE LICENSE + * TERMS. THE "EFFECTIVE DATE" FOR THIS AGREEMENT SHALL BE THE DAY YOU + * CHECK THE "I UNDERSTAND AND ACCEPT" BOX. + */ + +package org.alfresco.web.app.servlet; + +import java.util.Locale; +import java.util.StringTokenizer; + +/** + * Accept Language Class + * + *

Holds the details of an accepted language from a HTTP Accept-Language header + * + * @author gkspencer + */ +class AcceptLanguage +{ + // Language name + + private String m_language; + + // Quality + + private float m_quality = 1.0f; + + /** + * Class constructor + * + * @param lang String + * @param quality float + */ + public AcceptLanguage(String lang, float quality) + { + // Convert the language to Java format + + m_language = lang.replace('-', '_'); + m_quality = quality; + } + + /** + * Return the language + * + * @return String + */ + public final String getLanguage() + { + return m_language; + } + + /** + * Return the quality + * + * @return float + */ + public final float getQuality() + { + return m_quality; + } + + /** + * Create a locale for this language + * + * @return Locale + */ + public final Locale createLocale() + { + return createLocale(getLanguage()); + } + + /** + * Create a locale for this language + * + * @param locName String + * @return Locale + */ + public final static Locale createLocale(String locName) + { + Locale locale = null; + + StringTokenizer t = new StringTokenizer(locName, "_"); + int tokens = t.countTokens(); + if (tokens == 1) + { + locale = new Locale(locName); + } + else if (tokens == 2) + { + locale = new Locale(t.nextToken(), t.nextToken()); + } + else if (tokens == 3) + { + locale = new Locale(t.nextToken(), t.nextToken(), t.nextToken()); + } + + return locale; + } + + /** + * Return the accept language as a string + * + * @return String + */ + public String toString() + { + StringBuilder str = new StringBuilder(); + + str.append("["); + str.append(getLanguage()); + str.append(","); + str.append(getQuality()); + str.append("]"); + + return str.toString(); + } +} diff --git a/source/java/org/alfresco/web/app/servlet/NTLMAuthenticationFilter.java b/source/java/org/alfresco/web/app/servlet/NTLMAuthenticationFilter.java new file mode 100644 index 0000000000..0e1232937c --- /dev/null +++ b/source/java/org/alfresco/web/app/servlet/NTLMAuthenticationFilter.java @@ -0,0 +1,920 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Alfresco Network License. You may obtain a + * copy of the License at + * + * http://www.alfrescosoftware.com/legal/ + * + * Please view the license relevant to your network subscription. + * + * BY CLICKING THE "I UNDERSTAND AND ACCEPT" BOX, OR INSTALLING, + * READING OR USING ALFRESCO'S Network SOFTWARE (THE "SOFTWARE"), + * YOU ARE AGREEING ON BEHALF OF THE ENTITY LICENSING THE SOFTWARE + * ("COMPANY") THAT COMPANY WILL BE BOUND BY AND IS BECOMING A PARTY TO + * THIS ALFRESCO NETWORK AGREEMENT ("AGREEMENT") AND THAT YOU HAVE THE + * AUTHORITY TO BIND COMPANY. IF COMPANY DOES NOT AGREE TO ALL OF THE + * TERMS OF THIS AGREEMENT, DO NOT SELECT THE "I UNDERSTAND AND AGREE" + * BOX AND DO NOT INSTALL THE SOFTWARE OR VIEW THE SOURCE CODE. COMPANY + * HAS NOT BECOME A LICENSEE OF, AND IS NOT AUTHORIZED TO USE THE + * SOFTWARE UNLESS AND UNTIL IT HAS AGREED TO BE BOUND BY THESE LICENSE + * TERMS. THE "EFFECTIVE DATE" FOR THIS AGREEMENT SHALL BE THE DAY YOU + * CHECK THE "I UNDERSTAND AND ACCEPT" BOX. + */ +package org.alfresco.web.app.servlet; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Random; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import javax.transaction.UserTransaction; + +import net.sf.acegisecurity.BadCredentialsException; + +import org.alfresco.config.ConfigService; +import org.alfresco.filesys.server.auth.PasswordEncryptor; +import org.alfresco.filesys.server.auth.ntlm.NTLM; +import org.alfresco.filesys.server.auth.ntlm.NTLMLogonDetails; +import org.alfresco.filesys.server.auth.ntlm.NTLMMessage; +import org.alfresco.filesys.server.auth.ntlm.TargetInfo; +import org.alfresco.filesys.server.auth.ntlm.Type1NTLMMessage; +import org.alfresco.filesys.server.auth.ntlm.Type2NTLMMessage; +import org.alfresco.filesys.server.auth.ntlm.Type3NTLMMessage; +import org.alfresco.filesys.server.config.ServerConfiguration; +import org.alfresco.filesys.util.DataPacker; +import org.alfresco.i18n.I18NUtil; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.security.authentication.AuthenticationComponent; +import org.alfresco.repo.security.authentication.AuthenticationException; +import org.alfresco.repo.security.authentication.MD4PasswordEncoder; +import org.alfresco.repo.security.authentication.MD4PasswordEncoderImpl; +import org.alfresco.repo.security.authentication.NTLMMode; +import org.alfresco.repo.security.authentication.ntlm.NTLMPassthruToken; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.AuthenticationService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.web.app.Application; +import org.alfresco.web.bean.LoginBean; +import org.alfresco.web.bean.repository.User; +import org.alfresco.web.config.LanguagesConfigElement; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.context.support.WebApplicationContextUtils; + +/** + * NTLM Authentication Filter Class + * + * @author GKSpencer + */ +public class NTLMAuthenticationFilter extends AbstractAuthenticationFilter implements Filter +{ + // NTLM authentication session object names + + public static final String NTLM_AUTH_SESSION = "_alfNTLMAuthSess"; + public static final String NTLM_AUTH_DETAILS = "_alfNTLMDetails"; + + // Locale object stored in the session + + private static final String LOCALE = "locale"; + public static final String MESSAGE_BUNDLE = "alfresco.messages.webclient"; + + // NTLM flags mask, used to mask out features that are not supported + + private static final int NTLM_FLAGS = NTLM.Flag56Bit + NTLM.FlagLanManKey + NTLM.FlagNegotiateNTLM + + NTLM.FlagNegotiateOEM + NTLM.FlagNegotiateUnicode; + + // Debug logging + + private static Log logger = LogFactory.getLog(NTLMAuthenticationFilter.class); + + // Servlet context, required to get authentication service + + private ServletContext m_context; + + // File server configuration + + private ServerConfiguration m_srvConfig; + + // Various services required by NTLM authenticator + + private AuthenticationService m_authService; + private AuthenticationComponent m_authComponent; + private PersonService m_personService; + private NodeService m_nodeService; + private TransactionService m_transactionService; + private ConfigService m_configService; + + // Password encryptor + + private PasswordEncryptor m_encryptor = new PasswordEncryptor(); + + // Allow guest access + + private boolean m_allowGuest; + + // Login page address + + private String m_loginPage; + + // Random number generator used to generate challenge keys + + private Random m_random = new Random(System.currentTimeMillis()); + + // MD4 hash decoder + + private MD4PasswordEncoder m_md4Encoder = new MD4PasswordEncoderImpl(); + + // Local server name, from either the file servers config or DNS host name + + private String m_srvName; + + // List of available locales (from the web-client configuration) + + private List m_languages; + + /** + * Initialize the filter + * + * @param args FilterConfig + * @exception ServletException + */ + public void init(FilterConfig args) throws ServletException + { + // Save the servlet context, needed to get hold of the authentication service + + m_context = args.getServletContext(); + + // Setup the authentication context + + WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(m_context); + + ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY); + m_nodeService = serviceRegistry.getNodeService(); + m_transactionService = serviceRegistry.getTransactionService(); + + m_authService = (AuthenticationService) ctx.getBean("authenticationService"); + m_authComponent = (AuthenticationComponent) ctx.getBean("authenticationComponent"); + m_personService = (PersonService) ctx.getBean("personService"); + m_configService = (ConfigService) ctx.getBean("webClientConfigService"); + + m_srvConfig = (ServerConfiguration) ctx.getBean(ServerConfiguration.SERVER_CONFIGURATION); + + // Check that the authentication component supports the required mode + + if ( m_authComponent.getNTLMMode() != NTLMMode.MD4_PROVIDER && + m_authComponent.getNTLMMode() != NTLMMode.PASS_THROUGH) + { + throw new ServletException("Required authentication mode not available"); + } + + // Get the local server name, try the file server config first + + if ( m_srvConfig != null) + { + m_srvName = m_srvConfig.getServerName(); + + if ( m_srvName == null) + { + // CIFS server may not be running so the local server name has not been set, generate + // a server name + + m_srvName = m_srvConfig.getLocalServerName(true) + "_A"; + } + } + else + { + // Get the host name + + try + { + // Get the local host name + + m_srvName = InetAddress.getLocalHost().getHostName(); + + // Strip any domain name + + int pos = m_srvName.indexOf("."); + if ( pos != -1) + m_srvName = m_srvName.substring(0, pos - 1); + } + catch (UnknownHostException ex) + { + // Log the error + + if ( logger.isErrorEnabled()) + logger.error("NTLM filter, error getting local host name", ex); + } + + } + + // Check if the server name is valid + + if ( m_srvName == null || m_srvName.length() == 0) + throw new ServletException("Failed to get local server name"); + + // Check if guest access is to be allowed + + String guestAccess = args.getInitParameter("AllowGuest"); + if ( guestAccess != null) + { + m_allowGuest = Boolean.parseBoolean(guestAccess); + + // Debug + + if ( logger.isDebugEnabled() && m_allowGuest) + logger.debug("NTLM filter guest access allowed"); + } + + // Get a list of the available locales + + LanguagesConfigElement config = (LanguagesConfigElement) m_configService. + getConfig("Languages").getConfigElement(LanguagesConfigElement.CONFIG_ELEMENT_ID); + + m_languages = config.getLanguages(); + } + + /** + * Run the filter + * + * @param sreq ServletRequest + * @param sresp ServletResponse + * @param chain FilterChain + * @exception IOException + * @exception ServletException + */ + public void doFilter(ServletRequest sreq, ServletResponse sresp, FilterChain chain) throws IOException, + ServletException + { + // Get the HTTP request/response/session + + HttpServletRequest req = (HttpServletRequest) sreq; + HttpServletResponse resp = (HttpServletResponse) sresp; + + HttpSession httpSess = req.getSession(true); + + // Check if there is an authorization header with an NTLM security blob + + String authHdr = req.getHeader("Authorization"); + boolean reqAuth = false; + + if ( authHdr != null && authHdr.startsWith("NTLM")) + reqAuth = true; + + // Check if the user is already authenticated + + User user = (User) httpSess.getAttribute(AuthenticationHelper.AUTHENTICATION_USER); + + if ( user != null && reqAuth == false) + { + try + { + // Debug + + if ( logger.isDebugEnabled()) + logger.debug("User " + user.getUserName() + " validate ticket"); + + // Validate the user ticket + + m_authService.validate( user.getTicket()); + reqAuth = false; + + // Set the current locale + + I18NUtil.setLocale(Application.getLanguage(httpSess)); + } + catch (AuthenticationException ex) + { + if ( logger.isErrorEnabled()) + logger.error("Failed to validate user " + user.getUserName(), ex); + + reqAuth = true; + } + } + + // If the user has been validated and we do not require re-authentication then continue to + // the next filter + + if ( reqAuth == false && user != null) + { + // Debug + + if ( logger.isDebugEnabled()) + logger.debug("Authentication not required, chaining ..."); + + // Chain to the next filter + + chain.doFilter(sreq, sresp); + return; + } + + // Check if the login page is being accessed, do not intercept the login page + + if ( req.getRequestURI().endsWith(getLoginPage()) == true) + { + // Debug + + if ( logger.isDebugEnabled()) + logger.debug("Login page requested, chaining ..."); + + // Chain to the next filter + + chain.doFilter( sreq, sresp); + return; + } + + // Check if the browser is Opera, if so then display the login page as Opera does not + // support NTLM and displays an error page if a request to use NTLM is sent to it + + String userAgent = req.getHeader("user-agent"); + + if ( userAgent != null && userAgent.indexOf("Opera ") != -1) + { + // Debug + + if ( logger.isDebugEnabled()) + logger.debug("Opera detected, redirecting to login page"); + + // Redirect to the login page + + resp.sendRedirect(req.getContextPath() + "/faces" + getLoginPage()); + return; + } + + // Check the authorization header + + if ( authHdr == null) { + + // Debug + + if ( logger.isDebugEnabled()) + logger.debug("New NTLM auth request from " + req.getRemoteHost() + " (" + + req.getRemoteAddr() + ":" + req.getRemotePort() + ")"); + + // Send back a request for NTLM authentication + + resp.setHeader("WWW-Authenticate", "NTLM"); + resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + + resp.flushBuffer(); + } + else + { + // Decode the received NTLM blob and validate + + final byte[] ntlmByts = Base64.decodeBase64( authHdr.substring(5).getBytes()); + int ntlmTyp = NTLMMessage.isNTLMType(ntlmByts); + + if ( ntlmTyp == NTLM.Type1) + { + // Process the type 1 NTLM message + + Type1NTLMMessage type1Msg = new Type1NTLMMessage(ntlmByts); + processType1(type1Msg, req, resp, httpSess); + } + else if ( ntlmTyp == NTLM.Type3) + { + // Process the type 3 NTLM message + + Type3NTLMMessage type3Msg = new Type3NTLMMessage(ntlmByts); + processType3(type3Msg, req, resp, httpSess, chain); + } + else + { + // Debug + + if ( logger.isDebugEnabled()) + logger.debug("NTLM not handled, redirecting to login page"); + + // Redirect to the login page + + resp.sendRedirect(req.getContextPath() + "/faces" + getLoginPage()); + } + } + } + + /** + * Return the login page address + * + * @return String + */ + private String getLoginPage() + { + if (m_loginPage == null) + { + m_loginPage = Application.getLoginPage(m_context); + } + + return m_loginPage; + } + + /** + * Delete the servlet filter + */ + public void destroy() + { + } + + /** + * Process a type 1 NTLM message + * + * @param type1Msg Type1NTLMMessage + * @param req HttpServletRequest + * @param resp HttpServletResponse + * @param httpSess HttpSession + * @exception IOException + */ + private void processType1(Type1NTLMMessage type1Msg, HttpServletRequest req, HttpServletResponse resp, + HttpSession httpSess) throws IOException + { + // Debug + + if ( logger.isDebugEnabled()) + logger.debug("Received type1 " + type1Msg); + + // Get the existing NTLM details + + NTLMLogonDetails ntlmDetails = null; + + if ( httpSess != null) + { + ntlmDetails = (NTLMLogonDetails) httpSess.getAttribute(NTLM_AUTH_DETAILS); + } + + // Check if cached logon details are available + + if ( ntlmDetails != null && ntlmDetails.hasType2Message() && ntlmDetails.hasNTLMHashedPassword() && + ntlmDetails.hasAuthenticationToken()) + { + // Get the authentication server type2 response + + Type2NTLMMessage cachedType2 = ntlmDetails.getType2Message(); + + byte[] type2Bytes = cachedType2.getBytes(); + String ntlmBlob = "NTLM " + new String(Base64.encodeBase64(type2Bytes)); + + // Debug + + if ( logger.isDebugEnabled()) + logger.debug("Sending cached NTLM type2 to client - " + cachedType2); + + // Send back a request for NTLM authentication + + resp.setHeader("WWW-Authenticate", ntlmBlob); + resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + + resp.flushBuffer(); + return; + } + else + { + // Clear any cached logon details + + httpSess.removeAttribute(NTLM_AUTH_DETAILS); + + // Set the 8 byte challenge for the new logon request + + byte[] challenge = null; + NTLMPassthruToken authToken = null; + + if ( m_authComponent.getNTLMMode() == NTLMMode.MD4_PROVIDER) + { + // Generate a random 8 byte challenge + + challenge = new byte[8]; + DataPacker.putIntelLong(m_random.nextLong(), challenge, 0); + } + else + { + // Create an authentication token for the new logon + + authToken = new NTLMPassthruToken(); + + // Run the first stage of the passthru authentication to get the challenge + + m_authComponent.authenticate( authToken); + + // Get the challenge from the token + + if ( authToken.getChallenge() != null) + challenge = authToken.getChallenge().getBytes(); + } + + // Get the flags from the client request and mask out unsupported features + + int ntlmFlags = type1Msg.getFlags() & NTLM_FLAGS; + + // Build a type2 message to send back to the client, containing the challenge + + List tList = new ArrayList(); + tList.add(new TargetInfo(NTLM.TargetServer, m_srvName)); + + Type2NTLMMessage type2Msg = new Type2NTLMMessage(); + type2Msg.buildType2(ntlmFlags, m_srvName, challenge, null, tList); + + // Store the NTLM logon details, cache the type2 message, and token if using passthru + + ntlmDetails = new NTLMLogonDetails(); + ntlmDetails.setType2Message( type2Msg); + ntlmDetails.setAuthenticationToken(authToken); + + httpSess.setAttribute(NTLM_AUTH_DETAILS, ntlmDetails); + + // Debug + + if ( logger.isDebugEnabled()) + logger.debug("Sending NTLM type2 to client - " + type2Msg); + + // Send back a request for NTLM authentication + + byte[] type2Bytes = type2Msg.getBytes(); + String ntlmBlob = "NTLM " + new String(Base64.encodeBase64(type2Bytes)); + + resp.setHeader("WWW-Authenticate", ntlmBlob); + resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + + resp.flushBuffer(); + return; + } + } + + /** + * Process a type 3 NTLM message + * + * @param type3Msg Type3NTLMMessage + * @param req HttpServletRequest + * @param resp HttpServletResponse + * @param httpSess HttpSession + * @param chain FilterChain + * @exception IOException + * @exception ServletException + */ + private void processType3(Type3NTLMMessage type3Msg, HttpServletRequest req, HttpServletResponse resp, + HttpSession httpSess, FilterChain chain) throws IOException, ServletException + { + // Debug + + if ( logger.isDebugEnabled()) + logger.debug("Received type3 " + type3Msg); + + // Get the existing NTLM details + + NTLMLogonDetails ntlmDetails = null; + User user = null; + + if ( httpSess != null) + { + ntlmDetails = (NTLMLogonDetails) httpSess.getAttribute(NTLM_AUTH_DETAILS); + user = (User) httpSess.getAttribute(AuthenticationHelper.AUTHENTICATION_USER); + } + + // Get the NTLM logon details + + String userName = type3Msg.getUserName(); + String workstation = type3Msg.getWorkstation(); + String domain = type3Msg.getDomain(); + + boolean authenticated = false; + boolean useNTLM = true; + + // Check if we are using cached details for the authentication + + if ( user != null && ntlmDetails != null && ntlmDetails.hasNTLMHashedPassword()) + { + // Check if the received NTLM hashed password matches the cached password + + byte[] ntlmPwd = type3Msg.getNTLMHash(); + byte[] cachedPwd = ntlmDetails.getNTLMHashedPassword(); + + if ( ntlmPwd != null) + { + if ( ntlmPwd.length == cachedPwd.length) + { + authenticated = true; + for ( int i = 0; i < ntlmPwd.length; i++) + { + if ( ntlmPwd[i] != cachedPwd[i]) + authenticated = false; + } + } + } + + // Debug + + if ( logger.isDebugEnabled()) + logger.debug("Using cached NTLM hash, authenticated = " + authenticated); + + try + { + // Debug + + if ( logger.isDebugEnabled()) + logger.debug("User " + user.getUserName() + " validate ticket"); + + // Validate the user ticket + + m_authService.validate( user.getTicket()); + + // Set the current locale + + I18NUtil.setLocale(Application.getLanguage(httpSess)); + } + catch (AuthenticationException ex) + { + if ( logger.isErrorEnabled()) + logger.error("Failed to validate user " + user.getUserName(), ex); + + // Redirect to the login page + + resp.sendRedirect(req.getContextPath() + "/faces" + getLoginPage()); + return; + } + + // Allow the user to access the requested page + + chain.doFilter( req, resp); + return; + } + else + { + // Check if we are using local MD4 password hashes or passthru authentication + + if ( m_authComponent.getNTLMMode() == NTLMMode.MD4_PROVIDER) + { + // Get the stored MD4 hashed password for the user, or null if the user does not exist + + String md4hash = m_authComponent.getMD4HashedPassword(userName); + + if ( md4hash != null) + { + // Generate the local encrypted password using the challenge that was sent to the client + + byte[] p21 = new byte[21]; + byte[] md4byts = m_md4Encoder.decodeHash(md4hash); + System.arraycopy(md4byts, 0, p21, 0, 16); + + // Generate the local hash of the password using the same challenge + + byte[] localHash = null; + + try + { + localHash = m_encryptor.doNTLM1Encryption(p21, ntlmDetails.getChallengeKey()); + } + catch (NoSuchAlgorithmException ex) + { + } + + // Validate the password + + byte[] clientHash = type3Msg.getNTLMHash(); + + if ( clientHash != null && localHash != null && clientHash.length == localHash.length) + { + int i = 0; + + while ( i < clientHash.length && clientHash[i] == localHash[i]) + i++; + + if ( i == clientHash.length) + authenticated = true; + } + } + else + { + // Debug + + if ( logger.isDebugEnabled()) + logger.debug("User " + userName + " does not have Alfresco account"); + + // Bypass NTLM authentication and display the logon screen, user account does not + // exist in Alfresco + + authenticated = false; + } + } + else + { + // Passthru mode, send the hashed password details to the passthru authentication server + + NTLMPassthruToken authToken = (NTLMPassthruToken) ntlmDetails.getAuthenticationToken(); + authToken.setUserAndPassword( type3Msg.getUserName(), type3Msg.getNTLMHash(), PasswordEncryptor.NTLM1); + + try + { + // Run the second stage of the passthru authentication + + m_authComponent.authenticate(authToken); + authenticated = true; + } + catch (BadCredentialsException ex) + { + // Debug + + if ( logger.isDebugEnabled()) + logger.debug("Authentication failed, " + ex.getMessage()); + } + catch (AuthenticationException ex) + { + // Debug + + if ( logger.isDebugEnabled()) + logger.debug("Authentication failed, " + ex.getMessage()); + } + finally + { + // Clear the authentication token from the NTLM details + + ntlmDetails.setAuthenticationToken(null); + } + } + + // Check if the user has been authenticated, if so then setup the user environment + + if ( authenticated == true) + { + UserTransaction tx = m_transactionService.getUserTransaction(); + NodeRef homeSpaceRef = null; + + try + { + tx.begin(); + + // Get user details for the authenticated user + m_authComponent.setCurrentUser(userName.toLowerCase()); + + // The user name used may be a different case to the NTLM supplied user name, read the current + // user and use that name + userName = m_authComponent.getCurrentUserName(); + + // Setup User object and Home space ID etc. + NodeRef personNodeRef = m_personService.getPerson(userName); + String currentTicket = m_authService.getCurrentTicket(); + user = new User(userName, currentTicket, personNodeRef); + + homeSpaceRef = (NodeRef) m_nodeService.getProperty( + personNodeRef, + ContentModel.PROP_HOMEFOLDER); + user.setHomeSpaceId(homeSpaceRef.getId()); + + // commit + tx.commit(); + } + catch (Throwable ex) + { + try + { + tx.rollback(); + } + catch (Exception ex2) + { + logger.error("Failed to rollback transaction", ex2); + } + if(ex instanceof RuntimeException) + { + throw (RuntimeException)ex; + } + else if(ex instanceof IOException) + { + throw (IOException)ex; + } + else if(ex instanceof ServletException) + { + throw (ServletException)ex; + } + else + { + throw new RuntimeException("Authentication setup failed", ex); + } + } + + // Store the user + + httpSess.setAttribute(AuthenticationHelper.AUTHENTICATION_USER, user); + httpSess.setAttribute(LoginBean.LOGIN_EXTERNAL_AUTH, Boolean.TRUE); + + // Set the current locale from the Accept-Lanaguage header if available + + Locale userLocale = parseAcceptLanguageHeader(req, m_languages); + + if ( userLocale != null) + { + httpSess.setAttribute(LOCALE, userLocale); + httpSess.removeAttribute(MESSAGE_BUNDLE); + } + + // Set the locale using the session + + I18NUtil.setLocale(Application.getLanguage(httpSess)); + + // Update the NTLM logon details in the session + + if ( ntlmDetails == null) + { + // No cached NTLM details + + ntlmDetails = new NTLMLogonDetails( userName, workstation, domain, false, m_srvName); + + httpSess.setAttribute(NTLM_AUTH_DETAILS, ntlmDetails); + + // Debug + + if ( logger.isDebugEnabled()) + logger.debug("No cached NTLM details, created"); + + } + else + { + // Update the cached NTLM details + + ntlmDetails.setDetails(userName, workstation, domain, false, m_srvName); + ntlmDetails.setNTLMHashedPassword(type3Msg.getNTLMHash()); + + // Debug + + if ( logger.isDebugEnabled()) + logger.debug("Updated cached NTLM details"); + } + + // Debug + + if ( logger.isDebugEnabled()) + logger.debug("User logged on via NTLM, " + ntlmDetails); + + // If the original URL requested was the login page then redirect to the browse view + + if (req.getRequestURI().endsWith(getLoginPage()) == true) + { + // Debug + + if ( logger.isDebugEnabled()) + logger.debug("Login page requested, redirecting to browse page"); + + // Redirect to the browse view + + resp.sendRedirect(req.getContextPath() + "/faces/jsp/browse/browse.jsp"); + return; + } + else + { + // Allow the user to access the requested page + + chain.doFilter( req, resp); + return; + } + } + else + { + // Check if NTLM should be used, switched off if the user does not exist in the Alfresco + // user database + + if (useNTLM == true) + { + // Remove any existing session and NTLM details from the session + + httpSess.removeAttribute(NTLM_AUTH_SESSION); + httpSess.removeAttribute(NTLM_AUTH_DETAILS); + + // Force the logon to start again + + resp.setHeader("WWW-Authenticate", "NTLM"); + resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + + resp.flushBuffer(); + return; + } + else + { + // Debug + + if ( logger.isDebugEnabled()) + logger.debug("Redirecting to login page"); + + // Redirect to the login page + + resp.sendRedirect(req.getContextPath() + "/faces" + getLoginPage()); + return; + } + } + } + } +} diff --git a/source/java/org/alfresco/web/app/servlet/NovellIChainsHTTPRequestAuthenticationFilter.java b/source/java/org/alfresco/web/app/servlet/NovellIChainsHTTPRequestAuthenticationFilter.java new file mode 100644 index 0000000000..b239d24e44 --- /dev/null +++ b/source/java/org/alfresco/web/app/servlet/NovellIChainsHTTPRequestAuthenticationFilter.java @@ -0,0 +1,317 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Alfresco Network License. You may obtain a + * copy of the License at + * + * http://www.alfrescosoftware.com/legal/ + * + * Please view the license relevant to your network subscription. + * + * BY CLICKING THE "I UNDERSTAND AND ACCEPT" BOX, OR INSTALLING, + * READING OR USING ALFRESCO'S Network SOFTWARE (THE "SOFTWARE"), + * YOU ARE AGREEING ON BEHALF OF THE ENTITY LICENSING THE SOFTWARE + * ("COMPANY") THAT COMPANY WILL BE BOUND BY AND IS BECOMING A PARTY TO + * THIS ALFRESCO NETWORK AGREEMENT ("AGREEMENT") AND THAT YOU HAVE THE + * AUTHORITY TO BIND COMPANY. IF COMPANY DOES NOT AGREE TO ALL OF THE + * TERMS OF THIS AGREEMENT, DO NOT SELECT THE "I UNDERSTAND AND AGREE" + * BOX AND DO NOT INSTALL THE SOFTWARE OR VIEW THE SOURCE CODE. COMPANY + * HAS NOT BECOME A LICENSEE OF, AND IS NOT AUTHORIZED TO USE THE + * SOFTWARE UNLESS AND UNTIL IT HAS AGREED TO BE BOUND BY THESE LICENSE + * TERMS. THE "EFFECTIVE DATE" FOR THIS AGREEMENT SHALL BE THE DAY YOU + * CHECK THE "I UNDERSTAND AND ACCEPT" BOX. + */ +package org.alfresco.web.app.servlet; + +import java.io.IOException; +import java.util.List; +import java.util.Locale; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import javax.transaction.UserTransaction; + +import org.alfresco.config.ConfigService; +import org.alfresco.i18n.I18NUtil; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.security.authentication.AuthenticationComponent; +import org.alfresco.repo.security.authentication.AuthenticationException; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.AuthenticationService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.web.app.Application; +import org.alfresco.web.bean.LoginBean; +import org.alfresco.web.bean.repository.User; +import org.alfresco.web.config.LanguagesConfigElement; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.context.support.WebApplicationContextUtils; + +/** + * Sample authentication for Novell ICHAINS. + * + * @author Andy Hind + */ +public class NovellIChainsHTTPRequestAuthenticationFilter extends AbstractAuthenticationFilter implements Filter +{ + private static final String LOCALE = "locale"; + + public static final String MESSAGE_BUNDLE = "alfresco.messages.webclient"; + + private static Log logger = LogFactory.getLog(NovellIChainsHTTPRequestAuthenticationFilter.class); + + private ServletContext context; + + private String loginPage; + + private AuthenticationComponent authComponent; + + private AuthenticationService authService; + + private TransactionService transactionService; + + private PersonService personService; + + private NodeService nodeService; + + private List m_languages; + + public NovellIChainsHTTPRequestAuthenticationFilter() + { + super(); + } + + public void destroy() + { + // Nothing to do + } + + /** + * Run the filter + * + * @param sreq + * ServletRequest + * @param sresp + * ServletResponse + * @param chain + * FilterChain + * @exception IOException + * @exception ServletException + */ + public void doFilter(ServletRequest sreq, ServletResponse sresp, FilterChain chain) throws IOException, + ServletException + { + // Get the HTTP request/response/session + + HttpServletRequest req = (HttpServletRequest) sreq; + HttpServletResponse resp = (HttpServletResponse) sresp; + + HttpSession httpSess = req.getSession(true); + + // Check for the ICHAINS header + + String authHdr = req.getHeader("x-user"); + if(logger.isDebugEnabled()) + { + if(authHdr == null) + { + logger.debug("x-user header not found."); + } + else + { + logger.debug("x-user header is <" + authHdr + ">"); + } + } + + // Throw an error if we have an unknown authentication + + if ((authHdr == null) || (authHdr.length() < 1)) + { + resp.sendRedirect(req.getContextPath() + "/jsp/noaccess.jsp"); + return; + } + + // Get the user + + String userName = authHdr; + + if(logger.isDebugEnabled()) + { + logger.debug("User = "+ userName); + } + + // See if there is a user in the session and test if it matches + + User user = (User) httpSess.getAttribute(AuthenticationHelper.AUTHENTICATION_USER); + + if (user != null) + { + try + { + // Debug + + if (logger.isDebugEnabled()) + logger.debug("User " + user.getUserName() + " validate ticket"); + + // Validate the user ticket + + if (user.getUserName().equals(userName)) + { + + // Set the current locale + authComponent.setCurrentUser(user.getUserName()); + I18NUtil.setLocale(Application.getLanguage(httpSess)); + chain.doFilter(sreq, sresp); + return; + } + else + { + // No match + setAuthenticatedUser(req, httpSess, userName); + } + } + catch (AuthenticationException ex) + { + if (logger.isErrorEnabled()) + logger.error("Failed to validate user " + user.getUserName(), ex); + } + } + + setAuthenticatedUser(req, httpSess, userName); + + // Redirect the login page as it is never seen as we always login by name + if (req.getRequestURI().endsWith(getLoginPage()) == true) + { + if (logger.isDebugEnabled()) + logger.debug("Login page requested, chaining ..."); + + resp.sendRedirect(req.getContextPath() + "/faces/jsp/browse/browse.jsp"); + return; + } + else + { + chain.doFilter(sreq, sresp); + return; + } + } + + /** + * Set the authenticated user. + * + * It does not check that the user exists at the moment. + * + * @param req + * @param httpSess + * @param userName + */ + private void setAuthenticatedUser(HttpServletRequest req, HttpSession httpSess, String userName) + { + // Set the authentication + authComponent.setCurrentUser(userName); + + User user = new User(userName, authService.getCurrentTicket(), personService.getPerson(userName)); + + // Set up the user information + UserTransaction tx = transactionService.getUserTransaction(); + NodeRef homeSpaceRef = null; + + try + { + tx.begin(); + homeSpaceRef = (NodeRef) nodeService.getProperty(personService.getPerson(userName), + ContentModel.PROP_HOMEFOLDER); + user.setHomeSpaceId(homeSpaceRef.getId()); + tx.commit(); + } + catch (Throwable ex) + { + logger.error(ex); + + try + { + tx.rollback(); + } + catch (Exception ex2) + { + logger.error("Failed to rollback transaction", ex2); + } + + if(ex instanceof RuntimeException) + { + throw (RuntimeException)ex; + } + else + { + throw new RuntimeException("Failed to set authenticated user", ex); + } + } + + // Store the user + + httpSess.setAttribute(AuthenticationHelper.AUTHENTICATION_USER, user); + httpSess.setAttribute(LoginBean.LOGIN_EXTERNAL_AUTH, Boolean.TRUE); + + // Set the current locale from the Accept-Lanaguage header if available + + Locale userLocale = parseAcceptLanguageHeader(req, m_languages); + + if (userLocale != null) + { + httpSess.setAttribute(LOCALE, userLocale); + httpSess.removeAttribute(MESSAGE_BUNDLE); + } + + // Set the locale using the session + + I18NUtil.setLocale(Application.getLanguage(httpSess)); + } + + public void init(FilterConfig config) throws ServletException + { + this.context = config.getServletContext(); + WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(context); + ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY); + transactionService = serviceRegistry.getTransactionService(); + nodeService = serviceRegistry.getNodeService(); + + authComponent = (AuthenticationComponent) ctx.getBean("authenticationComponent"); + authService = (AuthenticationService) ctx.getBean("authenticationService"); + personService = (PersonService) ctx.getBean("personService"); + + // Get a list of the available locales + + ConfigService configServiceService = (ConfigService) ctx.getBean("webClientConfigService"); + LanguagesConfigElement configElement = (LanguagesConfigElement) configServiceService. + getConfig("Languages").getConfigElement(LanguagesConfigElement.CONFIG_ELEMENT_ID); + + m_languages = configElement.getLanguages(); + } + + /** + * Return the login page address + * + * @return String + */ + private String getLoginPage() + { + if (loginPage == null) + { + loginPage = Application.getLoginPage(context); + } + + return loginPage; + } + +} diff --git a/source/java/org/alfresco/web/bean/GroupsBean.java b/source/java/org/alfresco/web/bean/GroupsBean.java new file mode 100644 index 0000000000..2d69e224bf --- /dev/null +++ b/source/java/org/alfresco/web/bean/GroupsBean.java @@ -0,0 +1,979 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Alfresco Network License. You may obtain a + * copy of the License at + * + * http://www.alfrescosoftware.com/legal/ + * + * Please view the license relevant to your network subscription. + * + * BY CLICKING THE "I UNDERSTAND AND ACCEPT" BOX, OR INSTALLING, + * READING OR USING ALFRESCO'S Network SOFTWARE (THE "SOFTWARE"), + * YOU ARE AGREEING ON BEHALF OF THE ENTITY LICENSING THE SOFTWARE + * ("COMPANY") THAT COMPANY WILL BE BOUND BY AND IS BECOMING A PARTY TO + * THIS ALFRESCO NETWORK AGREEMENT ("AGREEMENT") AND THAT YOU HAVE THE + * AUTHORITY TO BIND COMPANY. IF COMPANY DOES NOT AGREE TO ALL OF THE + * TERMS OF THIS AGREEMENT, DO NOT SELECT THE "I UNDERSTAND AND AGREE" + * BOX AND DO NOT INSTALL THE SOFTWARE OR VIEW THE SOURCE CODE. COMPANY + * HAS NOT BECOME A LICENSEE OF, AND IS NOT AUTHORIZED TO USE THE + * SOFTWARE UNLESS AND UNTIL IT HAS AGREED TO BE BOUND BY THESE LICENSE + * TERMS. THE "EFFECTIVE DATE" FOR THIS AGREEMENT SHALL BE THE DAY YOU + * CHECK THE "I UNDERSTAND AND ACCEPT" BOX. + */ +package org.alfresco.web.bean; + +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.faces.application.FacesMessage; +import javax.faces.component.UIComponent; +import javax.faces.context.FacesContext; +import javax.faces.event.ActionEvent; +import javax.faces.model.DataModel; +import javax.faces.model.ListDataModel; +import javax.faces.model.SelectItem; +import javax.faces.validator.ValidatorException; +import javax.transaction.UserTransaction; + +import org.alfresco.model.ContentModel; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.cmr.security.AuthorityType; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.web.app.Application; +import org.alfresco.web.app.context.IContextListener; +import org.alfresco.web.app.context.UIContextService; +import org.alfresco.web.bean.repository.Repository; +import org.alfresco.web.ui.common.SortableSelectItem; +import org.alfresco.web.ui.common.Utils; +import org.alfresco.web.ui.common.component.IBreadcrumbHandler; +import org.alfresco.web.ui.common.component.UIActionLink; +import org.alfresco.web.ui.common.component.UIBreadcrumb; +import org.alfresco.web.ui.common.component.UIGenericPicker; +import org.alfresco.web.ui.common.component.UIModeList; +import org.alfresco.web.ui.common.component.data.UIRichList; +import org.apache.log4j.Logger; + +/** + * Backing Bean for the Groups Management pages. + * + * @author Kevin Roast + */ +public class GroupsBean implements IContextListener +{ + private static final String FILTER_CHILDREN = "children"; + private static final String FILTER_ALL = "all"; + + private static final String DEFAULT_OUTCOME = "finish"; + + private static final String MSG_GROUPS = "root_groups"; + + private static Logger logger = Logger.getLogger(GroupsBean.class); + + /** The NodeService to be used by the bean */ + private NodeService nodeService; + + /** The AuthorityService to be used by the bean */ + private AuthorityService authService; + + /** personService bean reference */ + private PersonService personService; + + /** Component references */ + private UIRichList groupsRichList; + private UIRichList usersRichList; + + /** datamodel for table of users added to group */ + private DataModel usersDataModel = null; + + /** Currently visible Group Authority */ + private String group = null; + private String groupName = null; + + /** Action group authority */ + private String actionGroup = null; + private String actionGroupName = null; + private int actionGroupItems = 0; + + /** selected users to be added to a group */ + private List usersForGroup = null; + + /** Dialog properties */ + private String name = null; + + /** RichList view mode */ + private String viewMode = "icons"; + + /** List filter mode */ + private String filterMode = FILTER_CHILDREN; + + /** Groups path breadcrumb location */ + private List location = null; + + + // ------------------------------------------------------------------------------ + // Construction + + /** + * Default Constructor + */ + public GroupsBean() + { + UIContextService.getInstance(FacesContext.getCurrentInstance()).registerBean(this); + } + + + // ------------------------------------------------------------------------------ + // Bean property getters and setters + + /** + * @param nodeService The NodeService to set. + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * @param authService The AuthorityService to set. + */ + public void setAuthorityService(AuthorityService authService) + { + this.authService = authService; + } + + /** + * @param permissionService The PermissionService to set. + */ + public void setPersonService(PersonService personService) + { + this.personService = personService; + } + + /** + * @return Returns the groups RichList to set. + */ + public UIRichList getGroupsRichList() + { + return this.groupsRichList; + } + + /** + * @param list The RichList to set. + */ + public void setGroupsRichList(UIRichList list) + { + this.groupsRichList = list; + } + + /** + * @return Returns the users RichList. + */ + public UIRichList getUsersRichList() + { + return this.usersRichList; + } + + /** + * @param usersRichList The RichList to set. + */ + public void setUsersRichList(UIRichList usersRichList) + { + this.usersRichList = usersRichList; + } + + /** + * @return Returns the usersDataModel. + */ + public DataModel getUsersDataModel() + { + if (this.usersDataModel == null) + { + this.usersDataModel = new ListDataModel(); + } + + this.usersDataModel.setWrappedData(this.usersForGroup); + + return this.usersDataModel; + } + + /** + * @param usersDataModel The usersDataModel to set. + */ + public void setUsersDataModel(DataModel usersDataModel) + { + this.usersDataModel = usersDataModel; + } + + /** + * @return Returns the name. + */ + public String getName() + { + return this.name; + } + + /** + * @param name The name to set. + */ + public void setName(String name) + { + this.name = name; + } + + /** + * @return Returns the viewMode. + */ + public String getViewMode() + { + return this.viewMode; + } + + /** + * @param viewMode The viewMode to set. + */ + public void setViewMode(String viewMode) + { + this.viewMode = viewMode; + } + + /** + * @return Returns the filterMode. + */ + public String getFilterMode() + { + return this.filterMode; + } + + /** + * @param filterMode The filterMode to set. + */ + public void setFilterMode(String filterMode) + { + this.filterMode = filterMode; + + // clear datalist cache ready to change results based on filter setting + contextUpdated(); + } + + /** + * @return Returns the Group being used for the current action screen. + */ + public String getActionGroup() + { + return this.actionGroup; + } + + /** + * @param actionSpace Set the Group to be used for the current action screen. + */ + public void setActionGroup(String group) + { + this.actionGroup = group; + + if (group != null) + { + // calculate action group metadata + setActionGroupName(this.authService.getShortName(group)); + int count = this.authService.getContainedAuthorities(AuthorityType.GROUP, group, false).size(); + count += this.authService.getContainedAuthorities(AuthorityType.USER, group, false).size(); + setActionGroupItems(count); + } + else + { + setActionGroupName(null); + setActionGroupItems(0); + } + + // clear value used by Create Group form + this.name = null; + + // clear list for Add Users to Group screen + this.usersForGroup = new ArrayList(); + } + + /** + * @return Returns the actionGroupName. + */ + public String getActionGroupName() + { + return this.actionGroupName; + } + + /** + * @param actionGroupName The actionGroupName to set. + */ + public void setActionGroupName(String actionGroupName) + { + this.actionGroupName = actionGroupName; + } + + /** + * @return Returns the action Group Items count. + */ + public int getActionGroupItems() + { + return this.actionGroupItems; + } + + /** + * @param actionGroupItems The action Group Items count to set. + */ + public void setActionGroupItems(int actionGroupItems) + { + this.actionGroupItems = actionGroupItems; + } + + /** + * @return The currently displayed group or null if at the root. + */ + public String getCurrentGroup() + { + return this.group; + } + + /** + * Set the current Group Authority. + *

+ * Setting this value causes the UI to update and display the specified node as current. + * + * @param group The current group authority. + */ + public void setCurrentGroup(String group, String groupName) + { + if (logger.isDebugEnabled()) + logger.debug("Setting current group: " + group); + + // set the current Group Authority for our UI context operations + this.group = group; + this.groupName = groupName; + + // inform that the UI needs updating after this change + contextUpdated(); + } + + /** + * @return Returns the groupName. + */ + public String getGroupName() + { + return this.groupName; + } + + /** + * @param groupName The groupName to set. + */ + public void setGroupName(String groupName) + { + this.groupName = groupName; + } + + /** + * @return Breadcrumb location list + */ + public List getLocation() + { + if (this.location == null) + { + List loc = new ArrayList(8); + loc.add(new GroupBreadcrumbHandler(null, + Application.getMessage(FacesContext.getCurrentInstance(), MSG_GROUPS))); + + this.location = loc; + } + return this.location; + } + + /** + * @param location Breadcrumb location list + */ + public void setLocation(List location) + { + this.location = location; + } + + /** + * @return The list of group objects to display. Returns the list of root groups or the + * list of sub-groups for the current group if set. + */ + public List getGroups() + { + List groups; + + UserTransaction tx = null; + try + { + FacesContext context = FacesContext.getCurrentInstance(); + tx = Repository.getUserTransaction(context); + tx.begin(); + + Set authorities; + boolean immediate = (this.filterMode.equals(FILTER_CHILDREN)); + if (this.group == null) + { + // root groups + if (immediate == true) + { + authorities = this.authService.getAllRootAuthorities(AuthorityType.GROUP); + } + else + { + authorities = this.authService.getAllAuthorities(AuthorityType.GROUP); + } + } + else + { + // sub-group of an existing group + authorities = this.authService.getContainedAuthorities(AuthorityType.GROUP, group, immediate); + } + groups = new ArrayList(authorities.size()); + for (String authority : authorities) + { + Map authMap = new HashMap(3, 1.0f); + + String name = this.authService.getShortName(authority); + authMap.put("name", name); + authMap.put("id", authority); + + groups.add(authMap); + } + + // commit the transaction + tx.commit(); + } + catch (Throwable err) + { + Utils.addErrorMessage(MessageFormat.format(Application.getMessage( + FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), err.getMessage()), err); + groups = Collections.emptyList(); + try { if (tx != null) {tx.rollback();} } catch (Exception tex) {} + } + + return groups; + } + + /** + * @return The list of user objects to display. Returns the list of user for the current group. + */ + public List getUsers() + { + List users; + + UserTransaction tx = null; + try + { + FacesContext context = FacesContext.getCurrentInstance(); + tx = Repository.getUserTransaction(context); + tx.begin(); + + Set authorities; + if (this.group == null) + { + authorities = Collections.emptySet(); + } + else + { + // users of an existing group + boolean immediate = (this.filterMode.equals(FILTER_CHILDREN)); + authorities = this.authService.getContainedAuthorities(AuthorityType.USER, group, immediate); + } + users = new ArrayList(authorities.size()); + for (String authority : authorities) + { + Map authMap = new HashMap(3, 1.0f); + + String userName = this.authService.getShortName(authority); + authMap.put("userName", userName); + authMap.put("id", authority); + + // get Person details for this Authority + NodeRef ref = this.personService.getPerson(authority); + String firstName = (String)this.nodeService.getProperty(ref, ContentModel.PROP_FIRSTNAME); + String lastName = (String)this.nodeService.getProperty(ref, ContentModel.PROP_LASTNAME); + + // build a sensible label for display + StringBuilder label = new StringBuilder(48); + label.append(firstName) + .append(' ') + .append(lastName); + authMap.put("name", label.toString()); + + users.add(authMap); + } + + // commit the transaction + tx.commit(); + } + catch (Throwable err) + { + Utils.addErrorMessage(MessageFormat.format(Application.getMessage( + FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), err.getMessage()), err); + users = Collections.emptyList(); + try { if (tx != null) {tx.rollback();} } catch (Exception tex) {} + } + + return users; + } + + /** + * Validate password field data is acceptable + */ + public void validateGroupName(FacesContext context, UIComponent component, Object value) + throws ValidatorException + { + String name = (String)value; + if (name.indexOf('\'') != -1 || name.indexOf('"') != -1 || name.indexOf('\\') != -1) + { + String err = MessageFormat.format(Application.getMessage(context, "groups_err_group_name"), + new Object[]{"', \", \\"}); + throw new ValidatorException(new FacesMessage(err)); + } + } + + /** + * Query callback method executed by the Generic Picker component. + * This method is part of the contract to the Generic Picker, it is up to the backing bean + * to execute whatever query is appropriate and return the results. + * + * @param filterIndex Index of the filter drop-down selection + * @param contains Text from the contains textbox + * + * @return An array of SelectItem objects containing the results to display in the picker. + */ + public SelectItem[] pickerCallback(int filterIndex, String contains) + { + FacesContext context = FacesContext.getCurrentInstance(); + + SelectItem[] items; + + UserTransaction tx = null; + try + { + tx = Repository.getUserTransaction(context); + tx.begin(); + + // build xpath to match available User/Person objects + ServiceRegistry services = Repository.getServiceRegistry(context); + NodeRef peopleRef = personService.getPeopleContainer(); + String xpath = "*[like(@" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + "firstName, '%" + contains + "%', false)" + + " or " + "like(@" + NamespaceService.CONTENT_MODEL_PREFIX + ":" + "lastName, '%" + contains + "%', false)]"; + + List nodes = services.getSearchService().selectNodes( + peopleRef, + xpath, + null, + services.getNamespaceService(), + false); + + items = new SelectItem[nodes.size()]; + for (int index=0; index params = link.getParameterMap(); + String group = params.get("id"); + if (group != null && group.length() != 0) + { + if (logger.isDebugEnabled()) + logger.debug("Setup for action, setting current Group to: " + group); + + // prepare a node for the action context + setActionGroup(group); + + // clear datalist cache ready from return from action dialog + contextUpdated(); + } + } + + /** + * Clear the Group action context - e.g. ready for a Create Root Group operation + */ + public void clearGroupAction(ActionEvent event) + { + setActionGroup(null); + + // clear datalist cache ready from return from action dialog + contextUpdated(); + } + + /** + * Action called when a Group folder is clicked. + * Navigate into the Group and show child Groups and child Users. + */ + public void clickGroup(ActionEvent event) + { + UIActionLink link = (UIActionLink)event.getComponent(); + Map params = link.getParameterMap(); + String group = params.get("id"); + if (group != null && group.length() != 0) + { + // refresh UI based on node selection + updateUILocation(group); + } + } + + /** + * Action handler called on Create Group finish button click. + */ + public String finishCreate() + { + String outcome = DEFAULT_OUTCOME; + + UserTransaction tx = null; + try + { + FacesContext context = FacesContext.getCurrentInstance(); + tx = Repository.getUserTransaction(context); + tx.begin(); + + // create new Group using Authentication Service + this.authService.createAuthority(AuthorityType.GROUP, getActionGroup(), this.name); + + // commit the transaction + tx.commit(); + } + catch (Throwable err) + { + // rollback the transaction + try { if (tx != null) {tx.rollback();} } catch (Exception tex) {} + Utils.addErrorMessage(MessageFormat.format(Application.getMessage( + FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), err.getMessage()), err); + outcome = null; + } + + return outcome; + } + + /** + * Action handler called on Delete Group finish button click. + */ + public String finishDelete() + { + String outcome = DEFAULT_OUTCOME; + + UserTransaction tx = null; + try + { + FacesContext context = FacesContext.getCurrentInstance(); + tx = Repository.getUserTransaction(context); + tx.begin(); + + // delete group using the Authentication Service + this.authService.deleteAuthority(getActionGroup()); + + // commit the transaction + tx.commit(); + + // remove this node from the breadcrumb if required + List location = getLocation(); + GroupBreadcrumbHandler handler = (GroupBreadcrumbHandler)location.get(location.size() - 1); + + // see if the current breadcrumb location is our Group + if ( getActionGroup().equals(handler.Group) ) + { + location.remove(location.size() - 1); + + // now work out which Group to set the list to refresh against + if (location.size() != 0) + { + handler = (GroupBreadcrumbHandler)location.get(location.size() - 1); + this.setCurrentGroup(handler.Group, handler.Label); + } + } + + // clear action context + setActionGroup(null); + } + catch (Throwable err) + { + // rollback the transaction + try { if (tx != null) {tx.rollback();} } catch (Exception tex) {} + Utils.addErrorMessage(MessageFormat.format(Application.getMessage( + FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), err.getMessage()), err); + outcome = null; + } + + return outcome; + } + + /** + * Remove specified user from the current group + */ + public void removeUser(ActionEvent event) + { + UIActionLink link = (UIActionLink)event.getComponent(); + Map params = link.getParameterMap(); + String authority = params.get("id"); + if (authority != null && authority.length() != 0) + { + try + { + this.authService.removeAuthority(this.group, authority); + + // refresh UI after change + contextUpdated(); + } + catch (Throwable err) + { + Utils.addErrorMessage(MessageFormat.format(Application.getMessage( + FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), err.getMessage()), err); + } + } + } + + /** + * Action handler called when Finish button clicked on Add User to Group page + */ + public String finishAddUser() + { + String outcome = DEFAULT_OUTCOME; + + UserTransaction tx = null; + try + { + FacesContext context = FacesContext.getCurrentInstance(); + tx = Repository.getUserTransaction(context); + tx.begin(); + + // add each selected user to the current group in turn + for (UserAuthorityDetails wrapper : this.usersForGroup) + { + this.authService.addAuthority(getActionGroup(), wrapper.authority); + } + + // commit the transaction + tx.commit(); + } + catch (Throwable err) + { + // rollback the transaction + try { if (tx != null) {tx.rollback();} } catch (Exception tex) {} + Utils.addErrorMessage(MessageFormat.format(Application.getMessage( + FacesContext.getCurrentInstance(), Repository.ERROR_GENERIC), err.getMessage()), err); + outcome = null; + } + + return outcome; + } + + /** + * Add the selected User to the list for adding to a Group + */ + public void addSelectedUsers(ActionEvent event) + { + UIGenericPicker picker = (UIGenericPicker)event.getComponent().findComponent("picker"); + String[] results = picker.getSelectedResults(); + if (results != null) + { + for (int i=0; i\n"; private final static String STYLES_START = "\n"; + +/** + * Please ensure you understand the terms of the license before changing the contents of this file. + */ + private final static String ALF_URL = "http://www.alfresco.com"; - private final static String ALF_LOGO = "/images/logo/alfresco_logo.gif"; - private final static String ALF_TEXT = "Content managed by Alfresco"; - private final static String ALF_COPY = "Alfresco Software Inc. (C) 2005-2006 All rights reserved."; + private final static String ALF_LOGO = "http://www.alfresco.com/images/alfresco_community_horizont.gif"; + private final static String ALF_TEXT = "Alfresco Community"; + private final static String ALF_COPY = "Supplied free of charge with " + + "no support, " + + "no certification, " + + "no maintenance, " + + "no warranty and " + + "no indemnity by " + + "Alfresco or its " + + "Certified Partners. " + + "Click here for support. " + + "Alfresco Software Inc. © 2005-2006 All rights reserved."; private static Log logger = LogFactory.getLog(PageTag.class); private static String alfresco = null; @@ -185,17 +199,22 @@ public class PageTag extends TagSupport return loginPage; } +/** + * Please ensure you understand the terms of the license before changing the contents of this file. + */ + private String getAlfrescoButton() { if (alfresco == null) { String reqPath = ((HttpServletRequest)pageContext.getRequest()).getContextPath(); - alfresco = "

" + + alfresco = "
" + "" + - "" + - " " + - "" + ALF_COPY + - ""; + "" + + "" + + "" + ALF_COPY + + "" + + "
"; } return alfresco; diff --git a/source/web/WEB-INF/faces-config-beans.xml b/source/web/WEB-INF/faces-config-beans.xml index 107153db43..0290414ab2 100644 --- a/source/web/WEB-INF/faces-config-beans.xml +++ b/source/web/WEB-INF/faces-config-beans.xml @@ -296,6 +296,27 @@ + + + The bean that holds state for the Groups Management screens. + + GroupsBean + org.alfresco.web.bean.GroupsBean + session + + nodeService + #{NodeService} + + + authorityService + #{AuthorityService} + + + personService + #{PersonService} + + + The bean that holds state for the Category Management screens. diff --git a/source/web/WEB-INF/faces-config-navigation.xml b/source/web/WEB-INF/faces-config-navigation.xml index 9048446658..3dba6f0e99 100644 --- a/source/web/WEB-INF/faces-config-navigation.xml +++ b/source/web/WEB-INF/faces-config-navigation.xml @@ -191,6 +191,10 @@ manageUsers /jsp/users/users.jsp + + manageGroups + /jsp/groups/groups.jsp + showSystemInfo /jsp/dialog/system-info.jsp @@ -216,7 +220,7 @@ /jsp/dialog/checkout-file.jsp - + /jsp/dialog/edit-text-inline.jsp @@ -633,6 +637,35 @@ + + + /jsp/groups/* + + newGroup + /jsp/groups/new-group.jsp + + + deleteGroup + /jsp/groups/delete-group.jsp + + + addUser + /jsp/groups/add-user.jsp + + + deleteUser + /jsp/groups/delete-user.jsp + + + finish + /jsp/groups/groups.jsp + + + cancel + /jsp/groups/groups.jsp + + + /jsp/categories/* diff --git a/source/web/css/main.css b/source/web/css/main.css index c7b7b280ce..dec15aad95 100644 --- a/source/web/css/main.css +++ b/source/web/css/main.css @@ -118,6 +118,26 @@ a.small:hover color: #4272B4; } +a.footer:link, a.footer:visited +{ + font-size: 10px; + text-decoration: underline; + color: #003366; +} + +a.footer:hover +{ + font-size: 10px; + text-decoration: underline; + color: #4272B4; +} + +.footer +{ + font-family:Arial,Helvetica; + font-size: 10px; +} + .pageTitle { color: #003366; diff --git a/source/web/images/logo/alfresco_enterprise.gif b/source/web/images/logo/alfresco_enterprise.gif new file mode 100644 index 0000000000..f1b8240c8d Binary files /dev/null and b/source/web/images/logo/alfresco_enterprise.gif differ diff --git a/source/web/jsp/admin/admin-console.jsp b/source/web/jsp/admin/admin-console.jsp index a17ca58c6e..963009709d 100644 --- a/source/web/jsp/admin/admin-console.jsp +++ b/source/web/jsp/admin/admin-console.jsp @@ -100,6 +100,9 @@ + + + diff --git a/source/web/jsp/groups/add-user.jsp b/source/web/jsp/groups/add-user.jsp new file mode 100644 index 0000000000..5de103fe66 --- /dev/null +++ b/source/web/jsp/groups/add-user.jsp @@ -0,0 +1,194 @@ +<%-- + Copyright (C) 2005 Alfresco, Inc. + + Licensed under the Alfresco Network License. You may obtain a + copy of the License at + + http://www.alfrescosoftware.com/legal/ + + Please view the license relevant to your network subscription. + + BY CLICKING THE "I UNDERSTAND AND ACCEPT" BOX, OR INSTALLING, + READING OR USING ALFRESCO'S Network SOFTWARE (THE "SOFTWARE"), + YOU ARE AGREEING ON BEHALF OF THE ENTITY LICENSING THE SOFTWARE + ("COMPANY") THAT COMPANY WILL BE BOUND BY AND IS BECOMING A PARTY TO + THIS ALFRESCO NETWORK AGREEMENT ("AGREEMENT") AND THAT YOU HAVE THE + AUTHORITY TO BIND COMPANY. IF COMPANY DOES NOT AGREE TO ALL OF THE + TERMS OF THIS AGREEMENT, DO NOT SELECT THE "I UNDERSTAND AND AGREE" + BOX AND DO NOT INSTALL THE SOFTWARE OR VIEW THE SOURCE CODE. COMPANY + HAS NOT BECOME A LICENSEE OF, AND IS NOT AUTHORIZED TO USE THE + SOFTWARE UNLESS AND UNTIL IT HAS AGREED TO BE BOUND BY THESE LICENSE + TERMS. THE "EFFECTIVE DATE" FOR THIS AGREEMENT SHALL BE THE DAY YOU + CHECK THE "I UNDERSTAND AND ACCEPT" BOX. +--%> +<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> +<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="/WEB-INF/alfresco.tld" prefix="a" %> +<%@ taglib uri="/WEB-INF/repo.tld" prefix="r" %> + +<%@ page buffer="32kb" contentType="text/html;charset=UTF-8" %> +<%@ page isELIgnored="false" %> +<%@ page import="org.alfresco.web.ui.common.PanelGenerator" %> + + + + + + <%-- load a bundle of properties with I18N strings --%> + + + + + <%-- Main outer table --%> + + + <%-- Title bar --%> + + + + + <%-- Main area --%> + + <%-- Shelf --%> + + + <%-- Work Area --%> + + +
+ <%@ include file="../parts/titlebar.jsp" %> +
+ <%@ include file="../parts/shelf.jsp" %> + + + <%-- Breadcrumb --%> + <%@ include file="../parts/breadcrumb.jsp" %> + + <%-- Status and Actions --%> + + + + + + + <%-- separator row with gradient shadow --%> + + + + + + + <%-- Details --%> + + + + + + + <%-- separator row with bottom panel graphics --%> + + + + + + +
+ + <%-- Status and Actions inner contents table --%> + <%-- Generally this consists of an icon, textual summary and actions for the current object --%> + + + + + +
+ +
+
+
+
+ +
+ + + + + + +
+ + + + <% PanelGenerator.generatePanelStart(out, request.getContextPath(), "white", "white"); %> + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + +
+
+
+ <% PanelGenerator.generatePanelEnd(out, request.getContextPath(), "white"); %> +
+ <% PanelGenerator.generatePanelStart(out, request.getContextPath(), "blue", "#D3E6FE"); %> + + + + + + + +
+ +
+ +
+ <% PanelGenerator.generatePanelEnd(out, request.getContextPath(), "blue"); %> +
+
+
+ +
+ +
+ +
\ No newline at end of file diff --git a/source/web/jsp/groups/delete-group.jsp b/source/web/jsp/groups/delete-group.jsp new file mode 100644 index 0000000000..79c6567f5d --- /dev/null +++ b/source/web/jsp/groups/delete-group.jsp @@ -0,0 +1,182 @@ +<%-- + Copyright (C) 2005 Alfresco, Inc. + + Licensed under the Alfresco Network License. You may obtain a + copy of the License at + + http://www.alfrescosoftware.com/legal/ + + Please view the license relevant to your network subscription. + + BY CLICKING THE "I UNDERSTAND AND ACCEPT" BOX, OR INSTALLING, + READING OR USING ALFRESCO'S Network SOFTWARE (THE "SOFTWARE"), + YOU ARE AGREEING ON BEHALF OF THE ENTITY LICENSING THE SOFTWARE + ("COMPANY") THAT COMPANY WILL BE BOUND BY AND IS BECOMING A PARTY TO + THIS ALFRESCO NETWORK AGREEMENT ("AGREEMENT") AND THAT YOU HAVE THE + AUTHORITY TO BIND COMPANY. IF COMPANY DOES NOT AGREE TO ALL OF THE + TERMS OF THIS AGREEMENT, DO NOT SELECT THE "I UNDERSTAND AND AGREE" + BOX AND DO NOT INSTALL THE SOFTWARE OR VIEW THE SOURCE CODE. COMPANY + HAS NOT BECOME A LICENSEE OF, AND IS NOT AUTHORIZED TO USE THE + SOFTWARE UNLESS AND UNTIL IT HAS AGREED TO BE BOUND BY THESE LICENSE + TERMS. THE "EFFECTIVE DATE" FOR THIS AGREEMENT SHALL BE THE DAY YOU + CHECK THE "I UNDERSTAND AND ACCEPT" BOX. +--%> +<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> +<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="/WEB-INF/alfresco.tld" prefix="a" %> +<%@ taglib uri="/WEB-INF/repo.tld" prefix="r" %> + +<%@ page buffer="32kb" contentType="text/html;charset=UTF-8" %> +<%@ page isELIgnored="false" %> +<%@ page import="org.alfresco.web.ui.common.PanelGenerator" %> + + + + + + <%-- load a bundle of properties with I18N strings --%> + + + <%-- set the form name here --%> + + + <%-- Main outer table --%> + + + <%-- Title bar --%> + + + + + <%-- Main area --%> + + <%-- Shelf --%> + + + <%-- Work Area --%> + + +
+ <%@ include file="../parts/titlebar.jsp" %> +
+ <%@ include file="../parts/shelf.jsp" %> + + + <%-- Breadcrumb --%> + <%@ include file="../parts/breadcrumb.jsp" %> + + <%-- Status and Actions --%> + + + + + + + <%-- separator row with gradient shadow --%> + + + + + + + <%-- Details --%> + + + + + + + <%-- separator row with bottom panel graphics --%> + + + + + + +
+ + <%-- Status and Actions inner contents table --%> + <%-- Generally this consists of an icon, textual summary and actions for the current object --%> + + + + + +
+ + +
''
+
+
+ +
+ + + + + + + +
+ <% PanelGenerator.generatePanelStart(out, request.getContextPath(), "white", "white"); %> + + + + + + + + + + + +
+ <% PanelGenerator.generatePanelStart(out, request.getContextPath(), "yellowInner", "#ffffcc"); %> + + + + + +
+ + + +
+ <% PanelGenerator.generatePanelEnd(out, request.getContextPath(), "yellowInner"); %> +
+ + + +
+ <%-- Error Messages --%> + <%-- messages tag to show messages not handled by other specific message tags --%> + + +
+ <% PanelGenerator.generatePanelEnd(out, request.getContextPath(), "white"); %> +
+ <% PanelGenerator.generatePanelStart(out, request.getContextPath(), "blue", "#D3E6FE"); %> + + + + + + + + +
+ +
+ +
+ <% PanelGenerator.generatePanelEnd(out, request.getContextPath(), "blue"); %> +
+
+
+ +
+ +
+ +
diff --git a/source/web/jsp/groups/groups.jsp b/source/web/jsp/groups/groups.jsp new file mode 100644 index 0000000000..779b0c4fba --- /dev/null +++ b/source/web/jsp/groups/groups.jsp @@ -0,0 +1,319 @@ +<%-- + Copyright (C) 2005 Alfresco, Inc. + + Licensed under the Alfresco Network License. You may obtain a + copy of the License at + + http://www.alfrescosoftware.com/legal/ + + Please view the license relevant to your network subscription. + + BY CLICKING THE "I UNDERSTAND AND ACCEPT" BOX, OR INSTALLING, + READING OR USING ALFRESCO'S Network SOFTWARE (THE "SOFTWARE"), + YOU ARE AGREEING ON BEHALF OF THE ENTITY LICENSING THE SOFTWARE + ("COMPANY") THAT COMPANY WILL BE BOUND BY AND IS BECOMING A PARTY TO + THIS ALFRESCO NETWORK AGREEMENT ("AGREEMENT") AND THAT YOU HAVE THE + AUTHORITY TO BIND COMPANY. IF COMPANY DOES NOT AGREE TO ALL OF THE + TERMS OF THIS AGREEMENT, DO NOT SELECT THE "I UNDERSTAND AND AGREE" + BOX AND DO NOT INSTALL THE SOFTWARE OR VIEW THE SOURCE CODE. COMPANY + HAS NOT BECOME A LICENSEE OF, AND IS NOT AUTHORIZED TO USE THE + SOFTWARE UNLESS AND UNTIL IT HAS AGREED TO BE BOUND BY THESE LICENSE + TERMS. THE "EFFECTIVE DATE" FOR THIS AGREEMENT SHALL BE THE DAY YOU + CHECK THE "I UNDERSTAND AND ACCEPT" BOX. +--%> +<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> +<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="/WEB-INF/alfresco.tld" prefix="a" %> +<%@ taglib uri="/WEB-INF/repo.tld" prefix="r" %> + +<%@ page buffer="32kb" contentType="text/html;charset=UTF-8" %> +<%@ page isELIgnored="false" %> +<%@ page import="org.alfresco.web.ui.common.PanelGenerator" %> + + + + + + <%-- load a bundle of properties with I18N strings --%> + + + + + <%-- Main outer table --%> + + + <%-- Title bar --%> + + + + + <%-- Main area --%> + + <%-- Shelf --%> + + + <%-- Work Area --%> + + +
+ <%@ include file="../parts/titlebar.jsp" %> +
+ <%@ include file="../parts/shelf.jsp" %> + + + <%-- Breadcrumb --%> + <%@ include file="../parts/breadcrumb.jsp" %> + + <%-- Status and Actions --%> + + + + + + + <%-- separator row with gradient shadow --%> + + + + + + + <%-- Details --%> + + + + + + + <%-- Error Messages --%> + + + + + + + <%-- separator row with bottom panel graphics --%> + + + + + + +
+ + <%-- Status and Actions inner contents table --%> + <%-- Generally this consists of an icon, textual summary and actions for the current object --%> + + + + + + + + + + + + +
+ +
+
+ <%-- show either root message or the current group name --%> + + +
+
+
+ <%-- Create actions menu --%> + + + + + + + + + + + + <%-- More actions menu --%> + + + + + + + + + <%-- TODO: should this be add user(S) - multiple required on generic picker? --%> + + + + + + + <%-- Filter settings --%> + + + + + + <%-- View mode settings --%> + + + + +
+
+ + + + + + +
+ + <%-- Group Path Breadcrumb --%> +
+ +
+ + <%-- Groups List --%> +
+ + + + + + <%-- Primary column for icons view mode --%> + + + + + + + + + + + + <%-- Primary column for details view mode --%> + + + + + + + + + + + + + + + <%-- Actions column --%> + + + + + + + + + + + + + + + + + + + + +
+ + <%-- Users in Group list --%> +
+ + + + + + <%-- Primary column for icons view mode --%> + + + + + + + + <%-- Primary column for details view mode --%> + + + + + + + + + + + <%-- Username column --%> + + + + + + + + <%-- Actions column --%> + + + + + + + + + + + + + + +
+ +
+ <% PanelGenerator.generatePanelStart(out, request.getContextPath(), "blue", "#D3E6FE"); %> + + + + +
+ +
+ <% PanelGenerator.generatePanelEnd(out, request.getContextPath(), "blue"); %> +
+
+ <%-- messages tag to show messages not handled by other specific message tags --%> + +
+
+ +
+ +
+ +
\ No newline at end of file diff --git a/source/web/jsp/groups/new-group.jsp b/source/web/jsp/groups/new-group.jsp new file mode 100644 index 0000000000..a1dc82fcd5 --- /dev/null +++ b/source/web/jsp/groups/new-group.jsp @@ -0,0 +1,203 @@ +<%-- + Copyright (C) 2005 Alfresco, Inc. + + Licensed under the Alfresco Network License. You may obtain a + copy of the License at + + http://www.alfrescosoftware.com/legal/ + + Please view the license relevant to your network subscription. + + BY CLICKING THE "I UNDERSTAND AND ACCEPT" BOX, OR INSTALLING, + READING OR USING ALFRESCO'S Network SOFTWARE (THE "SOFTWARE"), + YOU ARE AGREEING ON BEHALF OF THE ENTITY LICENSING THE SOFTWARE + ("COMPANY") THAT COMPANY WILL BE BOUND BY AND IS BECOMING A PARTY TO + THIS ALFRESCO NETWORK AGREEMENT ("AGREEMENT") AND THAT YOU HAVE THE + AUTHORITY TO BIND COMPANY. IF COMPANY DOES NOT AGREE TO ALL OF THE + TERMS OF THIS AGREEMENT, DO NOT SELECT THE "I UNDERSTAND AND AGREE" + BOX AND DO NOT INSTALL THE SOFTWARE OR VIEW THE SOURCE CODE. COMPANY + HAS NOT BECOME A LICENSEE OF, AND IS NOT AUTHORIZED TO USE THE + SOFTWARE UNLESS AND UNTIL IT HAS AGREED TO BE BOUND BY THESE LICENSE + TERMS. THE "EFFECTIVE DATE" FOR THIS AGREEMENT SHALL BE THE DAY YOU + CHECK THE "I UNDERSTAND AND ACCEPT" BOX. +--%> +<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> +<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="/WEB-INF/alfresco.tld" prefix="a" %> +<%@ taglib uri="/WEB-INF/repo.tld" prefix="r" %> + +<%@ page buffer="32kb" contentType="text/html;charset=UTF-8" %> +<%@ page isELIgnored="false" %> +<%@ page import="org.alfresco.web.ui.common.PanelGenerator" %> + + + + + + + + <%-- load a bundle of properties with I18N strings --%> + + + + + <%-- Main outer table --%> + + + <%-- Title bar --%> + + + + + <%-- Main area --%> + + <%-- Shelf --%> + + + <%-- Work Area --%> + + +
+ <%@ include file="../parts/titlebar.jsp" %> +
+ <%@ include file="../parts/shelf.jsp" %> + + + <%-- Breadcrumb --%> + <%@ include file="../parts/breadcrumb.jsp" %> + + <%-- Status and Actions --%> + + + + + + + <%-- separator row with gradient shadow --%> + + + + + + + <%-- Details --%> + + + + + + + <%-- separator row with bottom panel graphics --%> + + + + + + +
+ + <%-- Status and Actions inner contents table --%> + <%-- Generally this consists of an icon, textual summary and actions for the current object --%> + + + + + +
+ +
+ + +
+
+
+
+ +
+ + + + + + +
+ + + + <% PanelGenerator.generatePanelStart(out, request.getContextPath(), "white", "white"); %> + + + + + + + + + + + +
+ <% PanelGenerator.generatePanelStart(out, request.getContextPath(), "yellowInner", "#ffffcc"); %> + + + + + +
+ +
+ <% PanelGenerator.generatePanelEnd(out, request.getContextPath(), "yellowInner"); %> +
: +  * +
+ <% PanelGenerator.generatePanelEnd(out, request.getContextPath(), "white"); %> +
+ <% PanelGenerator.generatePanelStart(out, request.getContextPath(), "blue", "#D3E6FE"); %> + + + + + + + +
+ +
+ +
+ <% PanelGenerator.generatePanelEnd(out, request.getContextPath(), "blue"); %> +
+
+
+ +
+ +
+ +
\ No newline at end of file