diff options
Diffstat (limited to 'odl-aaa-moon/aaa/aaa-authn-store/src')
6 files changed, 305 insertions, 0 deletions
diff --git a/odl-aaa-moon/aaa/aaa-authn-store/src/main/java/org/opendaylight/aaa/store/Activator.java b/odl-aaa-moon/aaa/aaa-authn-store/src/main/java/org/opendaylight/aaa/store/Activator.java new file mode 100644 index 00000000..f3299723 --- /dev/null +++ b/odl-aaa-moon/aaa/aaa-authn-store/src/main/java/org/opendaylight/aaa/store/Activator.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2014, 2015 Hewlett-Packard Development Company, L.P. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.aaa.store; + +import java.util.Dictionary; +import org.apache.felix.dm.DependencyActivatorBase; +import org.apache.felix.dm.DependencyManager; +import org.opendaylight.aaa.api.TokenStore; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.service.cm.ManagedService; + +/** + * An activator for the default datastore implementation of {@link TokenStore}. + * + * @author liemmn + */ +public class Activator extends DependencyActivatorBase { + + private static final String TOKEN_PID = "org.opendaylight.aaa.tokens"; + + @Override + public void init(BundleContext context, DependencyManager manager) throws Exception { + DefaultTokenStore ts = new DefaultTokenStore(); + manager.add(createComponent().setInterface(new String[] { TokenStore.class.getName() }, + null).setImplementation(ts)); + context.registerService(ManagedService.class.getName(), ts, + addPid(DefaultTokenStore.defaults)); + } + + @Override + public void destroy(BundleContext context, DependencyManager manager) throws Exception { + } + + private Dictionary<String, ?> addPid(Dictionary<String, String> dict) { + dict.put(Constants.SERVICE_PID, TOKEN_PID); + return dict; + } +} diff --git a/odl-aaa-moon/aaa/aaa-authn-store/src/main/java/org/opendaylight/aaa/store/DefaultTokenStore.java b/odl-aaa-moon/aaa/aaa-authn-store/src/main/java/org/opendaylight/aaa/store/DefaultTokenStore.java new file mode 100644 index 00000000..df65be32 --- /dev/null +++ b/odl-aaa-moon/aaa/aaa-authn-store/src/main/java/org/opendaylight/aaa/store/DefaultTokenStore.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2014, 2015 Hewlett-Packard Development Company, L.P. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.aaa.store; + +import java.io.File; +import java.lang.management.ManagementFactory; +import java.util.Dictionary; +import java.util.Hashtable; +import java.util.concurrent.locks.ReentrantLock; +import javax.management.MBeanServer; +import net.sf.ehcache.Cache; +import net.sf.ehcache.CacheManager; +import net.sf.ehcache.Element; +import net.sf.ehcache.config.CacheConfiguration; +import net.sf.ehcache.management.ManagementService; +import org.apache.felix.dm.Component; +import org.opendaylight.aaa.api.Authentication; +import org.opendaylight.aaa.api.TokenStore; +import org.osgi.service.cm.ConfigurationException; +import org.osgi.service.cm.ManagedService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A default token store for STS. + * + * @author liemmn + * + */ +public class DefaultTokenStore implements TokenStore, ManagedService { + private static final Logger LOG = LoggerFactory.getLogger(DefaultTokenStore.class); + private static final String TOKEN_STORE_CONFIG_ERR = "Token store configuration error"; + + private static final String TOKEN_CACHE_MANAGER = "org.opendaylight.aaa"; + private static final String TOKEN_CACHE = "tokens"; + private static final String EHCACHE_XML = "etc/ehcache.xml"; + + static final String MAX_CACHED_MEMORY = "maxCachedTokensInMemory"; + static final String MAX_CACHED_DISK = "maxCachedTokensOnDisk"; + static final String SECS_TO_LIVE = "secondsToLive"; + static final String SECS_TO_IDLE = "secondsToIdle"; + + // Defaults (needed only for non-Karaf deployments) + static final Dictionary<String, String> defaults = new Hashtable<>(); + static { + defaults.put(MAX_CACHED_MEMORY, Long.toString(10000)); + defaults.put(MAX_CACHED_DISK, Long.toString(1000000)); + defaults.put(SECS_TO_IDLE, Long.toString(3600)); + defaults.put(SECS_TO_LIVE, Long.toString(3600)); + } + + // Token cache lock + private static final ReentrantLock cacheLock = new ReentrantLock(); + + // Token cache + private Cache tokens; + + // This should be a singleton + DefaultTokenStore() { + } + + // Called by DM when all required dependencies are satisfied. + void init(Component c) { + File ehcache = new File(EHCACHE_XML); + CacheManager cm; + if (ehcache.exists()) { + cm = CacheManager.create(ehcache.getAbsolutePath()); + tokens = cm.getCache(TOKEN_CACHE); + LOG.info("Initialized token store with custom cache config"); + } else { + cm = CacheManager.getInstance(); + tokens = new Cache( + new CacheConfiguration(TOKEN_CACHE, + Integer.parseInt(defaults.get(MAX_CACHED_MEMORY))).maxEntriesLocalDisk( + Integer.parseInt(defaults.get(MAX_CACHED_DISK))) + .timeToLiveSeconds( + Long.parseLong(defaults.get(SECS_TO_LIVE))) + .timeToIdleSeconds( + Long.parseLong(defaults.get(SECS_TO_IDLE)))); + cm.addCache(tokens); + LOG.info("Initialized token store with default cache config"); + } + cm.setName(TOKEN_CACHE_MANAGER); + + // JMX for cache management + MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer(); + ManagementService.registerMBeans(cm, mBeanServer, false, false, false, true); + } + + // Called on shutdown + void destroy() { + LOG.info("Shutting down token store..."); + CacheManager.getInstance().shutdown(); + } + + @Override + public Authentication get(String token) { + Element elem = tokens.get(token); + return (Authentication) ((elem != null) ? elem.getObjectValue() : null); + } + + @Override + public void put(String token, Authentication auth) { + tokens.put(new Element(token, auth)); + } + + @Override + public boolean delete(String token) { + return tokens.remove(token); + } + + @Override + public long tokenExpiration() { + return tokens.getCacheConfiguration().getTimeToLiveSeconds(); + } + + @Override + public void updated(@SuppressWarnings("rawtypes") Dictionary props) + throws ConfigurationException { + LOG.info("Updating token store configuration..."); + if (props == null) { + // Someone deleted the configuration, use defaults + props = defaults; + } + reconfig(props); + } + + // Refresh cache configuration... + private void reconfig(@SuppressWarnings("rawtypes") Dictionary props) + throws ConfigurationException { + cacheLock.lock(); + try { + long secsToIdle = Long.parseLong(props.get(SECS_TO_IDLE).toString()); + long secsToLive = Long.parseLong(props.get(SECS_TO_LIVE).toString()); + int maxMem = Integer.parseInt(props.get(MAX_CACHED_MEMORY).toString()); + int maxDisk = Integer.parseInt(props.get(MAX_CACHED_DISK).toString()); + CacheConfiguration config = tokens.getCacheConfiguration(); + config.setTimeToIdleSeconds(secsToIdle); + config.setTimeToLiveSeconds(secsToLive); + config.maxEntriesLocalHeap(maxMem); + config.maxEntriesLocalDisk(maxDisk); + } catch (Throwable t) { + throw new ConfigurationException(null, TOKEN_STORE_CONFIG_ERR, t); + } finally { + cacheLock.unlock(); + } + } +} diff --git a/odl-aaa-moon/aaa/aaa-authn-store/src/main/resources/OSGI-INF/metatype/metatype.properties b/odl-aaa-moon/aaa/aaa-authn-store/src/main/resources/OSGI-INF/metatype/metatype.properties new file mode 100644 index 00000000..b88d5c10 --- /dev/null +++ b/odl-aaa-moon/aaa/aaa-authn-store/src/main/resources/OSGI-INF/metatype/metatype.properties @@ -0,0 +1,14 @@ +org.opendaylight.aaa.tokens.name = Opendaylight AAA Token Configuration +org.opendaylight.aaa.tokens.description = Configuration for AAA tokens +org.opendaylight.aaa.tokens.maxCachedTokensInMemory.name = Memory Configuration +org.opendaylight.aaa.tokens.maxCachedTokensInMemory.description = Maximum number of \ +tokens in memory +org.opendaylight.aaa.tokens.maxCachedTokensOnDisk.name = Disk Configuration +org.opendaylight.aaa.tokens.maxCachedTokensOnDisk.description = Maximum number of \ +tokens in memory +org.opendaylight.aaa.tokens.secondsToLive.name = Token Expiration +org.opendaylight.aaa.tokens.secondsToLive.description = Maximum number of \ +seconds a token can exist regardless of use. Zero (0) means never expires. +org.opendaylight.aaa.tokens.secondsToIdle.name = Unused Token Expiration +org.opendaylight.aaa.tokens.secondsToIdle.description = Maximum number of \ +seconds a token can exist without being accessed. Zero (0) means never expires.
\ No newline at end of file diff --git a/odl-aaa-moon/aaa/aaa-authn-store/src/main/resources/OSGI-INF/metatype/metatype.xml b/odl-aaa-moon/aaa/aaa-authn-store/src/main/resources/OSGI-INF/metatype/metatype.xml new file mode 100644 index 00000000..d04874f4 --- /dev/null +++ b/odl-aaa-moon/aaa/aaa-authn-store/src/main/resources/OSGI-INF/metatype/metatype.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<metatype:MetaData xmlns:metatype="http://www.osgi.org/xmlns/metatype/v1.0.0" + localization="OSGI-INF/metatype/metatype"> + <OCD id="org.opendaylight.aaa.tokens" name="%org.opendaylight.aaa.tokens.name" + description="%org.opendaylight.aaa.tokens.description"> + <AD id="maxCachedTokensInMemory" type="Long" default="10000" + name="%org.opendaylight.aaa.tokens.maxCachedTokensInMemory.name" + description="%org.opendaylight.aaa.tokens.maxCachedTokensInMemory.description" /> + <AD id="maxCachedTokensOnDisk" type="Long" default="1000000" + name="%org.opendaylight.aaa.tokens.maxCachedTokensOnDisk.name" + description="%org.opendaylight.aaa.tokens.maxCachedTokensOnDisk.description" /> + <AD id="secondsToLive" type="Long" default="3600" + name="%org.opendaylight.aaa.tokens.secondsToLive.name" + description="%org.opendaylight.aaa.tokens.secondsToLive.description" /> + <AD id="secondsToIdle" type="Long" default="3600" + name="%org.opendaylight.aaa.tokens.secondsToIdle.name" + description="%org.opendaylight.aaa.tokens.secondsToIdle.description" /> + </OCD> + <Designate pid="org.opendaylight.aaa.tokens"> + <Object ocdref="org.opendaylight.aaa.tokens" /> + </Designate> +</metatype:MetaData>
\ No newline at end of file diff --git a/odl-aaa-moon/aaa/aaa-authn-store/src/main/resources/tokens.cfg b/odl-aaa-moon/aaa/aaa-authn-store/src/main/resources/tokens.cfg new file mode 100644 index 00000000..d3dda90e --- /dev/null +++ b/odl-aaa-moon/aaa/aaa-authn-store/src/main/resources/tokens.cfg @@ -0,0 +1,4 @@ +maxCachedTokensInMemory=10000 +maxCachedTokensOnDisk=1000000 +secondsToLive=3600 +secondsToIdle=3600
\ No newline at end of file diff --git a/odl-aaa-moon/aaa/aaa-authn-store/src/test/java/org/opendaylight/aaa/store/DefaultTokenStoreTest.java b/odl-aaa-moon/aaa/aaa-authn-store/src/test/java/org/opendaylight/aaa/store/DefaultTokenStoreTest.java new file mode 100644 index 00000000..e5c837bf --- /dev/null +++ b/odl-aaa-moon/aaa/aaa-authn-store/src/test/java/org/opendaylight/aaa/store/DefaultTokenStoreTest.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2014, 2015 Hewlett-Packard Development Company, L.P. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.aaa.store; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.mock; +import static org.opendaylight.aaa.store.DefaultTokenStore.MAX_CACHED_DISK; +import static org.opendaylight.aaa.store.DefaultTokenStore.MAX_CACHED_MEMORY; +import static org.opendaylight.aaa.store.DefaultTokenStore.SECS_TO_IDLE; +import static org.opendaylight.aaa.store.DefaultTokenStore.SECS_TO_LIVE; + +import java.util.Dictionary; +import java.util.Hashtable; +import org.apache.felix.dm.Component; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.opendaylight.aaa.AuthenticationBuilder; +import org.opendaylight.aaa.ClaimBuilder; +import org.opendaylight.aaa.api.Authentication; +import org.osgi.service.cm.ConfigurationException; + +public class DefaultTokenStoreTest { + private static final String FOO_TOKEN = "foo_token"; + private final DefaultTokenStore dts = new DefaultTokenStore(); + private static final Dictionary<String, String> config = new Hashtable<>(); + static { + config.put(MAX_CACHED_MEMORY, Long.toString(3)); + config.put(MAX_CACHED_DISK, Long.toString(3)); + config.put(SECS_TO_IDLE, Long.toString(1)); + config.put(SECS_TO_LIVE, Long.toString(1)); + } + + @Before + public void setup() throws ConfigurationException { + dts.init(mock(Component.class)); + dts.updated(config); + } + + @After + public void teardown() { + dts.destroy(); + } + + @Test + public void testCache() throws InterruptedException { + Authentication auth = new AuthenticationBuilder(new ClaimBuilder().setUser("foo") + .setUserId("1234") + .addRole("admin").build()).build(); + dts.put(FOO_TOKEN, auth); + assertEquals(auth, dts.get(FOO_TOKEN)); + dts.delete(FOO_TOKEN); + assertNull(dts.get(FOO_TOKEN)); + dts.put(FOO_TOKEN, auth); + Thread.sleep(1200); + assertNull(dts.get(FOO_TOKEN)); + } + +} |