Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
TextConfigurationRealm |
|
| 2.923076923076923;2.923 |
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 | 31 | super(); |
55 | 31 | } |
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 | 16 | super.onInit(); |
66 | 16 | processDefinitions(); |
67 | 16 | } |
68 | ||
69 | public String getUserDefinitions() { | |
70 | 18 | 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 | 7 | this.userDefinitions = userDefinitions; |
91 | 7 | } |
92 | ||
93 | public String getRoleDefinitions() { | |
94 | 18 | 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 (") to avoid parsing errors (e.g. | |
110 | * <code>"printer:5thFloor:print,info"</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 | 7 | this.roleDefinitions = roleDefinitions; |
121 | 7 | } |
122 | ||
123 | protected void processDefinitions() { | |
124 | try { | |
125 | 17 | processRoleDefinitions(); |
126 | 17 | processUserDefinitions(); |
127 | 0 | } catch (ParseException e) { |
128 | 0 | String msg = "Unable to parse user and/or role definitions."; |
129 | 0 | throw new ConfigurationException(msg, e); |
130 | 17 | } |
131 | 17 | } |
132 | ||
133 | protected void processRoleDefinitions() throws ParseException { | |
134 | 18 | String roleDefinitions = getRoleDefinitions(); |
135 | 18 | if (roleDefinitions == null) { |
136 | 11 | return; |
137 | } | |
138 | 7 | Map<String, String> roleDefs = toMap(toLines(roleDefinitions)); |
139 | 7 | processRoleDefinitions(roleDefs); |
140 | 7 | } |
141 | ||
142 | protected void processRoleDefinitions(Map<String, String> roleDefs) { | |
143 | 11 | if (roleDefs == null || roleDefs.isEmpty()) { |
144 | 0 | return; |
145 | } | |
146 | 11 | for (String rolename : roleDefs.keySet()) { |
147 | 17 | String value = roleDefs.get(rolename); |
148 | ||
149 | 17 | SimpleRole role = getRole(rolename); |
150 | 17 | if (role == null) { |
151 | 17 | role = new SimpleRole(rolename); |
152 | 17 | add(role); |
153 | } | |
154 | ||
155 | 17 | Set<Permission> permissions = PermissionUtils.resolveDelimitedPermissions(value, getPermissionResolver()); |
156 | 17 | role.setPermissions(permissions); |
157 | 17 | } |
158 | 11 | } |
159 | ||
160 | protected void processUserDefinitions() throws ParseException { | |
161 | 18 | String userDefinitions = getUserDefinitions(); |
162 | 18 | if (userDefinitions == null) { |
163 | 11 | return; |
164 | } | |
165 | ||
166 | 7 | Map<String, String> userDefs = toMap(toLines(userDefinitions)); |
167 | ||
168 | 7 | processUserDefinitions(userDefs); |
169 | 7 | } |
170 | ||
171 | protected void processUserDefinitions(Map<String, String> userDefs) { | |
172 | 26 | if (userDefs == null || userDefs.isEmpty()) { |
173 | 0 | return; |
174 | } | |
175 | 26 | for (String username : userDefs.keySet()) { |
176 | ||
177 | 37 | String value = userDefs.get(username); |
178 | ||
179 | 37 | String[] passwordAndRolesArray = StringUtils.split(value); |
180 | ||
181 | 37 | String password = passwordAndRolesArray[0]; |
182 | ||
183 | 37 | SimpleAccount account = getUser(username); |
184 | 37 | if (account == null) { |
185 | 37 | account = new SimpleAccount(username, password, getName()); |
186 | 37 | add(account); |
187 | } | |
188 | 37 | account.setCredentials(password); |
189 | ||
190 | 37 | if (passwordAndRolesArray.length > 1) { |
191 | 64 | for (int i = 1; i < passwordAndRolesArray.length; i++) { |
192 | 38 | String rolename = passwordAndRolesArray[i]; |
193 | 38 | account.addRole(rolename); |
194 | ||
195 | 38 | SimpleRole role = getRole(rolename); |
196 | 38 | if (role != null) { |
197 | 24 | account.addObjectPermissions(role.getPermissions()); |
198 | } | |
199 | } | |
200 | } else { | |
201 | 11 | account.setRoles(null); |
202 | } | |
203 | 37 | } |
204 | 26 | } |
205 | ||
206 | protected static Set<String> toLines(String s) { | |
207 | 14 | LinkedHashSet<String> set = new LinkedHashSet<String>(); |
208 | 14 | Scanner scanner = new Scanner(s); |
209 | 40 | while (scanner.hasNextLine()) { |
210 | 26 | set.add(scanner.nextLine()); |
211 | } | |
212 | 14 | return set; |
213 | } | |
214 | ||
215 | protected static Map<String, String> toMap(Collection<String> keyValuePairs) throws ParseException { | |
216 | 14 | if (keyValuePairs == null || keyValuePairs.isEmpty()) { |
217 | 0 | return null; |
218 | } | |
219 | ||
220 | 14 | Map<String, String> pairs = new HashMap<String, String>(); |
221 | 14 | for (String pairString : keyValuePairs) { |
222 | 26 | String[] pair = StringUtils.splitKeyValue(pairString); |
223 | 26 | if (pair != null) { |
224 | 26 | pairs.put(pair[0].trim(), pair[1].trim()); |
225 | } | |
226 | 26 | } |
227 | ||
228 | 14 | return pairs; |
229 | } | |
230 | } |