001 /*
002 * Copyright 2010 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package com.hs.mail.security.login;
017
018 import java.security.Principal;
019 import java.util.Map;
020 import java.util.Set;
021
022 import javax.security.auth.Subject;
023 import javax.security.auth.callback.Callback;
024 import javax.security.auth.callback.CallbackHandler;
025 import javax.security.auth.callback.NameCallback;
026 import javax.security.auth.callback.PasswordCallback;
027 import javax.security.auth.login.FailedLoginException;
028 import javax.security.auth.login.LoginException;
029 import javax.security.auth.spi.LoginModule;
030
031 /**
032 * Base class for custom LoginModule.
033 *
034 * @author Won Chul Doh
035 * @since Jul 18, 2007
036 *
037 */
038 public abstract class BasicLoginModule implements LoginModule {
039
040 /**
041 * The authentication status.
042 */
043 protected boolean success;
044
045 /**
046 * The commit status.
047 */
048 protected boolean commitSuccess;
049
050 /**
051 * The Subject to be authenticated.
052 */
053 protected Subject subject;
054
055 /**
056 * The Principals authenticated.
057 */
058 protected Principal[] principals;
059
060 /**
061 * A CallbackHandler for communicating with the end user (prompting for
062 * usernames and passwords, for example).
063 */
064 protected CallbackHandler callbackHandler;
065
066 /**
067 * State shared with other configured LoginModules.
068 */
069 protected Map sharedState;
070
071 /**
072 * Options specified in the login Configuration for this particular
073 * LoginModule.
074 */
075 protected Map options;
076
077 /**
078 * Class for password encoder
079 */
080 protected Class encoder;
081
082 protected boolean debug = false;
083
084 private static final Class DEFAULT_ENCODER_CLASS = com.hs.mail.security.login.PlaintextPasswordEncoder.class;
085
086 protected BasicLoginModule() {
087 success = false;
088 commitSuccess = false;
089 principals = null;
090 encoder = DEFAULT_ENCODER_CLASS;
091 }
092
093 /**
094 * Overriding to allow for proper initialization.
095 *
096 * Standard JAAS.
097 */
098 public void initialize(Subject subject, CallbackHandler callbackHandler,
099 Map<String, ?> sharedState, Map<String, ?> options) {
100 this.subject = subject;
101 this.callbackHandler = callbackHandler;
102 this.sharedState = sharedState;
103 this.options = options;
104 String s = getOption("encoder", null);
105 if (s != null) {
106 try {
107 encoder = Class.forName(s);
108 } catch (ClassNotFoundException ex) {
109 throw new IllegalArgumentException(
110 "Password encoder not found: " + s);
111 }
112 }
113 debug = "true".equals(getOption("debug", "false"));
114 }
115
116 /**
117 * Overriding to allow for certificate-based login.
118 *
119 * Standard JAAS.
120 */
121 public boolean login() throws LoginException {
122 if (null == callbackHandler) {
123 throw new LoginException("Error: no CallbackHandler available "
124 + "to gather authentication information from the user");
125 }
126 try {
127 // Setup default callback handlers.
128 Callback[] callbacks = getDefaultCallbacks();
129
130 callbackHandler.handle(callbacks);
131
132 principals = validate(callbacks);
133
134 if (null == principals) {
135 throw new FailedLoginException(
136 "Authentication failed: Password does not match");
137 } else {
138 success = true;
139 }
140 return true;
141 } catch (LoginException ex) {
142 throw ex;
143 } catch (Exception ex) {
144 throw new LoginException(ex.getMessage());
145 }
146 }
147
148 /**
149 * Standard JAAS override.
150 */
151 public boolean logout() throws LoginException {
152 subject.getPrincipals().clear();
153 success = false;
154 commitSuccess = false;
155 principals = null;
156 return true;
157 }
158
159 /**
160 * Standard JAAS override.
161 */
162 public boolean abort() throws LoginException {
163 // Clean out state
164 logout();
165 return true;
166 }
167
168 /**
169 * Overriding to complete login process.
170 *
171 * Standard JAAS.
172 */
173 public boolean commit() throws LoginException {
174 if (success) {
175 if (subject.isReadOnly()) {
176 throw new LoginException("Subject is Readonly");
177 }
178 Set<Principal> p = subject.getPrincipals();
179 for (int i = 0; i < principals.length; i++)
180 p.add(principals[i]);
181 commitSuccess = true;
182 }
183 return commitSuccess;
184 }
185
186 protected boolean checkPassword(String encoded, char plain[])
187 throws LoginException {
188 try {
189 PasswordEncoder e = (PasswordEncoder) encoder.newInstance();
190 return e.compare(encoded, plain);
191 } catch (Exception e) {
192 throw new LoginException("Internal error");
193 }
194 }
195
196 protected abstract Principal[] validate(Callback[] callbacks)
197 throws LoginException;
198
199 protected Callback[] getDefaultCallbacks() {
200 // Setup default callback handlers.
201 Callback[] callbacks = { new NameCallback("user name: "),
202 new PasswordCallback("password: ", false) };
203 return callbacks;
204 }
205
206 protected String getOption(String name, String defaultValue) {
207 if (null == options) {
208 return defaultValue;
209 }
210 String option = (String) options.get(name);
211 return (null == option) ? defaultValue : option;
212 }
213
214 }