KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > mmbase > security > implementation > cloudcontext > builders > Users


1 /*
2
3 This software is OSI Certified Open Source Software.
4 OSI Certified is a certification mark of the Open Source Initiative.
5
6 The license (Mozilla version 1.0) can be read at the MMBase site.
7 See http://www.MMBase.org/license
8
9 */

10 package org.mmbase.security.implementation.cloudcontext.builders;
11
12 import org.mmbase.security.implementation.cloudcontext.*;
13 import java.util.*;
14 import org.mmbase.module.core.*;
15 import org.mmbase.security.*;
16 import org.mmbase.security.SecurityException;
17 import org.mmbase.storage.search.*;
18 import org.mmbase.storage.search.implementation.*;
19 import org.mmbase.cache.Cache;
20 import org.mmbase.util.Encode;
21 import org.mmbase.util.logging.Logger;
22 import org.mmbase.util.logging.Logging;
23 import org.mmbase.util.functions.*;
24
25 /**
26  * This MMObjectBuilder implementation belongs to the object type
27  * 'mmbaseusers' It contains functionality to MD5 encode passwords,
28  * and so on.
29  *
30  *
31  * @author Eduard Witteveen
32  * @author Pierre van Rooden
33  * @author Michiel Meeuwissen
34  * @version $Id: Users.java,v 1.48.2.1 2006/10/13 15:56:18 nklasens Exp $
35  * @since MMBase-1.7
36  */

