View Javadoc

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  package org.apache.shiro.config;
20  
21  import org.apache.shiro.mgt.DefaultSecurityManager;
22  import org.apache.shiro.mgt.RealmSecurityManager;
23  import org.apache.shiro.mgt.SecurityManager;
24  import org.apache.shiro.realm.Realm;
25  import org.apache.shiro.realm.RealmFactory;
26  import org.apache.shiro.realm.text.IniRealm;
27  import org.apache.shiro.util.CollectionUtils;
28  import org.apache.shiro.util.Factory;
29  import org.apache.shiro.util.LifecycleUtils;
30  import org.apache.shiro.util.Nameable;
31  import org.slf4j.Logger;
32  import org.slf4j.LoggerFactory;
33  
34  import java.util.ArrayList;
35  import java.util.Collection;
36  import java.util.Collections;
37  import java.util.LinkedHashMap;
38  import java.util.List;
39  import java.util.Map;
40  
41  /**
42   * A {@link Factory} that creates {@link SecurityManager} instances based on {@link Ini} configuration.
43   *
44   * @since 1.0
45   */
46  public class IniSecurityManagerFactory extends IniFactorySupport<SecurityManager> {
47  
48      public static final String MAIN_SECTION_NAME = "main";
49  
50      public static final String SECURITY_MANAGER_NAME = "securityManager";
51      public static final String INI_REALM_NAME = "iniRealm";
52  
53      private static transient final Logger log = LoggerFactory.getLogger(IniSecurityManagerFactory.class);
54  
55      private ReflectionBuilder builder;
56  
57      /**
58       * Creates a new instance.  See the {@link #getInstance()} JavaDoc for detailed explanation of how an INI
59       * source will be resolved to use to build the instance.
60       */
61      public IniSecurityManagerFactory() {
62      }
63  
64      public IniSecurityManagerFactory(Ini config) {
65          setIni(config);
66      }
67  
68      public IniSecurityManagerFactory(String iniResourcePath) {
69          this(Ini.fromResourcePath(iniResourcePath));
70      }
71  
72      public Map<String, ?> getBeans() {
73          return this.builder != null ? Collections.unmodifiableMap(builder.getObjects()) : null;
74      }
75  
76      public void destroy() {
77          if(this.builder != null) {
78              builder.destroy();
79          }
80      }
81  
82      private SecurityManager getSecurityManagerBean() {
83          return builder.getBean(SECURITY_MANAGER_NAME, SecurityManager.class);
84      }
85  
86      protected SecurityManager createDefaultInstance() {
87          return new DefaultSecurityManager();
88      }
89  
90      protected SecurityManager createInstance(Ini ini) {
91          if (CollectionUtils.isEmpty(ini)) {
92              throw new NullPointerException("Ini argument cannot be null or empty.");
93          }
94          SecurityManager securityManager = createSecurityManager(ini);
95          if (securityManager == null) {
96              String msg = SecurityManager.class + " instance cannot be null.";
97              throw new ConfigurationException(msg);
98          }
99          return securityManager;
100     }
101 
102     private SecurityManager createSecurityManager(Ini ini) {
103         Ini.Section mainSection = ini.getSection(MAIN_SECTION_NAME);
104         if (CollectionUtils.isEmpty(mainSection)) {
105             //try the default:
106             mainSection = ini.getSection(Ini.DEFAULT_SECTION_NAME);
107         }
108         return createSecurityManager(ini, mainSection);
109     }
110 
111     protected boolean isAutoApplyRealms(SecurityManager securityManager) {
112         boolean autoApply = true;
113         if (securityManager instanceof RealmSecurityManager) {
114             //only apply realms if they haven't been explicitly set by the user:
115             RealmSecurityManager realmSecurityManager = (RealmSecurityManager) securityManager;
116             Collection<Realm> realms = realmSecurityManager.getRealms();
117             if (!CollectionUtils.isEmpty(realms)) {
118                 log.info("Realms have been explicitly set on the SecurityManager instance - auto-setting of " +
119                         "realms will not occur.");
120                 autoApply = false;
121             }
122         }
123         return autoApply;
124     }
125 
126     @SuppressWarnings({"unchecked"})
127     private SecurityManager createSecurityManager(Ini ini, Ini.Section mainSection) {
128 
129         Map<String, ?> defaults = createDefaults(ini, mainSection);
130         Map<String, ?> objects = buildInstances(mainSection, defaults);
131 
132         SecurityManager securityManager = getSecurityManagerBean();
133 
134         boolean autoApplyRealms = isAutoApplyRealms(securityManager);
135 
136         if (autoApplyRealms) {
137             //realms and realm factory might have been created - pull them out first so we can
138             //initialize the securityManager:
139             Collection<Realm> realms = getRealms(objects);
140             //set them on the SecurityManager
141             if (!CollectionUtils.isEmpty(realms)) {
142                 applyRealmsToSecurityManager(realms, securityManager);
143             }
144         }
145 
146         return securityManager;
147     }
148 
149     protected Map<String, ?> createDefaults(Ini ini, Ini.Section mainSection) {
150         Map<String, Object> defaults = new LinkedHashMap<String, Object>();
151 
152         SecurityManager securityManager = createDefaultInstance();
153         defaults.put(SECURITY_MANAGER_NAME, securityManager);
154 
155         if (shouldImplicitlyCreateRealm(ini)) {
156             Realm realm = createRealm(ini);
157             if (realm != null) {
158                 defaults.put(INI_REALM_NAME, realm);
159             }
160         }
161 
162         return defaults;
163     }
164 
165     private Map<String, ?> buildInstances(Ini.Section section, Map<String, ?> defaults) {
166         this.builder = new ReflectionBuilder(defaults);
167         return this.builder.buildObjects(section);
168     }
169 
170     private void addToRealms(Collection<Realm> realms, RealmFactory factory) {
171         LifecycleUtils.init(factory);
172         Collection<Realm> factoryRealms = factory.getRealms();
173         //SHIRO-238: check factoryRealms (was 'realms'):
174         if (!CollectionUtils.isEmpty(factoryRealms)) {
175             realms.addAll(factoryRealms);
176         }
177     }
178 
179     private Collection<Realm> getRealms(Map<String, ?> instances) {
180 
181         //realms and realm factory might have been created - pull them out first so we can
182         //initialize the securityManager:
183         List<Realm> realms = new ArrayList<Realm>();
184 
185         //iterate over the map entries to pull out the realm factory(s):
186         for (Map.Entry<String, ?> entry : instances.entrySet()) {
187 
188             String name = entry.getKey();
189             Object value = entry.getValue();
190 
191             if (value instanceof RealmFactory) {
192                 addToRealms(realms, (RealmFactory) value);
193             } else if (value instanceof Realm) {
194                 Realm realm = (Realm) value;
195                 //set the name if null:
196                 String existingName = realm.getName();
197                 if (existingName == null || existingName.startsWith(realm.getClass().getName())) {
198                     if (realm instanceof Nameable) {
199                         ((Nameable) realm).setName(name);
200                         log.debug("Applied name '{}' to Nameable realm instance {}", name, realm);
201                     } else {
202                         log.info("Realm does not implement the {} interface.  Configured name will not be applied.",
203                                 Nameable.class.getName());
204                     }
205                 }
206                 realms.add(realm);
207             }
208         }
209 
210         return realms;
211     }
212 
213     private void assertRealmSecurityManager(SecurityManager securityManager) {
214         if (securityManager == null) {
215             throw new NullPointerException("securityManager instance cannot be null");
216         }
217         if (!(securityManager instanceof RealmSecurityManager)) {
218             String msg = "securityManager instance is not a " + RealmSecurityManager.class.getName() +
219                     " instance.  This is required to access or configure realms on the instance.";
220             throw new ConfigurationException(msg);
221         }
222     }
223 
224     protected void applyRealmsToSecurityManager(Collection<Realm> realms, SecurityManager securityManager) {
225         assertRealmSecurityManager(securityManager);
226         ((RealmSecurityManager) securityManager).setRealms(realms);
227     }
228 
229     /**
230      * Returns {@code true} if the Ini contains account data and a {@code Realm} should be implicitly
231      * {@link #createRealm(Ini) created} to reflect the account data, {@code false} if no realm should be implicitly
232      * created.
233      *
234      * @param ini the Ini instance to inspect for account data resulting in an implicitly created realm.
235      * @return {@code true} if the Ini contains account data and a {@code Realm} should be implicitly
236      *         {@link #createRealm(Ini) created} to reflect the account data, {@code false} if no realm should be
237      *         implicitly created.
238      */
239     protected boolean shouldImplicitlyCreateRealm(Ini ini) {
240         return !CollectionUtils.isEmpty(ini) &&
241                 (!CollectionUtils.isEmpty(ini.getSection(IniRealm.ROLES_SECTION_NAME)) ||
242                         !CollectionUtils.isEmpty(ini.getSection(IniRealm.USERS_SECTION_NAME)));
243     }
244 
245     /**
246      * Creates a {@code Realm} from the Ini instance containing account data.
247      *
248      * @param ini the Ini instance from which to acquire the account data.
249      * @return a new Realm instance reflecting the account data discovered in the {@code Ini}.
250      */
251     protected Realm createRealm(Ini ini) {
252         //IniRealm realm = new IniRealm(ini); changed to support SHIRO-322
253         IniRealm realm = new IniRealm();
254         realm.setName(INI_REALM_NAME);
255         realm.setIni(ini); //added for SHIRO-322
256         return realm;
257     }
258 }