92a07bc582ebd515def8a99e61186a8c15493806
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / idp / authn / provider / UsernamePasswordLoginServlet.java
1 /*
2  * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package edu.internet2.middleware.shibboleth.idp.authn.provider;
18
19 import java.io.IOException;
20 import java.security.Principal;
21 import java.util.Set;
22
23 import javax.security.auth.Subject;
24 import javax.security.auth.callback.Callback;
25 import javax.security.auth.callback.CallbackHandler;
26 import javax.security.auth.callback.NameCallback;
27 import javax.security.auth.callback.PasswordCallback;
28 import javax.security.auth.callback.UnsupportedCallbackException;
29 import javax.security.auth.login.LoginException;
30 import javax.servlet.ServletException;
31 import javax.servlet.http.HttpServlet;
32 import javax.servlet.http.HttpServletRequest;
33 import javax.servlet.http.HttpServletResponse;
34
35 import org.opensaml.util.URLBuilder;
36 import org.opensaml.xml.util.DatatypeHelper;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 import edu.internet2.middleware.shibboleth.idp.authn.AuthenticationEngine;
41 import edu.internet2.middleware.shibboleth.idp.authn.LoginHandler;
42
43 /**
44  * This servlet should be protected by a filter which populates REMOTE_USER. The serlvet will then set the remote user
45  * field in a LoginContext.
46  */
47 public class UsernamePasswordLoginServlet extends HttpServlet {
48
49     /** Serial version UID. */
50     private static final long serialVersionUID = -572799841125956990L;
51
52     /** Class logger. */
53     private final Logger log = LoggerFactory.getLogger(RemoteUserAuthServlet.class);
54
55     /** Name of JAAS configuration used to authenticate users. */
56     private final String jaasConfigName = "ShibUserPassAuth";
57
58     /** Login page name. */
59     private final String loginPage = "login.jsp";
60
61     /** HTTP request parameter containing the user name. */
62     private final String usernameAttribute = "j_username";
63
64     /** HTTP request parameter containing the user's password. */
65     private final String passwordAttribute = "j_password";
66
67     /** {@inheritDoc} */
68     protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException,
69             IOException {
70         String username = DatatypeHelper.safeTrimOrNullString(request.getParameter(usernameAttribute));
71         String password = DatatypeHelper.safeTrimOrNullString(request.getParameter(passwordAttribute));
72
73         if (username == null || password == null) {
74             redirectToLoginPage(request, response);
75             return;
76         }
77
78         if (authenticateUser(request)) {
79             AuthenticationEngine.returnToAuthenticationEngine(request, response);
80         } else {
81             redirectToLoginPage(request, response);
82             return;
83         }
84     }
85
86     /**
87      * Sends the user to the login page.
88      * 
89      * @param request current request
90      * @param response current response
91      */
92     protected void redirectToLoginPage(HttpServletRequest request, HttpServletResponse response) {
93         try {
94             StringBuilder pathBuilder = new StringBuilder();
95             pathBuilder.append(request.getContextPath());
96             pathBuilder.append("/");
97             pathBuilder.append(loginPage);
98
99             URLBuilder urlBuilder = new URLBuilder();
100             urlBuilder.setScheme(request.getScheme());
101             urlBuilder.setHost(request.getLocalName());
102             urlBuilder.setPort(request.getLocalPort());
103             urlBuilder.setPath(pathBuilder.toString());
104
105             log.debug("Redirecting to login page {}", urlBuilder.buildURL());
106             response.sendRedirect(urlBuilder.buildURL());
107             return;
108         } catch (IOException ex) {
109             log.error("Unable to redirect to login page.", ex);
110         }
111     }
112
113     /**
114      * Authenticate a username and password against JAAS. If authentication succeeds the name of the first principal, or
115      * the username if that is empty, and the subject are placed into the request in their respective attributes.
116      * 
117      * @param request current authentication request
118      * 
119      * @return true of authentication succeeds, false if not
120      */
121     protected boolean authenticateUser(HttpServletRequest request) {
122
123         try {
124             String username = DatatypeHelper.safeTrimOrNullString(request.getParameter(usernameAttribute));
125             String password = DatatypeHelper.safeTrimOrNullString(request.getParameter(passwordAttribute));
126
127             SimpleCallbackHandler cbh = new SimpleCallbackHandler(username, password);
128
129             javax.security.auth.login.LoginContext jaasLoginCtx = new javax.security.auth.login.LoginContext(
130                     jaasConfigName, cbh);
131
132             jaasLoginCtx.login();
133             log.debug("Successfully authenticated user {}", username);
134
135             Subject subject = jaasLoginCtx.getSubject();
136             Set<Principal> principals = subject.getPrincipals();
137
138             if(principals.isEmpty()){
139                 request.setAttribute(LoginHandler.PRINCIPAL_NAME_KEY, username);
140             }else{       
141                 Principal principal = principals.iterator().next();
142                 String principalName = DatatypeHelper.safeTrimOrNullString(principal.getName());
143                 if(principalName == null){
144                     request.setAttribute(LoginHandler.PRINCIPAL_NAME_KEY, username);
145                 }else{
146                     request.setAttribute(LoginHandler.PRINCIPAL_NAME_KEY, principal.getName());
147                 }
148                 request.setAttribute(LoginHandler.SUBJECT_KEY, jaasLoginCtx.getSubject());
149             }
150
151             return true;
152         } catch (LoginException e) {
153             log.debug("User authentication failed", e);
154             return false;
155         }
156     }
157
158     /**
159      * A callback handler that provides static name and password data to a JAAS loging process.
160      * 
161      * This handler only supports {@link NameCallback} and {@link PasswordCallback}.
162      */
163     protected class SimpleCallbackHandler implements CallbackHandler {
164
165         /** Name of the user. */
166         private String uname;
167
168         /** User's password. */
169         private String pass;
170
171         /**
172          * Constructor.
173          * 
174          * @param username The username
175          * @param password The password
176          */
177         public SimpleCallbackHandler(String username, String password) {
178             uname = username;
179             pass = password;
180         }
181
182         /**
183          * Handle a callback.
184          * 
185          * @param callbacks The list of callbacks to process.
186          * 
187          * @throws UnsupportedCallbackException If callbacks has a callback other than {@link NameCallback} or
188          *             {@link PasswordCallback}.
189          */
190         public void handle(final Callback[] callbacks) throws UnsupportedCallbackException {
191
192             if (callbacks == null || callbacks.length == 0) {
193                 return;
194             }
195
196             for (Callback cb : callbacks) {
197                 if (cb instanceof NameCallback) {
198                     NameCallback ncb = (NameCallback) cb;
199                     ncb.setName(uname);
200                 } else if (cb instanceof PasswordCallback) {
201                     PasswordCallback pcb = (PasswordCallback) cb;
202                     pcb.setPassword(pass.toCharArray());
203                 }
204             }
205         }
206     }
207 }