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