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.authc.SimpleAccount;
22  import org.apache.shiro.authz.Permission;
23  import org.apache.shiro.authz.SimpleRole;
24  import org.apache.shiro.config.ConfigurationException;
25  import org.apache.shiro.realm.SimpleAccountRealm;
26  import org.apache.shiro.util.PermissionUtils;
27  import org.apache.shiro.util.StringUtils;
28  
29  import java.text.ParseException;
30  import java.util.*;
31  
32  
33  /**
34   * A SimpleAccountRealm that enables text-based configuration of the initial User, Role, and Permission objects
35   * created at startup.
36   * <p/>
37   * Each User account definition specifies the username, password, and roles for a user.  Each Role definition
38   * specifies a name and an optional collection of assigned Permissions.  Users can be assigned Roles, and Roles can be
39   * assigned Permissions.  By transitive association, each User 'has' all of their Role's Permissions.
40   * <p/>
41   * User and user-to-role definitions are specified via the {@link #setUserDefinitions} method and
42   * Role-to-permission definitions are specified via the {@link #setRoleDefinitions} method.
43   *
44   * @since 0.9
45   */
46  public class TextConfigurationRealm extends SimpleAccountRealm {
47  
48      //TODO - complete JavaDoc
49  
50      private volatile String userDefinitions;
51      private volatile String roleDefinitions;
52  
53      public TextConfigurationRealm() {
54          super();
55      }
56  
57      /**
58       * Will call 'processDefinitions' on startup.
59       *
60       * @since 1.2
61       * @see <a href="https://issues.apache.org/jira/browse/SHIRO-223">SHIRO-223</a>
62       */
63      @Override
64      protected void onInit() {
65          super.onInit();
66          processDefinitions();
67      }
68  
69      public String getUserDefinitions() {
70          return userDefinitions;
71      }
72  
73      /**
74       * <p>Sets a newline (\n) delimited String that defines user-to-password-and-role(s) key/value pairs according
75       * to the following format:
76       * <p/>
77       * <p><code><em>username</em> = <em>password</em>, role1, role2,...</code></p>
78       * <p/>
79       * <p>Here are some examples of what these lines might look like:</p>
80       * <p/>
81       * <p><code>root = <em>reallyHardToGuessPassword</em>, administrator<br/>
82       * jsmith = <em>jsmithsPassword</em>, manager, engineer, employee<br/>
83       * abrown = <em>abrownsPassword</em>, qa, employee<br/>
84       * djones = <em>djonesPassword</em>, qa, contractor<br/>
85       * guest = <em>guestPassword</em></code></p>
86       *
87       * @param userDefinitions the user definitions to be parsed and converted to Map.Entry elements
88       */
89      public void setUserDefinitions(String userDefinitions) {
90          this.userDefinitions = userDefinitions;
91      }
92  
93      public String getRoleDefinitions() {
94          return roleDefinitions;
95      }
96  
97      /**
98       * Sets a newline (\n) delimited String that defines role-to-permission definitions.
99       * <p/>
100      * <p>Each line within the string must define a role-to-permission(s) key/value mapping with the
101      * equals character signifies the key/value separation, like so:</p>
102      * <p/>
103      * <p><code><em>rolename</em> = <em>permissionDefinition1</em>, <em>permissionDefinition2</em>, ...</code></p>
104      * <p/>
105      * <p>where <em>permissionDefinition</em> is an arbitrary String, but must people will want to use
106      * Strings that conform to the {@link org.apache.shiro.authz.permission.WildcardPermission WildcardPermission}
107      * format for ease of use and flexibility.  Note that if an individual <em>permissionDefnition</em> needs to
108      * be internally comma-delimited (e.g. <code>printer:5thFloor:print,info</code>), you will need to surround that
109      * definition with double quotes (&quot;) to avoid parsing errors (e.g.
110      * <code>&quot;printer:5thFloor:print,info&quot;</code>).
111      * <p/>
112      * <p><b>NOTE:</b> if you have roles that don't require permission associations, don't include them in this
113      * definition - just defining the role name in the {@link #setUserDefinitions(String) userDefinitions} is
114      * enough to create the role if it does not yet exist.  This property is really only for configuring realms that
115      * have one or more assigned Permission.
116      *
117      * @param roleDefinitions the role definitions to be parsed at initialization
118      */
119     public void setRoleDefinitions(String roleDefinitions) {
120         this.roleDefinitions = roleDefinitions;
121     }
122 
123     protected void processDefinitions() {
124         try {
125             processRoleDefinitions();
126             processUserDefinitions();
127         } catch (ParseException e) {
128             String msg = "Unable to parse user and/or role definitions.";
129             throw new ConfigurationException(msg, e);
130         }
131     }
132 
133     protected void processRoleDefinitions() throws ParseException {
134         String roleDefinitions = getRoleDefinitions();
135         if (roleDefinitions == null) {
136             return;
137         }
138         Map<String, String> roleDefs = toMap(toLines(roleDefinitions));
139         processRoleDefinitions(roleDefs);
140     }
141 
142     protected void processRoleDefinitions(Map<String, String> roleDefs) {
143         if (roleDefs == null || roleDefs.isEmpty()) {
144             return;
145         }
146         for (String rolename : roleDefs.keySet()) {
147             String value = roleDefs.get(rolename);
148 
149             SimpleRole role = getRole(rolename);
150             if (role == null) {
151                 role = new SimpleRole(rolename);
152                 add(role);
153             }
154 
155             Set<Permission> permissions = PermissionUtils.resolveDelimitedPermissions(value, getPermissionResolver());
156             role.setPermissions(permissions);
157         }
158     }
159 
160     protected void processUserDefinitions() throws ParseException {
161         String userDefinitions = getUserDefinitions();
162         if (userDefinitions == null) {
163             return;
164         }
165 
166         Map<String, String> userDefs = toMap(toLines(userDefinitions));
167 
168         processUserDefinitions(userDefs);
169     }
170 
171     protected void processUserDefinitions(Map<String, String> userDefs) {
172         if (userDefs == null || userDefs.isEmpty()) {
173             return;
174         }
175         for (String username : userDefs.keySet()) {
176 
177             String value = userDefs.get(username);
178 
179             String[] passwordAndRolesArray = StringUtils.split(value);
180 
181             String password = passwordAndRolesArray[0];
182 
183             SimpleAccount account = getUser(username);
184             if (account == null) {
185                 account = new SimpleAccount(username, password, getName());
186                 add(account);
187             }
188             account.setCredentials(password);
189 
190             if (passwordAndRolesArray.length > 1) {
191                 for (int i = 1; i < passwordAndRolesArray.length; i++) {
192                     String rolename = passwordAndRolesArray[i];
193                     account.addRole(rolename);
194 
195                     SimpleRole role = getRole(rolename);
196                     if (role != null) {
197                         account.addObjectPermissions(role.getPermissions());
198                     }
199                 }
200             } else {
201                 account.setRoles(null);
202             }
203         }
204     }
205 
206     protected static Set<String> toLines(String s) {
207         LinkedHashSet<String> set = new LinkedHashSet<String>();
208         Scanner scanner = new Scanner(s);
209         while (scanner.hasNextLine()) {
210             set.add(scanner.nextLine());
211         }
212         return set;
213     }
214 
215     protected static Map<String, String> toMap(Collection<String> keyValuePairs) throws ParseException {
216         if (keyValuePairs == null || keyValuePairs.isEmpty()) {
217             return null;
218         }
219 
220         Map<String, String> pairs = new HashMap<String, String>();
221         for (String pairString : keyValuePairs) {
222             String[] pair = StringUtils.splitKeyValue(pairString);
223             if (pair != null) {
224                 pairs.put(pair[0].trim(), pair[1].trim());
225             }
226         }
227 
228         return pairs;
229     }
230 }