View Javadoc

1   /*
2    * Copyright (c) 2004 UNINETT FAS
3    *
4    * This program is free software; you can redistribute it and/or modify it
5    * under the terms of the GNU General Public License as published by the Free
6    * Software Foundation; either version 2 of the License, or (at your option)
7    * any later version.
8    *
9    * This program is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11   * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12   * more details.
13   *
14   * You should have received a copy of the GNU General Public License along with
15   * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
16   * Place - Suite 330, Boston, MA 02111-1307, USA.
17   *
18   * $Id: AuthenticationImpl.java,v 1.6 2006/02/16 14:53:56 catoolsen Exp $
19   */
20  
21  package no.feide.moria.webservices.v2_1;
22  
23  import java.util.ArrayList;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.Properties;
28  import java.util.Set;
29  
30  import no.feide.moria.controller.AuthenticationException;
31  import no.feide.moria.controller.AuthorizationException;
32  import no.feide.moria.controller.DirectoryUnavailableException;
33  import no.feide.moria.controller.InoperableStateException;
34  import no.feide.moria.controller.MoriaController;
35  import no.feide.moria.log.MessageLogger;
36  import no.feide.moria.servlet.RequestUtil;
37  import no.feide.moria.servlet.soap.AuthenticationFailedException;
38  import no.feide.moria.servlet.soap.AuthenticationUnavailableException;
39  import no.feide.moria.servlet.soap.AuthorizationFailedException;
40  import no.feide.moria.servlet.soap.IllegalInputException;
41  import no.feide.moria.servlet.soap.InternalException;
42  import no.feide.moria.servlet.soap.SOAPException;
43  import no.feide.moria.servlet.soap.UnknownTicketException;
44  
45  import org.apache.axis.MessageContext;
46  import org.apache.axis.session.Session;
47  import org.apache.axis.transport.http.AxisHttpSession;
48  import org.apache.log4j.Level;
49  
50  /***
51   * @author Bjørn Ola Smievoll <b.o.smievoll@conduct.no>
52   * @version $Revision: 1.6 $
53   */
54  public final class AuthenticationImpl
55  implements Authentication {
56  
57      /*** Class wide logger. */
58      private MessageLogger messageLogger;
59  
60      /*** Log message for AuthorizationExceptions. */
61      private static final String AUTHZ_EX_MESSAGE = "Authorization failed. Throwing RemoteException to service: ";
62  
63      /*** Log message for AuthenticationExceptions. */
64      private static final String AUTHN_EX_MSG = "Authentication failed. Throwing RemoteException to service: ";
65  
66      /*** Log message for DirectoryUnavailableExceptions. */
67      private static final String DIR_UNAV_EX_MSG = "Directory unavailable. Throwing RemoteException to service: ";
68  
69      /*** Log message for MoriaControllerExceptions. */
70      private static final String MORIACTRL_EX_MESSAGE = "Exception from MoriaController. Throwing RemoteException to service: ";
71  
72      /*** Log message for InoperableStateExceptions. */
73      private static final String INOP_STATE_EX_MSG = "Controller in inoperable state. Throwing RemoteException to service: ";
74  
75      /*** Log message for UnknownTicketExceptions. */
76      private static final String UNKNOWN_TICKET_EX_MSG = "Ticket is unknown. Throwing RemoteException to service: ";
77  
78  
79      /***
80       * Default constructor. Initializes the message logger.
81       */
82      public AuthenticationImpl() {
83  
84          messageLogger = new MessageLogger(AuthenticationImpl.class);
85      }
86  
87  
88      /***
89       * @see no.feide.moria.webservices.v2_1.Authentication#initiateAuthentication(java.lang.String[],
90       *      java.lang.String, java.lang.String, boolean)
91       */
92      public String initiateAuthentication(final String[] attributes, final String returnURLPrefix, final String returnURLPostfix, final boolean forceInteractiveAuthentication)
93      throws SOAPException {
94  
95          /* Axis message context containg request data. */
96          MessageContext messageContext = MessageContext.getCurrentContext();
97          String servicePrincipal = messageContext.getUsername();
98  
99          String urlPrefix = null;
100         Session genericSession = messageContext.getSession();
101 
102         if (genericSession instanceof AxisHttpSession) {
103             AxisHttpSession axisHttpSession = (AxisHttpSession) genericSession;
104             Properties properties = (Properties) axisHttpSession.getRep().getServletContext().getAttribute("no.feide.moria.web.config");
105             urlPrefix = (properties.getProperty(RequestUtil.PROP_LOGIN_URL_PREFIX) + "?" + properties.getProperty(RequestUtil.PROP_LOGIN_TICKET_PARAM) + "=");
106         }
107 
108         try {
109 
110             return urlPrefix + MoriaController.initiateAuthentication(attributes, returnURLPrefix, returnURLPostfix, forceInteractiveAuthentication, servicePrincipal);
111 
112         } catch (AuthorizationException e) {
113 
114             // Client service did something it was not authorized to do.
115             messageLogger.logWarn(AUTHZ_EX_MESSAGE + servicePrincipal, e);
116             throw new AuthorizationFailedException(e.getMessage());
117 
118         } catch (no.feide.moria.controller.IllegalInputException e) {
119 
120             // Illegal input from client service.
121             messageLogger.logWarn(MORIACTRL_EX_MESSAGE + servicePrincipal, e);
122             throw new IllegalInputException(e.getMessage());
123 
124         } catch (InoperableStateException e) {
125 
126             // Moria is in an inoperable state.
127             messageLogger.logCritical(INOP_STATE_EX_MSG + servicePrincipal, e);
128             throw new InternalException(e.getMessage());
129 
130         }
131     }
132 
133 
134     /***
135      * @see no.feide.moria.webservices.v2_1.Authentication#directNonInteractiveAuthentication(java.lang.String[],
136      *      java.lang.String, java.lang.String)
137      */
138     public Attribute[] directNonInteractiveAuthentication(final String[] attributes, final String username, final String password)
139     throws SOAPException {
140 
141         /* Axis message context containg request data. */
142         MessageContext messageContext = MessageContext.getCurrentContext();
143         String servicePrincipal = messageContext.getUsername();
144 
145         try {
146             Map returnAttributes = MoriaController.directNonInteractiveAuthentication(attributes, username, password, servicePrincipal);
147             return mapToAttributeArray(returnAttributes, null);
148         } catch (AuthorizationException e) {
149 
150             // Client service did something it was not authorized to do.
151             messageLogger.logWarn(AUTHZ_EX_MESSAGE + servicePrincipal, e);
152             throw new AuthorizationFailedException(e.getMessage());
153 
154         } catch (AuthenticationException e) {
155 
156             // User failed authentication.
157             messageLogger.logWarn(AUTHN_EX_MSG + servicePrincipal, e);
158             throw new AuthenticationFailedException(e.getMessage());
159 
160         } catch (DirectoryUnavailableException e) {
161 
162             // Authentication server was unavailable.
163             messageLogger.logWarn(DIR_UNAV_EX_MSG + servicePrincipal, e);
164             throw new AuthenticationUnavailableException(e.getMessage());
165 
166         } catch (no.feide.moria.controller.IllegalInputException e) {
167 
168             // Illegal input from client service.
169             messageLogger.logWarn(MORIACTRL_EX_MESSAGE + servicePrincipal, e);
170             throw new IllegalInputException(e.getMessage());
171 
172         } catch (InoperableStateException e) {
173 
174             // Moria is in an inoperable state.
175             messageLogger.logCritical(INOP_STATE_EX_MSG + servicePrincipal, e);
176             throw new InternalException(e.getMessage());
177 
178         }
179     }
180 
181 
182     /***
183      * @see no.feide.moria.webservices.v2_1.Authentication#proxyAuthentication(java.lang.String[],
184      *      java.lang.String)
185      */
186     public Attribute[] proxyAuthentication(final String[] attributes, final String proxyTicket)
187     throws SOAPException {
188 
189         /* Axis message context containg request data. */
190         MessageContext messageContext = MessageContext.getCurrentContext();
191         String servicePrincipal = messageContext.getUsername();
192 
193         try {
194 
195             Map returnAttributes = MoriaController.proxyAuthentication(attributes, proxyTicket, servicePrincipal);
196             return mapToAttributeArray(returnAttributes, proxyTicket);
197 
198         } catch (AuthorizationException e) {
199 
200             // Client service did something it was not authorized to do.
201             messageLogger.logWarn(AUTHZ_EX_MESSAGE + servicePrincipal, e);
202             throw new AuthorizationFailedException(e.getMessage());
203 
204         } catch (no.feide.moria.controller.IllegalInputException e) {
205 
206             // Illegal input from client service.
207             messageLogger.logWarn(MORIACTRL_EX_MESSAGE + servicePrincipal, e);
208             throw new IllegalInputException(e.getMessage());
209 
210         } catch (InoperableStateException e) {
211 
212             // Moria is in an inoperable state.
213             messageLogger.logCritical(INOP_STATE_EX_MSG + servicePrincipal, e);
214             throw new InternalException(e.getMessage());
215 
216         } catch (no.feide.moria.controller.UnknownTicketException e) {
217 
218             // An unknown ticket was used by the client service.
219             messageLogger.logWarn(UNKNOWN_TICKET_EX_MSG + servicePrincipal, e);
220             throw new UnknownTicketException(e.getMessage());
221 
222         }
223     }
224 
225 
226     /***
227      * @see no.feide.moria.webservices.v2_1.Authentication#getProxyTicket(java.lang.String,
228      *      java.lang.String)
229      */
230     public String getProxyTicket(final String ticketGrantingTicket, final String proxyServicePrincipal)
231     throws SOAPException {
232 
233         /* Axis message context containg request data. */
234         MessageContext messageContext = MessageContext.getCurrentContext();
235         String servicePrincipal = messageContext.getUsername();
236 
237         try {
238 
239             return MoriaController.getProxyTicket(ticketGrantingTicket, proxyServicePrincipal, servicePrincipal);
240 
241         } catch (AuthorizationException e) {
242 
243             // Client service did something it was not supposed to do.
244             messageLogger.logWarn(AUTHZ_EX_MESSAGE + servicePrincipal, e);
245             throw new AuthorizationFailedException(e.getMessage());
246 
247         } catch (no.feide.moria.controller.IllegalInputException e) {
248 
249             // Illegal input from client service.
250             messageLogger.logWarn(MORIACTRL_EX_MESSAGE + servicePrincipal, e);
251             throw new IllegalInputException(e.getMessage());
252 
253         } catch (InoperableStateException e) {
254 
255             // Moria is in an inoperable state.
256             messageLogger.logCritical(INOP_STATE_EX_MSG + servicePrincipal, e);
257             throw new InternalException(e.getMessage());
258 
259         } catch (no.feide.moria.controller.UnknownTicketException e) {
260 
261             // Client service used an unknown ticket.
262             messageLogger.logWarn(UNKNOWN_TICKET_EX_MSG + servicePrincipal, e);
263             throw new UnknownTicketException(e.getMessage());
264 
265         }
266     }
267 
268 
269     /***
270      * @see no.feide.moria.webservices.v2_1.Authentication#getUserAttributes(java.lang.String)
271      */
272     public Attribute[] getUserAttributes(final String serviceTicket)
273     throws AuthorizationFailedException, IllegalInputException,
274     InternalException, UnknownTicketException {
275 
276         /* Axis message context containg request data. */
277         MessageContext messageContext = MessageContext.getCurrentContext();
278         String servicePrincipal = messageContext.getUsername();
279 
280         try {
281             Map returnAttributes = MoriaController.getUserAttributes(serviceTicket, servicePrincipal);
282             
283             // Prepare debug output.
284             if (messageLogger.isEnabledFor(Level.DEBUG)) {
285                 
286                 String attributeNames = "";
287                 Set keySet = returnAttributes.keySet();
288                 if (keySet != null) {
289                     Iterator i = keySet.iterator();
290                     while (i.hasNext())
291                         attributeNames = attributeNames + i.next() + ", ";
292                     if (attributeNames.length() > 0)
293                         attributeNames = attributeNames.substring(0, attributeNames.length() - 2);
294                 }
295                 messageLogger.logDebug("Returned attributes [" + attributeNames + "] to service '" + servicePrincipal + "'", serviceTicket);
296             }
297             
298             return mapToAttributeArray(returnAttributes, serviceTicket);
299         } catch (no.feide.moria.controller.IllegalInputException e) {
300 
301             // Illegal input used by service.
302             messageLogger.logWarn(MORIACTRL_EX_MESSAGE + servicePrincipal, e);
303             throw new IllegalInputException(e.getMessage());
304 
305         } catch (InoperableStateException e) {
306 
307             // Moria is in an inoperable state!
308             messageLogger.logCritical(INOP_STATE_EX_MSG + servicePrincipal, e);
309             throw new InternalException(e.getMessage());
310 
311         } catch (no.feide.moria.controller.UnknownTicketException e) {
312 
313             // An unknown ticket was used.
314             messageLogger.logWarn(UNKNOWN_TICKET_EX_MSG + servicePrincipal, e);
315             throw new UnknownTicketException(e.getMessage());
316 
317         } catch (AuthorizationException e) {
318 
319             // Service was not authorized for this operation.
320             messageLogger.logWarn("Service not allowed for organization. Throwing RemoteException to service: ", e);
321             throw new AuthorizationFailedException(e.getMessage());
322         }
323 
324     }
325 
326 
327     /***
328      * @see no.feide.moria.webservices.v2_1.Authentication#verifyUserExistence(java.lang.String)
329      */
330     public boolean verifyUserExistence(final String username)
331     throws SOAPException {
332 
333         /* Axis message context containg request data. */
334         MessageContext messageContext = MessageContext.getCurrentContext();
335         String servicePrincipal = messageContext.getUsername();
336 
337         try {
338 
339             return MoriaController.verifyUserExistence(username, servicePrincipal);
340 
341         } catch (AuthorizationException e) {
342 
343             // Client service did something it was not supposed to do.
344             messageLogger.logWarn(AUTHZ_EX_MESSAGE + servicePrincipal, e);
345             throw new AuthorizationFailedException(e.getMessage());
346 
347         } catch (DirectoryUnavailableException e) {
348 
349             // Authentication server is unavailable.
350             messageLogger.logWarn(DIR_UNAV_EX_MSG + servicePrincipal, e);
351             throw new AuthenticationUnavailableException(e.getMessage());
352 
353         } catch (no.feide.moria.controller.IllegalInputException e) {
354 
355             // Illegal input from client service.
356             messageLogger.logWarn(MORIACTRL_EX_MESSAGE + servicePrincipal, e);
357             throw new IllegalInputException(e.getMessage());
358 
359         } catch (InoperableStateException e) {
360 
361             // Moria is in an inoperable state.
362             messageLogger.logCritical(INOP_STATE_EX_MSG + servicePrincipal, e);
363             throw new InternalException(e.getMessage());
364 
365         }
366     }
367 
368 
369     /***
370      * Utility method to convert a <code>Map</code> to an array of
371      * <code>Attribute</code>s.
372      * @param map
373      *            The <code>Map</code> to be converted.
374      * @param activeTicketId
375      *            Optional variable for logging purposes.
376      * @return Array of <code>Attribute</code> objects.
377      */
378     private Attribute[] mapToAttributeArray(final Map map, final String activeTicketId) {
379 
380         /* Get iterator for map keys. */
381         Iterator iterator = map.keySet().iterator();
382 
383         /* List to hold finished Attributes while processing map. */
384         List attributeList = new ArrayList();
385 
386         /* Iterate over keys in map. */
387         while (iterator.hasNext()) {
388             Object key = iterator.next();
389             Object value = map.get(key);
390 
391             Attribute attribute = new Attribute();
392 
393             /* Check that key is a String, if not will ignore the whole entry. */
394             if (key instanceof String) {
395                 /* Add the key as name of attribute. */
396                 attribute.setName((String) key);
397 
398                 /*
399                  * Check type of value. If not String or String[] we don't add
400                  * it to the attribute list resulting in the whole entry beeing
401                  * ignored.
402                  */
403                 if (value instanceof String) {
404                     /* Create one-element String[] of value before setting. */
405                     attribute.setValues(new String[] {(String) value});
406                     attributeList.add(attribute);
407                 } else if (value instanceof String[]) {
408                     attribute.setValues((String[]) value);
409                     attributeList.add(attribute);
410                 } else if (value != null) {
411                     messageLogger.logInfo("Attribute value not String or String[]. Entry not added to Attribute[]. ", activeTicketId);
412                 }
413             } else if (value != null) {
414                 messageLogger.logInfo("Attribute key not String. Entry not added to Attribute[]", activeTicketId);
415             }
416         }
417         return (Attribute[]) attributeList.toArray(new Attribute[] {});
418     }
419 }