View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.shiro.realm.text;
20  
21  import org.apache.shiro.authz.AuthorizationException;
22  import org.apache.shiro.subject.PrincipalCollection;
23  import org.apache.shiro.subject.SimplePrincipalCollection;
24  import org.junit.Test;
25  
26  import java.text.ParseException;
27  import java.util.Arrays;
28  
29  import static org.junit.Assert.*;
30  
31  public class TextConfigurationRealmTest {
32  
33      private TestRealm realm;
34  
35      private void setRoles() {
36          StringBuilder roleDefinitions = new StringBuilder()
37                  .append("role1 = role1_permission1\n")
38                  .append("role2 = role2_persission1, role2_permission2\n");
39          realm.setRoleDefinitions(roleDefinitions.toString());
40      }
41  
42      private void setUsers() {
43          StringBuilder userDefinitions = new StringBuilder();
44          for (int i = 1; i < 3; i++) {
45              userDefinitions.append(String.format("user%1$d = user%1$d_password, role1, role2%n", i));
46          }
47          realm.setUserDefinitions(userDefinitions.toString());
48      }
49  
50      private void setUpForReadConfigurationTest() {
51          realm = new TestRealm() {
52              /*
53               * Demonstrates that a lock can't be obtained on the realm by a read thread until after
54               * the lock is released.
55               */
56              public void test(Thread runnable) throws InterruptedException {
57                  // Obtain the realm's locks
58                  USERS_LOCK.writeLock().lock();
59                  try {
60                      ROLES_LOCK.writeLock().lock();
61                      try {
62                          // Any read threads attempting to obtain the realms lock will block
63                          runnable.start();
64                          Thread.sleep(500);
65                          // Process role and user definitions
66                          realm.onInit();
67  
68                      } finally {
69                          ROLES_LOCK.writeLock().unlock();
70                      }
71                  } finally {
72                      USERS_LOCK.writeLock().unlock();
73                  }
74              }
75          };
76          setRoles();
77          setUsers();
78      }
79  
80      /*
81       * Executes a test that attempts to read to read from a realm before it is loaded.
82       */
83      private void executeTest(Runnable runnable) throws InterruptedException {
84          TestThread testThread = new TestThread(runnable);
85          Thread testTask = new Thread(testThread);
86          realm.test(testTask);
87          testTask.join(500);
88          // Check whether any assertion error was thrown by the read thread
89          testThread.test();
90      }
91  
92      /*
93       * Tests that roles and account can't be tested while the realm is being loaded. 
94       */
95      @Test
96      public void testRoleAndUserAccount() throws InterruptedException {
97          setUpForReadConfigurationTest();
98          executeTest(new Runnable() {
99              public void run() {
100                 assertTrue("role not found when it was expected", realm.roleExists("role1"));
101                 assertTrue("user not found when it was expected", realm.accountExists("user1"));
102             }
103         });
104     }
105 
106     /*
107      * Tests that roles can't be read while the realm is being loaded. 
108      */
109     @Test
110     public void testHasRole() throws InterruptedException {
111         setUpForReadConfigurationTest();
112         executeTest(new Runnable() {
113             public void run() {
114                 PrincipalCollection principalCollection = new SimplePrincipalCollection("user1", "realm1");
115                 assertTrue("principal doesn't have role when it should",
116                         realm.hasRole(principalCollection, "role2"));
117                 assertTrue("principal doesn't have all roles when it should",
118                         realm.hasAllRoles(principalCollection, Arrays.asList(new String[]{"role1", "role2"})));
119             }
120         });
121     }
122 
123     /*
124      * Tests that roles can't be checked while the realm is being loaded. 
125      */
126     @Test
127     public void testCheckRole() throws InterruptedException {
128         setUpForReadConfigurationTest();
129         executeTest(new Runnable() {
130             public void run() {
131                 PrincipalCollection principalCollection = new SimplePrincipalCollection("user1", "realm1");
132                 try {
133                     realm.checkRoles(principalCollection, new String[]{"role1", "role2"});
134                 } catch (AuthorizationException ae) {
135                     fail("principal doesn't have all roles when it should");
136                 }
137             }
138         });
139     }
140 
141     /*
142      * Tests that a principal's permissions can't be checked while the realm is being loaded. 
143      */
144     @Test
145     public void testCheckPermission() throws InterruptedException {
146         setUpForReadConfigurationTest();
147         executeTest(new Runnable() {
148             public void run() {
149                 PrincipalCollection principalCollection = new SimplePrincipalCollection("user1", "realm1");
150                 try {
151                     realm.checkPermission(principalCollection, "role1_permission1");
152                     realm.checkPermissions(principalCollection, new String[]{"role1_permission1", "role2_permission2"});
153                 } catch (AuthorizationException ae) {
154                     fail("principal doesn't have permission when it should");
155                 }
156             }
157         });
158     }
159 
160     /*
161      * Tests that a principal's permissions can't be checked while the realm is being loaded. 
162      */
163     @Test
164     public void testIsPermitted() throws InterruptedException {
165         setUpForReadConfigurationTest();
166         executeTest(new Runnable() {
167             public void run() {
168                 PrincipalCollection principalCollection = new SimplePrincipalCollection("user1", "realm1");
169                 assertTrue("permission not permitted when it should be", realm.isPermitted(principalCollection, "role1_permission1"));
170                 assertTrue("permission not permitted when it should be",
171                         realm.isPermittedAll(principalCollection, new String[]{"role1_permission1", "role2_permission2"}));
172             }
173         });
174     }
175 
176     /*
177      * Test that role definitions cannot be updated when a read thread holds the realm's lock.
178      */
179     @Test
180     public void testProcessRoleDefinitions() throws InterruptedException {
181         realm = new TestRealm() {
182             public void test(Thread runnable) throws InterruptedException {
183                 // While the realm's lock is held by this thread role definitions cannot be processed
184                 // Obtain the realm's locks
185                 ROLES_LOCK.writeLock().lock();
186                 try {
187                     runnable.start();
188                     Thread.sleep(500);
189                     // No role until lock is released and role definitions are processed
190                     assertFalse("role exists when it shouldn't", realm.roleExists("role1"));
191                 } finally {
192                     ROLES_LOCK.writeLock().unlock();
193                 }
194             }
195         };
196         // A thread to process new role definitions
197         TestThread testThread = new TestThread(new Runnable() {
198             public void run() {
199                 try {
200                     realm.processRoleDefinitions();
201                 } catch (ParseException e) {
202                     fail("Unable to parse role definitions");
203                 }
204             }
205         });
206         setRoles();
207         Thread testTask = new Thread(testThread);
208         realm.test(testTask);
209         testTask.join(500);
210         assertTrue("role doesn't exist when it should", realm.roleExists("role1"));
211         testThread.test();
212     }
213 
214     /*
215      * Test that user definitions cannot be updated when a read thread holds the realm's lock.
216      */
217     @Test
218     public void testProcessUserDefinitions() throws InterruptedException {
219         realm = new TestRealm() {
220             public void test(Thread runnable) throws InterruptedException {
221                 // While the realm's lock is held by this thread user definitions cannot be processed
222                 // Obtain the realm's locks
223                 USERS_LOCK.writeLock().lock();
224                 try {
225                     runnable.start();
226                     Thread.sleep(500);
227                     // No account until lock is released and user definitions are processed
228                     assertFalse("account exists when it shouldn't", realm.accountExists("user1"));
229                 } finally {
230                     USERS_LOCK.writeLock().unlock();
231                 }
232             }
233         };
234         TestThread testThread = new TestThread(new Runnable() {
235             public void run() {
236                 try {
237                     realm.processUserDefinitions();
238                 } catch (ParseException e) {
239                     fail("Unable to parse user definitions");
240                 }
241             }
242         });
243         setUsers();
244         Thread testTask = new Thread(testThread);
245         realm.test(testTask);
246         testTask.join(500);
247         assertTrue("account doesn't exist when it should", realm.accountExists("user1"));
248         testThread.test();
249     }
250 
251     /*
252      * A Class that captures a thread's assertion error.
253      */
254     private class TestThread implements Runnable {
255         private Runnable test;
256         private volatile AssertionError ae;
257 
258         public TestThread(Runnable test) {
259             this.test = test;
260         }
261 
262         public void run() {
263             try {
264                 test.run();
265             } catch (AssertionError ae) {
266                 this.ae = ae;
267             }
268         }
269 
270         public void test() {
271             if (ae != null)
272                 throw ae;
273         }
274     }
275 
276     /*
277      * Provides an additional method that has access to the realm's lock for mutual exclusion.
278      */
279     private abstract class TestRealm extends TextConfigurationRealm {
280         abstract public void test(Thread runnable) throws InterruptedException;
281     }
282 }