1   package com.atlassian.user.impl.osuser;
2   
3   import com.atlassian.user.*;
4   import com.atlassian.user.Entity;
5   import com.atlassian.user.User;
6   import com.atlassian.user.Group;
7   import com.atlassian.user.impl.RepositoryException;
8   import com.atlassian.user.repository.RepositoryIdentifier;
9   import com.atlassian.user.search.EntityNameAlphaComparator;
10  import com.atlassian.user.search.page.DefaultPager;
11  import com.atlassian.user.search.page.Pager;
12  import com.atlassian.user.util.Assert;
13  import com.opensymphony.user.*;
14  import com.opensymphony.user.provider.AccessProvider;
15  import org.apache.log4j.Logger;
16  
17  import java.text.Collator;
18  import java.util.*;
19  
20  /**
21   * An adaptor class for {@link com.opensymphony.user.provider.AccessProvider} and some of the higher level operations of
22   * {@link com.opensymphony.user.UserManager}
23   * <p/>
24   * The rule is to use the credentialsProvider and/or profileProvider (for propertySets) for most things. Store()
25   * operations must be called on the entity itself.
26   */
27  public class OSUGroupManager extends OSUEntityManager implements GroupManager
28  {
29      protected final Logger log = Logger.getLogger(this.getClass());
30  
31      private final OSUAccessor osuserAccessor;
32      private final GenericAccessProviderWrapper accessProvider;
33  
34      public OSUGroupManager(RepositoryIdentifier repository, OSUAccessor accessor)
35      {
36          super(repository);
37          this.osuserAccessor = accessor;
38          this.accessProvider = new GenericAccessProviderWrapper(accessor.getAccessProvider());
39      }
40  
41      public Pager<Group> getGroups()
42      {
43          SortedSet<Group> atlassianGroups = getGroupsFromAccessProvider();
44          return new DefaultPager<Group>(atlassianGroups);
45      }
46  
47      private SortedSet<Group> getGroupsFromAccessProvider()
48      {
49          SortedSet<Group> atlassianGroups = new TreeSet<Group>(new EntityNameAlphaComparator());
50          for (Object o : accessProvider.list())
51          {
52              String groupName = (String) o;
53              Group atlassianGroup = getGroup(groupName);
54  
55              if (atlassianGroup != null)
56                  atlassianGroups.add(atlassianGroup);
57          }
58          return atlassianGroups;
59      }
60  
61      public Group createGroup(String groupName) throws EntityException
62      {
63          Group group = null;
64  
65          if (accessProvider.handles(groupName))
66              throw new com.atlassian.user.impl.DuplicateEntityException(
67                  "Group named [" + groupName + "] already exists in accessProvider ["
68                      + accessProvider.toString());
69  
70          /**
71           * Whenever a load(entityName) is called on a UserProvider the result
72           * is, in fact, a reconstructed object, as below.
73           *
74           * This should obviously be cached.
75           */
76          if (accessProvider.create(groupName))
77              group = new OSUGroup(new com.opensymphony.user.Group(groupName, osuserAccessor));
78  
79          return group;
80      }
81  
82      public void removeGroup(Group group) throws EntityException, IllegalArgumentException
83      {
84          if (group == null)
85              throw new IllegalArgumentException("Group is null.");
86          else if (!(group instanceof OSUGroup))
87              throw new IllegalArgumentException("User is not a OSUGroup [" + group.getClass().getName());
88  
89          Group groupToRemove = getGroup(group.getName());
90  
91          List<String> users = accessProvider.listUsersInGroup(groupToRemove.getName());
92          users = new ArrayList<String>(users);
93  
94          for (String username : users)
95          {
96              accessProvider.removeFromGroup(username, groupToRemove.getName());
97          }
98  
99          accessProvider.remove(group.getName());
100     }
101 
102     public void addMembership(Group group, User user)
103     {
104         if (group == null || getGroup(group.getName()) == null)
105             throw new IllegalArgumentException(
106                 "Cannot add membership for unknown group: [" + (group == null ? "null" : group.getName()) + "]");
107 
108         accessProvider.addToGroup(user.getName(), group.getName());
109     }
110 
111     public boolean hasMembership(Group group, User user)
112     {
113         /**
114          * if the group is not an OpenSymphony wrapper, this manager will not have membership information
115          * for the user.
116          */
117         if (!(group instanceof OSUGroup))
118             return false;
119 
120         return accessProvider.inGroup(user.getName(), group.getName());
121     }
122 
123     public void removeMembership(Group group, User user)
124     {
125         if (group == null || getGroup(group.getName()) == null)
126             throw new IllegalArgumentException("Can't remove membership for unknown group: [" +
127                 (group == null ? "null" : group.getName()) + "]");
128         accessProvider.removeFromGroup(user.getName(), group.getName());
129     }
130 
131     public boolean isReadOnly(Group group) throws EntityException
132     {
133         return !(accessProvider.handles(group.getName()));
134     }
135 
136     public boolean supportsExternalMembership() throws EntityException
137     {
138         return false;
139     }
140 
141     public Pager<String> getMemberNames(Group group) throws EntityException
142     {
143         if (!(group instanceof OSUGroup))
144             return DefaultPager.emptyPager();
145 
146         List<String> memberNames = new ArrayList<String>(accessProvider.listUsersInGroup(group.getName()));
147         memberNames.removeAll(Arrays.asList(new Object[]{ null })); // remove all nulls
148         Collections.sort(memberNames, Collator.getInstance());
149 
150         return new DefaultPager<String>(memberNames);
151     }
152 
153     public Pager<String> getLocalMemberNames(Group group) throws EntityException
154     {
155         if (!(group instanceof OSUGroup))
156             return DefaultPager.emptyPager();
157 
158         List<String> memberNames = new ArrayList<String>(accessProvider.listUsersInGroup(group.getName()));
159         Collections.sort(memberNames, Collator.getInstance());
160         return new DefaultPager<String>(memberNames);
161     }
162 
163     public Pager<String> getExternalMemberNames(Group group) throws EntityException
164     {
165         throw new UnsupportedOperationException("External membership is not supported.");
166     }
167 
168     public void saveGroup(Group group) throws EntityException
169     {
170         if (!(accessProvider.handles(group.getName())))
171             return;
172 
173         com.opensymphony.user.Group g = new com.opensymphony.user.Group(group.getName(), osuserAccessor);
174 
175         try
176         {
177             g.store();
178         }
179         catch (ImmutableException e)
180         {
181             throw new RepositoryException(e);
182         }
183     }
184 
185     public Group getGroup(String groupName)
186     {
187         if (!accessProvider.handles(groupName))
188             return null;
189 
190         com.opensymphony.user.Group osgroup = new com.opensymphony.user.Group(groupName, osuserAccessor);
191         return new OSUGroup(osgroup);
192     }
193 
194     public Pager<Group> getGroups(User user) throws RepositoryException
195     {
196         Assert.notNull(user, "User must not be null");
197         if (!osuserAccessor.getCredentialsProvider().handles(user.getName()))
198             return DefaultPager.emptyPager();
199 
200         final SortedSet<Group> groups = new TreeSet<Group>(new EntityNameAlphaComparator());
201 
202         Collection<String> groupNames = accessProvider.listGroupsContainingUser(user.getName());
203         for (String groupName : groupNames)
204         {
205             groups.add(getGroup(groupName));
206         }
207 
208         return new DefaultPager<Group>(groups);
209     }
210 
211     public List<Group> getWritableGroups()
212     {
213         return new ArrayList<Group>(getGroupsFromAccessProvider());
214     }
215 
216     /**
217      * @return the {@link com.atlassian.user.repository.RepositoryIdentifier} which is managed by this instance.
218      */
219     public RepositoryIdentifier getIdentifier()
220     {
221         return repository;
222     }
223 
224     public RepositoryIdentifier getRepository(Entity entity) throws EntityException
225     {
226         if (getGroup(entity.getName()) != null)
227             return repository;
228 
229         return null;
230     }
231 
232     /**
233      * @return mutability is hard-coded within each {@link com.opensymphony.user.provider.UserProvider} then applied to
234      *         the entity produced (here a {@link com.opensymphony.user.Group}).
235      *         <p/>
236      *         All providers in the base OSUUser package are hardcoded to be mutable. This implementation returns the
237      *         mutability value of the first group returned by {@link com.opensymphony.user.provider.UserProvider#list()}.
238      *         <p/>
239      *         If there are no groups it returns true.
240      *         <p/>
241      *         Thus, if you want immutable groups you should override this method or ensure that there is at least one
242      *         entity which is handled by the provider. All groups should have the same mutability value.
243      */
244     public boolean isCreative()
245     {
246         List groupNames = accessProvider.list();
247         if (groupNames.isEmpty())
248             return true;
249 
250         String groupName = (String) groupNames.get(0);
251         return new com.opensymphony.user.Group(groupName, osuserAccessor).isMutable();
252     }
253 
254     @SuppressWarnings("unchecked")
255     private static class GenericAccessProviderWrapper
256     {
257         private final AccessProvider provider;
258 
259         public GenericAccessProviderWrapper(AccessProvider provider)
260         {
261             this.provider = provider;
262         }
263 
264         public boolean addToGroup(String s, String s1)
265         {
266             return provider.addToGroup(s, s1);
267         }
268 
269         public boolean inGroup(String s, String s1)
270         {
271             return provider.inGroup(s, s1);
272         }
273 
274         public List<String> listGroupsContainingUser(String s)
275         {
276             return provider.listGroupsContainingUser(s);
277         }
278 
279         public List<String> listUsersInGroup(String s)
280         {
281             return provider.listUsersInGroup(s);
282         }
283 
284         public boolean removeFromGroup(String s, String s1)
285         {
286             return provider.removeFromGroup(s, s1);
287         }
288 
289         public boolean create(String s)
290         {
291             return provider.create(s);
292         }
293 
294         public void flushCaches()
295         {
296             provider.flushCaches();
297         }
298 
299         public boolean handles(String s)
300         {
301             return provider.handles(s);
302         }
303 
304         public boolean init(Properties properties)
305         {
306             return provider.init(properties);
307         }
308 
309         public List list()
310         {
311             return provider.list();
312         }
313 
314         public boolean load(String s, com.opensymphony.user.Entity.Accessor accessor)
315         {
316             return provider.load(s, accessor);
317         }
318 
319         public boolean remove(String s)
320         {
321             return provider.remove(s);
322         }
323 
324         public boolean store(String s, com.opensymphony.user.Entity.Accessor accessor)
325         {
326             return provider.store(s, accessor);
327         }
328     }
329 }