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.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 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 private Ini ini; //reference added in 1.2 for SHIRO-322 054 055 public IniRealm() { 056 super(); 057 } 058 059 /** 060 * This constructor will immediately process the definitions in the {@code Ini} argument. If you need to perform 061 * additional configuration before processing (e.g. setting a permissionResolver, etc), do not call this 062 * constructor. Instead, do the following: 063 * <ol> 064 * <li>Call the default no-arg constructor</li> 065 * <li>Set the Ini instance you wish to use via {@code #setIni}</li> 066 * <li>Set any other configuration properties</li> 067 * <li>Call {@link #init()}</li> 068 * </ol> 069 * 070 * @param ini the Ini instance which will be inspected to create accounts, groups and permissions for this realm. 071 */ 072 public IniRealm(Ini ini) { 073 this(); 074 processDefinitions(ini); 075 } 076 077 /** 078 * This constructor will immediately process the definitions in the {@code Ini} resolved from the specified 079 * {@code resourcePath}. If you need to perform additional configuration before processing (e.g. setting a 080 * permissionResolver, etc), do not call this constructor. Instead, do the following: 081 * <ol> 082 * <li>Call the default no-arg constructor</li> 083 * <li>Set the Ini instance you wish to use via {@code #setIni}</li> 084 * <li>Set any other configuration properties</li> 085 * <li>Call {@link #init()}</li> 086 * </ol> 087 * 088 * @param resourcePath the resource path of the Ini config which will be inspected to create accounts, groups and 089 * permissions for this realm. 090 */ 091 public IniRealm(String resourcePath) { 092 this(); 093 Ini ini = Ini.fromResourcePath(resourcePath); 094 this.ini = ini; 095 this.resourcePath = resourcePath; 096 processDefinitions(ini); 097 } 098 099 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}