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.realm.text;
20  
21  import org.apache.shiro.config.Ini;
22  import org.apache.shiro.util.CollectionUtils;
23  import org.apache.shiro.util.StringUtils;
24  import org.slf4j.Logger;
25  import org.slf4j.LoggerFactory;
26  
27  /**
28   * A {@link org.apache.shiro.realm.Realm Realm} implementation that creates
29   * {@link org.apache.shiro.authc.SimpleAccount SimpleAccount} instances based on
30   * {@link Ini} configuration.
31   * <p/>
32   * This implementation looks for two {@link Ini.Section sections} in the {@code Ini} configuration:
33   * <pre>
34   * [users]
35   * # One or more {@link org.apache.shiro.realm.text.TextConfigurationRealm#setUserDefinitions(String) user definitions}
36   * ...
37   * [roles]
38   * # One or more {@link org.apache.shiro.realm.text.TextConfigurationRealm#setRoleDefinitions(String) role definitions}</pre>
39   * <p/>
40   * This class also supports setting the {@link #setResourcePath(String) resourcePath} property to create account
41   * data from an .ini resource.  This will only be used if there isn't already account data in the Realm.
42   *
43   * @since 1.0
44   */
45  public class IniRealm extends TextConfigurationRealm {
46  
47      public static final String USERS_SECTION_NAME = "users";
48      public static final String ROLES_SECTION_NAME = "roles";
49  
50      private static transient final Logger log = LoggerFactory.getLogger(IniRealm.class);
51  
52      private String resourcePath;
53      private Ini ini; //reference added in 1.2 for SHIRO-322
54  
55      public IniRealm() {
56          super();
57      }
58  
59      /**
60       * This constructor will immediately process the definitions in the {@code Ini} argument.  If you need to perform
61       * additional configuration before processing (e.g. setting a permissionResolver, etc), do not call this
62       * constructor.  Instead, do the following:
63       * <ol>
64       * <li>Call the default no-arg constructor</li>
65       * <li>Set the Ini instance you wish to use via {@code #setIni}</li>
66       * <li>Set any other configuration properties</li>
67       * <li>Call {@link #init()}</li>
68       * </ol>
69       *
70       * @param ini the Ini instance which will be inspected to create accounts, groups and permissions for this realm.
71       */
72      public IniRealm(Ini ini) {
73          this();
74          processDefinitions(ini);
75      }
76  
77      /**
78       * This constructor will immediately process the definitions in the {@code Ini} resolved from the specified
79       * {@code resourcePath}.  If you need to perform additional configuration before processing (e.g. setting a
80       * permissionResolver, etc), do not call this constructor.  Instead, do the following:
81       * <ol>
82       * <li>Call the default no-arg constructor</li>
83       * <li>Set the Ini instance you wish to use via {@code #setIni}</li>
84       * <li>Set any other configuration properties</li>
85       * <li>Call {@link #init()}</li>
86       * </ol>
87       *
88       * @param resourcePath the resource path of the Ini config which will be inspected to create accounts, groups and
89       *                     permissions for this realm.
90       */
91      public IniRealm(String resourcePath) {
92          this();
93          Ini ini = Ini.fromResourcePath(resourcePath);
94          this.ini = ini;
95          this.resourcePath = resourcePath;
96          processDefinitions(ini);
97      }
98  
99      public String getResourcePath() {
100         return resourcePath;
101     }
102 
103     public void setResourcePath(String resourcePath) {
104         this.resourcePath = resourcePath;
105     }
106 
107     /**
108      * Returns the Ini instance used to configure this realm.  Provided for JavaBeans-style configuration of this
109      * realm, particularly useful in Dependency Injection environments.
110      * 
111      * @return the Ini instance which will be inspected to create accounts, groups and permissions for this realm.
112      */
113     public Ini getIni() {
114         return ini;
115     }
116 
117     /**
118      * Sets the Ini instance used to configure this realm.  Provided for JavaBeans-style configuration of this
119      * realm, particularly useful in Dependency Injection environments.
120      * 
121      * @param ini the Ini instance which will be inspected to create accounts, groups and permissions for this realm.
122      */
123     public void setIni(Ini ini) {
124         this.ini = ini;
125     }
126 
127     @Override
128     protected void onInit() {
129         super.onInit();
130 
131         // This is an in-memory realm only - no need for an additional cache when we're already
132         // as memory-efficient as we can be.
133         
134         Ini ini = getIni();
135         String resourcePath = getResourcePath();
136                 
137         if (!CollectionUtils.isEmpty(this.users) || !CollectionUtils.isEmpty(this.roles)) {
138             if (!CollectionUtils.isEmpty(ini)) {
139                 log.warn("Users or Roles are already populated.  Configured Ini instance will be ignored.");
140             }
141             if (StringUtils.hasText(resourcePath)) {
142                 log.warn("Users or Roles are already populated.  resourcePath '{}' will be ignored.", resourcePath);
143             }
144             
145             log.debug("Instance is already populated with users or roles.  No additional user/role population " +
146                     "will be performed.");
147             return;
148         }
149         
150         if (CollectionUtils.isEmpty(ini)) {
151             log.debug("No INI instance configuration present.  Checking resourcePath...");
152             
153             if (StringUtils.hasText(resourcePath)) {
154                 log.debug("Resource path {} defined.  Creating INI instance.", resourcePath);
155                 ini = Ini.fromResourcePath(resourcePath);
156                 if (!CollectionUtils.isEmpty(ini)) {
157                     setIni(ini);
158                 }
159             }
160         }
161         
162         if (CollectionUtils.isEmpty(ini)) {
163             String msg = "Ini instance and/or resourcePath resulted in null or empty Ini configuration.  Cannot " +
164                     "load account data.";
165             throw new IllegalStateException(msg);
166         }
167 
168         processDefinitions(ini);
169     }
170 
171     private void processDefinitions(Ini ini) {
172         if (CollectionUtils.isEmpty(ini)) {
173             log.warn("{} defined, but the ini instance is null or empty.", getClass().getSimpleName());
174             return;
175         }
176 
177         Ini.Section rolesSection = ini.getSection(ROLES_SECTION_NAME);
178         if (!CollectionUtils.isEmpty(rolesSection)) {
179             log.debug("Discovered the [{}] section.  Processing...", ROLES_SECTION_NAME);
180             processRoleDefinitions(rolesSection);
181         }
182 
183         Ini.Section usersSection = ini.getSection(USERS_SECTION_NAME);
184         if (!CollectionUtils.isEmpty(usersSection)) {
185             log.debug("Discovered the [{}] section.  Processing...", USERS_SECTION_NAME);
186             processUserDefinitions(usersSection);
187         } else {
188             log.info("{} defined, but there is no [{}] section defined.  This realm will not be populated with any " +
189                     "users and it is assumed that they will be populated programatically.  Users must be defined " +
190                     "for this Realm instance to be useful.", getClass().getSimpleName(), USERS_SECTION_NAME);
191         }
192     }
193 }