Coverage Report - org.apache.shiro.web.env.IniWebEnvironment
 
Classes in this File Line Coverage Branch Coverage Complexity
IniWebEnvironment
37%
35/93
20%
12/58
3.833
 
 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.web.env;
 20  
 
 21  
 import org.apache.shiro.config.ConfigurationException;
 22  
 import org.apache.shiro.config.Ini;
 23  
 import org.apache.shiro.config.IniFactorySupport;
 24  
 import org.apache.shiro.io.ResourceUtils;
 25  
 import org.apache.shiro.util.CollectionUtils;
 26  
 import org.apache.shiro.util.Destroyable;
 27  
 import org.apache.shiro.util.Initializable;
 28  
 import org.apache.shiro.util.StringUtils;
 29  
 import org.apache.shiro.web.config.IniFilterChainResolverFactory;
 30  
 import org.apache.shiro.web.config.WebIniSecurityManagerFactory;
 31  
 import org.apache.shiro.web.filter.mgt.FilterChainResolver;
 32  
 import org.apache.shiro.web.mgt.WebSecurityManager;
 33  
 import org.apache.shiro.web.util.WebUtils;
 34  
 import org.slf4j.Logger;
 35  
 import org.slf4j.LoggerFactory;
 36  
 
 37  
 import javax.servlet.ServletContext;
 38  
 import java.io.IOException;
 39  
 import java.io.InputStream;
 40  
 import java.util.Map;
 41  
 
 42  
 /**
 43  
  * {@link WebEnvironment} implementation configured by an {@link Ini} instance or {@code Ini} resource locations.
 44  
  *
 45  
  * @since 1.2
 46  
  */
 47  1
 public class IniWebEnvironment extends ResourceBasedWebEnvironment implements Initializable, Destroyable {
 48  
 
 49  
     public static final String DEFAULT_WEB_INI_RESOURCE_PATH = "/WEB-INF/shiro.ini";
 50  
 
 51  1
     private static final Logger log = LoggerFactory.getLogger(IniWebEnvironment.class);
 52  
 
 53  
     /**
 54  
      * The Ini that configures this WebEnvironment instance.
 55  
      */
 56  
     private Ini ini;
 57  
 
 58  
     /**
 59  
      * Initializes this instance by resolving any potential (explicit or resource-configured) {@link Ini}
 60  
      * configuration and calling {@link #configure() configure} for actual instance configuration.
 61  
      */
 62  
     public void init() {
 63  1
         Ini ini = getIni();
 64  
 
 65  1
         String[] configLocations = getConfigLocations();
 66  
 
 67  1
         if (log.isWarnEnabled() && !CollectionUtils.isEmpty(ini) &&
 68  
                 configLocations != null && configLocations.length > 0) {
 69  0
             log.warn("Explicit INI instance has been provided, but configuration locations have also been " +
 70  
                     "specified.  The {} implementation does not currently support multiple Ini config, but this may " +
 71  
                     "be supported in the future. Only the INI instance will be used for configuration.",
 72  
                     IniWebEnvironment.class.getName());
 73  
         }
 74  
 
 75  1
         if (CollectionUtils.isEmpty(ini)) {
 76  0
             log.debug("Checking any specified config locations.");
 77  0
             ini = getSpecifiedIni(configLocations);
 78  
         }
 79  
 
 80  1
         if (CollectionUtils.isEmpty(ini)) {
 81  0
             log.debug("No INI instance or config locations specified.  Trying default config locations.");
 82  0
             ini = getDefaultIni();
 83  
         }
 84  
 
 85  1
         if (CollectionUtils.isEmpty(ini)) {
 86  0
             String msg = "Shiro INI configuration was either not found or discovered to be empty/unconfigured.";
 87  0
             throw new ConfigurationException(msg);
 88  
         }
 89  
 
 90  1
         setIni(ini);
 91  
 
 92  1
         configure();
 93  1
     }
 94  
 
 95  
     protected void configure() {
 96  
 
 97  1
         this.objects.clear();
 98  
 
 99  1
         WebSecurityManager securityManager = createWebSecurityManager();
 100  1
         setWebSecurityManager(securityManager);
 101  
 
 102  1
         FilterChainResolver resolver = createFilterChainResolver();
 103  1
         if (resolver != null) {
 104  0
             setFilterChainResolver(resolver);
 105  
         }
 106  1
     }
 107  
 
 108  
     protected Ini getSpecifiedIni(String[] configLocations) throws ConfigurationException {
 109  
 
 110  0
         Ini ini = null;
 111  
 
 112  0
         if (configLocations != null && configLocations.length > 0) {
 113  
 
 114  0
             if (configLocations.length > 1) {
 115  0
                 log.warn("More than one Shiro .ini config location has been specified.  Only the first will be " +
 116  
                         "used for configuration as the {} implementation does not currently support multiple " +
 117  
                         "files.  This may be supported in the future however.", IniWebEnvironment.class.getName());
 118  
             }
 119  
 
 120  
             //required, as it is user specified:
 121  0
             ini = createIni(configLocations[0], true);
 122  
         }
 123  
 
 124  0
         return ini;
 125  
     }
 126  
 
 127  
     protected Ini getDefaultIni() {
 128  
 
 129  0
         Ini ini = null;
 130  
 
 131  0
         String[] configLocations = getDefaultConfigLocations();
 132  0
         if (configLocations != null) {
 133  0
             for (String location : configLocations) {
 134  0
                 ini = createIni(location, false);
 135  0
                 if (!CollectionUtils.isEmpty(ini)) {
 136  0
                     log.debug("Discovered non-empty INI configuration at location '{}'.  Using for configuration.",
 137  
                             location);
 138  0
                     break;
 139  
                 }
 140  
             }
 141  
         }
 142  
 
 143  0
         return ini;
 144  
     }
 145  
 
 146  
     /**
 147  
      * Creates an {@link Ini} instance reflecting the specified path, or {@code null} if the path does not exist and
 148  
      * is not required.
 149  
      * <p/>
 150  
      * If the path is required and does not exist or is empty, a {@link ConfigurationException} will be thrown.
 151  
      *
 152  
      * @param configLocation the resource path to load into an {@code Ini} instance.
 153  
      * @param required       if the path must exist and be converted to a non-empty {@link Ini} instance.
 154  
      * @return an {@link Ini} instance reflecting the specified path, or {@code null} if the path does not exist and
 155  
      *         is not required.
 156  
      * @throws ConfigurationException if the path is required but results in a null or empty Ini instance.
 157  
      */
 158  
     protected Ini createIni(String configLocation, boolean required) throws ConfigurationException {
 159  
 
 160  0
         Ini ini = null;
 161  
 
 162  0
         if (configLocation != null) {
 163  0
             ini = convertPathToIni(configLocation, required);
 164  
         }
 165  0
         if (required && CollectionUtils.isEmpty(ini)) {
 166  0
             String msg = "Required configuration location '" + configLocation + "' does not exist or did not " +
 167  
                     "contain any INI configuration.";
 168  0
             throw new ConfigurationException(msg);
 169  
         }
 170  
 
 171  0
         return ini;
 172  
     }
 173  
 
 174  
     protected FilterChainResolver createFilterChainResolver() {
 175  
 
 176  1
         FilterChainResolver resolver = null;
 177  
 
 178  1
         Ini ini = getIni();
 179  
 
 180  1
         if (!CollectionUtils.isEmpty(ini)) {
 181  
             //only create a resolver if the 'filters' or 'urls' sections are defined:
 182  1
             Ini.Section urls = ini.getSection(IniFilterChainResolverFactory.URLS);
 183  1
             Ini.Section filters = ini.getSection(IniFilterChainResolverFactory.FILTERS);
 184  1
             if (!CollectionUtils.isEmpty(urls) || !CollectionUtils.isEmpty(filters)) {
 185  
                 //either the urls section or the filters section was defined.  Go ahead and create the resolver:
 186  0
                 IniFilterChainResolverFactory factory = new IniFilterChainResolverFactory(ini, this.objects);
 187  0
                 resolver = factory.getInstance();
 188  
             }
 189  
         }
 190  
 
 191  1
         return resolver;
 192  
     }
 193  
 
 194  
     protected WebSecurityManager createWebSecurityManager() {
 195  
         WebIniSecurityManagerFactory factory;
 196  1
         Ini ini = getIni();
 197  1
         if (CollectionUtils.isEmpty(ini)) {
 198  0
             factory = new WebIniSecurityManagerFactory();
 199  
         } else {
 200  1
             factory = new WebIniSecurityManagerFactory(ini);
 201  
         }
 202  
 
 203  1
         WebSecurityManager wsm = (WebSecurityManager)factory.getInstance();
 204  
 
 205  
         //SHIRO-306 - get beans after they've been created (the call was before the factory.getInstance() call,
 206  
         //which always returned null.
 207  1
         Map<String, ?> beans = factory.getBeans();
 208  1
         if (!CollectionUtils.isEmpty(beans)) {
 209  1
             this.objects.putAll(beans);
 210  
         }
 211  
 
 212  1
         return wsm;
 213  
     }
 214  
 
 215  
     /**
 216  
      * Returns an array with two elements, {@code /WEB-INF/shiro.ini} and {@code classpath:shiro.ini}.
 217  
      *
 218  
      * @return an array with two elements, {@code /WEB-INF/shiro.ini} and {@code classpath:shiro.ini}.
 219  
      */
 220  
     protected String[] getDefaultConfigLocations() {
 221  0
         return new String[]{
 222  
                 DEFAULT_WEB_INI_RESOURCE_PATH,
 223  
                 IniFactorySupport.DEFAULT_INI_RESOURCE_PATH
 224  
         };
 225  
     }
 226  
 
 227  
     /**
 228  
      * Converts the specified file path to an {@link Ini} instance.
 229  
      * <p/>
 230  
      * If the path does not have a resource prefix as defined by {@link org.apache.shiro.io.ResourceUtils#hasResourcePrefix(String)}, the
 231  
      * path is expected to be resolvable by the {@code ServletContext} via
 232  
      * {@link javax.servlet.ServletContext#getResourceAsStream(String)}.
 233  
      *
 234  
      * @param path     the path of the INI resource to load into an INI instance.
 235  
      * @param required if the specified path must exist
 236  
      * @return an INI instance populated based on the given INI resource path.
 237  
      */
 238  
     private Ini convertPathToIni(String path, boolean required) {
 239  
 
 240  
         //TODO - this logic is ugly - it'd be ideal if we had a Resource API to polymorphically encaspulate this behavior
 241  
 
 242  0
         Ini ini = null;
 243  
 
 244  0
         if (StringUtils.hasText(path)) {
 245  0
             InputStream is = null;
 246  
 
 247  
             //SHIRO-178: Check for servlet context resource and not only resource paths:
 248  0
             if (!ResourceUtils.hasResourcePrefix(path)) {
 249  0
                 is = getServletContextResourceStream(path);
 250  
             } else {
 251  
                 try {
 252  0
                     is = ResourceUtils.getInputStreamForPath(path);
 253  0
                 } catch (IOException e) {
 254  0
                     if (required) {
 255  0
                         throw new ConfigurationException(e);
 256  
                     } else {
 257  0
                         if (log.isDebugEnabled()) {
 258  0
                             log.debug("Unable to load optional path '" + path + "'.", e);
 259  
                         }
 260  
                     }
 261  0
                 }
 262  
             }
 263  0
             if (is != null) {
 264  0
                 ini = new Ini();
 265  0
                 ini.load(is);
 266  
             } else {
 267  0
                 if (required) {
 268  0
                     throw new ConfigurationException("Unable to load resource path '" + path + "'");
 269  
                 }
 270  
             }
 271  
         }
 272  
 
 273  0
         return ini;
 274  
     }
 275  
 
 276  
     //TODO - this logic is ugly - it'd be ideal if we had a Resource API to polymorphically encaspulate this behavior
 277  
     private InputStream getServletContextResourceStream(String path) {
 278  0
         InputStream is = null;
 279  
 
 280  0
         path = WebUtils.normalize(path);
 281  0
         ServletContext sc = getServletContext();
 282  0
         if (sc != null) {
 283  0
             is = sc.getResourceAsStream(path);
 284  
         }
 285  
 
 286  0
         return is;
 287  
     }
 288  
 
 289  
     /**
 290  
      * Returns the {@code Ini} instance reflecting this WebEnvironment's configuration.
 291  
      *
 292  
      * @return the {@code Ini} instance reflecting this WebEnvironment's configuration.
 293  
      */
 294  
     public Ini getIni() {
 295  3
         return this.ini;
 296  
     }
 297  
 
 298  
     /**
 299  
      * Allows for configuration via a direct {@link Ini} instance instead of via
 300  
      * {@link #getConfigLocations() config locations}.
 301  
      * <p/>
 302  
      * If the specified instance is null or empty, the fallback/default resource-based configuration will be used.
 303  
      *
 304  
      * @param ini the ini instance to use for creation.
 305  
      */
 306  
     public void setIni(Ini ini) {
 307  2
         this.ini = ini;
 308  2
     }
 309  
 }