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