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 */
019 package org.apache.shiro.realm.text;
020
021 import org.apache.shiro.config.Ini;
022 import org.apache.shiro.util.CollectionUtils;
023 import org.apache.shiro.util.StringUtils;
024 import org.slf4j.Logger;
025 import org.slf4j.LoggerFactory;
026
027 /**
028 * A {@link org.apache.shiro.realm.Realm Realm} implementation that creates
029 * {@link org.apache.shiro.authc.SimpleAccount SimpleAccount} instances based on
030 * {@link Ini} configuration.
031 * <p/>
032 * This implementation looks for two {@link Ini.Section sections} in the {@code Ini} configuration:
033 * <pre>
034 * [users]
035 * # One or more {@link org.apache.shiro.realm.text.TextConfigurationRealm#setUserDefinitions(String) user definitions}
036 * ...
037 * [roles]
038 * # One or more {@link org.apache.shiro.realm.text.TextConfigurationRealm#setRoleDefinitions(String) role definitions}</pre>
039 * <p/>
040 * This class also supports setting the {@link #setResourcePath(String) resourcePath} property to create account
041 * data from an .ini resource. This will only be used if there isn't already account data in the Realm.
042 *
043 * @since 1.0
044 */
045 public class IniRealm extends TextConfigurationRealm {
046
047 public static final String USERS_SECTION_NAME = "users";
048 public static final String ROLES_SECTION_NAME = "roles";
049
050 private static transient final Logger log = LoggerFactory.getLogger(IniRealm.class);
051
052 private String resourcePath;
053
054 public IniRealm() {
055 super();
056 }
057
058 public IniRealm(Ini ini) {
059 this();
060 processDefinitions(ini);
061 }
062
063 public IniRealm(String resourcePath) {
064 this();
065 Ini ini = Ini.fromResourcePath(resourcePath);
066 this.resourcePath = resourcePath;
067 processDefinitions(ini);
068 }
069
070 public String getResourcePath() {
071 return resourcePath;
072 }
073
074 public void setResourcePath(String resourcePath) {
075 this.resourcePath = resourcePath;
076 }
077
078 @Override
079 protected void onInit() {
080 // This is an in-memory realm only - no need for an additional cache when we're already
081 // as memory-efficient as we can be.
082 String resourcePath = getResourcePath();
083
084 if (CollectionUtils.isEmpty(this.users) && CollectionUtils.isEmpty(this.roles)) {
085 //no account data manually populated - try the resource path:
086 if (StringUtils.hasText(resourcePath)) {
087 log.debug("Resource path {} defined. Creating INI instance.", resourcePath);
088 Ini ini = Ini.fromResourcePath(resourcePath);
089 processDefinitions(ini);
090 } else {
091 throw new IllegalStateException("No resource path was specified. Cannot load account data.");
092 }
093 } else {
094 if (StringUtils.hasText(resourcePath)) {
095 log.warn("Users or Roles are already populated. Resource path property will be ignored.");
096 }
097 }
098 }
099
100 private void processDefinitions(Ini ini) {
101 if (CollectionUtils.isEmpty(ini)) {
102 log.warn("{} defined, but the ini instance is null or empty.", getClass().getSimpleName());
103 return;
104 }
105
106 Ini.Section rolesSection = ini.getSection(ROLES_SECTION_NAME);
107 if (!CollectionUtils.isEmpty(rolesSection)) {
108 log.debug("Discovered the [{}] section. Processing...", ROLES_SECTION_NAME);
109 processRoleDefinitions(rolesSection);
110 }
111
112 Ini.Section usersSection = ini.getSection(USERS_SECTION_NAME);
113 if (!CollectionUtils.isEmpty(usersSection)) {
114 log.debug("Discovered the [{}] section. Processing...", USERS_SECTION_NAME);
115 processUserDefinitions(usersSection);
116 } else {
117 log.info("{} defined, but there is no [{}] section defined. This realm will not be populated with any " +
118 "users and it is assumed that they will be populated programatically. Users must be defined " +
119 "for this Realm instance to be useful.", getClass().getSimpleName(), USERS_SECTION_NAME);
120 }
121 }
122 }