Statistics
| Branch: | Tag: | Revision:

root / src / org / gss_project / gss / server / Login.java @ 1205:fbeae20462e6

History | View | Annotate | Download (12.7 kB)

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 org.gss_project.gss.server;
20

    
21
import static org.gss_project.gss.server.configuration.GSSConfigurationFactory.getConfiguration;
22
import org.gss_project.gss.common.exceptions.DuplicateNameException;
23
import org.gss_project.gss.common.exceptions.ObjectNotFoundException;
24
import org.gss_project.gss.common.exceptions.RpcException;
25
import org.gss_project.gss.server.domain.Nonce;
26
import org.gss_project.gss.server.domain.User;
27
import org.gss_project.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", "HTTP_SHIB_HOMEORGANIZATION"};
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
        Object homeOrganizationAttr = request.getAttribute("HTTP_SHIB_HOMEORGANIZATION");
117
                // Use a configured test username if found, as a shortcut for development deployments.
118
                String gwtServer = null;
119
                if (getConfiguration().getString("testUsername") != null) {
120
                        usernameAttr = getConfiguration().getString("testUsername");
121
                        // Fetch the GWT code server URL, if any.
122
                        gwtServer = request.getParameter(GWT_SERVER_PARAM);
123
                }
124
                if (usernameAttr == null) {
125
                        String authErrorUrl = "authenticationError.jsp";
126
                        authErrorUrl += "?name=" + (nameAttr==null? "-": nameAttr.toString());
127
                        authErrorUrl += "&givenname=" + (givennameAttr==null? "-": givennameAttr.toString());
128
                        authErrorUrl += "&sn=" + (snAttr==null? "-": snAttr.toString());
129
                        authErrorUrl += "&cn=" + (cnAttr==null? "-": cnAttr.toString());
130
                        authErrorUrl += "&mail=" + (mailAttr==null? "-": mailAttr.toString());
131
            authErrorUrl += "&homeOrg=" + (homeOrganizationAttr == null ? "-" : homeOrganizationAttr.toString());
132
                        response.sendRedirect(authErrorUrl);
133
                        return;
134
                }
135
                String username = decodeAttribute(usernameAttr);
136
                String name;
137
                if (nameAttr != null && !nameAttr.toString().isEmpty())
138
                        name = decodeAttribute(nameAttr);
139
                else if (cnAttr != null && !cnAttr.toString().isEmpty()) {
140
                        name = decodeAttribute(cnAttr);
141
                        if (name.indexOf(';') != -1)
142
                                name = name.substring(0, name.indexOf(';'));
143
                } else if (givennameAttr != null && snAttr != null && !givennameAttr.toString().isEmpty() && !snAttr.toString().isEmpty()) {
144
                        String givenname = decodeAttribute(givennameAttr);
145
                        if (givenname.indexOf(';') != -1)
146
                                givenname = givenname.substring(0, givenname.indexOf(';'));
147
                        String sn = decodeAttribute(snAttr);
148
                        if (sn.indexOf(';') != -1)
149
                                sn = sn.substring(0, sn.indexOf(';'));
150
                        name = givenname + ' ' + sn;
151
                } else if (givennameAttr == null && snAttr != null && !snAttr.toString().isEmpty()) {
152
                        name = decodeAttribute(snAttr);
153
                        if (name.indexOf(';') != -1)
154
                                name = name.substring(0, name.indexOf(';'));
155
                } else
156
                        name = username;
157
                String mail = mailAttr != null ? mailAttr.toString() : username;
158
                if (mail.indexOf(';') != -1)
159
                        mail = mail.substring(0, mail.indexOf(';'));
160
                String persistentId = persistentIdAttr != null ? persistentIdAttr.toString() : "";
161
                String idp = "";
162
                String idpid = "";
163
                if (!persistentId.isEmpty()) {
164
                        int bang = persistentId.indexOf('!');
165
                        if (bang > -1) {
166
                                idp = persistentId.substring(0, bang);
167
                                idpid = persistentId.substring(bang + 1);
168
                        }
169
                }
170
        String homeOrganization = homeOrganizationAttr != null ? decodeAttribute(homeOrganizationAttr.toString()) : "";
171
                try {
172
                        user = getService().findUser(username);
173
                        if (user == null)
174
                                user = getService().createUser(username, name, mail, idp, idpid, homeOrganization);
175
                        if (!user.isActive()) {
176
                                logger.info("Disabled user " + username + " tried to login.");
177
                                response.sendError(HttpServletResponse.SC_FORBIDDEN, "This account is disabled");
178
                                return;
179
                        }
180
                        if (!user.hasAcceptedPolicy()) {
181
                                String policyUrl = "policy.jsp";
182
                                if (request.getQueryString() != null)
183
                                        policyUrl += "?user=" + username + "&" + request.getQueryString();
184
                                response.sendRedirect(policyUrl);
185
                                return;
186
                        }
187
                        user.setName(name);
188
                        user.setEmail(mail);
189
                        user.setIdentityProvider(idp);
190
                        user.setIdentityProviderId(idpid);
191
            user.setHomeOrganization(homeOrganization);
192
                        
193
                        UserLogin userLogin = new UserLogin();
194
                        userLogin.setLoginDate(new Date());
195
                        userLogin.setUser(user);
196
                        if (user.getAuthToken() == null)
197
                                user = getService().updateUserToken(user.getId());
198
                        // Set WebDAV password to token if it's never been set.
199
                        if (user.getWebDAVPassword() == null || user.getWebDAVPassword().length() == 0) {
200
                                String tokenEncoded = new String(Base64.encodeBase64(user.getAuthToken()), "US-ASCII");
201
                                user.setWebDAVPassword(tokenEncoded);
202
                        }
203
                        // Set the default user class if none was set.
204
                        if (user.getUserClass() == null)
205
                                user.setUserClass(getService().getUserClasses().get(0));                        
206
                        getService().updateUser(user);
207
                        getService().addUserLogin(userLogin);                
208
                } catch (RpcException e) {
209
                        String error = "An error occurred while communicating with the service";
210
                        logger.error(error, e);
211
                        response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, error);
