Fixed query syntax in case of empty group list
[pithos] / src / gr / ebs / gss / server / Login.java
1 /*
2  * Copyright 2008, 2009 Electronic Business Systems Ltd.
3  *
4  * This file is part of GSS.
5  *
6  * GSS is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GSS is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with GSS.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 package gr.ebs.gss.server;
20
21 import static gr.ebs.gss.server.configuration.GSSConfigurationFactory.getConfiguration;
22 import gr.ebs.gss.client.exceptions.DuplicateNameException;
23 import gr.ebs.gss.client.exceptions.ObjectNotFoundException;
24 import gr.ebs.gss.client.exceptions.RpcException;
25 import gr.ebs.gss.server.domain.Nonce;
26 import gr.ebs.gss.server.domain.User;
27 import gr.ebs.gss.server.domain.UserLogin;
28
29 import java.io.IOException;
30 import java.io.PrintWriter;
31 import java.net.URI;
32 import java.net.URISyntaxException;
33 import java.net.URLEncoder;
34 import java.util.Date;
35
36 import javax.servlet.http.Cookie;
37 import javax.servlet.http.HttpServletRequest;
38 import javax.servlet.http.HttpServletResponse;
39
40 import org.apache.commons.codec.binary.Base64;
41 import org.apache.commons.logging.Log;
42 import org.apache.commons.logging.LogFactory;
43
44 /**
45  * The servlet that handles user logins.
46  *
47  * @author past
48  */
49 public class Login extends BaseServlet {
50         /**
51          * The request parameter name for the nonce.
52          */
53         private static final String NONCE_PARAM = "nonce";
54
55         /**
56          * The request parameter name for the URL to redirect
57          * to after authentication.
58          */
59         private static final String NEXT_URL_PARAM = "next";
60
61         /**
62          * The serial version UID of the class.
63          */
64         private static final long serialVersionUID = 1L;
65
66         /**
67          * The name of the authentication cookie.
68          */
69         public static final String AUTH_COOKIE = "_gss_a";
70
71         /**
72          * The separator character for the authentication cookie.
73          */
74         public static final char COOKIE_SEPARATOR = '|';
75
76         /**
77          * The name of the the webdav cookie.
78          */
79         public static final String WEBDAV_COOKIE = "_gss_wd";
80
81         /**
82          * The logger.
83          */
84         private static Log logger = LogFactory.getLog(Login.class);
85
86         @Override
87         public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
88                 // Fetch the next URL to display, if any.
89                 String nextUrl = request.getParameter(NEXT_URL_PARAM);
90                 // Fetch the supplied nonce, if any.
91                 String nonce = request.getParameter(NONCE_PARAM);
92                 String[] attrs = new String[] {"REMOTE_USER", "HTTP_SHIB_INETORGPERSON_DISPLAYNAME",
93                                         "HTTP_SHIB_INETORGPERSON_GIVENNAME", "HTTP_SHIB_PERSON_COMMONNAME",
94                                         "HTTP_SHIB_PERSON_SURNAME", "HTTP_SHIB_INETORGPERSON_MAIL",
95                                         "HTTP_SHIB_EP_UNSCOPEDAFFILIATION", "HTTP_PERSISTENT_ID"};
96                 StringBuilder buf = new StringBuilder("Shibboleth Attributes\n");
97                 for (String attr: attrs)
98                         buf.append(attr+": ").append(request.getAttribute(attr)).append('\n');
99                 logger.info(buf);
100                 if (logger.isDebugEnabled()) {
101                         buf = new StringBuilder("Shibboleth Attributes as bytes\n");
102                         for (String attr: attrs)
103                                 if (request.getAttribute(attr) != null)
104                                         buf.append(attr+": ").append(getHexString(request.getAttribute(attr).toString().getBytes("UTF-8"))).append('\n');
105                         logger.debug(buf);
106                 }
107                 User user = null;
108                 response.setContentType("text/html");
109                 Object usernameAttr = request.getAttribute("REMOTE_USER");
110                 Object nameAttr = request.getAttribute("HTTP_SHIB_INETORGPERSON_DISPLAYNAME");
111                 Object givennameAttr = request.getAttribute("HTTP_SHIB_INETORGPERSON_GIVENNAME"); // Multi-valued
112                 Object cnAttr = request.getAttribute("HTTP_SHIB_PERSON_COMMONNAME"); // Multi-valued
113                 Object snAttr = request.getAttribute("HTTP_SHIB_PERSON_SURNAME"); // Multi-valued
114                 Object mailAttr = request.getAttribute("HTTP_SHIB_INETORGPERSON_MAIL"); // Multi-valued
115                 Object persistentIdAttr = request.getAttribute("HTTP_PERSISTENT_ID");
116                 // Use a configured test username if found, as a shortcut for development deployments.
117                 String gwtServer = null;
118                 if (getConfiguration().getString("testUsername") != null) {
119                         usernameAttr = getConfiguration().getString("testUsername");
120                         // Fetch the GWT code server URL, if any.
121                         gwtServer = request.getParameter(GWT_SERVER_PARAM);
122                 }
123                 if (usernameAttr == null) {
124                         String authErrorUrl = "authenticationError.jsp";
125                         authErrorUrl += "?name=" + (nameAttr==null? "-": nameAttr.toString());
126                         authErrorUrl += "&givenname=" + (givennameAttr==null? "-": givennameAttr.toString());
127                         authErrorUrl += "&sn=" + (snAttr==null? "-": snAttr.toString());
128                         authErrorUrl += "&cn=" + (cnAttr==null? "-": cnAttr.toString());
129                         authErrorUrl += "&mail=" + (mailAttr==null? "-": mailAttr.toString());
130                         response.sendRedirect(authErrorUrl);
131                         return;
132                 }
133                 String username = decodeAttribute(usernameAttr);
134                 String name;
135                 if (nameAttr != null && !nameAttr.toString().isEmpty())
136                         name = decodeAttribute(nameAttr);
137                 else if (cnAttr != null && !cnAttr.toString().isEmpty()) {
138                         name = decodeAttribute(cnAttr);
139                         if (name.indexOf(';') != -1)
140                                 name = name.substring(0, name.indexOf(';'));
141                 } else if (givennameAttr != null && snAttr != null && !givennameAttr.toString().isEmpty() && !snAttr.toString().isEmpty()) {
142                         String givenname = decodeAttribute(givennameAttr);
143                         if (givenname.indexOf(';') != -1)
144                                 givenname = givenname.substring(0, givenname.indexOf(';'));
145                         String sn = decodeAttribute(snAttr);
146                         if (sn.indexOf(';') != -1)
147                                 sn = sn.substring(0, sn.indexOf(';'));
148                         name = givenname + ' ' + sn;
149                 } else if (givennameAttr == null && snAttr != null && !snAttr.toString().isEmpty()) {
150                         name = decodeAttribute(snAttr);
151                         if (name.indexOf(';') != -1)
152                                 name = name.substring(0, name.indexOf(';'));
153                 } else
154                         name = username;
155                 String mail = mailAttr != null ? mailAttr.toString() : username;
156                 if (mail.indexOf(';') != -1)
157                         mail = mail.substring(0, mail.indexOf(';'));
158                 String persistentId = persistentIdAttr != null ? persistentIdAttr.toString() : "";
159                 String idp = "";
160                 String idpid = "";
161                 if (!persistentId.isEmpty()) {
162                         int bang = persistentId.indexOf('!');
163                         if (bang > -1) {
164                                 idp = persistentId.substring(0, bang);
165                                 idpid = persistentId.substring(bang + 1);
166                         }
167                 }
168                 try {
169                         user = getService().findUser(username);
170                         if (user == null)
171                                 user = getService().createUser(username, name, mail, idp, idpid);
172                         if (!user.isActive()) {
173                                 logger.info("Disabled user " + username + " tried to login.");
174                                 response.sendError(HttpServletResponse.SC_FORBIDDEN, "This account is disabled");
175                                 return;
176                         }
177                         if (!user.hasAcceptedPolicy()) {
178                                 String policyUrl = "policy.jsp";
179                                 if (request.getQueryString() != null)
180                                         policyUrl += "?user=" + username + "&" + request.getQueryString();
181                                 response.sendRedirect(policyUrl);
182                                 return;
183                         }
184                         user.setName(name);
185                         user.setEmail(mail);
186                         user.setIdentityProvider(idp);
187                         user.setIdentityProviderId(idpid);
188                         
189                         UserLogin userLogin = new UserLogin();
190                         userLogin.setLoginDate(new Date());
191                         userLogin.setUser(user);
192                         if (user.getAuthToken() == null)
193                                 user = getService().updateUserToken(user.getId());
194                         // Set WebDAV password to token if it's never been set.
195                         if (user.getWebDAVPassword() == null || user.getWebDAVPassword().length() == 0) {
196                                 String tokenEncoded = new String(Base64.encodeBase64(user.getAuthToken()), "US-ASCII");
197                                 user.setWebDAVPassword(tokenEncoded);
198                         }
199                         // Set the default user class if none was set.
200                         if (user.getUserClass() == null)
201                                 user.setUserClass(getService().getUserClasses().get(0));                        
202                         getService().updateUser(user);
203                         getService().addUserLogin(userLogin);           
204                 } catch (RpcException e) {
205                         String error = "An error occurred while communicating with the service";
206                         logger.error(error, e);
207                         response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, error);
208                         return;
209                 } catch (DuplicateNameException e) {
210                         String error = "User with username " + username + " already exists";
211                         logger.error(error, e);
212                         response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, error);
213                         return;
214                 } catch (ObjectNotFoundException e) {
215                         String error = "No username was provided";
216                         logger.error(error, e);
217                         response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, error);
218                         return;
219                 }
220                 String tokenEncoded = new String(Base64.encodeBase64(user.getAuthToken()), "US-ASCII");
221                 String userEncoded = URLEncoder.encode(user.getUsername(), "US-ASCII");
222                 if (logger.isDebugEnabled())
223                         logger.debug("user: "+userEncoded+" token: "+tokenEncoded);
224                 if (nextUrl != null && !nextUrl.isEmpty()) {
225                         URI next;
226                         if (gwtServer != null)
227                                 nextUrl += '?' + GWT_SERVER_PARAM + '=' + gwtServer;
228                         try {
229                                 next = new URI(nextUrl);
230                         } catch (URISyntaxException e) {
231                                 response.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
232                                 return;
233                         }
234                         if ("x-gr-ebs-igss".equalsIgnoreCase(next.getScheme()))
235                                 nextUrl += "?u=" + userEncoded + "&t=" + tokenEncoded;
236                         else {
237                                 String domain = next.getHost();
238                                 String path = getServletContext().getContextPath() + '/';
239                                 Cookie cookie = new Cookie(AUTH_COOKIE, userEncoded + COOKIE_SEPARATOR +
240                                                         tokenEncoded);
241                                 cookie.setMaxAge(-1);
242                                 cookie.setDomain(domain);
243                                 cookie.setPath(path);
244                             response.addCookie(cookie);
245                             cookie = new Cookie(WEBDAV_COOKIE, user.getWebDAVPassword());
246                                 cookie.setMaxAge(-1);
247                                 cookie.setDomain(domain);
248                                 cookie.setPath(path);
249                                 response.addCookie(cookie);
250                         }
251                     response.sendRedirect(nextUrl);
252                 } else if (nonce != null) {
253                         nonce = URLEncoder.encode(nonce, "US-ASCII");
254                         Nonce n = null;
255                         try {
256                                 if (logger.isDebugEnabled())
257                                         logger.debug("user: "+user.getId()+" nonce: "+nonce);
258                                 n = getService().getNonce(nonce, user.getId());
259                         } catch (ObjectNotFoundException e) {
260                             PrintWriter out = response.getWriter();
261                             out.println("<HTML>");
262                             out.println("<HEAD><TITLE>" + getServiceName() + " Authentication</TITLE>" +
263                                         "<LINK TYPE='text/css' REL='stylesheet' HREF='gss.css'></HEAD>");
264                             out.println("<BODY><CENTER><P>");
265                             out.println("The supplied nonce could not be found!");
266                             out.println("</CENTER></BODY></HTML>");
267                             return;
268                         } catch (RpcException e) {
269                                 String error = "An error occurred while communicating with the service";
270                                 logger.error(error, e);
271                                 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, error);
272                                 return;
273                         }
274                         try {
275                                 getService().activateUserNonce(user.getId(), nonce, n.getNonceExpiryDate());
276                         } catch (ObjectNotFoundException e) {
277                                 String error = "Unable to find user";
278                                 logger.error(error, e);
279                                 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, error);
280                                 return;
281                         } catch (RpcException e) {
282                                 String error = "An error occurred while communicating with the service";
283                                 logger.error(error, e);
284                                 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, error);
285                                 return;
286                         }
287                         try {
288                                 getService().removeNonce(n.getId());
289                         } catch (ObjectNotFoundException e) {
290                                 logger.info("Nonce already removed!", e);
291                         } catch (RpcException e) {
292                                 logger.warn("Could not remove nonce from data store", e);
293                         }
294                     PrintWriter out = response.getWriter();
295                     out.println("<HTML>");
296                     out.println("<HEAD><TITLE>" + getServiceName() + " Authentication</TITLE>" +
297                                 "<LINK TYPE='text/css' REL='stylesheet' HREF='gss.css'></HEAD>");
298                     out.println("<BODY><CENTER><P>");
299                     out.println("You can now close this browser window and return to your application.");
300                     out.println("</CENTER></BODY></HTML>");
301                 } else {
302                     PrintWriter out = response.getWriter();
303                     out.println("<HTML>");
304                     out.println("<HEAD><TITLE>" + getServiceName() + " Authentication</TITLE>" +
305                                 "<LINK TYPE='text/css' REL='stylesheet' HREF='gss.css'></HEAD>");
306                     out.println("<BODY><CENTER><P>");
307                     out.println("Name: " + user.getName() + "<BR>");
308                     out.println("E-mail: " + user.getEmail() + "<BR><P>");
309                     out.println("Username: " + user.getUsername() + "<BR>");
310                     out.println("Athentication token: " + tokenEncoded + "<BR>");
311                     out.println("</CENTER></BODY></HTML>");
312                 }
313         }
314 }