001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.jetspeed.security.impl;
018:
019: import java.security.Principal;
020: import java.util.List;
021: import java.util.Map;
022:
023: import javax.security.auth.Subject;
024: import javax.security.auth.callback.Callback;
025: import javax.security.auth.callback.CallbackHandler;
026: import javax.security.auth.callback.NameCallback;
027: import javax.security.auth.callback.PasswordCallback;
028: import javax.security.auth.login.FailedLoginException;
029: import javax.security.auth.login.LoginException;
030: import javax.security.auth.spi.LoginModule;
031:
032: import org.apache.jetspeed.security.LoginModuleProxy;
033: import org.apache.jetspeed.security.RolePrincipal;
034: import org.apache.jetspeed.security.SecurityHelper;
035: import org.apache.jetspeed.security.User;
036: import org.apache.jetspeed.security.UserManager;
037: import org.apache.jetspeed.security.UserPrincipal;
038:
039: /**
040: * <p>LoginModule implementation that authenticates a user
041: * against a relational database. OJB based implementation.</p>
042: * <p>When a user is successfully authenticated, the user principal
043: * are added to the current subject.</p>
044: * <p>The LoginModule also recognizes the debug option.</p>
045: * <p>Configuration files should provide:</p>
046: * <pre><code>
047: * Jetspeed {
048: * org.apache.jetspeed.security.impl.DefaultLoginModule required debug=true;
049: * };
050: * </code></pre>
051: * @author <a href="mailto:dlestrat@apache.org">David Le Strat</a>
052: */
053: public class DefaultLoginModule implements LoginModule {
054:
055: /** <p>LoginModule debug mode is turned off by default.</p> */
056: protected boolean debug;
057:
058: /** <p>The authentication status.</p> */
059: protected boolean success;
060:
061: /** <p>The commit status.</p> */
062: protected boolean commitSuccess;
063:
064: /** <p>The Subject to be authenticated.</p> */
065: protected Subject subject;
066:
067: /** <p>A CallbackHandler for communicating with the end user (prompting for usernames and passwords, for example).</p> */
068: protected CallbackHandler callbackHandler;
069:
070: /** <p>State shared with other configured LoginModules.</p> */
071: protected Map sharedState;
072:
073: /** <p>Options specified in the login Configuration for this particular LoginModule.</p> */
074: protected Map options;
075:
076: /** <p>InternalUserPrincipal manager service.</p> */
077: protected UserManager ums;
078:
079: /** The portal user role. */
080: protected String portalUserRole;
081:
082: /** <p>The user name.</p> */
083: protected String username;
084:
085: /**
086: * <p>The default login module constructor.</p>
087: */
088: public DefaultLoginModule() {
089: LoginModuleProxy loginModuleProxy = LoginModuleProxyImpl.loginModuleProxy;
090: if (loginModuleProxy != null) {
091: this .ums = loginModuleProxy.getUserManager();
092: this .portalUserRole = loginModuleProxy.getPortalUserRole();
093: }
094: debug = false;
095: success = false;
096: commitSuccess = false;
097: username = null;
098: }
099:
100: /**
101: * Create a new login module that uses the given user manager.
102: * @param userManager the user manager to use
103: * @param portalUserRole the portal user role to use
104: */
105: protected DefaultLoginModule(UserManager userManager,
106: String portalUserRole) {
107: this .ums = userManager;
108: this .portalUserRole = portalUserRole;
109: debug = false;
110: success = false;
111: commitSuccess = false;
112: username = null;
113: }
114:
115: protected DefaultLoginModule(UserManager userManager) {
116: this (userManager,
117: LoginModuleProxy.DEFAULT_PORTAL_USER_ROLE_NAME);
118: }
119:
120: /**
121: * @see javax.security.auth.spi.LoginModule#abort()
122: */
123: public boolean abort() throws LoginException {
124: // Clean out state
125: success = false;
126: commitSuccess = false;
127: username = null;
128: if (callbackHandler instanceof PassiveCallbackHandler) {
129: ((PassiveCallbackHandler) callbackHandler).clearPassword();
130: }
131: logout();
132: return true;
133: }
134:
135: protected void refreshProxy() {
136: if (this .ums == null) {
137: LoginModuleProxy loginModuleProxy = LoginModuleProxyImpl.loginModuleProxy;
138: if (loginModuleProxy != null) {
139: this .ums = loginModuleProxy.getUserManager();
140: }
141: }
142: }
143:
144: /**
145: * @see javax.security.auth.spi.LoginModule#commit()
146: */
147: public boolean commit() throws LoginException {
148: if (success) {
149: if (subject.isReadOnly()) {
150: throw new LoginException("Subject is Readonly");
151: }
152: try {
153: // TODO We should get the user profile here and had it in cache so that we do not have to retrieve it again.
154: // TODO Ideally the User should be available from the session. Need discussion around this.
155: refreshProxy();
156: commitPrincipals(subject, ums.getUser(username));
157:
158: username = null;
159: commitSuccess = true;
160:
161: if (callbackHandler instanceof PassiveCallbackHandler) {
162: ((PassiveCallbackHandler) callbackHandler)
163: .clearPassword();
164: }
165:
166: } catch (Exception ex) {
167: ex.printStackTrace(System.out);
168: throw new LoginException(ex.getMessage());
169: }
170: }
171:
172: return commitSuccess;
173: }
174:
175: /**
176: * @see javax.security.auth.spi.LoginModule#login()
177: */
178: public boolean login() throws LoginException {
179: if (callbackHandler == null) {
180: throw new LoginException(
181: "Error: no CallbackHandler available "
182: + "to garner authentication information from the user");
183: }
184: try {
185: // Setup default callback handlers.
186: Callback[] callbacks = new Callback[] {
187: new NameCallback("Username: "),
188: new PasswordCallback("Password: ", false) };
189:
190: callbackHandler.handle(callbacks);
191:
192: username = ((NameCallback) callbacks[0]).getName();
193: String password = new String(
194: ((PasswordCallback) callbacks[1]).getPassword());
195:
196: ((PasswordCallback) callbacks[1]).clearPassword();
197:
198: refreshProxy();
199: success = ums.authenticate(this .username, password);
200:
201: callbacks[0] = null;
202: callbacks[1] = null;
203: if (!success) {
204: throw new FailedLoginException(
205: "Authentication failed: Password does not match");
206: }
207:
208: return (true);
209: } catch (LoginException ex) {
210: throw ex;
211: } catch (Exception ex) {
212: success = false;
213: throw new LoginException(ex.getMessage());
214: }
215: }
216:
217: /**
218: * @see javax.security.auth.spi.LoginModule#logout()
219: */
220: public boolean logout() throws LoginException {
221: // TODO Can we set subject to null?
222: subject.getPrincipals().clear();
223: subject.getPrivateCredentials().clear();
224: subject.getPublicCredentials().clear();
225: success = false;
226: commitSuccess = false;
227:
228: return true;
229: }
230:
231: /**
232: * @see javax.security.auth.spi.LoginModule#initialize(javax.security.auth.Subject, javax.security.auth.callback.CallbackHandler, java.util.Map, java.util.Map)
233: */
234: public void initialize(Subject subject,
235: CallbackHandler callbackHandler, Map sharedState,
236: Map options) {
237: this .subject = subject;
238: this .callbackHandler = callbackHandler;
239: this .sharedState = sharedState;
240: this .options = options;
241:
242: // Initialize debug mode if configure option.
243: if (options.containsKey("debug")) {
244: debug = "true".equalsIgnoreCase((String) options
245: .get("debug"));
246: }
247: }
248:
249: protected Principal getUserPrincipal(User user) {
250: return SecurityHelper.getPrincipal(user.getSubject(),
251: UserPrincipal.class);
252: }
253:
254: protected List getUserRoles(User user) {
255: return SecurityHelper.getPrincipals(user.getSubject(),
256: RolePrincipal.class);
257: }
258:
259: /**
260: * Default setup of the logged on Subject Principals for Tomcat
261: * @param subject
262: * @param user
263: */
264: protected void commitPrincipals(Subject subject, User user) {
265: // add user specific portal user name and roles
266: subject.getPrincipals().add(getUserPrincipal(user));
267: subject.getPrincipals().addAll(getUserRoles(user));
268:
269: // add portal user role: used in web.xml authorization to
270: // detect authenticated portal users
271: subject.getPrincipals().add(
272: new RolePrincipalImpl(portalUserRole));
273: }
274: }
|