1 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 37 public class Users extends MMObjectBuilder { 38 39 private static final Logger log = Logging.getLoggerInstance(Users.class); 40 41 42 public final static String FIELD_STATUS = "status"; 43 public final static String FIELD_USERNAME = "username"; 44 public final static String FIELD_PASSWORD = "password"; 45 public final static String FIELD_DEFAULTCONTEXT = "defaultcontext"; 46 public final static String FIELD_VALID_FROM = "validfrom"; 47 public final static String FIELD_VALID_TO = "validto"; 48 public final static String FIELD_LAST_LOGON = "lastlogon"; 49 50 public final static long VALID_TO_DEFAULT = 4102441200L; 52 53 public final static String STATUS_RESOURCE = "org.mmbase.security.status"; 54 55 56 protected static Cache rankCache = new Cache(20) { 57 public String getName() { return "CCS:SecurityRank"; } 58 public String getDescription() { return "Caches the rank of users. User node --> Rank"; } 59 }; 60 61 protected static Cache userCache = new Cache(20) { 62 public String getName() { return "CCS:SecurityUser"; } 63 public String getDescription() { return "Caches the users. UserName --> User Node"; } 64 }; 65 66 67 protected Function encodeFunction = new AbstractFunction("encode", new Parameter[] {new Parameter("password", String .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 getFunctionValue(Parameters parameters) { 72 return encode((String )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 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 public boolean init() { 93 rankCache.putCache(); 94 userCache.putCache(); 95 96 String s = (String )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 )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 119 static final String ADMIN_USERNAME = "admin"; 120 123 static final String ANONYMOUS_USERNAME = "anonymous"; 124 125 private Encode encoder = null; 126 127 130 public static Users getBuilder() { 131 return (Users) MMBase.getMMBase().getBuilder("mmbaseusers"); 132 } 133 134 135 public Rank getRank(MMObjectNode userNode) { 136 Integer userNumber = new Integer (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; } 163 } 164 } 165 rankCache.put(userNumber, rank); 166 } 167 return rank; 168 } 169 170 171 public boolean setValue(MMObjectNode node, String field, Object originalValue) { 173 if (field.equals(FIELD_USERNAME)) { 174 Object value = node.getValue(field); 175 if (node.getIntValue(FIELD_STATUS) >= 0) { 176 if (originalValue != null && ! originalValue.equals(value)) { 177 182 log.debug("Changing account '" + originalValue + "' to '" + value + "'"); 183 } 184 } 185 } 186 return true; 187 } 188 189 190 193 public MMObjectNode getAnonymousUser() throws SecurityException { 194 return getUser("anonymous", ""); 195 } 196 197 203 public MMObjectNode getUser(String userName, String password) { 204 return getUser(userName, password, true); 205 } 206 207 public MMObjectNode getUser(String userName, String 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 ("no node for anonymous user"); } 219 return user; 220 } 221 222 if (user == null) { 223 log.debug("username: '" + userName + "' --> USERNAME NOT CORRECT"); 224 return null; 225 } 226 String encodedPassword = encode ? encode(password) : password; 227 String 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 ("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 ("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 ("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 270 public MMObjectNode getUser(String 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; } 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 (e); 304 } 305 userCache.put(userName, user); 306 } 307 return user; 308 } 309 310 315 public MMObjectNode getUserByRank(String rank, String 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 (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 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 357 public int insert(String owner, MMObjectNode node) { 358 int res = super.insert(owner, node); 359 String 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 ("Cannot insert user '" + userName + "', because there is already is a user with that name"); 371 } 372 } catch (SearchQueryException sqe) { 373 throw new SecurityException ("Cannot insert user '" + userName + "', because check-query failed:" + sqe.getMessage() ,sqe ); 374 } 375 userCache.clear(); 376 return res; 377 } 378 379 382 public String 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 391 public String 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 402 public String encode(String s) { 403 return encoder.encode(s); 404 } 405 406 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 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 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 462 public boolean check() { 463 return true; 464 } 465 466 protected Object executeFunction(MMObjectNode node, String function, List args) { 467 if (function.equals("info")) { 468 List empty = new ArrayList(); 469 java.util.Map info = (java.util.Map ) 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 field = (String ) args.get(0); 479 480 if (FIELD_STATUS.equals(field)) { 481 String 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 lang = (String ) 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 toString(MMObjectNode n) { 516 return n.getStringValue("username"); 517 } 518 519 public boolean nodeLocalChanged(String machine, String number, String builder, String ctype) { 520 nodeChanged(machine, number, builder, ctype); 521 return super.nodeLocalChanged(machine, number, builder, ctype); 522 } 523 524 public boolean nodeRemoteChanged(String machine, String number, String builder, String 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 (nodeNumber)); 532 533 Iterator i = userCache.entrySet().iterator(); 534 while (i.hasNext()) { 535 Map.Entry entry = (Map.Entry) i.next(); 536 Object 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 machine, String number, String builder, String ctype) { 550 if (ctype.equals("d")) { 551 int nodeNumber = Integer.parseInt(number); 552 invalidateCaches(nodeNumber); 553 } else if (ctype.equals("r")) { 554 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 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 |