1   /* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
2    *
3    * Licensed under the Apache License, Version 2.0 (the "License");
4    * you may not use this file except in compliance with the License.
5    * You may obtain a copy of the License at
6    *
7    *     http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  
16  package org.acegisecurity.acl.basic;
17  
18  import junit.framework.TestCase;
19  
20  import org.acegisecurity.Authentication;
21  import org.acegisecurity.PopulatedDatabase;
22  
23  import org.acegisecurity.acl.AclEntry;
24  import org.acegisecurity.acl.basic.cache.BasicAclEntryHolder;
25  import org.acegisecurity.acl.basic.cache.NullAclEntryCache;
26  import org.acegisecurity.acl.basic.jdbc.JdbcDaoImpl;
27  
28  import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
29  
30  import java.util.HashMap;
31  import java.util.Map;
32  
33  
34  /**
35   * Tests {@link BasicAclProvider}.
36   *
37   * @author Ben Alex
38   * @version $Id: BasicAclProviderTests.java 1496 2006-05-23 13:38:33Z benalex $
39   */
40  public class BasicAclProviderTests extends TestCase {
41      //~ Static fields/initializers =====================================================================================
42  
43      public static final String OBJECT_IDENTITY = "org.acegisecurity.acl.DomainObject";
44  
45      //~ Constructors ===================================================================================================
46  
47      public BasicAclProviderTests() {
48          super();
49      }
50  
51      public BasicAclProviderTests(String arg0) {
52          super(arg0);
53      }
54  
55      //~ Methods ========================================================================================================
56  
57      public static void main(String[] args) {
58          junit.textui.TestRunner.run(BasicAclProviderTests.class);
59      }
60  
61      private JdbcDaoImpl makePopulatedJdbcDao() throws Exception {
62          JdbcDaoImpl dao = new JdbcDaoImpl();
63          dao.setDataSource(PopulatedDatabase.getDataSource());
64          dao.afterPropertiesSet();
65  
66          return dao;
67      }
68  
69      public final void setUp() throws Exception {
70          super.setUp();
71      }
72  
73      public void testCachingUsedProperly() throws Exception {
74          BasicAclProvider provider = new BasicAclProvider();
75          provider.setBasicAclDao(makePopulatedJdbcDao());
76  
77          MockCache cache = new MockCache();
78          provider.setBasicAclEntryCache(cache);
79  
80          assertEquals(0, cache.getGets());
81          assertEquals(0, cache.getGetsHits());
82          assertEquals(0, cache.getPuts());
83          assertEquals(0, cache.getBackingMap().size());
84  
85          Object object = new MockDomain(1); // has no parents
86          provider.getAcls(object);
87  
88          assertEquals(1, cache.getGets());
89          assertEquals(0, cache.getGetsHits());
90          assertEquals(1, cache.getPuts());
91          assertEquals(1, cache.getBackingMap().size());
92  
93          provider.getAcls(object);
94  
95          assertEquals(2, cache.getGets());
96          assertEquals(1, cache.getGetsHits());
97          assertEquals(1, cache.getPuts());
98          assertEquals(1, cache.getBackingMap().size());
99  
100         object = new MockDomain(1000); // does not exist
101 
102         provider.getAcls(object);
103 
104         assertEquals(3, cache.getGets());
105         assertEquals(1, cache.getGetsHits());
106         assertEquals(2, cache.getPuts());
107         assertEquals(2, cache.getBackingMap().size());
108 
109         provider.getAcls(object);
110 
111         assertEquals(4, cache.getGets());
112         assertEquals(2, cache.getGetsHits());
113         assertEquals(2, cache.getPuts());
114         assertEquals(2, cache.getBackingMap().size());
115 
116         provider.getAcls(object);
117 
118         assertEquals(5, cache.getGets());
119         assertEquals(3, cache.getGetsHits());
120         assertEquals(2, cache.getPuts());
121         assertEquals(2, cache.getBackingMap().size());
122     }
123 
124     public void testExceptionThrownIfUnsupportedObjectIsSubmitted()
125         throws Exception {
126         BasicAclProvider provider = new BasicAclProvider();
127         provider.setBasicAclDao(makePopulatedJdbcDao());
128 
129         // this one should NOT be supported, as it has no getId() method
130         assertFalse(provider.supports(new Integer(34)));
131 
132         // try anyway
133         try {
134             provider.getAcls(new Integer(34));
135             fail("Should have thrown IllegalArgumentException");
136         } catch (IllegalArgumentException expected) {
137             assertTrue(true);
138         }
139     }
140 
141     public void testGetAclsForInstanceNotFound() throws Exception {
142         BasicAclProvider provider = new BasicAclProvider();
143         provider.setBasicAclDao(makePopulatedJdbcDao());
144 
145         Object object = new MockDomain(546464646);
146         AclEntry[] acls = provider.getAcls(object);
147         assertNull(acls);
148     }
149 
150     public void testGetAclsForInstanceWithParentLevels()
151         throws Exception {
152         BasicAclProvider provider = new BasicAclProvider();
153         provider.setBasicAclDao(makePopulatedJdbcDao());
154 
155         Object object = new MockDomain(6);
156         AclEntry[] acls = provider.getAcls(object);
157         assertEquals(2, acls.length);
158 
159         assertEquals("scott", ((BasicAclEntry) acls[0]).getRecipient());
160         assertEquals(1, ((BasicAclEntry) acls[0]).getMask());
161         assertEquals("ROLE_SUPERVISOR", ((BasicAclEntry) acls[1]).getRecipient());
162     }
163 
164     public void testGetAclsForInstanceWithParentLevelsButNoDirectAclsAgainstInstance()
165         throws Exception {
166         BasicAclProvider provider = new BasicAclProvider();
167         provider.setBasicAclDao(makePopulatedJdbcDao());
168 
169         Object object = new MockDomain(5);
170         AclEntry[] acls = provider.getAcls(object);
171 
172         assertEquals(3, acls.length);
173 
174         assertEquals("scott", ((BasicAclEntry) acls[0]).getRecipient());
175         assertEquals(14, ((BasicAclEntry) acls[0]).getMask());
176         assertEquals("ROLE_SUPERVISOR", ((BasicAclEntry) acls[1]).getRecipient());
177         assertEquals(1, ((BasicAclEntry) acls[1]).getMask());
178         assertEquals(JdbcDaoImpl.RECIPIENT_USED_FOR_INHERITENCE_MARKER, ((BasicAclEntry) acls[2]).getRecipient());
179     }
180 
181     public void testGetAclsWithAuthentication() throws Exception {
182         BasicAclProvider provider = new BasicAclProvider();
183         provider.setBasicAclDao(makePopulatedJdbcDao());
184 
185         Authentication scott = new UsernamePasswordAuthenticationToken("scott", "unused");
186 
187         Object object = new MockDomain(6);
188         AclEntry[] acls = provider.getAcls(object, scott);
189 
190         assertEquals(1, acls.length);
191         assertEquals("scott", ((BasicAclEntry) acls[0]).getRecipient());
192     }
193 
194     public void testGettersSetters() {
195         BasicAclProvider provider = new BasicAclProvider();
196         assertEquals(NullAclEntryCache.class, provider.getBasicAclEntryCache().getClass());
197         assertEquals(NamedEntityObjectIdentity.class, provider.getDefaultAclObjectIdentityClass());
198         assertEquals(GrantedAuthorityEffectiveAclsResolver.class, provider.getEffectiveAclsResolver().getClass());
199 
200         provider.setBasicAclEntryCache(null);
201         assertNull(provider.getBasicAclEntryCache());
202 
203         provider.setDefaultAclObjectIdentityClass(null);
204         assertNull(provider.getDefaultAclObjectIdentityClass());
205 
206         provider.setEffectiveAclsResolver(null);
207         assertNull(provider.getEffectiveAclsResolver());
208 
209         provider.setBasicAclDao(new MockDao());
210         assertNotNull(provider.getBasicAclDao());
211 
212         assertNull(provider.getRestrictSupportToClass());
213         provider.setRestrictSupportToClass(SomeDomain.class);
214         assertEquals(SomeDomain.class, provider.getRestrictSupportToClass());
215     }
216 
217     public void testStartupFailsIfNullAclDao() throws Exception {
218         BasicAclProvider provider = new BasicAclProvider();
219 
220         try {
221             provider.afterPropertiesSet();
222             fail("Should have thrown IllegalArgumentException");
223         } catch (IllegalArgumentException expected) {
224             assertTrue(true);
225         }
226     }
227 
228     public void testStartupFailsIfNullEffectiveAclsResolver()
229         throws Exception {
230         BasicAclProvider provider = new BasicAclProvider();
231         provider.setBasicAclDao(makePopulatedJdbcDao());
232 
233         provider.setEffectiveAclsResolver(null);
234 
235         try {
236             provider.afterPropertiesSet();
237             fail("Should have thrown IllegalArgumentException");
238         } catch (IllegalArgumentException expected) {
239             assertTrue(true);
240         }
241     }
242 
243     public void testStartupFailsIfNullEntryCache() throws Exception {
244         BasicAclProvider provider = new BasicAclProvider();
245         provider.setBasicAclDao(makePopulatedJdbcDao());
246 
247         provider.setBasicAclEntryCache(null);
248 
249         try {
250             provider.afterPropertiesSet();
251             fail("Should have thrown IllegalArgumentException");
252         } catch (IllegalArgumentException expected) {
253             assertTrue(true);
254         }
255     }
256 
257     public void testStartupFailsIfProblemWithAclObjectIdentityClass()
258         throws Exception {
259         BasicAclProvider provider = new BasicAclProvider();
260         provider.setBasicAclDao(makePopulatedJdbcDao());
261 
262         // check nulls rejected
263         provider.setDefaultAclObjectIdentityClass(null);
264 
265         try {
266             provider.afterPropertiesSet();
267             fail("Should have thrown IllegalArgumentException");
268         } catch (IllegalArgumentException expected) {
269             assertTrue(true);
270         }
271 
272         // check non-AclObjectIdentity classes are also rejected
273         provider.setDefaultAclObjectIdentityClass(String.class);
274 
275         try {
276             provider.afterPropertiesSet();
277             fail("Should have thrown IllegalArgumentException");
278         } catch (IllegalArgumentException expected) {
279             assertTrue(true);
280         }
281 
282         // check AclObjectIdentity class without constructor accepting a
283         // domain object is also rejected
284         provider.setDefaultAclObjectIdentityClass(MockAclObjectIdentity.class);
285 
286         try {
287             provider.afterPropertiesSet();
288             fail("Should have thrown IllegalArgumentException");
289         } catch (IllegalArgumentException expected) {
290             assertEquals("defaultAclObjectIdentityClass must provide a constructor that accepts the domain object instance!",
291                 expected.getMessage());
292         }
293     }
294 
295     public void testSupports() throws Exception {
296         BasicAclProvider provider = new BasicAclProvider();
297         provider.setBasicAclDao(makePopulatedJdbcDao());
298 
299         // this one should NOT be supported, as it has no getId() method
300         assertFalse(provider.supports(new Integer(34)));
301 
302         // this one SHOULD be supported, as it has a getId() method
303         assertTrue(provider.supports(new SomeDomain()));
304 
305         // this one SHOULD be supported, as it implements AclObjectIdentityAware
306         assertTrue(provider.supports(new MockDomain(4)));
307 
308         // now restrict the provider to only respond to SomeDomain.class requests
309         provider.setRestrictSupportToClass(SomeDomain.class);
310         assertEquals(SomeDomain.class, provider.getRestrictSupportToClass());
311 
312         // this one SHOULD be supported, as it has a getId() method AND it meets the restrictSupportToClass criteria
313         assertTrue(provider.supports(new SomeDomain()));
314 
315         // this one should NOT be suported, as whilst it implement AclObjectIdentityAware (as proven earlier in the test), it does NOT meet the restrictSupportToClass criteria
316         assertFalse(provider.supports(new MockDomain(4)));
317     }
318 
319     public void testSupportsReturnsNullIfObjectNull() {
320         BasicAclProvider provider = new BasicAclProvider();
321         assertFalse(provider.supports(new Integer(34)));
322     }
323 
324     //~ Inner Classes ==================================================================================================
325 
326     private class MockCache implements BasicAclEntryCache {
327         private Map map = new HashMap();
328         private int gets = 0;
329         private int getsHits = 0;
330         private int puts = 0;
331 
332         public Map getBackingMap() {
333             return map;
334         }
335 
336         public BasicAclEntry[] getEntriesFromCache(AclObjectIdentity aclObjectIdentity) {
337             gets++;
338 
339             Object result = map.get(aclObjectIdentity);
340 
341             if (result == null) {
342                 return null;
343             }
344 
345             getsHits++;
346 
347             BasicAclEntryHolder holder = (BasicAclEntryHolder) result;
348 
349             return holder.getBasicAclEntries();
350         }
351 
352         public int getGets() {
353             return gets;
354         }
355 
356         public int getGetsHits() {
357             return getsHits;
358         }
359 
360         public int getPuts() {
361             return puts;
362         }
363 
364         public void putEntriesInCache(BasicAclEntry[] basicAclEntry) {
365             puts++;
366 
367             BasicAclEntryHolder holder = new BasicAclEntryHolder(basicAclEntry);
368             map.put(basicAclEntry[0].getAclObjectIdentity(), holder);
369         }
370 
371         public void removeEntriesFromCache(AclObjectIdentity aclObjectIdentity) {}
372     }
373 
374     private class MockDao implements BasicAclDao {
375         public BasicAclEntry[] getAcls(AclObjectIdentity aclObjectIdentity) {
376             return null;
377         }
378     }
379 
380     private class MockDomain implements AclObjectIdentityAware {
381         private int id;
382 
383         public MockDomain(int id) {
384             this.id = id;
385         }
386 
387         public AclObjectIdentity getAclObjectIdentity() {
388             return new NamedEntityObjectIdentity(OBJECT_IDENTITY, new Integer(id).toString());
389         }
390     }
391 }