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