001 /* 002 Copyright (C) 2002-2003 Laurent Martelli <laurent@aopsys.com> 003 Renaud Pawlak <renaud@aopsys.com> 004 005 This program is free software; you can redistribute it and/or modify 006 it under the terms of the GNU Lesser General Public License as 007 published by the Free Software Foundation; either version 2 of the 008 License, or (at your option) any later version. 009 010 This program is distributed in the hope that it will be useful, 011 but WITHOUT ANY WARRANTY; without even the implied warranty of 012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 GNU Lesser General Public License for more details. 014 015 You should have received a copy of the GNU Lesser General Public License 016 along with this program; if not, write to the Free Software 017 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ 018 019 package org.objectweb.jac.aspects.user; 020 021 import org.aopalliance.intercept.ConstructorInvocation; 022 import org.aopalliance.intercept.MethodInvocation; 023 import org.objectweb.jac.aspects.authentication.AuthenticationAC; 024 import org.objectweb.jac.aspects.gui.DisplayContext; 025 import org.objectweb.jac.aspects.gui.GuiAC; 026 import org.objectweb.jac.core.ACManager; 027 import org.objectweb.jac.core.AspectComponent; 028 import org.objectweb.jac.core.Collaboration; 029 import org.objectweb.jac.core.NameRepository; 030 import org.objectweb.jac.core.ObjectRepository; 031 import org.objectweb.jac.core.Wrappee; 032 import org.objectweb.jac.core.Wrapper; 033 import org.objectweb.jac.core.rtti.AttributeController; 034 import org.objectweb.jac.core.rtti.ClassItem; 035 import org.objectweb.jac.core.rtti.ClassRepository; 036 import org.objectweb.jac.core.rtti.CollectionItem; 037 import org.objectweb.jac.core.rtti.FieldItem; 038 import org.objectweb.jac.core.rtti.MemberItem; 039 import org.objectweb.jac.core.rtti.MetaItem; 040 import org.objectweb.jac.core.rtti.MethodItem; 041 import org.objectweb.jac.util.Log; 042 import org.objectweb.jac.util.ObjectArray; 043 import java.util.Collection; 044 import java.util.HashMap; 045 import java.util.Hashtable; 046 import java.util.Iterator; 047 import java.util.List; 048 import java.util.Map; 049 import java.util.Vector; 050 import org.objectweb.jac.core.Interaction; 051 052 /** 053 * This aspect handles users within an application. 054 * 055 * <p>Any class of the application can be declared as a user 056 * representation. The aspect configurator should then declare which 057 * are the fields of this class that corresponds to the user's id 058 * (that is used to login) and to the password (not required). Users 059 * can then be bounded to profiles that define what are the elements 060 * of the application that have the right to access or not. 061 * 062 * <p>A profile has a list of rules associated to it. When the aspect 063 * needs to know if a user with a given profile is allowed to acess 064 * resource, it inspects the rules of the profile in the order in 065 * their declaration order, and as soon as a rule matches the 066 * resource, this rule determines if the user is granted access to the 067 * resource. A resource is a field or a method of a class. If the 068 * profile inherits another profile, the rules of the inherited 069 * profile are examined first.</p> 070 * 071 * @see #setUserClass(ClassItem,String,String,String) 072 * @see #declareProfile(String) 073 * @see #declareProfile(String,String) 074 * @see Rule 075 * @see Profile 076 */ 077 078 public class UserAC 079 extends AspectComponent 080 implements UserConf, AttributeController { 081 082 public static final String USER = "UserAC.USER"; 083 public static final String CONTEXTUAL_PROFILE = "UserAC.CONTEXTUAL_PROFILE"; 084 public static final String HABILITATION = "UserAC.HABILITATION"; 085 public static final String FILTER = "UserAC.FILTER"; // MethodItem 086 087 /** 088 * The default controller registers its 089 * <code>controlAttribute</code> method as an access controller for 090 * the RTTI. */ 091 092 public UserAC() { 093 blockKeywords = new String[] { "profile" }; 094 try { 095 MetaItem.registerAccessController(this); 096 } catch (RuntimeException e) { 097 e.printStackTrace(); 098 } 099 } 100 101 public void setContextualProfile( 102 ClassItem cl, 103 String field, 104 String profile) { 105 Log.trace( 106 "profile", 107 "defined contextual profile: " + field + " <--> " + profile); 108 cl.getField(field).setAttribute(CONTEXTUAL_PROFILE, profile); 109 } 110 111 /** 112 * Adds a contextually profiled user. 113 * 114 * @param substance object whose field(s) to set 115 * @param user user object 116 * @param profile set fields tagged with this profile 117 */ 118 119 public static void addContextualProfiledUser( 120 Object substance, 121 Object user, 122 Profile profile) { 123 124 Collection c = 125 ClassRepository.get().getClass(substance).getTaggedMembers( 126 CONTEXTUAL_PROFILE, 127 profile.getName()); 128 129 Log.trace("profile", "found contextual profiles for: " + c); 130 Iterator it = c.iterator(); 131 while (it.hasNext()) { 132 MemberItem member = (MemberItem) it.next(); 133 try { 134 if (member instanceof CollectionItem) { 135 ((CollectionItem) member).addThroughAdder(substance, user); 136 } else if (member instanceof FieldItem) { 137 ((FieldItem) member).setThroughWriter(substance, user); 138 } 139 } catch (Exception e) { 140 Log.error( 141 "Failed to set field " 142 + substance 143 + "." 144 + member 145 + " with " 146 + user 147 + ": " 148 + e); 149 e.printStackTrace(); 150 } 151 } 152 } 153 154 public Profile getProfileFromUser(Object user) { 155 return (Profile) profileField.getThroughAccessor(user); 156 } 157 158 public void setProfileToUser(Object user, Profile profile) { 159 try { 160 profileField.setThroughWriter(user, profile); 161 } catch (Exception e) { 162 e.printStackTrace(); 163 Log.error( 164 "Failed to set profile of user (" 165 + user 166 + "." 167 + profileField 168 + ") with " 169 + profile 170 + ": " 171 + e); 172 } 173 } 174 175 /** 176 * Gets the profiles of a user for checking access rights for an 177 * object. 178 * 179 * <p>If the user is the owner of the checked object, the "owner" 180 * profile is returned in addition to the user's profile.</p> 181 * 182 * @param authuser the user's name 183 * @param substance the checked object 184 */ 185 186 protected List getProfiles(String authuser, Object substance) { 187 Vector profiles = new Vector(); 188 Object user = getUserFromLogin(authuser); 189 Profile profile = getProfileFromUser(user); 190 if (profile != null) 191 profiles.add(profile); 192 if (substance != null) { 193 Object owner = getOwner(substance); 194 // We consider that everybody is the owner of an object with 195 // no owner so that new objects whose owner is not set yet 196 // can be edited 197 if (owner == user || owner == null) { 198 profile = userManager.getProfile("owner"); 199 if (profile != null) 200 profiles.add(profile); 201 } 202 203 // gets the contextual profiles 204 Collection fs = 205 ClassRepository.get().getClass(substance).getTaggedFields( 206 CONTEXTUAL_PROFILE, 207 false); 208 if (fs.size() > 0) { 209 Iterator it = fs.iterator(); 210 while (it.hasNext()) { 211 FieldItem field = (FieldItem) it.next(); 212 if ((field instanceof CollectionItem 213 && ((CollectionItem) field).getActualCollection( 214 substance).contains( 215 user)) 216 || (field.isReference() 217 && field.getThroughAccessor(substance) == user)) { 218 profile = 219 userManager.getProfile( 220 (String) field.getAttribute( 221 CONTEXTUAL_PROFILE)); 222 if (profile != null) 223 profiles.add(profile); 224 } 225 } 226 } 227 } 228 229 Log.trace("profile", "Profiles=" + profiles); 230 return profiles; 231 } 232 233 Hashtable usersCache = new Hashtable(); 234 235 /** 236 * Invalidate controlAttribute's cache. 237 * 238 * @see #controlAttribute(Object,MetaItem,String,Object) 239 */ 240 public void invalidateCache() { 241 usersCache.clear(); 242 } 243 244 static class BooleanThreadLocal extends ThreadLocal { 245 protected Object initialValue() { 246 return Boolean.FALSE; 247 } 248 } 249 250 private BooleanThreadLocal inHabilitation = new BooleanThreadLocal(); 251 252 /** 253 * This method controls the access to a given meta item of the RTTI. 254 * 255 * <p>The profile of the current user is fetched and the 256 * permissions are checked against this profile. If the user owns 257 * the object being controlled, the "owner" profile is checked 258 * first. 259 * 260 * @param substance 261 * @param item the meta item that is currently accessed 262 * @param attrName the attribute that is asked on <code>item</code> 263 * @param value the already fetched value (can be overriden or 264 * returned as is) 265 * @return the value that will finally be returned by the RTTI 266 * 267 * @see org.objectweb.jac.core.rtti.MetaItem#getAttribute(String) 268 * @see #invalidateCache() */ 269 270 public Object controlAttribute( 271 Object substance, 272 MetaItem item, 273 String attrName, 274 Object value) { 275 Log.trace( 276 "profile", 277 "controlAttribute(" + item + "," + attrName + "," + value + ")"); 278 // Avoid infinite recursion when displaying an error box. 279 if (inHabilitation.equals(Boolean.TRUE)) { 280 return value; 281 } 282 String authuser = (String) attr(AuthenticationAC.USER); 283 if (authuser == null) { 284 Log.trace( 285 "profile", 286 2, 287 "user not defined, " 288 + "no access check for " 289 + item 290 + "." 291 + attrName); 292 return value; 293 } 294 295 Log.trace("profile", "1"); 296 297 ClassItem cli = null; 298 if (substance != null) 299 cli = ClassRepository.get().getClass(substance); 300 MethodItem classHabilitation = null; 301 if (cli != null) 302 classHabilitation = (MethodItem) cli.getAttribute(HABILITATION); 303 if (classHabilitation == null) 304 classHabilitation = habilitation; 305 if (classHabilitation != null) { 306 Log.trace("profile", "Calling habilitation " + classHabilitation); 307 Object savedInhabilitation = inHabilitation.get(); 308 try { 309 inHabilitation.set(Boolean.TRUE); 310 if (attrName.equals(GuiAC.VISIBLE) 311 || attrName.equals(GuiAC.EDITABLE) 312 || attrName.equals(GuiAC.ADDABLE) 313 || attrName.equals(GuiAC.REMOVABLE)) { 314 value = 315 classHabilitation.invokeStatic( 316 new Object[] { 317 substance, 318 item, 319 getUserFromLogin(authuser), 320 attrName }); 321 } 322 } finally { 323 inHabilitation.set(savedInhabilitation); 324 } 325 return value; 326 } 327 328 Log.trace("profile", "3"); 329 330 Map userCache = (HashMap) usersCache.get(authuser); 331 if (userCache == null) { 332 userCache = new HashMap(); 333 usersCache.put(authuser, userCache); 334 } 335 ObjectArray key = 336 new ObjectArray(new Object[] { substance, item, attrName }); 337 Log.trace("profile.cache." + attrName, "key = " + key); 338 if (userCache.containsKey(key)) { 339 Log.trace( 340 "profile.cache." + attrName, 341 "return cached value " + userCache.get(key)); 342 return userCache.get(key); 343 } 344 345 if (attrName.equals(GuiAC.VISIBLE)) { 346 Collection profiles = getProfiles(authuser, substance); 347 if (profiles.isEmpty()) { 348 ; 349 } else if (!Profile.isReadable(profiles, item)) { 350 Log.trace( 351 "profile", 352 "overriding " + attrName + " for " + item + " -> FALSE"); 353 value = Boolean.FALSE; 354 // do not show adders if isAddable==false 355 } else if ( 356 item instanceof MethodItem && ((MethodItem) item).isAdder()) { 357 CollectionItem[] addedColls = 358 ((MethodItem) item).getAddedCollections(); 359 for (int i = 0; i < addedColls.length; i++) { 360 if (!Profile.isAddable(profiles, addedColls[i])) { 361 Log.trace( 362 "profile", 363 "overriding " 364 + attrName 365 + " for " 366 + item 367 + " -> FALSE"); 368 value = Boolean.FALSE; 369 break; 370 } 371 } 372 // do not show removers if isRemovable==false 373 } else if ( 374 item instanceof MethodItem 375 && ((MethodItem) item).isRemover()) { 376 CollectionItem[] removedColls = 377 ((MethodItem) item).getRemovedCollections(); 378 for (int i = 0; i < removedColls.length; i++) { 379 if (!Profile.isRemovable(profiles, removedColls[i])) { 380 Log.trace( 381 "profile", 382 "overriding " 383 + attrName 384 + " for " 385 + item 386 + " -> FALSE"); 387 value = Boolean.FALSE; 388 break; 389 } 390 } 391 } 392 } else if (attrName.equals(GuiAC.EDITABLE)) { 393 Collection profiles = getProfiles(authuser, substance); 394 if (profiles.isEmpty()) { 395 ; 396 } else if (!Profile.isWritable(profiles, item)) { 397 Log.trace( 398 "profile", 399 "overriding " + attrName + " for " + item + " -> FALSE"); 400 value = Boolean.FALSE; 401 } 402 } else if (attrName.equals(GuiAC.ADDABLE)) { 403 Collection profiles = getProfiles(authuser, substance); 404 if (profiles.isEmpty()) { 405 ; 406 } else if (!Profile.isAddable(profiles, item)) { 407 Log.trace( 408 "profile", 409 "overriding " + attrName + " for " + item + " -> FALSE"); 410 value = Boolean.FALSE; 411 } 412 } else if (attrName.equals(GuiAC.REMOVABLE)) { 413 Collection profiles = getProfiles(authuser, substance); 414 if (profiles.isEmpty()) { 415 ; 416 } else if (!Profile.isRemovable(profiles, item)) { 417 Log.trace( 418 "profile", 419 "overriding " + attrName + " for " + item + " -> FALSE"); 420 value = Boolean.FALSE; 421 } 422 } else if (attrName.equals(GuiAC.CREATABLE)) { 423 Collection profiles = getProfiles(authuser, substance); 424 if (profiles.isEmpty()) { 425 ; 426 } else if (!Profile.isCreatable(profiles, item)) { 427 Log.trace( 428 "profile", 429 "overriding " + attrName + " for " + item + " -> FALSE"); 430 value = Boolean.FALSE; 431 } 432 } 433 userCache.put(key, value); 434 return value; 435 } 436 437 /** 438 * Returns the user that is currently logged in. 439 * 440 * @return the user */ 441 442 public Object getCurrentUser() { 443 String authuser = 444 (String) Collaboration.get().getAttribute(AuthenticationAC.USER); 445 if (authuser != null) { 446 return getUserFromLogin(authuser); 447 } 448 return null; 449 } 450 451 /** login -> User */ 452 Hashtable cachedUsers = new Hashtable(); 453 454 /** 455 * Gets a user from its login as defined in <code>setUserClass</code>. 456 * 457 * @param login the user's id 458 * @return the user (instance of the user's class) 459 * @see #setUserClass(ClassItem,String,String,String) 460 * @see #getUserLogin(Object) 461 * @see #getUserPassword(Object) */ 462 463 public Object getUserFromLogin(String login) { 464 Object user = null; 465 user = cachedUsers.get(login); 466 if (user == null) { 467 Collection users = ObjectRepository.getObjects(userClass); 468 Iterator i = users.iterator(); 469 // Find a user whose loginField matches the authenticated username 470 while (i.hasNext()) { 471 Object testedUser = i.next(); 472 Object id = loginField.getThroughAccessor(testedUser); 473 if (login.equals(id)) { 474 user = testedUser; 475 break; 476 } 477 } 478 if (login != null && user != null) 479 cachedUsers.put(login, user); 480 } 481 return user; 482 } 483 484 /** 485 * Gets the login value for a given user. 486 * 487 * @param user the user object 488 * @return its login value (<code>null<code> if the user is 489 * <code>null</code> or if <code>setUserClass</code> is not 490 * correctly defined) 491 * @see #setUserClass(ClassItem,String,String,String) 492 * @see #getUserFromLogin(String) */ 493 494 public String getUserLogin(Object user) { 495 if (user == null) 496 return null; 497 if (loginField == null) 498 return null; 499 return (String) loginField.getThroughAccessor(user); 500 } 501 502 /** 503 * Gets the password value for a given user. 504 * 505 * @param user the user object 506 * @return its password value (<code>null<code> if the user is 507 * <code>null</code> or if <code>setUserClass</code> is not 508 * correctly defined) 509 * @see #setUserClass(ClassItem,String,String,String) 510 * @see #getUserFromLogin(String) */ 511 512 public String getUserPassword(Object user) { 513 if (user == null) 514 return null; 515 if (passwordField == null) 516 return null; 517 return (String) passwordField.getThroughAccessor(user); 518 } 519 520 /** 521 * Gets the login for the currently logged user. 522 * 523 * @return the login 524 * @see #getCurrentUser() */ 525 526 public String getCurrentUserLogin() { 527 Object currentUser = getCurrentUser(); 528 if (currentUser == null) 529 return null; 530 if (loginField == null) 531 return null; 532 return (String) loginField.getThroughAccessor(currentUser); 533 } 534 535 /** 536 * Gets the password for the currently logged user. 537 * 538 * @return the password 539 * @see #getCurrentUser() */ 540 541 public String getCurrentUserPassword() { 542 Object currentUser = getCurrentUser(); 543 if (currentUser == null) 544 return null; 545 if (passwordField == null) 546 return null; 547 return (String) passwordField.getThroughAccessor(currentUser); 548 } 549 550 /** 551 * This controlling method can be used by the authentification 552 * aspect to control that the authenticated user is valid. 553 * 554 * @param username the username that is given by the authenticator 555 * @param wrappee the object that is currently accessed 556 * @param method the method that is currently called 557 * @see org.objectweb.jac.aspects.authentication.AuthenticationAC 558 * @see org.objectweb.jac.aspects.authentication.AuthenticationAC#setController(String,String,MethodItem) 559 */ 560 public static boolean userController( 561 String username, 562 Object wrappee, 563 MethodItem method) { 564 Log.trace("authentication", "userController(" + username + "...)"); 565 if (username == null) 566 return false; 567 UserAC userAC = (UserAC) ACManager.getACM().getAC("user"); 568 return username.equals(userAC.getCurrentUserLogin()); 569 } 570 571 public ClassItem getUserClass() { 572 return userClass; 573 } 574 575 public FieldItem getLoginField() { 576 return loginField; 577 } 578 579 // UserConf interface 580 581 UserWrapper wrapper = new UserWrapper(this); 582 583 ClassItem userClass; 584 FieldItem loginField; 585 FieldItem passwordField; 586 FieldItem profileField; 587 588 public void setUserClass( 589 ClassItem userClass, 590 String loginField, 591 String passwordField, 592 String profileField) { 593 /*if(!User.class.isAssignableFrom(userClass.getActualClass())) { 594 Log.error(userClass+" MUST implement org.objectweb.jac.aspects.user.User.\n"+ 595 "Please correct this and start the application again."); 596 System.exit(-1); 597 }*/ 598 this.userClass = userClass; 599 this.loginField = userClass.getField(loginField); 600 if (passwordField != null) { 601 this.passwordField = userClass.getField(passwordField); 602 } 603 if (profileField != null) { 604 this.profileField = userClass.getField(profileField); 605 } 606 } 607 608 public void defineAdministrator(String login, String password) { 609 if (userClass == null) { 610 Log.error( 611 "cannot define administrator if no user class is defined before."); 612 return; 613 } 614 if (getUserFromLogin(login) != null) { 615 Log.trace("user", "Admin user `" + login + "' already defined"); 616 // admin already defined 617 return; 618 } 619 Object admin = null; 620 try { 621 admin = userClass.newInstance(); 622 } catch (Exception e) { 623 Log.error("administrator instantiation failed"); 624 e.printStackTrace(); 625 return; 626 } 627 try { 628 loginField.setThroughWriter(admin, login); 629 } catch (Exception e) { 630 Log.error( 631 "Failed to set login of admin (" 632 + admin 633 + "." 634 + loginField 635 + ") with " 636 + login 637 + ": " 638 + e); 639 } 640 try { 641 passwordField.setThroughWriter(admin, password); 642 } catch (Exception e) { 643 Log.error( 644 "Failed to set password of admin (" 645 + admin 646 + "." 647 + passwordField 648 + "): " 649 + e); 650 } 651 setProfileToUser(admin, userManager.getProfile("administrator")); 652 userManager.setDefaultAdmin((Wrappee) admin); 653 Log.trace("user", "Created admin user " + admin); 654 } 655 656 public void autoInitClasses(String classExpr) { 657 Log.trace("user", "autoInitClasses " + classExpr); 658 pointcut( 659 "ALL", 660 classExpr, 661 "CONSTRUCTORS", 662 wrapper, 663 null); 664 } 665 666 public void autoInitClasses( 667 ClassItem cl, 668 String triggerClassExpr, 669 String triggerMethodExpr) { 670 Log.trace( 671 "user", 672 "autoInitClasses " 673 + cl 674 + " on " 675 + triggerClassExpr 676 + "." 677 + triggerMethodExpr); 678 wrapper.addClass(cl); 679 pointcut( 680 "ALL", 681 triggerClassExpr, 682 triggerMethodExpr, 683 wrapper, 684 null); 685 } 686 687 UserManager userManager = new UserManager(); 688 689 public UserManager getUserManager() { 690 return userManager; 691 } 692 693 public void declareProfile(String name) { 694 Collection profiles = 695 ObjectRepository.getObjects( 696 ClassRepository.get().getClass(Profile.class)); 697 Iterator it = profiles.iterator(); 698 while (it.hasNext()) { 699 Profile cur = (Profile) it.next(); 700 if (cur.getName().equals(name)) { 701 Log.trace("profile", "profile " + name + " already defined"); 702 cur.setIsNew(false); 703 //cur.clear(); 704 return; 705 } 706 } 707 Profile profile = new Profile(); 708 userManager.addProfile(name, profile); 709 } 710 711 public void declareProfile(String name, String parent) { 712 Collection profiles = 713 ObjectRepository.getObjects( 714 ClassRepository.get().getClass(Profile.class)); 715 Iterator it = profiles.iterator(); 716 while (it.hasNext()) { 717 Profile cur = (Profile) it.next(); 718 if (cur.getName().equals(name)) { 719 Log.trace("profile", "profile " + name + " already defined"); 720 cur.setIsNew(false); 721 //cur.clear(); 722 return; 723 } 724 } 725 Profile parentProfile = userManager.getProfile(parent); 726 Profile profile = new Profile(); 727 if (parentProfile != null) 728 profile.setParent(parentProfile); 729 userManager.addProfile(name, profile); 730 } 731 732 public Profile getProfile(String name) { 733 //(Profile)ObjectRepository.getObjects("org.objectweb.jac.aspects.user.Profile","name='"+name+"'"); 734 Profile profile = userManager.getProfile(name); 735 if (profile == null) 736 throw new RuntimeException("No such profile " + name); 737 return profile; 738 } 739 740 /** 741 * Use this config method to clear a profile so that it can be 742 * reinitialized from the config file. 743 * @param name name of the profile to clear 744 */ 745 public void clearProfile(String name) { 746 Profile profile = getProfile(name); 747 profile.clear(); 748 profile.setIsNew(true); 749 } 750 751 public void addReadable(String profile, String resourceExpr) { 752 if (getProfile(profile).isNew()) 753 getProfile(profile).addReadable(resourceExpr); 754 } 755 756 public void addUnreadable(String profile, String resourceExpr) { 757 if (getProfile(profile).isNew()) 758 getProfile(profile).addUnreadable(resourceExpr); 759 } 760 761 public void addWritable(String profile, String resourceExpr) { 762 if (getProfile(profile).isNew()) 763 getProfile(profile).addWritable(resourceExpr); 764 } 765 766 public void addUnwritable(String profile, String resourceExpr) { 767 if (getProfile(profile).isNew()) 768 getProfile(profile).addUnwritable(resourceExpr); 769 } 770 771 public void addRemovable(String profile, String resourceExpr) { 772 if (getProfile(profile).isNew()) 773 getProfile(profile).addRemovable(resourceExpr); 774 } 775 776 public void addUnremovable(String profile, String resourceExpr) { 777 if (getProfile(profile).isNew()) 778 getProfile(profile).addUnremovable(resourceExpr); 779 } 780 781 public void addAddable(String profile, String resourceExpr) { 782 if (getProfile(profile).isNew()) 783 getProfile(profile).addAddable(resourceExpr); 784 } 785 786 public void addCreatable(String profile, String resourceExpr) { 787 if (getProfile(profile).isNew()) 788 getProfile(profile).addCreatable(resourceExpr); 789 } 790 791 public void addUnaddable(String profile, String resourceExpr) { 792 if (getProfile(profile).isNew()) 793 getProfile(profile).addUnaddable(resourceExpr); 794 } 795 796 public MethodItem habilitation; 797 798 public void defineHabilitation(MethodItem habilitation) { 799 this.habilitation = habilitation; 800 } 801 802 public void defineHabilitation(ClassItem cli, MethodItem habilitation) { 803 cli.setAttribute(HABILITATION, habilitation); 804 } 805 806 public void addOwnerFilter( 807 String profile, 808 ClassItem cl, 809 String collectionName) { 810 pointcut( 811 "ALL", 812 cl.getName(), 813 "GETTERS(" + collectionName + ")", 814 "org.objectweb.jac.aspects.user.UserAC$OwnerFilterWrapper", 815 new Object[] { profile }, 816 null, 817 SHARED); 818 } 819 820 public void addFilter(CollectionItem collection, MethodItem filter) { 821 collection.setAttribute(FILTER, filter); 822 pointcut( 823 "ALL", 824 collection.getClassItem().getName(), 825 "GETTERS(" + collection.getName() + ")", 826 "org.objectweb.jac.aspects.user.UserAC$FilterWrapper", 827 null, 828 SHARED); 829 } 830 831 public class FilterWrapper extends Wrapper { 832 public FilterWrapper(AspectComponent ac) { 833 super(ac); 834 } 835 public Object filterResult(Interaction interaction) { 836 Object result = proceed(interaction); 837 String authuser = (String) this.attr(AuthenticationAC.USER); 838 if (authuser == null) { 839 Log.trace( 840 "user.filter", 841 "user not defined, cannot filter " + interaction.method); 842 return result; 843 } 844 FieldItem returnedField = ((MethodItem)interaction.method).getReturnedField(); 845 if (!(returnedField instanceof CollectionItem)) { 846 Log.warning("Cannot filter non collection field "+returnedField+ 847 " returned by "+interaction.method); 848 return result; 849 } 850 CollectionItem collection = (CollectionItem)returnedField; 851 if (collection == null) { 852 Log.trace( 853 "user.filter", 854 "no returned collection for " + interaction.method); 855 return result; 856 } 857 Log.trace( 858 "user.filter", 859 "filtering collection " + collection + ", user=" + authuser); 860 MethodItem filter = (MethodItem) collection.getAttribute(FILTER); 861 if (filter == null) { 862 Log.trace("user.filter", "no filter for " + collection); 863 return result; 864 } 865 return filter.invokeStatic( 866 new Object[] { 867 result, 868 interaction.wrappee, 869 collection, 870 getUserFromLogin(authuser)}); 871 } 872 873 public Object invoke(MethodInvocation invocation) throws Throwable { 874 return filterResult((Interaction) invocation); 875 } 876 public Object construct(ConstructorInvocation invocation) 877 throws Throwable { 878 throw new Exception("Wrapper "+this+" does not support construction interception."); 879 } 880 } 881 882 public class OwnerFilterWrapper extends Wrapper { 883 public OwnerFilterWrapper(AspectComponent ac) { 884 super(ac); 885 } 886 887 public OwnerFilterWrapper(AspectComponent ac, String profileName) { 888 super(ac); 889 this.profileName = profileName; 890 } 891 892 String profileName; 893 894 /** 895 * Filters the result of a collection's getter to keep only the 896 * object that are owned by the currently logged user. 897 */ 898 public Object filterResult(Interaction interaction) { 899 Log.trace( 900 "profile.filter", 901 "filterResult(" + interaction.method + ")"); 902 Collection c = (Collection) proceed(interaction); 903 UserAC ac = (UserAC) getAspectComponent(); 904 if (!ac 905 .getProfileFromUser(ac.getCurrentUser()) 906 .getName() 907 .equals(profileName)) { 908 return c; 909 } 910 Log.trace("profile.filter", "filtering..."); 911 Vector result = new Vector(); 912 Iterator it = c.iterator(); 913 while (it.hasNext()) { 914 Object o = it.next(); 915 Object owner = ac.getOwner(o); 916 if (owner == ac.getCurrentUser()) { 917 result.add(o); 918 } 919 } 920 Log.trace("profile.filter", "returning " + result); 921 return result; 922 } 923 public Object invoke(MethodInvocation invocation) throws Throwable { 924 return filterResult((Interaction) invocation); 925 } 926 public Object construct(ConstructorInvocation invocation) 927 throws Throwable { 928 throw new Exception("Wrapper "+this+" does not support construction interception."); 929 } 930 } 931 932 /** 933 * <p>Returns the owner of an object.</p> 934 * 935 * <p>The owner of an object is defined as the value of a field 936 * whose type is the type defined by <code>setUserClass</code> 937 * 938 * @param object the object 939 * @return the owner of the object, or null if the object does not 940 * have a owner. 941 */ 942 public Object getOwner(Object object) { 943 ClassItem classItem = ClassRepository.get().getClass(object); 944 FieldItem[] fields = classItem.getFields(); 945 for (int i = 0; i < fields.length; i++) { 946 if (fields[i].getTypeItem() == userClass) { 947 return fields[i].getThroughAccessor(object); 948 } 949 } 950 return null; 951 } 952 953 public String[] getDefaultConfigs() { 954 return new String[] { "org/objectweb/jac/aspects/user/user.acc" }; 955 } 956 957 /** 958 * Display the profiles. 959 * 960 * <p>This method can be used as a menu callback by applications. 961 */ 962 public static void viewProfiles(DisplayContext context, String panelID) { 963 org.objectweb.jac.aspects.gui.Actions.viewObject( 964 context, 965 "usermanager#0", 966 panelID); 967 } 968 969 public static UserManager getProfiles() { 970 return (UserManager) NameRepository.get().getObject("usermanager#0"); 971 } 972 973 } 974