001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019 package org.apache.shiro.realm.text;
020
021 import org.apache.shiro.authc.SimpleAccount;
022 import org.apache.shiro.authz.Permission;
023 import org.apache.shiro.authz.SimpleRole;
024 import org.apache.shiro.config.ConfigurationException;
025 import org.apache.shiro.realm.SimpleAccountRealm;
026 import org.apache.shiro.util.PermissionUtils;
027 import org.apache.shiro.util.StringUtils;
028
029 import java.text.ParseException;
030 import java.util.*;
031
032
033 /**
034 * A SimpleAccountRealm that enables text-based configuration of the initial User, Role, and Permission objects
035 * created at startup.
036 * <p/>
037 * Each User account definition specifies the username, password, and roles for a user. Each Role definition
038 * specifies a name and an optional collection of assigned Permissions. Users can be assigned Roles, and Roles can be
039 * assigned Permissions. By transitive association, each User 'has' all of their Role's Permissions.
040 * <p/>
041 * User and user-to-role definitinos are specified via the {@link #setUserDefinitions} method and
042 * Role-to-permission definitions are specified via the {@link #setRoleDefinitions} method.
043 *
044 * @author Les Hazlewood
045 * @since 0.9
046 */
047 public class TextConfigurationRealm extends SimpleAccountRealm {
048
049 //TODO - complete JavaDoc
050
051 private String userDefinitions;
052 private String roleDefinitions;
053
054 public TextConfigurationRealm() {
055 super();
056 }
057
058 public String getUserDefinitions() {
059 return userDefinitions;
060 }
061
062 /**
063 * <p>Sets a newline (\n) delimited String that defines user-to-password-and-role(s) key/value pairs according
064 * to the following format:
065 * <p/>
066 * <p><code><em>username</em> = <em>password</em>, role1, role2,...</code></p>
067 * <p/>
068 * <p>Here are some examples of what these lines might look like:</p>
069 * <p/>
070 * <p><code>root = <em>reallyHardToGuessPassword</em>, administrator<br/>
071 * jsmith = <em>jsmithsPassword</em>, manager, engineer, employee<br/>
072 * abrown = <em>abrownsPassword</em>, qa, employee<br/>
073 * djones = <em>djonesPassword</em>, qa, contractor<br/>
074 * guest = <em>guestPassword</em></code></p>
075 *
076 * @param userDefinitions the user definitions to be parsed and converted to Map.Entry elements
077 */
078 public void setUserDefinitions(String userDefinitions) {
079 this.userDefinitions = userDefinitions;
080 }
081
082 public String getRoleDefinitions() {
083 return roleDefinitions;
084 }
085
086 /**
087 * Sets a newline (\n) delimited String that defines role-to-permission definitions.
088 * <p/>
089 * <p>Each line within the string must define a role-to-permission(s) key/value mapping with the
090 * equals character signifies the key/value separation, like so:</p>
091 * <p/>
092 * <p><code><em>rolename</em> = <em>permissionDefinition1</em>, <em>permissionDefinition2</em>, ...</code></p>
093 * <p/>
094 * <p>where <em>permissionDefinition</em> is an arbitrary String, but must people will want to use
095 * Strings that conform to the {@link org.apache.shiro.authz.permission.WildcardPermission WildcardPermission}
096 * format for ease of use and flexibility. Note that if an individual <em>permissionDefnition</em> needs to
097 * be internally comma-delimited (e.g. <code>printer:5thFloor:print,info</code>), you will need to surround that
098 * definition with double quotes (") to avoid parsing errors (e.g.
099 * <code>"printer:5thFloor:print,info"</code>).
100 * <p/>
101 * <p><b>NOTE:</b> if you have roles that don't require permission associations, don't include them in this
102 * definition - just defining the role name in the {@link #setUserDefinitions(String) userDefinitions} is
103 * enough to create the role if it does not yet exist. This property is really only for configuring realms that
104 * have one or more assigned Permission.
105 *
106 * @param roleDefinitions the role definitions to be parsed at initialization
107 */
108 public void setRoleDefinitions(String roleDefinitions) {
109 this.roleDefinitions = roleDefinitions;
110 }
111
112 protected void processDefinitions() {
113 try {
114 processRoleDefinitions();
115 processUserDefinitions();
116 } catch (ParseException e) {
117 String msg = "Unable to parse user and/or role definitions.";
118 throw new ConfigurationException(msg, e);
119 }
120 }
121
122 protected void processRoleDefinitions() throws ParseException {
123 String roleDefinitions = getRoleDefinitions();
124 if (roleDefinitions == null) {
125 return;
126 }
127 Map<String, String> roleDefs = toMap(toLines(roleDefinitions));
128 processRoleDefinitions(roleDefs);
129 }
130
131 protected void processRoleDefinitions(Map<String, String> roleDefs) {
132 if (roleDefs == null || roleDefs.isEmpty()) {
133 return;
134 }
135
136 for (String rolename : roleDefs.keySet()) {
137 String value = roleDefs.get(rolename);
138
139 SimpleRole role = getRole(rolename);
140 if (role == null) {
141 role = new SimpleRole(rolename);
142 add(role);
143 }
144
145 Set<Permission> permissions = PermissionUtils.resolveDelimitedPermissions(value, getPermissionResolver());
146 role.setPermissions(permissions);
147 }
148 }
149
150 protected void processUserDefinitions() throws ParseException {
151
152 String userDefinitions = getUserDefinitions();
153 if (userDefinitions == null) {
154 return;
155 }
156
157 Map<String, String> userDefs = toMap(toLines(userDefinitions));
158
159 processUserDefinitions(userDefs);
160 }
161
162 protected void processUserDefinitions(Map<String, String> userDefs) {
163 if (userDefs == null || userDefs.isEmpty()) {
164 return;
165 }
166
167 for (String username : userDefs.keySet()) {
168
169 String value = userDefs.get(username);
170
171 String[] passwordAndRolesArray = StringUtils.split(value);
172
173 String password = passwordAndRolesArray[0];
174
175 SimpleAccount account = getUser(username);
176 if (account == null) {
177 account = new SimpleAccount(username, password, getName());
178 add(account);
179 }
180 account.setCredentials(password);
181
182 if (passwordAndRolesArray.length > 1) {
183 for (int i = 1; i < passwordAndRolesArray.length; i++) {
184 String rolename = passwordAndRolesArray[i];
185 account.addRole(rolename);
186
187 SimpleRole role = getRole(rolename);
188 if (role != null) {
189 account.addObjectPermissions(role.getPermissions());
190 }
191 }
192 } else {
193 account.setRoles(null);
194 }
195 }
196 }
197
198 protected static Set<String> toLines(String s) {
199 LinkedHashSet<String> set = new LinkedHashSet<String>();
200 Scanner scanner = new Scanner(s);
201 while (scanner.hasNextLine()) {
202 set.add(scanner.nextLine());
203 }
204 return set;
205 }
206
207 protected static Map<String, String> toMap(Collection<String> keyValuePairs) throws ParseException {
208 if (keyValuePairs == null || keyValuePairs.isEmpty()) {
209 return null;
210 }
211
212 Map<String, String> pairs = new HashMap<String, String>();
213 for (String pairString : keyValuePairs) {
214 String[] pair = StringUtils.splitKeyValue(pairString);
215 if (pair != null) {
216 pairs.put(pair[0].trim(), pair[1].trim());
217 }
218 }
219
220 return pairs;
221 }
222 }