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.realm.text; 020 021import org.apache.shiro.config.Ini; 022import org.apache.shiro.util.CollectionUtils; 023import org.apache.shiro.lang.util.StringUtils; 024import org.slf4j.Logger; 025import 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 */ 045public class IniRealm extends TextConfigurationRealm { 046 047 /** 048 * users section name 049 */ 050 public static final String USERS_SECTION_NAME = "users"; 051 /** 052 * roles section name 053 */ 054 public static final String ROLES_SECTION_NAME = "roles"; 055 056 private static final Logger LOGGER = LoggerFactory.getLogger(IniRealm.class); 057 058 private String resourcePath; 059 060 /** 061 * reference added in 1.2 for SHIRO-322 062 */ 063 private Ini ini; 064 065 public IniRealm() { 066 super(); 067 } 068 069 /** 070 * This constructor will immediately process the definitions in the {@code Ini} argument. If you need to perform 071 * additional configuration before processing (e.g. setting a permissionResolver, etc.), do not call this 072 * constructor. Instead, do the following: 073 * <ol> 074 * <li>Call the default no-arg constructor</li> 075 * <li>Set the Ini instance you wish to use via {@code #setIni}</li> 076 * <li>Set any other configuration properties</li> 077 * <li>Call {@link #init()}</li> 078 * </ol> 079 * 080 * @param ini the Ini instance which will be inspected to create accounts, groups and permissions for this realm. 081 */ 082 public IniRealm(Ini ini) { 083 this(); 084 processDefinitions(ini); 085 } 086 087 /** 088 * This constructor will immediately process the definitions in the {@code Ini} resolved from the specified 089 * {@code resourcePath}. If you need to perform additional configuration before processing (e.g. setting a 090 * permissionResolver, etc.), do not call this constructor. Instead, do the following: 091 * <ol> 092 * <li>Call the default no-arg constructor</li> 093 * <li>Set the Ini instance you wish to use via {@code #setIni}</li> 094 * <li>Set any other configuration properties</li> 095 * <li>Call {@link #init()}</li> 096 * </ol> 097 * 098 * @param resourcePath the resource path of the Ini config which will be inspected to create accounts, groups and 099 * permissions for this realm. 100 */ 101 public IniRealm(String resourcePath) { 102 this(); 103 Ini ini = Ini.fromResourcePath(resourcePath); 104 this.ini = ini; 105 this.resourcePath = resourcePath; 106 processDefinitions(ini); 107 } 108 109 public String getResourcePath() { 110 return resourcePath; 111 } 112 113 public void setResourcePath(String resourcePath) { 114 this.resourcePath = resourcePath; 115 } 116 117 /** 118 * Returns 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 * @return the Ini instance which will be inspected to create accounts, groups and permissions for this realm. 122 */ 123 public Ini getIni() { 124 return ini; 125 } 126 127 /** 128 * Sets the Ini instance used to configure this realm. Provided for JavaBeans-style configuration of this 129 * realm, particularly useful in Dependency Injection environments. 130 * 131 * @param ini the Ini instance which will be inspected to create accounts, groups and permissions for this realm. 132 */ 133 public void setIni(Ini ini) { 134 this.ini = ini; 135 } 136 137 @Override 138 protected void onInit() { 139 super.onInit(); 140 141 // This is an in-memory realm only - no need for an additional cache when we're already 142 // as memory-efficient as we can be. 143 144 Ini ini = getIni(); 145 String resourcePath = getResourcePath(); 146 147 if (!CollectionUtils.isEmpty(this.users) || !CollectionUtils.isEmpty(this.roles)) { 148 if (!CollectionUtils.isEmpty(ini)) { 149 LOGGER.warn("Users or Roles are already populated. Configured Ini instance will be ignored."); 150 } 151 if (StringUtils.hasText(resourcePath)) { 152 LOGGER.warn("Users or Roles are already populated. resourcePath '{}' will be ignored.", resourcePath); 153 } 154 155 LOGGER.debug("Instance is already populated with users or roles. No additional user/role population " 156 + "will be performed."); 157 return; 158 } 159 160 if (CollectionUtils.isEmpty(ini)) { 161 LOGGER.debug("No INI instance configuration present. Checking resourcePath..."); 162 163 if (StringUtils.hasText(resourcePath)) { 164 LOGGER.debug("Resource path {} defined. Creating INI instance.", resourcePath); 165 ini = Ini.fromResourcePath(resourcePath); 166 if (!CollectionUtils.isEmpty(ini)) { 167 setIni(ini); 168 } 169 } 170 } 171 172 if (CollectionUtils.isEmpty(ini)) { 173 String msg = "Ini instance and/or resourcePath resulted in null or empty Ini configuration. Cannot " 174 + "load account data."; 175 throw new IllegalStateException(msg); 176 } 177 178 processDefinitions(ini); 179 } 180 181 private void processDefinitions(Ini ini) { 182 if (CollectionUtils.isEmpty(ini)) { 183 LOGGER.warn("{} defined, but the ini instance is null or empty.", getClass().getSimpleName()); 184 return; 185 } 186 187 Ini.Section rolesSection = ini.getSection(ROLES_SECTION_NAME); 188 if (!CollectionUtils.isEmpty(rolesSection)) { 189 LOGGER.debug("Discovered the [{}] section. Processing...", ROLES_SECTION_NAME); 190 processRoleDefinitions(rolesSection); 191 } 192 193 Ini.Section usersSection = ini.getSection(USERS_SECTION_NAME); 194 if (!CollectionUtils.isEmpty(usersSection)) { 195 LOGGER.debug("Discovered the [{}] section. Processing...", USERS_SECTION_NAME); 196 processUserDefinitions(usersSection); 197 } else { 198 LOGGER.info("{} defined, but there is no [{}] section defined. This realm will not be populated with any " 199 + "users and it is assumed that they will be populated programatically. Users must be defined " 200 + "for this Realm instance to be useful.", getClass().getSimpleName(), USERS_SECTION_NAME); 201 } 202 } 203}