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.config;
020
021 import org.apache.shiro.mgt.DefaultSecurityManager;
022 import org.apache.shiro.mgt.RealmSecurityManager;
023 import org.apache.shiro.mgt.SecurityManager;
024 import org.apache.shiro.realm.Realm;
025 import org.apache.shiro.realm.RealmFactory;
026 import org.apache.shiro.realm.text.IniRealm;
027 import org.apache.shiro.util.CollectionUtils;
028 import org.apache.shiro.util.Factory;
029 import org.apache.shiro.util.LifecycleUtils;
030 import org.apache.shiro.util.Nameable;
031 import org.slf4j.Logger;
032 import org.slf4j.LoggerFactory;
033
034 import java.util.*;
035
036 /**
037 * A {@link Factory} that creates {@link SecurityManager} instances based on {@link Ini} configuration.
038 *
039 * @author The Apache Shiro Project (shiro-dev@incubator.apache.org)
040 * @since 1.0
041 */
042 public class IniSecurityManagerFactory extends IniFactorySupport<SecurityManager> {
043
044 public static final String MAIN_SECTION_NAME = "main";
045
046 public static final String SECURITY_MANAGER_NAME = "securityManager";
047 public static final String INI_REALM_NAME = "iniRealm";
048
049 private static transient final Logger log = LoggerFactory.getLogger(IniSecurityManagerFactory.class);
050
051 private ReflectionBuilder builder;
052
053 /**
054 * Creates a new instance. See the {@link #getInstance()} JavaDoc for detailed explanation of how an INI
055 * source will be resolved to use to build the instance.
056 */
057 public IniSecurityManagerFactory() {
058 }
059
060 public IniSecurityManagerFactory(Ini config) {
061 setIni(config);
062 }
063
064 public IniSecurityManagerFactory(String iniResourcePath) {
065 this(Ini.fromResourcePath(iniResourcePath));
066 }
067
068 public Map<String, ?> getBeans() {
069 return this.builder != null ? Collections.unmodifiableMap(builder.getObjects()) : null;
070 }
071
072 private SecurityManager getSecurityManagerBean() {
073 return builder.getBean(SECURITY_MANAGER_NAME, SecurityManager.class);
074 }
075
076 protected SecurityManager createDefaultInstance() {
077 return new DefaultSecurityManager();
078 }
079
080 protected SecurityManager createInstance(Ini ini) {
081 if (CollectionUtils.isEmpty(ini)) {
082 throw new NullPointerException("Ini argument cannot be null or empty.");
083 }
084 SecurityManager securityManager = createSecurityManager(ini);
085 if (securityManager == null) {
086 String msg = SecurityManager.class + " instance cannot be null.";
087 throw new ConfigurationException(msg);
088 }
089 return securityManager;
090 }
091
092 private SecurityManager createSecurityManager(Ini ini) {
093 Ini.Section mainSection = ini.getSection(MAIN_SECTION_NAME);
094 if (CollectionUtils.isEmpty(mainSection)) {
095 //try the default:
096 mainSection = ini.getSection(Ini.DEFAULT_SECTION_NAME);
097 }
098 return createSecurityManager(ini, mainSection);
099 }
100
101 protected boolean isAutoApplyRealms(SecurityManager securityManager) {
102 boolean autoApply = true;
103 if (securityManager instanceof RealmSecurityManager) {
104 //only apply realms if they haven't been explicitly set by the user:
105 RealmSecurityManager realmSecurityManager = (RealmSecurityManager) securityManager;
106 Collection<Realm> realms = realmSecurityManager.getRealms();
107 if (!CollectionUtils.isEmpty(realms)) {
108 log.info("Realms have been explicitly set on the SecurityManager instance - auto-setting of " +
109 "realms will not occur.");
110 autoApply = false;
111 }
112 }
113 return autoApply;
114 }
115
116 @SuppressWarnings({"unchecked"})
117 private SecurityManager createSecurityManager(Ini ini, Ini.Section mainSection) {
118
119 Map<String, ?> defaults = createDefaults(ini, mainSection);
120 Map<String, ?> objects = buildInstances(mainSection, defaults);
121
122 SecurityManager securityManager = getSecurityManagerBean();
123
124 boolean autoApplyRealms = isAutoApplyRealms(securityManager);
125
126 if (autoApplyRealms) {
127 //realms and realm factory might have been created - pull them out first so we can
128 //initialize the securityManager:
129 Collection<Realm> realms = getRealms(objects);
130 //set them on the SecurityManager
131 if (!CollectionUtils.isEmpty(realms)) {
132 applyRealmsToSecurityManager(realms, securityManager);
133 }
134 }
135
136 initRealms(securityManager);
137
138 return securityManager;
139 }
140
141 private void initRealms(SecurityManager securityManager) {
142 Collection<Realm> realms = getRealms(securityManager);
143 if (!CollectionUtils.isEmpty(realms)) {
144 LifecycleUtils.init(realms);
145 }
146 }
147
148 private Collection<Realm> getRealms(SecurityManager securityManager) {
149 assertRealmSecurityManager(securityManager);
150 return ((RealmSecurityManager) securityManager).getRealms();
151 }
152
153 protected Map<String, ?> createDefaults(Ini ini, Ini.Section mainSection) {
154 Map<String, Object> defaults = new LinkedHashMap<String, Object>();
155
156 SecurityManager securityManager = createDefaultInstance();
157 defaults.put(SECURITY_MANAGER_NAME, securityManager);
158
159 if (shouldImplicitlyCreateRealm(ini)) {
160 Realm realm = createRealm(ini);
161 if (realm != null) {
162 defaults.put(INI_REALM_NAME, realm);
163 }
164 }
165
166 return defaults;
167 }
168
169 private Map<String, ?> buildInstances(Ini.Section section, Map<String, ?> defaults) {
170 this.builder = new ReflectionBuilder(defaults);
171 return this.builder.buildObjects(section);
172 }
173
174 private void addToRealms(Collection<Realm> realms, RealmFactory factory) {
175 LifecycleUtils.init(factory);
176 Collection<Realm> factoryRealms = factory.getRealms();
177 if (!CollectionUtils.isEmpty(realms)) {
178 realms.addAll(factoryRealms);
179 }
180 }
181
182 private Collection<Realm> getRealms(Map<String, ?> instances) {
183
184 //realms and realm factory might have been created - pull them out first so we can
185 //initialize the securityManager:
186 List<Realm> realms = new ArrayList<Realm>();
187
188 //iterate over the map entries to pull out the realm factory(s):
189 for (Map.Entry<String, ?> entry : instances.entrySet()) {
190
191 String name = entry.getKey();
192 Object value = entry.getValue();
193
194 if (value instanceof RealmFactory) {
195 addToRealms(realms, (RealmFactory) value);
196 } else if (value instanceof Realm) {
197 Realm realm = (Realm) value;
198 //set the name if null:
199 String existingName = realm.getName();
200 if (existingName == null || existingName.startsWith(realm.getClass().getName())) {
201 if (realm instanceof Nameable) {
202 ((Nameable) realm).setName(name);
203 log.debug("Applied name '{}' to Nameable realm instance {}", name, realm);
204 } else {
205 log.info("Realm does not implement the {} interface. Configured name will not be applied.",
206 Nameable.class.getName());
207 }
208 }
209 realms.add(realm);
210 }
211 }
212
213 return realms;
214 }
215
216 private void assertRealmSecurityManager(SecurityManager securityManager) {
217 if (securityManager == null) {
218 throw new NullPointerException("securityManager instance cannot be null");
219 }
220 if (!(securityManager instanceof RealmSecurityManager)) {
221 String msg = "securityManager instance is not a " + RealmSecurityManager.class.getName() +
222 " instance. This is required to access or configure realms on the instance.";
223 throw new ConfigurationException(msg);
224 }
225 }
226
227 protected void applyRealmsToSecurityManager(Collection<Realm> realms, SecurityManager securityManager) {
228 assertRealmSecurityManager(securityManager);
229 ((RealmSecurityManager) securityManager).setRealms(realms);
230 }
231
232 /**
233 * Returns {@code true} if the Ini contains account data and a {@code Realm} should be implicitly
234 * {@link #createRealm(Ini) created} to reflect the account data, {@code false} if no realm should be implicitly
235 * created.
236 *
237 * @param ini the Ini instance to inspect for account data resulting in an implicitly created realm.
238 * @return {@code true} if the Ini contains account data and a {@code Realm} should be implicitly
239 * {@link #createRealm(Ini) created} to reflect the account data, {@code false} if no realm should be
240 * implicitly created.
241 */
242 protected boolean shouldImplicitlyCreateRealm(Ini ini) {
243 return !CollectionUtils.isEmpty(ini) &&
244 (!CollectionUtils.isEmpty(ini.getSection(IniRealm.ROLES_SECTION_NAME)) ||
245 !CollectionUtils.isEmpty(ini.getSection(IniRealm.USERS_SECTION_NAME)));
246 }
247
248 /**
249 * Creates a {@code Realm} from the Ini instance containing account data.
250 *
251 * @param ini the Ini instance from which to acquire the account data.
252 * @return a new Realm instance reflecting the account data discovered in the {@code Ini}.
253 */
254 protected Realm createRealm(Ini ini) {
255 IniRealm realm = new IniRealm(ini);
256 realm.setName(INI_REALM_NAME);
257 return realm;
258 }
259 }