View Javadoc

1   /**
2    * Copyright (C) 2005-2009 Alfresco Software Limited.
3    *
4    * This file is part of the Spring Surf Extension project.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *  http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.springframework.extensions.webscripts;
20  
21  import javax.servlet.http.HttpServletRequest;
22  import javax.servlet.http.HttpServletResponse;
23  import javax.servlet.http.HttpSession;
24  
25  import org.apache.commons.logging.Log;
26  import org.apache.commons.logging.LogFactory;
27  import org.springframework.extensions.surf.UserFactory;
28  import org.springframework.extensions.surf.util.Base64;
29  import org.springframework.extensions.webscripts.Description.RequiredAuthentication;
30  import org.springframework.extensions.webscripts.connector.Connector;
31  import org.springframework.extensions.webscripts.connector.ConnectorService;
32  import org.springframework.extensions.webscripts.connector.CredentialVault;
33  import org.springframework.extensions.webscripts.connector.Credentials;
34  import org.springframework.extensions.webscripts.connector.CredentialsImpl;
35  import org.springframework.extensions.webscripts.connector.Response;
36  import org.springframework.extensions.webscripts.servlet.ServletAuthenticatorFactory;
37  import org.springframework.extensions.webscripts.servlet.WebScriptServletRequest;
38  import org.springframework.extensions.webscripts.servlet.WebScriptServletResponse;
39  
40  
41  /**
42   * HTTP Basic Authentication for web-tier.
43   * 
44   * Provides either delegated or direct HTTP authentication to the designated endpoint.
45   * If delegation is used, the endpoint supplied must perform the handshake when called.
46   * 
47   * @author Kevin Roast
48   */
49  public class BasicHttpAuthenticatorFactory implements ServletAuthenticatorFactory
50  {
51      private static Log logger = LogFactory.getLog(BasicHttpAuthenticatorFactory.class);
52      
53      private ConnectorService connectorService;
54      private String endpointId;
55      private boolean delegate = false;
56      
57      
58      /**
59       * @param connectorService      the ConnectorService to use
60       */
61      public void setConnectorService(ConnectorService connectorService)
62      {
63          this.connectorService = connectorService;
64      }
65      
66      /**
67       * @param endpointId            EndPoint Id to use
68       */
69      public void setEndpointId(String endpointId)
70      {
71          this.endpointId = endpointId;
72      }
73      
74      /**
75       * @param delegate              True to delegate actual auth to the connector framework
76       *                              False to perform the authentication directly
77       */
78      public void setDelegate(boolean delegate)
79      {
80          this.delegate = delegate;
81      }
82      
83      
84      /* (non-Javadoc)
85       * @see org.alfresco.web.scripts.servlet.ServletAuthenticatorFactory#create(org.alfresco.web.scripts.servlet.WebScriptServletRequest, org.alfresco.web.scripts.servlet.WebScriptServletResponse)
86       */
87      public Authenticator create(WebScriptServletRequest req, WebScriptServletResponse res)
88      {
89          return new BasicHttpAuthenticator(req, res);
90      }
91      
92      
93      /**
94       * HTTP Basic Authentication
95       */
96      public class BasicHttpAuthenticator implements Authenticator
97      {
98          // dependencies
99          private WebScriptServletRequest servletReq;
100         private WebScriptServletResponse servletRes;
101         
102         private String authorization;
103         private String ticket;
104         
105         /**
106          * Construct
107          * 
108          * @param authenticationService
109          * @param req
110          * @param res
111          */
112         public BasicHttpAuthenticator(WebScriptServletRequest req, WebScriptServletResponse res)
113         {
114             this.servletReq = req;
115             this.servletRes = res;
116             
117             HttpServletRequest httpReq = servletReq.getHttpServletRequest();
118             
119             this.authorization = httpReq.getHeader("Authorization");
120             this.ticket = httpReq.getParameter("alf_ticket");
121         }
122     
123         /* (non-Javadoc)
124          * @see org.alfresco.web.scripts.Authenticator#authenticate(org.alfresco.web.scripts.Description.RequiredAuthentication, boolean)
125          */
126         public boolean authenticate(RequiredAuthentication required, boolean isGuest)
127         {
128             boolean authorized = false;
129     
130             // validate credentials
131             HttpServletRequest req = servletReq.getHttpServletRequest();
132             HttpServletResponse res = servletRes.getHttpServletResponse();
133             
134             if (logger.isDebugEnabled())
135                 logger.debug("HTTP Authorization provided: " + (authorization != null && authorization.length() != 0));
136             
137             // authenticate as specified by HTTP Basic Authentication
138             if (authorization != null && authorization.length() != 0)
139             {
140                 String[] authorizationParts = authorization.split(" ");
141                 if (!authorizationParts[0].equalsIgnoreCase("basic"))
142                 {
143                     throw new WebScriptException("Authorization '" + authorizationParts[0] + "' not supported.");
144                 }
145                 String decodedAuthorisation = new String(Base64.decode(authorizationParts[1]));
146                 String[] parts = decodedAuthorisation.split(":");
147                 
148                 if (parts.length == 2)
149                 {
150                     // assume username and password passed as the parts
151                     String username = parts[0];
152                     if (logger.isDebugEnabled())
153                         logger.debug("Authenticating (BASIC HTTP) user " + parts[0]);
154                     
155                     try
156                     {
157                         // generate the credentials based on the auth details provided
158                         HttpSession session = req.getSession();
159                         Credentials credentials = new CredentialsImpl(endpointId);
160                         credentials.setProperty(Credentials.CREDENTIAL_USERNAME, username);
161                         credentials.setProperty(Credentials.CREDENTIAL_PASSWORD, parts[1]);
162                         CredentialVault vault = connectorService.getCredentialVault(session, username);
163                         vault.store(credentials);
164                         
165                         if (delegate)
166                         {
167                             // the handshake will be performed by the Connector when a remote call is first made
168                             session.setAttribute(UserFactory.SESSION_ATTRIBUTE_KEY_USER_ID, username);
169                             authorized = true;
170                         }
171                         else
172                         {
173                             // perform the authentication test directly using the connector
174                             Connector connector = connectorService.getConnector(endpointId, username, session);
175                             Response response = connector.call("/touch");
176                             authorized = (response.getStatus().getCode() != Status.STATUS_UNAUTHORIZED);
177                         }
178                     }
179                     catch (Throwable err)
180                     {
181                         logger.warn("Failed during authorization: " + err.getMessage(), err);
182                     }
183                 }
184             }
185             
186             // request credentials if not authorized
187             if (!authorized)
188             {
189                 if (logger.isDebugEnabled())
190                     logger.debug("Requesting authorization credentials");
191                 
192                 res.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
193                 res.setHeader("WWW-Authenticate", "Basic realm=\"Alfresco\"");
194             }
195             
196             return authorized;
197         }
198         
199         /* (non-Javadoc)
200          * @see org.alfresco.web.scripts.Authenticator#emptyCredentials()
201          */
202         public boolean emptyCredentials()
203         {
204             return ((ticket == null || ticket.length() == 0) && (authorization == null || authorization.length() == 0));
205         }
206     }
207 }