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.IniRealm; 20 21 import hunt.shiro.realm.text.TextConfigurationRealm; 22 23 import hunt.shiro.config.Ini; 24 import hunt.shiro.util.CollectionUtils; 25 // import hunt.shiro.util.StringUtils; 26 import hunt.logging.Logger; 27 import hunt.util.Configuration; 28 29 import hunt.Exceptions; 30 import std.array; 31 32 /** 33 * A {@link hunt.shiro.realm.Realm Realm} implementation that creates 34 * {@link hunt.shiro.authc.SimpleAccount SimpleAccount} instances based on 35 * {@link Ini} configuration. 36 * <p/> 37 * This implementation looks for two {@link IniSection sections} in the {@code Ini} configuration: 38 * <pre> 39 * [users] 40 * # One or more {@link hunt.shiro.realm.text.TextConfigurationRealm#setUserDefinitions(string) user definitions} 41 * ... 42 * [roles] 43 * # One or more {@link hunt.shiro.realm.text.TextConfigurationRealm#setRoleDefinitions(string) role definitions}</pre> 44 * <p/> 45 * This class also supports setting the {@link #setResourcePath(string) resourcePath} property to create account 46 * data from an .ini resource. This will only be used if there isn't already account data in the Realm. 47 * 48 */ 49 class IniRealm : TextConfigurationRealm { 50 51 enum string USERS_SECTION_NAME = "users"; 52 enum string ROLES_SECTION_NAME = "roles"; 53 54 55 private string resourcePath; 56 private Ini ini; //reference added in 1.2 for SHIRO-322 57 58 this() { 59 super(); 60 } 61 62 /** 63 * This constructor will immediately process the definitions in the {@code Ini} argument. If you need to perform 64 * additional configuration before processing (e.g. setting a permissionResolver, etc), do not call this 65 * constructor. Instead, do the following: 66 * <ol> 67 * <li>Call the default no-arg constructor</li> 68 * <li>Set the Ini instance you wish to use via {@code #setIni}</li> 69 * <li>Set any other configuration properties</li> 70 * <li>Call {@link #init()}</li> 71 * </ol> 72 * 73 * @param ini the Ini instance which will be inspected to create accounts, groups and permissions for this realm. 74 */ 75 this(Ini ini) { 76 this(); 77 processDefinitions(ini); 78 } 79 80 /** 81 * This constructor will immediately process the definitions in the {@code Ini} resolved from the specified 82 * {@code resourcePath}. If you need to perform additional configuration before processing (e.g. setting a 83 * permissionResolver, etc), do not call this constructor. Instead, do the following: 84 * <ol> 85 * <li>Call the default no-arg constructor</li> 86 * <li>Set the Ini instance you wish to use via {@code #setIni}</li> 87 * <li>Set any other configuration properties</li> 88 * <li>Call {@link #init()}</li> 89 * </ol> 90 * 91 * @param resourcePath the resource path of the Ini config which will be inspected to create accounts, groups and 92 * permissions for this realm. 93 */ 94 this(string resourcePath) { 95 this(); 96 Ini ini = Ini.fromResourcePath(resourcePath); 97 this.ini = ini; 98 this.resourcePath = resourcePath; 99 processDefinitions(ini); 100 } 101 102 string getResourcePath() { 103 return resourcePath; 104 } 105 106 void setResourcePath(string resourcePath) { 107 this.resourcePath = resourcePath; 108 } 109 110 /** 111 * Returns the Ini instance used to configure this realm. Provided for JavaBeans-style configuration of this 112 * realm, particularly useful in Dependency Injection environments. 113 * 114 * @return the Ini instance which will be inspected to create accounts, groups and permissions for this realm. 115 */ 116 Ini getIni() { 117 return ini; 118 } 119 120 /** 121 * Sets the Ini instance used to configure this realm. Provided for JavaBeans-style configuration of this 122 * realm, particularly useful in Dependency Injection environments. 123 * 124 * @param ini the Ini instance which will be inspected to create accounts, groups and permissions for this realm. 125 */ 126 void setIni(Ini ini) { 127 this.ini = ini; 128 } 129 130 override 131 protected void onInit() { 132 super.onInit(); 133 134 // This is an in-memory realm only - no need for an additional cache when we're already 135 // as memory-efficient as we can be. 136 137 Ini ini = getIni(); 138 string resourcePath = getResourcePath(); 139 140 if (!CollectionUtils.isEmpty(this.users) || !CollectionUtils.isEmpty(this.roles)) { 141 if (ini !is null && !ini.isEmpty()) { 142 warning("Users or Roles are already populated. Configured Ini instance will be ignored."); 143 } 144 if (!resourcePath.empty()) { 145 warning("Users or Roles are already populated. resourcePath '%s' will be ignored.", resourcePath); 146 } 147 148 tracef("Instance is already populated with users or roles. No additional user/role population " ~ 149 "will be performed."); 150 return; 151 } 152 153 if (ini is null || ini.isEmpty()) { 154 tracef("No INI instance configuration present. Checking resourcePath..."); 155 156 if (!resourcePath.empty()) { 157 tracef("Resource path %s defined. Creating INI instance.", resourcePath); 158 ini = Ini.fromResourcePath(resourcePath); 159 if (ini !is null && !ini.isEmpty()) { 160 setIni(ini); 161 } 162 } 163 } 164 165 if (ini is null || ini.isEmpty()) { 166 string msg = "Ini instance and/or resourcePath resulted in null or empty Ini configuration. Cannot " ~ 167 "load account data."; 168 throw new IllegalStateException(msg); 169 } 170 171 processDefinitions(ini); 172 } 173 174 private void processDefinitions(Ini ini) { 175 176 if (CollectionUtils.isEmpty(ini)) { 177 warning("%s defined, but the ini instance is null or empty.", typeid(this).name); 178 return; 179 } 180 181 IniSection rolesSection = ini.getSection(ROLES_SECTION_NAME); 182 if (!CollectionUtils.isEmpty(rolesSection)) { 183 tracef("Discovered the [%s] section. Processing...", ROLES_SECTION_NAME); 184 processRoleDefinitions(rolesSection); 185 } 186 187 IniSection usersSection = ini.getSection(USERS_SECTION_NAME); 188 if (!CollectionUtils.isEmpty(usersSection)) { 189 tracef("Discovered the [%s] section. Processing...", USERS_SECTION_NAME); 190 processUserDefinitions(usersSection); 191 } else { 192 info("%s defined, but there is no [%s] section defined. This realm will not be populated with any " ~ 193 "users and it is assumed that they will be populated programatically. Users must be defined " ~ 194 "for this Realm instance to be useful.", typeid(this).name, USERS_SECTION_NAME); 195 } 196 } 197 }