KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sslexplorer > security > LogonStateAndCache


1 /*
2  * SSL-Explorer
3  *
4  * Copyright (C) 2003-2006 3SP LTD. All Rights Reserved
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2 of
9  * the License, or (at your option) any later version.
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * License along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */

19             
20 package com.sslexplorer.security;
21
22 import java.io.File JavaDoc;
23 import java.util.ArrayList JavaDoc;
24 import java.util.Calendar JavaDoc;
25 import java.util.List JavaDoc;
26
27 import javax.servlet.http.HttpSession JavaDoc;
28
29 import org.apache.commons.cache.Cache;
30 import org.apache.commons.cache.FileStash;
31 import org.apache.commons.cache.SimpleCache;
32 import org.apache.commons.logging.Log;
33 import org.apache.commons.logging.LogFactory;
34
35 import com.sslexplorer.boot.ContextHolder;
36 import com.sslexplorer.boot.Util;
37 import com.sslexplorer.core.CoreUtil;
38 import com.sslexplorer.core.UserDatabaseManager;
39 import com.sslexplorer.policyframework.Policy;
40 import com.sslexplorer.policyframework.PolicyDatabaseFactory;
41 import com.sslexplorer.policyframework.ResourceUtil;
42 import com.sslexplorer.realms.Realm;
43
44 /**
45  * <p>
46  * State machine which holds the logon state so that the display to the user can
47  * be obfuscated.
48  *
49  * @author James D Robinson <a HREF="mailto:james@3sp.com">&lt;james@3sp.com&gt;</a>
50  *
51  * 03-Aug-2006
52  */

