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