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.config.IniSecurityManagerFactory; 20 21 import hunt.shiro.config.Ini; 22 import hunt.shiro.config.IniFactorySupport; 23 import hunt.shiro.config.ReflectionBuilder; 24 25 import hunt.shiro.mgt.DefaultSecurityManager; 26 import hunt.shiro.mgt.RealmSecurityManager; 27 import hunt.shiro.mgt.SecurityManager; 28 import hunt.shiro.realm.Realm; 29 import hunt.shiro.realm.RealmFactory; 30 import hunt.shiro.realm.text.IniRealm; 31 import hunt.shiro.util.CollectionUtils; 32 import hunt.shiro.util.Common; 33 import hunt.shiro.util.LifecycleUtils; 34 import hunt.logging.Logger; 35 36 import hunt.collection; 37 import hunt.Exceptions; 38 import hunt.util.Configuration; 39 40 import std.string; 41 import std.traits; 42 43 44 /** 45 * A {@link Factory} that creates {@link SecurityManager} instances based on {@link Ini} configuration. 46 * 47 * @since 1.0 48 * deprecated("") use Shiro's {@code Environment} mechanisms instead. 49 */ 50 // deprecated("") 51 class IniSecurityManagerFactory : IniFactorySupport!(SecurityManager) { 52 53 enum string MAIN_SECTION_NAME = "main"; 54 55 enum string SECURITY_MANAGER_NAME = "securityManager"; 56 enum string INI_REALM_NAME = "iniRealm"; 57 58 59 60 private ReflectionBuilder builder; 61 62 /** 63 * Creates a new instance. See the {@link #getInstance()} JavaDoc for detailed explanation of how an INI 64 * source will be resolved to use to build the instance. 65 */ 66 this() { 67 this.builder = new ReflectionBuilder(); 68 } 69 70 this(Ini config) { 71 this(); 72 setIni(config); 73 } 74 75 this(string iniResourcePath) { 76 this(Ini.fromResourcePath(iniResourcePath)); 77 } 78 79 // Map!(string, T) getBeans() { 80 // return this.builder !is null ? Collections.unmodifiableMap(builder.getObjects()) : null; 81 // } 82 83 void destroy() { 84 if(getReflectionBuilder() !is null) { 85 getReflectionBuilder().destroy(); 86 } 87 } 88 89 private SecurityManager getSecurityManagerBean() { 90 return getReflectionBuilder().getBean!SecurityManager(SECURITY_MANAGER_NAME); 91 } 92 93 override protected SecurityManager createDefaultInstance() { 94 return new DefaultSecurityManager(); 95 } 96 97 override protected SecurityManager createInstance(Ini ini) { 98 if (ini is null || ini.isEmpty()) { 99 throw new NullPointerException("Ini argument cannot be null or empty."); 100 } 101 SecurityManager securityManager = createSecurityManager(ini); 102 if (securityManager is null) { 103 string msg = "SecurityManager instance cannot be null."; 104 throw new ConfigurationException(msg); 105 } 106 return securityManager; 107 } 108 109 private SecurityManager createSecurityManager(Ini ini) { 110 return createSecurityManager(ini, getConfigSection(ini)); 111 } 112 113 private IniSection getConfigSection(Ini ini) { 114 115 IniSection mainSection = ini.getSection(MAIN_SECTION_NAME); 116 if (CollectionUtils.isEmpty(mainSection)) { 117 //try the default: 118 mainSection = ini.getSection(Ini.DEFAULT_SECTION_NAME); 119 } 120 return mainSection; 121 } 122 123 protected bool isAutoApplyRealms(SecurityManager securityManager) { 124 bool autoApply = true; 125 auto securityManagerCast = cast(RealmSecurityManager)securityManager; 126 if (securityManagerCast !is null) { 127 //only apply realms if they haven't been explicitly set by the user: 128 RealmSecurityManager realmSecurityManager = securityManagerCast; 129 Realm[] realms = realmSecurityManager.getRealms(); 130 if (!realms.empty()) { 131 info("Realms have been explicitly set on the SecurityManager instance - auto-setting of " ~ 132 "realms will not occur."); 133 autoApply = false; 134 } 135 } 136 return autoApply; 137 } 138 139 //@SuppressWarnings({"unchecked"}) 140 private SecurityManager createSecurityManager(Ini ini, IniSection mainSection) { 141 142 getReflectionBuilder().setObjects(createDefaults(ini, mainSection)); 143 Map!(string, Object) objects = buildInstances(mainSection); 144 145 SecurityManager securityManager = getSecurityManagerBean(); 146 bool autoApplyRealms = isAutoApplyRealms(securityManager); 147 148 149 if (autoApplyRealms) { 150 //realms and realm factory might have been created - pull them out first so we can 151 //initialize the securityManager: 152 Realm[] realms = getRealms(objects); 153 version(HUNT_DEBUG) infof("realms=%d, objects=%d", realms.length, objects.size()); 154 //set them on the SecurityManager 155 if (!realms.empty()) { 156 applyRealmsToSecurityManager(realms, securityManager); 157 } 158 } 159 160 return securityManager; 161 } 162 163 protected Map!(string, Object) createDefaults(Ini ini, IniSection mainSection) { 164 Map!(string, Object) defaults = new LinkedHashMap!(string, Object)(); 165 166 SecurityManager securityManager = createDefaultInstance(); 167 defaults.put(SECURITY_MANAGER_NAME, cast(Object)securityManager); 168 169 if (shouldImplicitlyCreateRealm(ini)) { 170 Realm realm = createRealm(ini); 171 if (realm !is null) { 172 defaults.put(INI_REALM_NAME, cast(Object)realm); 173 } 174 } 175 176 // The values from 'getDefaults()' will override the above. 177 Map!(string, SecurityManager) defaultBeans = getDefaults(); 178 if (!CollectionUtils.isEmpty(defaultBeans)) { 179 foreach(string key, SecurityManager value; defaultBeans) 180 defaults.put(key, cast(Object)value); 181 } 182 183 return defaults; 184 } 185 186 private Map!(string, Object) buildInstances(IniSection section) { 187 return getReflectionBuilder().buildObjects(section); 188 } 189 190 private void addToRealms(ref Realm[] realms, RealmFactory factory) { 191 LifecycleUtils.init(cast(Object)factory); 192 Realm[] factoryRealms = factory.getRealms(); 193 //SHIRO-238: check factoryRealms (was 'realms'): 194 if (!factoryRealms.empty()) { 195 realms ~= factoryRealms; 196 } 197 } 198 199 private Realm[] getRealms(Map!(string, Object) instances) { 200 201 //realms and realm factory might have been created - pull them out first so we can 202 //initialize the securityManager: 203 // List!(Realm) realms = new ArrayList!(Realm)(); 204 Realm[] realms; 205 206 //iterate over the map entries to pull out the realm factory(s): 207 foreach (string name, Object value; instances) { 208 RealmFactory bf = cast(RealmFactory) value; 209 Realm realm = cast(Realm) value; 210 if (bf !is null) { 211 addToRealms(realms, bf); 212 } else if (realm !is null) { 213 //set the name if null: 214 string existingName = realm.getName(); 215 if (existingName is null || existingName.startsWith(typeid(realm).name)) { 216 Nameable nameable = cast(Nameable) realm; 217 if (nameable !is null) { 218 nameable.setName(name); 219 tracef("Applied name '%s' to Nameable realm instance %s", name, realm); 220 } else { 221 info("Realm does not implement the %s interface. Configured name will not be applied.", 222 fullyQualifiedName!(Nameable)); 223 } 224 } 225 realms ~= realm; 226 } 227 } 228 229 return realms; 230 } 231 232 private void assertRealmSecurityManager(SecurityManager securityManager) { 233 if (securityManager is null) { 234 throw new NullPointerException("securityManager instance cannot be null"); 235 } 236 RealmSecurityManager rsm = cast(RealmSecurityManager)securityManager; 237 if (securityManager is null) { 238 string msg = "securityManager instance is not a " ~ typeid(RealmSecurityManager).name ~ 239 " instance. This is required to access or configure realms on the instance."; 240 throw new ConfigurationException(msg); 241 } 242 } 243 244 protected void applyRealmsToSecurityManager(Realm[] realms, SecurityManager securityManager) { 245 assertRealmSecurityManager(securityManager); 246 (cast(RealmSecurityManager) securityManager).setRealms(realms); 247 } 248 249 /** 250 * Returns {@code true} if the Ini contains account data and a {@code Realm} should be implicitly 251 * {@link #createRealm(Ini) created} to reflect the account data, {@code false} if no realm should be implicitly 252 * created. 253 * 254 * @param ini the Ini instance to inspect for account data resulting in an implicitly created realm. 255 * @return {@code true} if the Ini contains account data and a {@code Realm} should be implicitly 256 * {@link #createRealm(Ini) created} to reflect the account data, {@code false} if no realm should be 257 * implicitly created. 258 */ 259 protected bool shouldImplicitlyCreateRealm(Ini ini) { 260 return !CollectionUtils.isEmpty(ini) && 261 (!CollectionUtils.isEmpty(ini.getSection(IniRealm.ROLES_SECTION_NAME)) || 262 !CollectionUtils.isEmpty(ini.getSection(IniRealm.USERS_SECTION_NAME))); 263 } 264 265 /** 266 * Creates a {@code Realm} from the Ini instance containing account data. 267 * 268 * @param ini the Ini instance from which to acquire the account data. 269 * @return a new Realm instance reflecting the account data discovered in the {@code Ini}. 270 */ 271 protected Realm createRealm(Ini ini) { 272 //IniRealm realm = new IniRealm(ini); changed to support SHIRO-322 273 IniRealm realm = new IniRealm(); 274 realm.setName(INI_REALM_NAME); 275 realm.setIni(ini); //added for SHIRO-322 276 return realm; 277 } 278 279 /** 280 * Returns the ReflectionBuilder instance used to create SecurityManagers object graph. 281 * @return ReflectionBuilder instance used to create SecurityManagers object graph. 282 * @since 1.4 283 */ 284 ReflectionBuilder getReflectionBuilder() { 285 return builder; 286 } 287 288 /** 289 * Sets the ReflectionBuilder that will be used to create the SecurityManager based on the contents of 290 * the Ini configuration. 291 * @param builder The ReflectionBuilder used to parse the Ini configuration. 292 * @since 1.4 293 */ 294 295 void setReflectionBuilder(ReflectionBuilder builder) { 296 this.builder = builder; 297 } 298 }