212
                        return;
213
                } catch (DuplicateNameException e) {
214
                        String error = "User with username " + username + " already exists";
215
                        logger.error(error, e);
216
                        response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, error);
217
                        return;
218
                } catch (ObjectNotFoundException e) {
219
                        String error = "No username was provided";
220
                        logger.error(error, e);
221
                        response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, error);
222
                        return;
223
                }
224
                String tokenEncoded = new String(Base64.encodeBase64(user.getAuthToken()), "US-ASCII");
225
                String userEncoded = URLEncoder.encode(user.getUsername(), "US-ASCII");
226
                if (logger.isDebugEnabled())
227
                        logger.debug("user: "+userEncoded+" token: "+tokenEncoded);
228
                if (nextUrl != null && !nextUrl.isEmpty()) {
229
                        URI next;
230
                        if (gwtServer != null)
231
                                nextUrl += '?' + GWT_SERVER_PARAM + '=' + gwtServer;
232
                        try {
233
                                next = new URI(nextUrl);
234
                        } catch (URISyntaxException e) {
235
                                response.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
236
                                return;
237
                        }
238
                        if ("x-gr-ebs-igss".equalsIgnoreCase(next.getScheme()))
239
                                nextUrl += "?u=" + userEncoded + "&t=" + tokenEncoded;
240
                        else {
241
                                String domain = next.getHost();
242
                                String path = getServletContext().getContextPath() + '/';
243
                                Cookie cookie = new Cookie(AUTH_COOKIE, userEncoded + COOKIE_SEPARATOR +
244
                                                        tokenEncoded);
245
                                cookie.setMaxAge(-1);
246
                                cookie.setDomain(domain);
247
                                cookie.setPath(path);
248
                            response.addCookie(cookie);
249
                            cookie = new Cookie(WEBDAV_COOKIE, user.getWebDAVPassword());
250
                                cookie.setMaxAge(-1);
251
                                cookie.setDomain(domain);
252
                                cookie.setPath(path);
253
                                response.addCookie(cookie);
254
                        }
255
                    response.sendRedirect(nextUrl);
256
                } else if (nonce != null) {
257
                        nonce = URLEncoder.encode(nonce, "US-ASCII");
258
                        Nonce n = null;
259
                        try {
260
                                if (logger.isDebugEnabled())
261
                                        logger.debug("user: "+user.getId()+" nonce: "+nonce);
262
                                n = getService().getNonce(nonce, user.getId());
263
                        } catch (ObjectNotFoundException e) {
264
                            PrintWriter out = response.getWriter();
265
                            out.println("<HTML>");
266
                            out.println("<HEAD><TITLE>" + getServiceName() + " Authentication</TITLE>" +
267
                                            "<LINK TYPE='text/css' REL='stylesheet' HREF='gss.css'></HEAD>");
268
                            out.println("<BODY><CENTER><P>");
269
                            out.println("The supplied nonce could not be found!");
270
                            out.println("</CENTER></BODY></HTML>");
271
                            return;
272
                        } catch (RpcException e) {
273
                                String error = "An error occurred while communicating with the service";
274
                                logger.error(error, e);
275
                                response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, error);
276
                                return;
277
                        }
278
                        try {
279
                                getService().activateUserNonce(user.getId(), nonce, n.getNonceExpiryDate());
280
                        } catch (ObjectNotFoundException e) {
281
                                String error = "Unable to find user";
282
                                logger.error(error, e);
283
                                response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, error);
284
                                return;
285
                        } catch (RpcException e) {
286
                                String error = "An error occurred while communicating with the service";
287
                                logger.error(error, e);
288
                                response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, error);
289
                                return;
290
                        }
291
                        try {
292
                                getService().removeNonce(n.getId());
293
                        } catch (ObjectNotFoundException e) {
294
                                logger.info("Nonce already removed!", e);
295
                        } catch (RpcException e) {
296
                                logger.warn("Could not remove nonce from data store", e);
297
                        }
298
                    PrintWriter out = response.getWriter();
299
                    out.println("<HTML>");
300
                    out.println("<HEAD><TITLE>" + getServiceName() + " Authentication</TITLE>" +
301
                                    "<LINK TYPE='text/css' REL='stylesheet' HREF='gss.css'></HEAD>");
302
                    out.println("<BODY><CENTER><P>");
303
                    out.println("You can now close this browser window and return to your application.");
304
                    out.println("</CENTER></BODY></HTML>");
305
                } else {
306
                    PrintWriter out = response.getWriter();
307
                    out.println("<HTML>");
308
                    out.println("<HEAD><TITLE>" + getServiceName() + " Authentication</TITLE>" +
309
                                    "<LINK TYPE='text/css' REL='stylesheet' HREF='gss.css'></HEAD>");
310
                    out.println("<BODY><CENTER><P>");
311
                    out.println("Name: " + user.getName() + "<BR>");
312
                    out.println("E-mail: " + user.getEmail() + "<BR><P>");
313
                    out.println("Username: " + user.getUsername() + "<BR>");
314
                    out.println("Athentication token: " + tokenEncoded + "<BR>");
315
                    out.println("</CENTER></BODY></HTML>");
316
                }
317
        }
318
}