37 public class Users extends MMObjectBuilder {
38
39     private static final Logger log = Logging.getLoggerInstance(Users.class);
40
41
42     public final static String JavaDoc FIELD_STATUS = "status";
43     public final static String JavaDoc FIELD_USERNAME = "username";
44     public final static String JavaDoc FIELD_PASSWORD = "password";
45     public final static String JavaDoc FIELD_DEFAULTCONTEXT = "defaultcontext";
46     public final static String JavaDoc FIELD_VALID_FROM = "validfrom";
47     public final static String JavaDoc FIELD_VALID_TO = "validto";
48     public final static String JavaDoc FIELD_LAST_LOGON = "lastlogon";
49
50     public final static long VALID_TO_DEFAULT = 4102441200L; // 2100-1-1
51

52
53     public final static String JavaDoc STATUS_RESOURCE = "org.mmbase.security.status";
54
55
56     protected static Cache rankCache = new Cache(20) {
57             public String JavaDoc getName() { return "CCS:SecurityRank"; }
58             public String JavaDoc getDescription() { return "Caches the rank of users. User node --> Rank"; }
59         };
60
61     protected static Cache userCache = new Cache(20) {
62             public String JavaDoc getName() { return "CCS:SecurityUser"; }
63             public String JavaDoc getDescription() { return "Caches the users. UserName --> User Node"; }
64         };
65
66
67     protected Function encodeFunction = new AbstractFunction("encode", new Parameter[] {new Parameter("password", String JavaDoc.class, true) }, ReturnType.STRING) {
68             {
69                 setDescription("Encodes a string like it would happen with a password, when it's stored in the database.");
70             }
71             public Object JavaDoc getFunctionValue(Parameters parameters) {
72                 return encode((String JavaDoc)parameters.get(0));
73             }
74     };
75
76     protected Function rankFunction = new NodeFunction("rank", Parameter.EMPTY, new ReturnType(Rank.class, "Rank")) {
77             {
78                 setDescription("Returns the rank of an mmbaseusers node");
79             }
80             public Object JavaDoc getFunctionValue(org.mmbase.bridge.Node node, Parameters parameters) {
81                 return Users.this.getRank(getCoreNode(Users.this, node));
82             }
83     };
84     {
85         addFunction(encodeFunction);
86         addFunction(rankFunction);
87     }
88
89     private boolean userNameCaseSensitive = false;
90
91     // javadoc inherited
92
public boolean init() {
93         rankCache.putCache();
94         userCache.putCache();
95
96         String JavaDoc s = (String JavaDoc)getInitParameters().get("encoding");
97         if (s == null) {
98             log.debug("no property 'encoding' defined in '" + getTableName() + ".xml' using default encoding");
99             encoder = new Encode("MD5");
100         } else {
101             encoder = new Encode(s);
102         }
103         log.service("Using " + encoder.getEncoding() + " as our encoding for password");
104
105         s = (String JavaDoc)getInitParameters().get("userNameCaseSensitive");
106         if (s != null) {
107             userNameCaseSensitive = "true".equals(s);
108             log.debug("property 'userNameCaseSensitive' set to '" +userNameCaseSensitive);
109         }
110
111         return super.init();
112     }
113
114
115
116     /**
117      * The user with rank administrator
118      */

119     static final String JavaDoc ADMIN_USERNAME = "admin";
120     /**
121      * The user with rank anonymous
122      */

123     static final String JavaDoc ANONYMOUS_USERNAME = "anonymous";
124
125     private Encode encoder = null;
126
127     /**
128      * @javadoc
129      */

130     public static Users getBuilder() {
131         return (Users) MMBase.getMMBase().getBuilder("mmbaseusers");
132     }
133
134
135     public Rank getRank(MMObjectNode userNode) {
136         Integer JavaDoc userNumber = new Integer JavaDoc(userNode.getNumber());
137         Rank rank;
138         if (userNode != null) {
139             rank = (Rank) rankCache.get(userNumber);
140         } else {
141             log.warn("No node given, returning Anonymous.");
142             return Rank.ANONYMOUS;
143         }
144
145         if (rank == null) {
146             if (userNode instanceof Authenticate.AdminVirtualNode) {
147                 rank = Rank.ADMIN;
148             } else {
149                 List ranks = userNode.getRelatedNodes("mmbaseranks", RelationStep.DIRECTIONS_DESTINATION);
150                 if (ranks.size() > 1) {
151                     log.warn("More then one rank related to mmbase-user " + userNode.getNumber() + " (but " + ranks.size() + ")");
152                 }
153                 rank = Rank.ANONYMOUS;
154                 if (ranks.size() == 0) {
155                     log.debug("No ranks related to this user");
156                 } else {
157                     Iterator i = ranks.iterator();
158                     while (i.hasNext()) {
159                         Ranks rankBuilder = Ranks.getBuilder();
160                         Rank r = rankBuilder.getRank((MMObjectNode) i.next());
161                         if (r.compareTo(rank) > 0) rank = r; // choose the highest one
162
}
163                 }
164             }
165             rankCache.put(userNumber, rank);
166         }
167         return rank;
168     }
169
170
171     //javadoc inherited
172
public boolean setValue(MMObjectNode node, String JavaDoc field, Object JavaDoc originalValue) {
173         if (field.equals(FIELD_USERNAME)) {
174             Object JavaDoc value = node.getValue(field);
175             if (node.getIntValue(FIELD_STATUS) >= 0) {
176                 if (originalValue != null && ! originalValue.equals(value)) {
177                     /*
178                     node.values.put(field, value);
179                     log.warn("Cannot change username (unless account is blocked)");
180                     return false; // hmm?
181                     */

182                     log.debug("Changing account '" + originalValue + "' to '" + value + "'");
183                 }
184             }
185         }
186         return true;
187     }
188
189
190     /**
191      * @javadoc
192      */

193     public MMObjectNode getAnonymousUser() throws SecurityException JavaDoc {
194         return getUser("anonymous", "");
195     }
196
197     /**
198      * Gets the usernode and check its credential (password only, currently)
199      *
200      * @return the authenticated user, or null
201      * @throws SecurityException
202      */

203     public MMObjectNode getUser(String JavaDoc userName, String JavaDoc password) {
204         return getUser(userName, password, true);
205     }
206
207     public MMObjectNode getUser(String JavaDoc userName, String JavaDoc password, boolean encode) {
208
209         if (log.isDebugEnabled()) {
210             log.debug("username: '" + userName + "' password: '" + password + "'");
211         }
212         MMObjectNode user = getUser(userName);
213
214         if (userName.equals("anonymous")) {
215             log.debug("an anonymous username");
216             if (user == null) {
217                 throw new SecurityException JavaDoc("no node for anonymous user"); // odd.
218
}
219             return user;
220         }
221
222         if (user == null) {
223             log.debug("username: '" + userName + "' --> USERNAME NOT CORRECT");
224             return null;
225         }
226         String JavaDoc encodedPassword = encode ? encode(password) : password;
227         String JavaDoc dbPassword = user.getStringValue(FIELD_PASSWORD);
228         if (encodedPassword.equals(dbPassword)) {
229             if (log.isDebugEnabled()) {
230                 log.debug("username: '" + userName + "' password: '" + password + "' found in node #" + user.getNumber());
231             }
232             Rank userRank = getRank(user);
233             if (userRank == null) {
234                 userRank = Rank.ANONYMOUS;
235                 log.warn("rank for '" + userName + "' is unknown or not registered, using anonymous.");
236             }
237             if (userRank.getInt() < Rank.ADMIN.getInt() && getField(FIELD_STATUS) != null) {
238                 int status = user.getIntValue(FIELD_STATUS);
239                 if (status == -1) {
240                     throw new SecurityException JavaDoc("account for '" + userName + "' is blocked");
241                 }
242             }
243             if (userRank.getInt() < Rank.ADMIN_INT && getField(FIELD_VALID_FROM) != null) {
244                 long validFrom = user.getLongValue(FIELD_VALID_FROM);
245                 if (validFrom != -1 && validFrom * 1000 > System.currentTimeMillis() ) {
246                     throw new SecurityException JavaDoc("account for '" + userName + "' not yet active");
247                 }
248             }
249             if (userRank.getInt() < Rank.ADMIN_INT && getField(FIELD_VALID_TO) != null) {
250                 long validTo = user.getLongValue(FIELD_VALID_TO);
251                 if (validTo != -1 && validTo * 1000 < System.currentTimeMillis() ) {
252                     throw new SecurityException JavaDoc("account for '" + userName + "' is expired");
253                 }
254             }
255             if (getField(FIELD_LAST_LOGON) != null) {
256                 user.setValue(FIELD_LAST_LOGON, System.currentTimeMillis() / 1000);
257                 user.commit();
258             }
259             return user;
260         } else {
261             if (log.isDebugEnabled()) {
262                 log.debug("username: '" + userName + "' found in node #" + user.getNumber() + " --> PASSWORDS NOT EQUAL (" + encodedPassword + " != " + dbPassword + ")");
263             }
264             return null;
265         }
266     }
267     /**
268      * Gets the usernode by userName (the 'identifier'). Or 'null' if not found.
269      */

270     public MMObjectNode getUser(String JavaDoc userName) {
271         if (userName == null ) return null;
272         if (!userNameCaseSensitive) {
273             userName = userName.toLowerCase();
274         }
275         MMObjectNode user = (MMObjectNode) userCache.get(userName);
276         if (user == null) {
277             NodeSearchQuery nsq = new NodeSearchQuery(this);
278             StepField sf = nsq.getField(getField("username"));
279             BasicFieldValueConstraint cons = new BasicFieldValueConstraint(sf, userName);
280             cons.setCaseSensitive(userNameCaseSensitive);
281             nsq.setConstraint(cons);
282             nsq.addSortOrder(nsq.getField(getField("number")));
283             SearchQueryException e = null;
284             try {
285                 Iterator i = getNodes(nsq).iterator();
286                 if(i.hasNext()) {
287                     user = (MMObjectNode) i.next();
288                 }
289
290                 if(i.hasNext()) {
291                     log.warn("Found more users with username '" + userName + "'");
292                 }
293             } catch (SearchQueryException sqe) {
294                 e = sqe; // even if database down 'extra admins' can log on.
295
}
296             if (user == null) {
297                 User admin = Authenticate.getLoggedInExtraAdmin(userName);
298                 if (admin != null) {
299                     user = admin.getNode();
300                 }
301             }
302             if (user == null && e != null) {
303                 throw new SecurityException JavaDoc(e);
304             }
305             userCache.put(userName, user);
306         }
307         return user;
308     }
309
310     /**
311      * @param rank Rank to be searched. Never <code>null</code>.
312      * @param userName Username to match or <code>null</code>
313      * @since MMBase-1.8
314      */

315     public MMObjectNode getUserByRank(String JavaDoc rank, String JavaDoc userName) {
316         BasicSearchQuery query = new BasicSearchQuery();
317         MMObjectBuilder ranks = mmb.getBuilder("mmbaseranks");
318         BasicStep step = query.addStep(ranks);
319         StepField sf = query.addField(step, ranks.getField("name"));
320         Constraint cons = new BasicFieldValueConstraint(sf, rank);
321         query.addField(step, ranks.getField("number"));
322         BasicRelationStep relStep = query.addRelationStep(mmb.getInsRel(), this);
323         query.addField(relStep.getNext(), this.getField("number"));
324         relStep.setDirectionality(RelationStep.DIRECTIONS_SOURCE);
325         relStep.setRole(new Integer JavaDoc(mmb.getRelDef().getNumberByName("rank")));
326         if (userName != null) {
327             StepField sf2 = query.addField(relStep.getNext(), this.getField("username"));
328             Constraint cons2 = new BasicFieldValueConstraint(sf2, userName);
329             BasicCompositeConstraint composite = new BasicCompositeConstraint(CompositeConstraint.LOGICAL_AND);
330             composite.addChild(cons);
331             composite.addChild(cons2);
332             cons = composite;
333         }
334
335         query.setConstraint(cons);
336         // sometimes, I quite hate the 'core version' query-framework.
337

338         try {
339             List result = mmb.getClusterBuilder().getClusterNodes(query);
340             if (log.isDebugEnabled()) {
341                 log.debug("Executing " + query + " --> " + result);
342             }
343             if (result.size() > 0) {
344                 return ((MMObjectNode) result.get(0)).getNodeValue("mmbaseusers");
345             } else {
346                 return null;
347             }
348         } catch (SearchQueryException sqe) {
349             log.error(sqe);
350             return null;
351         }
352     }
353
354     /**
355      * UserName must be unique, check it also here (to throw nicer exceptions)
356      */

357     public int insert(String JavaDoc owner, MMObjectNode node) {
358         int res = super.insert(owner, node);
359         String JavaDoc userName = node.getStringValue("username");
360         NodeSearchQuery nsq = new NodeSearchQuery(this);
361         StepField sf = nsq.getField(getField("username"));
362         Constraint cons = new BasicFieldValueConstraint(sf, userName);
363         nsq.setConstraint(cons);
364         try {
365             Iterator i = getNodes(nsq).iterator();
366             while(i.hasNext()) {
367                 MMObjectNode n = (MMObjectNode) i.next();
368                 if (n.getNumber() == node.getNumber()) continue;
369                 removeNode(node);
370                 throw new SecurityException JavaDoc("Cannot insert user '" + userName + "', because there is already is a user with that name");
371             }
372         } catch (SearchQueryException sqe) {
373             throw new SecurityException JavaDoc("Cannot insert user '" + userName + "', because check-query failed:" + sqe.getMessage() ,sqe );
374         }
375         userCache.clear();
376         return res;
377     }
378
379     /**
380      * @see org.mmbase.security.implementation.cloudcontext.User#getOwnerField
381      */

382     public String JavaDoc getDefaultContext(MMObjectNode node) {
383         if (node == null) return "system";
384         MMObjectNode contextNode = node.getNodeValue(FIELD_DEFAULTCONTEXT);
385         return contextNode == null ? null : contextNode.getStringValue("name");
386     }
387
388     /**
389      * @return The string representation the username of the User node.
390      */

391     public String JavaDoc getUserName(MMObjectNode node) {
392         if (node.getBuilder().hasField(FIELD_USERNAME)) {
393             return node.getStringValue(FIELD_USERNAME);
394         } else {
395             return null;
396         }
397     }
398
399     /**
400      * Encodes a password for storage (to avoid plain text passwords).
401      */

402     public String JavaDoc encode(String JavaDoc s) {
403         return encoder.encode(s);
404     }
405
406     /**
407      * @javadoc
408      */

409     public boolean isValid(MMObjectNode node) {
410         if (! (node.getBuilder() instanceof Users)) {
411             log.info("Node is no Users object but " + node.getBuilder().getTableName() + ", corresponding user is invalid");
412             return false;
413         }
414         boolean valid = true;
415         long time = System.currentTimeMillis() / 1000;
416         if (hasField(FIELD_VALID_FROM)) {
417             long from = node.getLongValue(FIELD_VALID_FROM);
418             if (from > time) {
419                 valid = false;
420             }
421         }
422         if (hasField(FIELD_VALID_TO)) {
423             long to = node.getLongValue(FIELD_VALID_TO);
424             if (to > 0 && to < time) {
425                 valid = false;
426             }
427         }
428         if (node.getIntValue(FIELD_STATUS) < 0) {
429             valid = false;
430         }
431         if (! valid) {
432             invalidateCaches(node.getNumber());
433         }
434         return valid;
435     }
436
437     /**
438      * Makes sure unique values and not-null's are filed
439      */

440     public void setDefaults(MMObjectNode node) {
441         super.setDefaults(node);
442         MMObjectNode defaultDefaultContext = Contexts.getBuilder().getContextNode(node.getStringValue("owner"));
443         node.setValue(FIELD_DEFAULTCONTEXT, defaultDefaultContext);
444         node.setValue(FIELD_PASSWORD, "");
445         node.setValue(FIELD_STATUS, 0);
446         String JavaDoc currentUserName = node.getStringValue(FIELD_USERNAME);
447         if (currentUserName.equals("")) {
448             currentUserName = "user";
449         }
450         setUniqueValue(node, FIELD_USERNAME, currentUserName);
451         if (getField(FIELD_VALID_FROM) != null && node.isNull(FIELD_VALID_FROM)) {
452             node.setValue(FIELD_VALID_FROM, System.currentTimeMillis()/1000);
453         }
454         if (getField(FIELD_VALID_TO) != null && node.isNull(FIELD_VALID_TO)) {
455             node.setValue(FIELD_VALID_TO, VALID_TO_DEFAULT);
456         }
457      }
458
459     /**
460      * @javadoc
461      */

462     public boolean check() {
463         return true;
464     }
465
466    protected Object JavaDoc executeFunction(MMObjectNode node, String JavaDoc function, List args) {
467         if (function.equals("info")) {
468             List empty = new ArrayList();
469             java.util.Map JavaDoc info = (java.util.Map JavaDoc) super.executeFunction(node, function, empty);
470             info.put("gui", "(status..) Gui representation of this object.");
471                  if (args == null || args.size() == 0) {
472                 return info;
473             } else {
474                 return info.get(args.get(0));
475             }
476         } else if (args != null && args.size() > 0) {
477             if (function.equals("gui")) {
478                 String JavaDoc field = (String JavaDoc) args.get(0);
479
480                 if (FIELD_STATUS.equals(field)) {
481                     // THIS KIND OF STUFF SHOULD BE AVAILEBLE IN MMOBJECTBUILDER.
482
String JavaDoc val = node.getStringValue(field);
483                     ResourceBundle bundle;
484                     Parameters pars = Functions.buildParameters(GUI_PARAMETERS, args);
485                     Locale locale = (Locale) pars.get(Parameter.LOCALE);
486                     if (locale == null) {
487                         String JavaDoc lang = (String JavaDoc) pars.get(Parameter.LANGUAGE);
488                         if (lang != null){
489                             locale = new Locale(lang, "");
490                         }
491                     }
492                     if (locale == null) {
493                         locale = mmb.getLocale();
494                     }
495                     bundle = ResourceBundle.getBundle(STATUS_RESOURCE, locale, getClass().getClassLoader());
496
497                     try {
498                         return bundle.getString(val);
499                     } catch (MissingResourceException e) {
500                         return val;
501                     }
502                 }
503             }
504         }
505         if (log.isDebugEnabled()) {
506             log.debug("Function '" + function + "' not matched in users");
507         }
508         return super.executeFunction(node, function, args);
509     }
510
511     public boolean equals(MMObjectNode o1, MMObjectNode o2) {
512         return o1.getNumber() == o2.getNumber();
513     }
514
515     public String JavaDoc toString(MMObjectNode n) {
516         return n.getStringValue("username");
517     }
518
519     public boolean nodeLocalChanged(String JavaDoc machine, String JavaDoc number, String JavaDoc builder, String JavaDoc ctype) {
520         nodeChanged(machine, number, builder, ctype);
521         return super.nodeLocalChanged(machine, number, builder, ctype);
522     }
523
524     public boolean nodeRemoteChanged(String JavaDoc machine, String JavaDoc number, String JavaDoc builder, String JavaDoc ctype) {
525         nodeChanged(machine, number, builder, ctype);
526         return super.nodeRemoteChanged(machine, number, builder, ctype);
527     }
528
529
530     protected void invalidateCaches(int nodeNumber) {
531         rankCache.remove(new Integer JavaDoc(nodeNumber));
532
533         Iterator i = userCache.entrySet().iterator();
534         while (i.hasNext()) {
535             Map.Entry entry = (Map.Entry) i.next();
536             Object JavaDoc value = entry.getValue();
537             if (value == null) {
538                 i.remove();
539             } else {
540                 MMObjectNode node = (MMObjectNode) value;
541                 if (node.getNumber() == nodeNumber) {
542                     i.remove();
543                 }
544             }
545         }
546     }
547
548
549     public boolean nodeChanged(String JavaDoc machine, String JavaDoc number, String JavaDoc builder, String JavaDoc ctype) {
550         if (ctype.equals("d")) {
551             int nodeNumber = Integer.parseInt(number);
552             invalidateCaches(nodeNumber);
553         } else if (ctype.equals("r")) {
554             //FIXME we are always clearing the cache even when it is not the relation to ranks
555
rankCache.remove(Integer.valueOf(number));
556         } else if (ctype.equals("c")) {
557             rankCache.remove(Integer.valueOf(number));
558
559             MMObjectNode node = getNode(number);
560             Map users = new HashMap();
561             Iterator i = userCache.entrySet().iterator();
562             while (i.hasNext()) {
563                 Map.Entry entry = (Map.Entry) i.next();
564                 Object JavaDoc value = entry.getValue();
565                 if (value == null) {
566                     i.remove();
567                 } else {
568                     MMObjectNode cacheNode = (MMObjectNode) value;
569                     if (cacheNode.getNumber() == node.getNumber()) {
570                         users.put(entry.getKey(), node);
571                         i.remove();
572                     }
573                 }
574             }
575             userCache.putAll(users);
576         }
577         return true;
578
579     }
580
581
582 }
583
Popular Tags