View Javadoc

1   package no.feide.mellon.jaas.loginmodules;
2   
3   
4   import java.util.Map;
5   import java.util.*;
6   
7   import java.security.Principal;
8   import javax.security.auth.Subject;
9   import javax.security.auth.callback.*;
10  import javax.security.auth.login.FailedLoginException;
11  import javax.security.auth.login.LoginException;
12  import javax.security.auth.spi.LoginModule;
13  
14  import no.feide.mellon.jaas.callbackhandlers.*;
15  import no.feide.mellon.jaas.principals.*;
16  import no.feide.mellon.v2_1.*;
17  import no.feide.moria.webservices.v2_1.Attribute;
18  
19  /***
20   * @author Rikke Amilde Løvlid
21   */
22  public class MoriaLoginModule implements LoginModule{
23  	
24  	private Subject subject;
25  	private CallbackHandler callbackHandler;
26  	private Map sharedState;
27  	private Map options;
28  	
29  	private boolean loginSucceeded;
30  	private boolean commitSucceeded;
31  	
32  	private String username;
33  	private ArrayList tmpPrincipals;
34  	private ArrayList tmpPublicCredentials;
35  	
36  	private boolean debug;
37  	
38  	private final static String[] ATTRIBUTE_NAMES = {"eduPersonAffiliation", "eduPersonScopedAffiliation", "eduPersonPrimaryAffiliation"};
39  	private String endpoint;
40  	private String service_username;
41  	private String service_password;
42  
43  	public MoriaLoginModule(){
44  		tmpPrincipals = new ArrayList();
45  		tmpPublicCredentials = new ArrayList();
46  		loginSucceeded = false;
47  		commitSucceeded = false;
48  		debug = false;
49  	}
50  	
51  	/***
52  	 * Initialize this <code>LoginModule</code>
53  	 * <br><br>
54  	 * @param subject 			the <code>Subject</code> to be authenticated.
55  	 * @param callbackHandler 	a <code>CallbackHandler</code> for communicating with the user,
56  	 * 							(promting for username and password)
57  	 * @param sharedState 		state shared with the other <code>LoginModule</code>
58  	 * @param options 			options specified in the login <code>Configuration</code> for this
59  	 * 							particular <code>LoginModule</code>
60  	 */
61  	public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options){
62  		this.subject = subject;
63  		this.callbackHandler = callbackHandler;
64  		this.sharedState = sharedState;
65  		this.options = options;
66  		
67  		if(options.containsKey("debug")){
68  			debug = "true".equalsIgnoreCase((String)options.get("debug"));	
69  		}
70  		
71  		endpoint = (String)options.get("endpoint");
72  		service_username = (String)options.get("service_username");
73  		service_password = (String)options.get("service_password");
74  		
75  		if(debug){
76  			System.out.println("\t[MoriaLoginModule] initialize");
77  		}
78  	}
79  	
80  	/***
81  	 * The login method is called to authenticate the user.
82  	 * 
83  	 * @return true 	if the authentication succeedes (no exception is thrown).
84  	 *
85  	 * @throws FailedLoginException if authentication failes.
86  	 * @throws LoginException 		if this <code>LoginModule</code> is unable to perform the uthentication.
87  	 */
88  	public boolean login() throws LoginException{
89  		
90  		if(debug){
91  			System.out.println("\t[MoriaLoginModule] login");
92  		}
93  		
94  		if(callbackHandler == null){
95  			throw new LoginException("Error: no CallbackHandler available");
96  		}
97  		
98  		try{
99  			Callback[] callbacks = new Callback[2];
100 			callbacks[0] = new NameCallback("username: ");
101 			callbacks[1] = new PasswordCallback("password: ", false);
102 		
103 			callbackHandler.handle(callbacks);
104 			
105 			username = ((NameCallback)callbacks[0]).getName();
106 
107 			char[] password = ((PasswordCallback)callbacks[1]).getPassword();
108 			
109 			((PasswordCallback)callbacks[1]).clearPassword();
110 			callbacks[0] = null;
111 			callbacks[1] = null;
112 		
113 			if(debug){
114 				System.out.println("\t[MoriaLoginModule] " + "user entered user name: " + username);
115 				System.out.print("\t[MoriaLoginModule] " + "user entered password: ");
116 				for(int i=0; i<password.length; i++){
117 					System.out.print(password[i]);
118 				}
119 				System.out.println();
120 			}
121 			
122 			loginSucceeded = validate(username, password);
123 			
124 			if(loginSucceeded){
125 				if(debug){
126 					System.out.println("\t[MoriaLoginModule] authentication succeeded");
127 				}
128 				return true;
129 			}
130 			else{
131 				if(debug){
132 					System.out.println("\t[MoriaLoginModule] authentication failed");
133 				}
134 				username = null;
135 				throw new FailedLoginException("Authentication failed");
136 			}
137 		}
138 		catch(LoginException le){
139 			loginSucceeded = false;
140 			throw le;
141 		}
142 		catch(Exception e){
143 			loginSucceeded = false;
144 			throw new LoginException(e.getMessage());
145 		}
146 		
147 	}
148 	
149 	/***
150 	 * This method is called if the LoginContext's overall authentication succeeded.
151 	 * 
152 	 * @return true 	if this loginmodules own login attempt succeded and commit succeeds,
153 	 * @return false 	if the login attempt failed.
154 	 * 
155 	 * @throws LoginException if the commit attempt failes. 
156 	 */
157 	public boolean commit() throws LoginException{
158 		if(debug){
159 			System.out.println("\t[MoriaLoginModule] commit");
160 		}
161 		
162 		if(loginSucceeded){
163 			if(subject.isReadOnly()){
164 				throw new LoginException("Subject is Readonly");
165 			}
166 			
167 			try{
168 				if(debug){
169 					Iterator it = tmpPrincipals.iterator();
170 					while(it.hasNext()){
171 						System.out.println("\t[MoriaLoginModule] Principal: " + it.next().toString());
172 					}
173 				}		
174 				subject.getPrincipals().addAll(tmpPrincipals);
175 				subject.getPublicCredentials().addAll(tmpPublicCredentials);
176 				
177 				tmpPrincipals.clear();
178 				tmpPublicCredentials.clear();
179 				
180 				if(callbackHandler instanceof PassiveCallbackHandler){
181 					((PassiveCallbackHandler)callbackHandler).clearPassword();
182 				}
183 				
184 				commitSucceeded = true;
185 				
186 				return true;
187 			}
188 			catch(Exception ex){
189 				throw new LoginException(ex.getMessage());
190 			}
191 		}
192 		else{
193 			tmpPrincipals.clear();
194 			tmpPublicCredentials.clear();
195 			return false;
196 		}
197 	}
198 	
199 	/***
200 	 * This method is called if the LoginContext's overall authentication failed.
201 	 * 
202 	 * @return false 	if this loginmodules own login attempts failed
203 	 * @return true 	otherwise			
204 	 * 
205 	 * @throws LoginException if the abort failes
206 	 */
207 	public boolean abort() throws LoginException{
208 		if(debug){
209 			System.out.println("\t[MoriaLoginModule] abort");
210 		}
211 		
212 		if(!loginSucceeded){
213 			return false;
214 		}
215 		else if(loginSucceeded && !commitSucceeded){
216 			loginSucceeded = false;
217 			username = null;
218 			tmpPrincipals.clear();
219 			tmpPublicCredentials.clear();
220 			
221 			if(callbackHandler instanceof PassiveCallbackHandler){
222 				((PassiveCallbackHandler)callbackHandler).clearPassword();
223 			}
224 		}
225 		else{
226 			logout();
227 		}
228 		
229 		return true;
230 	}
231 	
232 	/***
233 	 * This method removes the principals that was added by the <code>commit</code> method.
234 	 * 
235 	 * @return true in all cases.
236 	 * 
237 	 * @throws LoginException if the logout fails.
238 	 */
239 	public boolean logout() throws LoginException{
240 		if(debug){
241 			System.out.println("\t[MoriaLoginModule] logout");
242 		}
243 		
244 		tmpPrincipals.clear();
245 		tmpPublicCredentials.clear();
246 		
247 		if(callbackHandler instanceof PassiveCallbackHandler){
248 			((PassiveCallbackHandler)callbackHandler).clearPassword();
249 		}
250 		
251 		Iterator it = subject.getPrincipals().iterator();
252 		while(it.hasNext()){
253 			Principal p = (Principal)it.next();
254 			if(debug){
255 				System.out.println("\t[MoriaLoginModule] removing principal " + p.toString());
256 			}
257 			it.remove();
258 		}
259 		
260 		it = subject.getPublicCredentials().iterator();
261 		while(it.hasNext()){
262 			Object c = it.next();
263 			if(debug){
264 				System.out.println("\t[MoriaLoginModule] removing credential " + c.toString());
265 			}
266 			it.remove();
267 		}
268 	
269 		return true;
270 	}
271 	
272 	/***
273 	 * This method checks the username and password and gets the users attribute values.
274 	 * 
275 	 * @param username
276 	 * @param password
277 	 * 
278 	 * @return true 	if username and password are correct and false if there are som exception or the
279 	 * 					username and/or password not is correct.
280 	 */
281 	private boolean validate(String username, char[] password){
282 		try{
283 			Moria service = new Moria(endpoint, service_username, service_password);
284 		
285 			Attribute[] attributes = service.directNonInteractiveAuthentication(ATTRIBUTE_NAMES, username, new String(password));
286 			
287 			for(int i=0; i<attributes.length; i++){
288 				Attribute att = attributes[i];
289 				for(int j=0; j<att.getValues().length; j++){
290 					tmpPrincipals.add(new MoriaPrincipal(att.getName(), att.getValues()[j]));
291 				}
292 			}
293 			tmpPublicCredentials.add(username);
294 			
295 			return true;
296 		}
297 		catch(Exception ex){
298 			System.err.println("Error: " + ex.getMessage());
299 			ex.printStackTrace();
300 			return false;
301 		}	
302 	}
303 }