You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@shiro.apache.org by lh...@apache.org on 2009/12/09 19:07:16 UTC
svn commit: r888899 - in /incubator/shiro/trunk/core/src:
main/java/org/apache/shiro/config/ main/java/org/apache/shiro/realm/
main/java/org/apache/shiro/realm/text/ test/java/org/apache/shiro/config/
Author: lhazlewood
Date: Wed Dec 9 18:07:16 2009
New Revision: 888899
URL: http://svn.apache.org/viewvc?rev=888899&view=rev
Log:
SHIRO-116 - ensured that an implicit IniRealm would be created if the startup configuration contains [users] or [roles] ini sections. Also provided the ability to turn caching on or off at a global level via CachingRealm.setCachingEnabled.
Added:
incubator/shiro/trunk/core/src/test/java/org/apache/shiro/config/IniSecurityManagerFactoryTest.java
Modified:
incubator/shiro/trunk/core/src/main/java/org/apache/shiro/config/IniSecurityManagerFactory.java
incubator/shiro/trunk/core/src/main/java/org/apache/shiro/realm/AuthorizingRealm.java
incubator/shiro/trunk/core/src/main/java/org/apache/shiro/realm/CachingRealm.java
incubator/shiro/trunk/core/src/main/java/org/apache/shiro/realm/SimpleAccountRealm.java
incubator/shiro/trunk/core/src/main/java/org/apache/shiro/realm/text/IniRealm.java
Modified: incubator/shiro/trunk/core/src/main/java/org/apache/shiro/config/IniSecurityManagerFactory.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/core/src/main/java/org/apache/shiro/config/IniSecurityManagerFactory.java?rev=888899&r1=888898&r2=888899&view=diff
==============================================================================
--- incubator/shiro/trunk/core/src/main/java/org/apache/shiro/config/IniSecurityManagerFactory.java (original)
+++ incubator/shiro/trunk/core/src/main/java/org/apache/shiro/config/IniSecurityManagerFactory.java Wed Dec 9 18:07:16 2009
@@ -23,9 +23,11 @@
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.realm.RealmFactory;
+import org.apache.shiro.realm.text.IniRealm;
import org.apache.shiro.util.CollectionUtils;
import org.apache.shiro.util.Factory;
import org.apache.shiro.util.LifecycleUtils;
+import org.apache.shiro.util.Nameable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -41,6 +43,8 @@
public static final String MAIN_SECTION_NAME = "main";
+ public static final String SECURITY_MANAGER_NAME = "securityManager";
+
private static transient final Logger log = LoggerFactory.getLogger(IniSecurityManagerFactory.class);
/**
@@ -77,68 +81,125 @@
//try the default:
mainSection = ini.getSection(Ini.DEFAULT_SECTION_NAME);
}
- SecurityManager securityManager;
- if (CollectionUtils.isEmpty(mainSection)) {
- if (log.isInfoEnabled()) {
- log.info("No main/default section was found in INI resource [" + ini + "]. A simple default " +
- "SecurityManager instance will be created automatically.");
- }
- securityManager = createDefaultInstance();
- } else {
- securityManager = doCreateSecurityManager(mainSection);
- }
- return securityManager;
+ return doCreateSecurityManager(ini, mainSection);
}
- @SuppressWarnings({"unchecked"})
- protected SecurityManager doCreateSecurityManager(Ini.Section mainSection) {
-
+ private Map<String, Object> buildMainInstances(Ini.Section main) {
Map<String, Object> defaults = new LinkedHashMap<String, Object>();
-
SecurityManager securityManager = createDefaultInstance();
defaults.put("securityManager", securityManager);
+ return buildInstances(main, defaults);
+ }
- ReflectionBuilder builder = new ReflectionBuilder(defaults);
- Map<String, Object> objects = builder.buildObjects(mainSection);
+ @SuppressWarnings({"unchecked"})
+ protected Map<String, Object> buildInstances(Ini.Section section, Map<String, Object> defaults) {
+ ReflectionBuilder builder;
+ if (CollectionUtils.isEmpty(defaults)) {
+ builder = new ReflectionBuilder();
+ } else {
+ builder = new ReflectionBuilder(defaults);
+ }
+ return builder.buildObjects(section);
+ }
+
+ private void addToRealms(Collection<Realm> realms, RealmFactory factory) {
+ LifecycleUtils.init(factory);
+ Collection<Realm> factoryRealms = factory.getRealms();
+ if (factoryRealms != null && !factoryRealms.isEmpty()) {
+ realms.addAll(factoryRealms);
+ }
+ }
+
+ private Collection<Realm> getRealms(Map<String, Object> instances) {
//realms and realm factory might have been created - pull them out first so we can
//initialize the securityManager:
List<Realm> realms = new ArrayList<Realm>();
//iterate over the map entries to pull out the realm factory(s):
- for (Map.Entry<String, Object> entry : objects.entrySet()) {
+ for (Map.Entry<String, Object> entry : instances.entrySet()) {
+
String name = entry.getKey();
Object value = entry.getValue();
- if (value instanceof SecurityManager) {
- securityManager = (SecurityManager) value;
- } else if (value instanceof RealmFactory) {
- RealmFactory factory = (RealmFactory) value;
- LifecycleUtils.init(factory);
- Collection<Realm> factoryRealms = factory.getRealms();
- if (factoryRealms != null && !factoryRealms.isEmpty()) {
- realms.addAll(factoryRealms);
- }
+
+ if (value instanceof RealmFactory) {
+ addToRealms(realms, (RealmFactory) value);
} else if (value instanceof Realm) {
Realm realm = (Realm) value;
//set the name if null:
String existingName = realm.getName();
if (existingName == null || existingName.startsWith(realm.getClass().getName())) {
- try {
- builder.applyProperty(realm, "name", name);
- } catch (Exception ignored) {
- log.debug("Unable to apply 'name' property value {} to realm {}.", name, realm);
+ if (realm instanceof Nameable) {
+ ((Nameable) realm).setName(name);
+ log.debug("Applied name '{}' to Nameable realm instance {}", name, realm);
+ } else {
+ log.info("Realm does not implement the {} interface. Configured name will not be applied.",
+ Nameable.class.getName());
}
}
realms.add(realm);
}
}
+ return realms;
+ }
+
+ protected void applyRealmsToSecurityManager(Collection<Realm> realms, SecurityManager securityManager) {
+ if (!(securityManager instanceof RealmSecurityManager)) {
+ String msg = realms.size() + " Realms were configured, but the underlying SecurityManager " +
+ "instance is not a " + RealmSecurityManager.class.getName() + " instance. This is required " +
+ "to apply any configured Realms.";
+ throw new ConfigurationException(msg);
+ }
+ ((RealmSecurityManager) securityManager).setRealms(realms);
+ //initialize the realms now that they have been configured on the security manager
+ LifecycleUtils.init(realms);
+ }
+
+ /**
+ * Returns {@code true} if the Ini contains account data and a {@code Realm} should be implicitly
+ * {@link #createRealm(Ini) created} to reflect the account data, {@code false} if no realm should be implicitly
+ * created.
+ *
+ * @param ini the Ini instance to inspect for account data resulting in an implicitly created realm.
+ * @return {@code true} if the Ini contains account data and a {@code Realm} should be implicitly
+ * {@link #createRealm(Ini) created} to reflect the account data, {@code false} if no realm should be
+ * implicitly created.
+ */
+ protected boolean shouldImplicitlyCreateRealm(Ini ini) {
+ return !CollectionUtils.isEmpty(ini.getSection(IniRealm.ROLES_SECTION_NAME)) ||
+ !CollectionUtils.isEmpty(ini.getSection(IniRealm.USERS_SECTION_NAME));
+ }
+
+ /**
+ * Creates a {@code Realm} from the Ini instance containing account data.
+ *
+ * @param ini the Ini instance from which to acquire the account data.
+ * @return a new Realm instance reflecting the account data discovered in the {@code Ini}.
+ */
+ protected Realm createRealm(Ini ini) {
+ return new IniRealm(ini);
+ }
+
+ @SuppressWarnings({"unchecked"})
+ protected SecurityManager doCreateSecurityManager(Ini ini, Ini.Section mainSection) {
+
+ Map<String, Object> objects = buildMainInstances(mainSection);
+
+ SecurityManager securityManager = (SecurityManager) objects.get(SECURITY_MANAGER_NAME);
+
+ //realms and realm factory might have been created - pull them out first so we can
+ //initialize the securityManager:
+ Collection<Realm> realms = getRealms(objects);
+
+ if (shouldImplicitlyCreateRealm(ini)) {
+ Realm realm = createRealm(ini);
+ realms.add(realm);
+ }
+
//set them on the SecurityManager
- if (!realms.isEmpty()) {
- if (securityManager instanceof RealmSecurityManager) {
- ((RealmSecurityManager) securityManager).setRealms(realms);
- }
- LifecycleUtils.init(realms);
+ if (!CollectionUtils.isEmpty(realms)) {
+ applyRealmsToSecurityManager(realms, securityManager);
}
return securityManager;
Modified: incubator/shiro/trunk/core/src/main/java/org/apache/shiro/realm/AuthorizingRealm.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/core/src/main/java/org/apache/shiro/realm/AuthorizingRealm.java?rev=888899&r1=888898&r2=888899&view=diff
==============================================================================
--- incubator/shiro/trunk/core/src/main/java/org/apache/shiro/realm/AuthorizingRealm.java (original)
+++ incubator/shiro/trunk/core/src/main/java/org/apache/shiro/realm/AuthorizingRealm.java Wed Dec 9 18:07:16 2009
@@ -77,6 +77,7 @@
/**
* The cache used by this realm to store AuthorizationInfo instances associated with individual Subject principals.
*/
+ private boolean authorizationCachingEnabled = true;
private Cache authorizationCache = null;
private String authorizationCacheName = null;
@@ -122,6 +123,33 @@
this.authorizationCacheName = authorizationCacheName;
}
+ /**
+ * Returns {@code true} if authorization caching should be utilized if a {@link CacheManager} has been
+ * {@link #setCacheManager(org.apache.shiro.cache.CacheManager) configured}, {@code false} otherwise.
+ * <p/>
+ * The default value is {@code true}.
+ *
+ * @return {@code true} if authorization caching should be utilized, {@code false} otherwise.
+ */
+ public boolean isAuthorizationCachingEnabled() {
+ return isCachingEnabled() && authorizationCachingEnabled;
+ }
+
+ /**
+ * Sets whether or not authorization caching should be utilized if a {@link CacheManager} has been
+ * {@link #setCacheManager(org.apache.shiro.cache.CacheManager) configured}, {@code false} otherwise.
+ * <p/>
+ * The default value is {@code true}.
+ *
+ * @param authorizationCachingEnabled the value to set
+ */
+ public void setAuthorizationCachingEnabled(boolean authorizationCachingEnabled) {
+ this.authorizationCachingEnabled = authorizationCachingEnabled;
+ if (authorizationCachingEnabled) {
+ setCachingEnabled(true);
+ }
+ }
+
public PermissionResolver getPermissionResolver() {
return permissionResolver;
}
@@ -154,7 +182,9 @@
* </ol>
*/
public final void init() {
- initAuthorizationCache();
+ if (isAuthorizationCachingEnabled()) {
+ initAuthorizationCache();
+ }
onInit();
}
@@ -163,7 +193,9 @@
protected void afterCacheManagerSet() {
this.authorizationCache = null;
- initAuthorizationCache();
+ if (isAuthorizationCachingEnabled()) {
+ initAuthorizationCache();
+ }
}
protected void afterAuthorizationCacheSet() {
@@ -175,6 +207,11 @@
}
public void initAuthorizationCache() {
+ if (!isAuthorizationCachingEnabled()) {
+ log.debug("Authorization caching is disabled. Returning immediately.");
+ return;
+ }
+
if (log.isTraceEnabled()) {
log.trace("Initializing authorization cache.");
}
Modified: incubator/shiro/trunk/core/src/main/java/org/apache/shiro/realm/CachingRealm.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/core/src/main/java/org/apache/shiro/realm/CachingRealm.java?rev=888899&r1=888898&r2=888899&view=diff
==============================================================================
--- incubator/shiro/trunk/core/src/main/java/org/apache/shiro/realm/CachingRealm.java (original)
+++ incubator/shiro/trunk/core/src/main/java/org/apache/shiro/realm/CachingRealm.java Wed Dec 9 18:07:16 2009
@@ -20,27 +20,31 @@
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.CacheManagerAware;
+import org.apache.shiro.util.Nameable;
+
+import java.util.concurrent.atomic.AtomicInteger;
/**
* <p>A very basic abstract extension point for the {@link Realm} interface that provides logging and caching support.
- *
+ * <p/>
* <p>All actual Realm method implementations are left to subclasses.
*
* @author Les Hazlewood
* @since 0.9
*/
-public abstract class CachingRealm implements Realm, CacheManagerAware {
+public abstract class CachingRealm implements Realm, Nameable, CacheManagerAware {
//TODO - complete JavaDoc
- private static int INSTANCE_COUNT = 0;
+ private static final AtomicInteger INSTANCE_COUNT = new AtomicInteger();
/*--------------------------------------------
| I N S T A N C E V A R I A B L E S |
============================================*/
- private String name = getClass().getName() + "_" + INSTANCE_COUNT++;
+ private String name = getClass().getName() + "_" + INSTANCE_COUNT.getAndIncrement();
+ private boolean cachingEnabled = true;
private CacheManager cacheManager;
public CachingRealm() {
@@ -63,7 +67,7 @@
/**
* Sets the <tt>CacheManager</tt> to be used for data caching to reduce EIS round trips.
- *
+ * <p/>
* <p>This property is <tt>null</tt> by default, indicating that caching is turned off.
*
* @param authzInfoCacheManager the <tt>CacheManager</tt> to use for data caching, or <tt>null</tt> to disable caching.
@@ -73,6 +77,31 @@
afterCacheManagerSet();
}
+ /**
+ * Returns {@code true} if caching should be used if a {@link CacheManager} has been
+ * {@link #setCacheManager(org.apache.shiro.cache.CacheManager) configured}, {@code false} otherwise.
+ * <p/>
+ * The default value is {@code true} since the large majority of Realms will benefit from caching if a CacheManager
+ * has been configured. However, memory-only realms should set this value to {@code false} since they would
+ * manage account data in memory already lookups would already be as efficient as possible.
+ *
+ * @return {@code true} if caching will be globally enabled if a {@link CacheManager} has been
+ * configured, {@code false} otherwise
+ */
+ public boolean isCachingEnabled() {
+ return cachingEnabled;
+ }
+
+ /**
+ * Sets whether or not caching should be used if a {@link CacheManager} has been
+ * {@link #setCacheManager(org.apache.shiro.cache.CacheManager) configured}.
+ *
+ * @param cachingEnabled whether or not to globally enable caching for this realm.
+ */
+ public void setCachingEnabled(boolean cachingEnabled) {
+ this.cachingEnabled = cachingEnabled;
+ }
+
protected void afterCacheManagerSet() {
}
Modified: incubator/shiro/trunk/core/src/main/java/org/apache/shiro/realm/SimpleAccountRealm.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/core/src/main/java/org/apache/shiro/realm/SimpleAccountRealm.java?rev=888899&r1=888898&r2=888899&view=diff
==============================================================================
--- incubator/shiro/trunk/core/src/main/java/org/apache/shiro/realm/SimpleAccountRealm.java (original)
+++ incubator/shiro/trunk/core/src/main/java/org/apache/shiro/realm/SimpleAccountRealm.java Wed Dec 9 18:07:16 2009
@@ -52,6 +52,9 @@
public SimpleAccountRealm() {
this.users = new LinkedHashMap<String, SimpleAccount>();
this.roles = new LinkedHashMap<String, SimpleRole>();
+ //SimpleAccountRealms are memory-only realms - no need for an additional cache mechanism since we're
+ //already as memory-efficient as we can be:
+ setCachingEnabled(false);
}
public SimpleAccountRealm(String name) {
@@ -145,8 +148,4 @@
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return this.users.get(getUsername(principals));
}
-
- protected Object getAuthorizationCacheKey(PrincipalCollection principals) {
- return getAvailablePrincipal(principals); //returns the username, being the only principal from this Realm
- }
}
\ No newline at end of file
Modified: incubator/shiro/trunk/core/src/main/java/org/apache/shiro/realm/text/IniRealm.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/core/src/main/java/org/apache/shiro/realm/text/IniRealm.java?rev=888899&r1=888898&r2=888899&view=diff
==============================================================================
--- incubator/shiro/trunk/core/src/main/java/org/apache/shiro/realm/text/IniRealm.java (original)
+++ incubator/shiro/trunk/core/src/main/java/org/apache/shiro/realm/text/IniRealm.java Wed Dec 9 18:07:16 2009
@@ -36,6 +36,9 @@
* ...
* [roles]
* # One or more {@link org.apache.shiro.realm.text.TextConfigurationRealm#setRoleDefinitions(String) role definitions}</pre>
+ * <p/>
+ * This class also supports setting the {@link #setResourcePath(String) resourcePath} property to create account
+ * data from an .ini resource. This will only be used if there isn't already account data in the Realm.
*/
public class IniRealm extends TextConfigurationRealm {
@@ -65,13 +68,25 @@
@Override
public void onInit() {
+ // We override init() instead of onInit() because we _don't_ want any caches to be created
+ // (see the superclass init() code).
+ // This is an in-memory realm only - no need for an additional cache when we're already
+ // as memory-efficient as we can be.
String resourcePath = getResourcePath();
- if (StringUtils.hasText(resourcePath)) {
- log.debug("Resource path {} defined. Creating INI instance.", resourcePath);
- Ini ini = Ini.fromResourcePath(resourcePath);
- processDefinitions(ini);
+
+ if (CollectionUtils.isEmpty(this.users) && CollectionUtils.isEmpty(this.roles)) {
+ //no account data manually populated - try the resource path:
+ if (StringUtils.hasText(resourcePath)) {
+ log.debug("Resource path {} defined. Creating INI instance.", resourcePath);
+ Ini ini = Ini.fromResourcePath(resourcePath);
+ processDefinitions(ini);
+ } else {
+ throw new IllegalStateException("No resource path was specified. Cannot load account data.");
+ }
} else {
- throw new IllegalStateException("No resource path was specified. Cannot load account data.");
+ if (StringUtils.hasText(resourcePath)) {
+ log.warn("Users or Roles are already populated. Resource path property will be ignored.");
+ }
}
}
Added: incubator/shiro/trunk/core/src/test/java/org/apache/shiro/config/IniSecurityManagerFactoryTest.java
URL: http://svn.apache.org/viewvc/incubator/shiro/trunk/core/src/test/java/org/apache/shiro/config/IniSecurityManagerFactoryTest.java?rev=888899&view=auto
==============================================================================
--- incubator/shiro/trunk/core/src/test/java/org/apache/shiro/config/IniSecurityManagerFactoryTest.java (added)
+++ incubator/shiro/trunk/core/src/test/java/org/apache/shiro/config/IniSecurityManagerFactoryTest.java Wed Dec 9 18:07:16 2009
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.config;
+
+import static junit.framework.Assert.*;
+import org.apache.shiro.mgt.DefaultSecurityManager;
+import org.apache.shiro.mgt.SecurityManager;
+import org.apache.shiro.realm.Realm;
+import org.apache.shiro.realm.text.IniRealm;
+import org.apache.shiro.realm.text.PropertiesRealm;
+import org.junit.Test;
+
+import java.util.Collection;
+
+/**
+ * Unit tests for the {@link IniSecurityManagerFactory} implementation.
+ *
+ * @author The Apache Shiro Project (shiro-dev@incubator.apache.org)
+ * @since 1.0
+ */
+public class IniSecurityManagerFactoryTest {
+
+ IniSecurityManagerFactory factory;
+
+ @Test
+ public void testGetInstanceWithoutIni() {
+ IniSecurityManagerFactory factory = new IniSecurityManagerFactory();
+ SecurityManager sm = factory.getInstance();
+ assertNotNull(sm);
+ assertTrue(sm instanceof DefaultSecurityManager);
+ }
+
+ @Test
+ public void testGetInstanceWithEmptyIni() {
+ Ini ini = new Ini();
+ IniSecurityManagerFactory factory = new IniSecurityManagerFactory(ini);
+ SecurityManager sm = factory.getInstance();
+ assertNotNull(sm);
+ assertTrue(sm instanceof DefaultSecurityManager);
+ }
+
+ @Test
+ public void testGetInstanceWithSimpleIni() {
+ Ini ini = new Ini();
+ ini.setSectionProperty(IniSecurityManagerFactory.MAIN_SECTION_NAME, "securityManager.globalSessionTimeout", "5000");
+ IniSecurityManagerFactory factory = new IniSecurityManagerFactory(ini);
+ SecurityManager sm = factory.getInstance();
+ assertNotNull(sm);
+ assertTrue(sm instanceof DefaultSecurityManager);
+ assertEquals(5000, ((DefaultSecurityManager) sm).getGlobalSessionTimeout());
+ }
+
+ @Test
+ public void testGetInstanceWithConfiguredRealm() {
+ Ini ini = new Ini();
+ Ini.Section section = ini.addSection(IniSecurityManagerFactory.MAIN_SECTION_NAME);
+ section.put("propsRealm", PropertiesRealm.class.getName());
+
+ IniSecurityManagerFactory factory = new IniSecurityManagerFactory(ini);
+ SecurityManager sm = factory.getInstance();
+ assertNotNull(sm);
+ assertTrue(sm instanceof DefaultSecurityManager);
+ Collection<Realm> realms = ((DefaultSecurityManager) sm).getRealms();
+ assertEquals(1, realms.size());
+ Realm realm = realms.iterator().next();
+ assertTrue(realm instanceof PropertiesRealm);
+ }
+
+ @Test
+ public void testGetInstanceWithAutomaticallyCreatedIniRealm() {
+ Ini ini = new Ini();
+ Ini.Section section = ini.addSection(IniRealm.USERS_SECTION_NAME);
+ section.put("admin", "admin");
+
+ IniSecurityManagerFactory factory = new IniSecurityManagerFactory(ini);
+ SecurityManager sm = factory.getInstance();
+ assertNotNull(sm);
+ assertTrue(sm instanceof DefaultSecurityManager);
+ Collection<Realm> realms = ((DefaultSecurityManager) sm).getRealms();
+ assertEquals(1, realms.size());
+ Realm realm = realms.iterator().next();
+ assertTrue(realm instanceof IniRealm);
+ assertTrue(((IniRealm) realm).accountExists("admin"));
+ }
+
+
+}