53 public class LogonStateAndCache {
54
55     final static Log log = LogFactory.getLog(LogonStateAndCache.class);
56
57     public static final String JavaDoc LOGON_STATE_MACHINE = "logonStateMachine";
58
59     public static final int STATE_INITIAL = 0;
60     public static final int STATE_STARTED = 1;
61     public static final int STATE_DISPLAY_USERNAME_ENTRY = 2;
62     public static final int STATE_DISPLAY_USERNAME_ENTERED = 3;
63     public static final int STATE_UNKNOWN_USERNAME = 4;
64     public static final int STATE_UNKNOWN_USERNAME_PROMPT_FOR_PASSWORD = 5;
65     public static final int STATE_USERNAME_KNOWN = 6;
66     public static final int STATE_KNOWN_USERNAME_SINGLE_SCHEME = 7;
67     public static final int STATE_KNOWN_USERNAME_MULTIPLE_SCHEMES = 8;
68     public static final int STATE_KNOWN_USERNAME_WRONG_PASSWORD = 9;
69     public static final int STATE_VALID_LOGON = 10;
70     public static final int STATE_RETURN_TO_LOGON = 11;
71     public static final int STATE_KNOWN_USERNAME_NO_SCHEME_SPOOF_PASSWORD_ENTRY = 12;
72     public static final int STATE_KNOWN_USERNAME_MULTIPLE_SCHEMES_SELECT = 13;
73
74     private int state = STATE_INITIAL;
75     private User user;
76     private String JavaDoc username;
77     private List JavaDoc<Integer JavaDoc> resourceIds = null;
78     private List JavaDoc<AuthenticationScheme> authSchemes = new ArrayList JavaDoc<AuthenticationScheme>();
79     private AuthenticationScheme highestPriorityScheme = null;
80     
81     /* Spoof cache used to store fake authentication schemes
82      *
83      * TODO Default to maximum of 2000 fake users. This should be configurable
84      */

85     private static Cache spoofCache;
86     static {
87         File JavaDoc dir = new File JavaDoc(ContextHolder.getContext().getTempDirectory(), "spoof");
88         Util.delTree(dir);
89         spoofCache = new SimpleCache(new FileStash(FileStash.DEFAULT_MAX_BYTES, 2000,
90                 new File JavaDoc[] { dir }, true));
91     }
92    
93     public LogonStateAndCache(int startState, HttpSession JavaDoc session) {
94         super();
95         session.setAttribute(LOGON_STATE_MACHINE, this);
96         this.setState(startState);
97         
98     }
99
100     public int getState() {
101         return state;
102     }
103
104     public void setState(int newState) {
105         if (log.isDebugEnabled()){
106             log.debug("State" + state + " is to be changed to " + newState);
107         }
108         this.state = newState;
109         
110         if (resourceIds != null && this.state == STATE_USERNAME_KNOWN){
111             if (resourceIds.size() == 0) {
112                 this.setState(LogonStateAndCache.STATE_KNOWN_USERNAME_NO_SCHEME_SPOOF_PASSWORD_ENTRY);
113             } else if (resourceIds.size() == 1) {
114                 this.setState(LogonStateAndCache.STATE_KNOWN_USERNAME_SINGLE_SCHEME);
115             } else if (resourceIds.size() > 1) {
116                 this.setState(LogonStateAndCache.STATE_KNOWN_USERNAME_MULTIPLE_SCHEMES);
117             }
118         }
119     }
120
121     public void setUser(User user) throws Exception JavaDoc {
122         this.user = user;
123         this.authSchemes.clear();
124         setResourceIds();
125         this.highestPriorityScheme.setAccountLock(LogonControllerFactory.getInstance().checkForAccountLock(user.getPrincipalName(), user.getRealm().getResourceName()));
126     }
127
128     public boolean hasUser() {
129         return user == null ? false : true;
130     }
131
132     public User getUser() {
133         return user;
134     }
135
136     public boolean enabledSchemesGraeterThanOne() {
137         return this.authSchemes.size() > 1;
138     }
139
140     public List JavaDoc getResourceIds() {
141         return resourceIds;
142     }
143
144     private void setResourceIds() throws Exception JavaDoc {
145         List JavaDoc resourceIds = ResourceUtil.getSignonAuthenticationSchemeIDs(user);
146
147         int highestPriority = Integer.MAX_VALUE;
148         highestPriorityScheme = null;
149         for (AuthenticationScheme element : SystemDatabaseFactory.getInstance().getAuthenticationSchemeSequences()) {
150             if (resourceIds.contains(new Integer JavaDoc(element.getResourceId())) && !element.isSystemScheme() && element.getEnabled()) {
151                 this.authSchemes.add(element);
152                 if (element.getPriorityInt() < highestPriority) {
153                     highestPriority = element.getPriorityInt();
154                     highestPriorityScheme = element;
155                 }
156             }
157             else{
158                 resourceIds.remove(new Integer JavaDoc(element.getResourceId()));
159             }
160         }
161         if(highestPriorityScheme == null) {
162             throw new Exception JavaDoc("User is not attached to any policies that are assigned to any valid authentication schemes. " +
163                     "This may be because they were assigned a scheme that contains an authentication module that no longer exists.");
164         }
165         this.resourceIds = resourceIds;
166         this.highestPriorityScheme.setUser(user);
167         this.setState(LogonStateAndCache.STATE_USERNAME_KNOWN);
168     }
169
170     public AuthenticationScheme getHighestPriorityScheme() {
171         return highestPriorityScheme;
172     }
173
174     public void forceHighestPriorityScheme(String JavaDoc id, String JavaDoc username) throws Exception JavaDoc {
175         this.highestPriorityScheme = SystemDatabaseFactory.getInstance().getAuthenticationSchemeSequence(Integer.parseInt(id));
176         if(!spoofCache.contains(username)) {
177             if (resourceIds.contains(new Integer JavaDoc(id))) {
178                 this.highestPriorityScheme.setUser(user);
179             }
180             else {
181                 throw new Exception JavaDoc("The selected scheme is not valid for the user.");
182             }
183         }
184     }
185
186     public List JavaDoc getAuthSchemes() {
187         return authSchemes;
188     }
189
190     /**
191      * Randomly choose a list of spoofed authentications schemes. This is
192      * to prevent an attacker from determining if a username is invalid or
193      * not by looking if there are multiple authentication schemes
194      * available. If there are none, he can assume the user is invalid.
195      * This method does its best to create a credible random list of
196      * possible schemes. None of them will actually work, but they
197      * will be presented to attacker.
198      *
199      * @param username username
200      * @throws Exception
201      */

202     public void setSpoofedHighestPriorityScheme(String JavaDoc username) throws Exception JavaDoc {
203         Calendar JavaDoc now = Calendar.getInstance();
204         authSchemes = new ArrayList JavaDoc<AuthenticationScheme>();
205
206         // Get the valid schemes// Look for cached scheme list
207
if (spoofCache.contains(username)) {
208             if (log.isDebugEnabled()) {
209                 log.debug("Using cached spoofed schemes for " + username);
210             }
211             int[] authSchemeIds = (int[]) spoofCache.retrieve(username);
212             for (int schemeId : authSchemeIds) {
213                 AuthenticationScheme scheme = SystemDatabaseFactory.getInstance().getAuthenticationSchemeSequence(schemeId);
214                 // The scheme could have been deleted since it was cached
215
if (scheme != null) {
216                     authSchemes.add(scheme);
217                 }
218             }
219         } else {
220             if (log.isDebugEnabled()) {
221                 log.debug("Building new list of spoofed schemes for " + username);
222             }
223
224             // Get the valid schemes
225
List JavaDoc<AuthenticationScheme> schemes = SystemDatabaseFactory.getInstance().getAuthenticationSchemeSequences();
226             for (AuthenticationScheme scheme : new ArrayList JavaDoc<AuthenticationScheme>(schemes)) {
227                 if (scheme.isSystemScheme()) {
228                     schemes.remove(scheme);
229                 }
230             }
231
232             // Add any schemes that are available to anyone
233
Realm realm = UserDatabaseManager.getInstance().getDefaultRealm();
234             Policy p = PolicyDatabaseFactory.getInstance().getPolicy(PolicyDatabaseFactory.getInstance()
235                 .getEveryonePolicyIDForRealm(realm));
236             for (AuthenticationScheme scheme : schemes) {
237                 if (PolicyDatabaseFactory.getInstance().isResourceAttachedToPolicy(scheme, p, realm)) {
238                     authSchemes.add(scheme);
239                 }
240             }
241
242             // If no schemes were available to everyone, add a dummy default
243
if (authSchemes.size() == 0) {
244                 AuthenticationScheme scheme = new DefaultAuthenticationScheme(-1, -1, "", "", now, now, true, 0);
245                 scheme.addModule("Password");
246                 authSchemes.add(scheme);
247             }
248
249             // If there is only one scheme, pick some randomly, each on gets 50/50 chance
250
if (authSchemes.size() == 1 && schemes.size() > 1) {
251                 for (AuthenticationScheme scheme : schemes) {
252                     if (scheme != authSchemes.get(0) && Math.random() >= 0.5) {
253                         authSchemes.add(scheme);
254                     }
255                 }
256             }
257
258             // If there is still only one scheme, pick a single random one
259
if (authSchemes.size() == 1 && schemes.size() > 1) {
260                 authSchemes.add(authSchemes.get(1 + (int) (Math.random() * (authSchemes.size() - 1))));
261             }
262
263             /* Cache the scheme id's so if the same user ID is attempted
264              * the same spoofed schemes will appear
265              */

266             int[] schemeNames = new int[authSchemes.size()];
267             for (int idx = authSchemes.size() - 1; idx >= 0; idx--) {
268                 schemeNames[idx] = authSchemes.get(idx).getResourceId();
269             }
270             // TODO Cache them for 3 days - make configurable?
271
if (log.isDebugEnabled()) {
272                 log.debug("Caching spoofed schemes for " + username);
273             }
274             CoreUtil.storeToCache(spoofCache, username, schemeNames, 360000 * 24 * 3, 0);
275         }
276
277         //
278
resourceIds = new ArrayList JavaDoc<Integer JavaDoc>();
279         this.highestPriorityScheme = authSchemes.get(0);
280     }
281
282     /**
283      * Remove cached spoofed user information. This should be called
284      * as a user is succesfully found. This deals with the situation where
285      * a user tries to logon with an invalid name (a user that has not
286      * yet been created). This fails, but the administrator later adds
287      * the user. The user then tries to logon again before the spoof cache
288      * is cleared. Unless this method is called as soon as the valid
289      * username is found, the spoofing mechanism will think the user
290      * is still invalid.
291      *
292      * @param username username to remove from spoof cache
293      */

294     public void removeFromSpoofCache(String JavaDoc username) {
295         spoofCache.clear(username);
296     }
297 }
Popular Tags