aboutsummaryrefslogtreecommitdiffstats
path: root/odl-aaa-moon/aaa-authn-sts
diff options
context:
space:
mode:
Diffstat (limited to 'odl-aaa-moon/aaa-authn-sts')
-rw-r--r--odl-aaa-moon/aaa-authn-sts/pom.xml112
-rw-r--r--odl-aaa-moon/aaa-authn-sts/src/main/java/org/opendaylight/aaa/sts/Activator.java207
-rw-r--r--odl-aaa-moon/aaa-authn-sts/src/main/java/org/opendaylight/aaa/sts/AnonymousPasswordValidator.java30
-rw-r--r--odl-aaa-moon/aaa-authn-sts/src/main/java/org/opendaylight/aaa/sts/AnonymousRefreshTokenValidator.java29
-rw-r--r--odl-aaa-moon/aaa-authn-sts/src/main/java/org/opendaylight/aaa/sts/OAuthRequest.java42
-rw-r--r--odl-aaa-moon/aaa-authn-sts/src/main/java/org/opendaylight/aaa/sts/ServiceLocator.java141
-rw-r--r--odl-aaa-moon/aaa-authn-sts/src/main/java/org/opendaylight/aaa/sts/TokenAuthFilter.java148
-rw-r--r--odl-aaa-moon/aaa-authn-sts/src/main/java/org/opendaylight/aaa/sts/TokenEndpoint.java242
-rw-r--r--odl-aaa-moon/aaa-authn-sts/src/main/resources/WEB-INF/web.xml23
-rw-r--r--odl-aaa-moon/aaa-authn-sts/src/test/java/org/opendaylight/aaa/sts/RestFixture.java34
-rw-r--r--odl-aaa-moon/aaa-authn-sts/src/test/java/org/opendaylight/aaa/sts/TokenAuthTest.java94
-rw-r--r--odl-aaa-moon/aaa-authn-sts/src/test/java/org/opendaylight/aaa/sts/TokenEndpointTest.java164
12 files changed, 1266 insertions, 0 deletions
diff --git a/odl-aaa-moon/aaa-authn-sts/pom.xml b/odl-aaa-moon/aaa-authn-sts/pom.xml
new file mode 100644
index 00000000..25ac0fe6
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-sts/pom.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.aaa</groupId>
+ <artifactId>aaa-parent</artifactId>
+ <version>0.3.1-Beryllium-SR1</version>
+ <relativePath>../parent</relativePath>
+ </parent>
+
+ <artifactId>aaa-authn-sts</artifactId>
+ <packaging>bundle</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.aaa</groupId>
+ <artifactId>aaa-authn</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.aaa</groupId>
+ <artifactId>aaa-authn-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.sun.jersey</groupId>
+ <artifactId>jersey-server</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>javax.servlet-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.oltu.oauth2</groupId>
+ <artifactId>org.apache.oltu.oauth2.authzserver</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.oltu.oauth2</groupId>
+ <artifactId>org.apache.oltu.oauth2.common</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.oltu.oauth2</groupId>
+ <artifactId>org.apache.oltu.oauth2.resourceserver</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.dependencymanager</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <!-- Testing Dependencies -->
+ <dependency>
+ <groupId>com.sun.jersey.jersey-test-framework</groupId>
+ <artifactId>jersey-test-framework-grizzly2</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-servlet-tester</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Import-Package>
+ *,
+ com.sun.jersey.spi.container.servlet
+ </Import-Package>
+ <Web-ContextPath>/oauth2</Web-ContextPath>
+ <Bundle-Activator>org.opendaylight.aaa.sts.Activator</Bundle-Activator>
+ <manifestLocation>${project.basedir}/META-INF</manifestLocation>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/odl-aaa-moon/aaa-authn-sts/src/main/java/org/opendaylight/aaa/sts/Activator.java b/odl-aaa-moon/aaa-authn-sts/src/main/java/org/opendaylight/aaa/sts/Activator.java
new file mode 100644
index 00000000..1bf4591d
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-sts/src/main/java/org/opendaylight/aaa/sts/Activator.java
@@ -0,0 +1,207 @@
+/*
+ * 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.sts;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import com.google.common.collect.Lists;
+import java.util.List;
+import org.apache.felix.dm.DependencyActivatorBase;
+import org.apache.felix.dm.DependencyManager;
+import org.opendaylight.aaa.api.AuthenticationService;
+import org.opendaylight.aaa.api.ClaimAuth;
+import org.opendaylight.aaa.api.ClientService;
+import org.opendaylight.aaa.api.CredentialAuth;
+import org.opendaylight.aaa.api.IdMService;
+import org.opendaylight.aaa.api.TokenAuth;
+import org.opendaylight.aaa.api.TokenStore;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * An activator for the secure token server to inject in a
+ * {@link CredentialAuth} implementation.
+ *
+ * @author liemmn
+ * @author Ryan Goulding (ryandgoulding@gmail.com)
+ */
+public class Activator extends DependencyActivatorBase {
+
+ private static final Logger LOG = LoggerFactory.getLogger(Activator.class);
+
+ // Definition of several methods called in the ServiceLocator through
+ // Reflection
+ private static final String AUTHENTICATION_SERVICE_REMOVED = "authenticationServiceRemoved";
+ private static final String AUTHENTICATION_SERVICE_ADDED = "authenticationServiceAdded";
+ private static final String TOKEN_STORE_REMOVED = "tokenStoreRemoved";
+ private static final String TOKEN_STORE_ADDED = "tokenStoreAdded";
+ private static final String TOKEN_AUTH_REMOVED = "tokenAuthRemoved";
+ private static final String TOKEN_AUTH_ADDED = "tokenAuthAdded";
+ private static final String CLAIM_AUTH_REMOVED = "claimAuthRemoved";
+ private static final String CLAIM_AUTH_ADDED = "claimAuthAdded";
+ private static final String CREDENTIAL_AUTH_REMOVED = "credentialAuthRemoved";
+ private static final String CREDENTIAL_AUTH_ADDED = "credentialAuthAdded";
+
+ // A collection of all services, which is used for closing ServiceTrackers
+ private ImmutableList<ServiceTracker<?, ?>> services;
+
+ @Override
+ public void init(BundleContext context, DependencyManager manager) throws Exception {
+
+ LOG.info("STS Activator initializing");
+ manager.add(createComponent().setImplementation(ServiceLocator.getInstance())
+ .add(createServiceDependency().setService(CredentialAuth.class)
+ .setRequired(true)
+ .setCallbacks(
+ CREDENTIAL_AUTH_ADDED,
+ CREDENTIAL_AUTH_REMOVED))
+ .add(createServiceDependency().setService(ClaimAuth.class)
+ .setRequired(false)
+ .setCallbacks(CLAIM_AUTH_ADDED,
+ CLAIM_AUTH_REMOVED))
+ .add(createServiceDependency().setService(TokenAuth.class)
+ .setRequired(false)
+ .setCallbacks(TOKEN_AUTH_ADDED,
+ TOKEN_AUTH_REMOVED))
+ .add(createServiceDependency().setService(TokenStore.class)
+ .setRequired(true)
+ .setCallbacks(TOKEN_STORE_ADDED,
+ TOKEN_STORE_REMOVED))
+ .add(createServiceDependency().setService(TokenStore.class)
+ .setRequired(true))
+ .add(createServiceDependency().setService(
+ AuthenticationService.class)
+ .setRequired(true)
+ .setCallbacks(
+ AUTHENTICATION_SERVICE_ADDED,
+ AUTHENTICATION_SERVICE_REMOVED))
+ .add(createServiceDependency().setService(IdMService.class)
+ .setRequired(true))
+ .add(createServiceDependency().setService(ClientService.class)
+ .setRequired(true)));
+
+ final Builder<ServiceTracker<?, ?>> servicesBuilder = new ImmutableList.Builder<ServiceTracker<?, ?>>();
+
+ // Async ServiceTrackers to track and load AAA STS bundles
+ final ServiceTracker<AuthenticationService, AuthenticationService> authenticationService = new ServiceTracker<>(
+ context, AuthenticationService.class,
+ new AAAServiceTrackerCustomizer<AuthenticationService>(
+ new Function<AuthenticationService, Void>() {
+ @Override
+ public Void apply(AuthenticationService authenticationService) {
+ ServiceLocator.getInstance().setAuthenticationService(
+ authenticationService);
+ return null;
+ }
+ }));
+ servicesBuilder.add(authenticationService);
+ authenticationService.open();
+
+ final ServiceTracker<IdMService, IdMService> idmService = new ServiceTracker<>(context,
+ IdMService.class, new AAAServiceTrackerCustomizer<IdMService>(
+ new Function<IdMService, Void>() {
+ @Override
+ public Void apply(IdMService idmService) {
+ ServiceLocator.getInstance().setIdmService(idmService);
+ return null;
+ }
+ }));
+ servicesBuilder.add(idmService);
+ idmService.open();
+
+ final ServiceTracker<TokenAuth, TokenAuth> tokenAuthService = new ServiceTracker<>(context,
+ TokenAuth.class, new AAAServiceTrackerCustomizer<TokenAuth>(
+ new Function<TokenAuth, Void>() {
+ @Override
+ public Void apply(TokenAuth tokenAuth) {
+ final List<TokenAuth> tokenAuthCollection = (List<TokenAuth>) Lists.newArrayList(tokenAuth);
+ ServiceLocator.getInstance().setTokenAuthCollection(
+ tokenAuthCollection);
+ return null;
+ }
+ }));
+ servicesBuilder.add(tokenAuthService);
+ tokenAuthService.open();
+
+ final ServiceTracker<TokenStore, TokenStore> tokenStoreService = new ServiceTracker<>(
+ context, TokenStore.class, new AAAServiceTrackerCustomizer<TokenStore>(
+ new Function<TokenStore, Void>() {
+ @Override
+ public Void apply(TokenStore tokenStore) {
+ ServiceLocator.getInstance().setTokenStore(tokenStore);
+ return null;
+ }
+ }));
+ servicesBuilder.add(tokenStoreService);
+ tokenStoreService.open();
+
+ final ServiceTracker<ClientService, ClientService> clientService = new ServiceTracker<>(
+ context, ClientService.class, new AAAServiceTrackerCustomizer<ClientService>(
+ new Function<ClientService, Void>() {
+ @Override
+ public Void apply(ClientService clientService) {
+ ServiceLocator.getInstance().setClientService(clientService);
+ return null;
+ }
+ }));
+ servicesBuilder.add(clientService);
+ clientService.open();
+
+ services = servicesBuilder.build();
+
+ LOG.info("STS Activator initialized; ServiceTracker may still be processing");
+ }
+
+ /**
+ * Wrapper for AAA generic service loading.
+ *
+ * @param <S>
+ */
+ static final class AAAServiceTrackerCustomizer<S> implements ServiceTrackerCustomizer<S, S> {
+
+ private Function<S, Void> callback;
+
+ public AAAServiceTrackerCustomizer(final Function<S, Void> callback) {
+ this.callback = callback;
+ }
+
+ @Override
+ public S addingService(ServiceReference<S> reference) {
+ S service = reference.getBundle().getBundleContext().getService(reference);
+ LOG.info("Unable to resolve {}", service.getClass());
+ try {
+ callback.apply(service);
+ } catch (Exception e) {
+ LOG.error("Unable to resolve {}", service.getClass(), e);
+ }
+ return service;
+ }
+
+ @Override
+ public void modifiedService(ServiceReference<S> reference, S service) {
+ }
+
+ @Override
+ public void removedService(ServiceReference<S> reference, S service) {
+ }
+ }
+
+ @Override
+ public void destroy(BundleContext context, DependencyManager manager) throws Exception {
+
+ for (ServiceTracker<?, ?> serviceTracker : services) {
+ serviceTracker.close();
+ }
+ }
+}
diff --git a/odl-aaa-moon/aaa-authn-sts/src/main/java/org/opendaylight/aaa/sts/AnonymousPasswordValidator.java b/odl-aaa-moon/aaa-authn-sts/src/main/java/org/opendaylight/aaa/sts/AnonymousPasswordValidator.java
new file mode 100644
index 00000000..55b5b61f
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-sts/src/main/java/org/opendaylight/aaa/sts/AnonymousPasswordValidator.java
@@ -0,0 +1,30 @@
+/*
+ * 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.sts;
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.oltu.oauth2.common.OAuth;
+import org.apache.oltu.oauth2.common.validators.AbstractValidator;
+
+/**
+ * A password validator that does not enforce client identification.
+ *
+ * @author liemmn
+ *
+ */
+public class AnonymousPasswordValidator extends AbstractValidator<HttpServletRequest> {
+
+ public AnonymousPasswordValidator() {
+ requiredParams.add(OAuth.OAUTH_GRANT_TYPE);
+ requiredParams.add(OAuth.OAUTH_USERNAME);
+ requiredParams.add(OAuth.OAUTH_PASSWORD);
+
+ enforceClientAuthentication = false;
+ }
+}
diff --git a/odl-aaa-moon/aaa-authn-sts/src/main/java/org/opendaylight/aaa/sts/AnonymousRefreshTokenValidator.java b/odl-aaa-moon/aaa-authn-sts/src/main/java/org/opendaylight/aaa/sts/AnonymousRefreshTokenValidator.java
new file mode 100644
index 00000000..5b50c7da
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-sts/src/main/java/org/opendaylight/aaa/sts/AnonymousRefreshTokenValidator.java
@@ -0,0 +1,29 @@
+/*
+ * 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.sts;
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.oltu.oauth2.common.OAuth;
+import org.apache.oltu.oauth2.common.validators.AbstractValidator;
+
+/**
+ * A refresh token validator that does not enforce client identification.
+ *
+ * @author liemmn
+ *
+ */
+public class AnonymousRefreshTokenValidator extends AbstractValidator<HttpServletRequest> {
+
+ public AnonymousRefreshTokenValidator() {
+ requiredParams.add(OAuth.OAUTH_GRANT_TYPE);
+ requiredParams.add(OAuth.OAUTH_REFRESH_TOKEN);
+
+ enforceClientAuthentication = false;
+ }
+}
diff --git a/odl-aaa-moon/aaa-authn-sts/src/main/java/org/opendaylight/aaa/sts/OAuthRequest.java b/odl-aaa-moon/aaa-authn-sts/src/main/java/org/opendaylight/aaa/sts/OAuthRequest.java
new file mode 100644
index 00000000..2a2b34b6
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-sts/src/main/java/org/opendaylight/aaa/sts/OAuthRequest.java
@@ -0,0 +1,42 @@
+/*
+ * 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.sts;
+
+import javax.servlet.http.HttpServletRequest;
+import org.apache.oltu.oauth2.as.request.AbstractOAuthTokenRequest;
+import org.apache.oltu.oauth2.as.validator.UnauthenticatedAuthorizationCodeValidator;
+import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
+import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
+import org.apache.oltu.oauth2.common.message.types.GrantType;
+import org.apache.oltu.oauth2.common.validators.OAuthValidator;
+
+/**
+ * OAuth request wrapper.
+ *
+ * @author liemmn
+ *
+ */
+public class OAuthRequest extends AbstractOAuthTokenRequest {
+
+ public OAuthRequest(HttpServletRequest request) throws OAuthSystemException,
+ OAuthProblemException {
+ super(request);
+ }
+
+ @Override
+ public OAuthValidator<HttpServletRequest> initValidator() throws OAuthProblemException,
+ OAuthSystemException {
+ validators.put(GrantType.PASSWORD.toString(), AnonymousPasswordValidator.class);
+ validators.put(GrantType.REFRESH_TOKEN.toString(), AnonymousRefreshTokenValidator.class);
+ validators.put(GrantType.AUTHORIZATION_CODE.toString(),
+ UnauthenticatedAuthorizationCodeValidator.class);
+ return super.initValidator();
+ }
+
+}
diff --git a/odl-aaa-moon/aaa-authn-sts/src/main/java/org/opendaylight/aaa/sts/ServiceLocator.java b/odl-aaa-moon/aaa-authn-sts/src/main/java/org/opendaylight/aaa/sts/ServiceLocator.java
new file mode 100644
index 00000000..2c1f84c3
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-sts/src/main/java/org/opendaylight/aaa/sts/ServiceLocator.java
@@ -0,0 +1,141 @@
+/*
+ * 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.sts;
+
+import java.util.List;
+import java.util.Vector;
+import org.opendaylight.aaa.api.AuthenticationService;
+import org.opendaylight.aaa.api.ClientService;
+import org.opendaylight.aaa.api.CredentialAuth;
+import org.opendaylight.aaa.api.IdMService;
+import org.opendaylight.aaa.api.PasswordCredentials;
+import org.opendaylight.aaa.api.TokenAuth;
+import org.opendaylight.aaa.api.TokenStore;
+
+/**
+ * A service locator to bridge between the web world and OSGi world.
+ *
+ * @author liemmn
+ *
+ */
+public class ServiceLocator {
+
+ private static final ServiceLocator instance = new ServiceLocator();
+
+ protected volatile List<TokenAuth> tokenAuthCollection = new Vector<>();
+
+ protected volatile CredentialAuth<PasswordCredentials> credentialAuth;
+
+ protected volatile TokenStore tokenStore;
+
+ protected volatile AuthenticationService authenticationService;
+
+ protected volatile IdMService idmService;
+
+ protected volatile ClientService clientService;
+
+ private ServiceLocator() {
+ }
+
+ public static ServiceLocator getInstance() {
+ return instance;
+ }
+
+ /**
+ * Called through reflection by the sts activator.
+ *
+ * @see org.opendaylight.aaa.sts.Activator
+ * @param ta
+ */
+ protected void tokenAuthAdded(TokenAuth ta) {
+ this.tokenAuthCollection.add(ta);
+ }
+
+ /**
+ * Called through reflection by the sts activator.
+ *
+ * @see org.opendaylight.aaa.sts.Activator
+ * @param ta
+ */
+ protected void tokenAuthRemoved(TokenAuth ta) {
+ this.tokenAuthCollection.remove(ta);
+ }
+
+ protected void tokenStoreAdded(TokenStore ts) {
+ this.tokenStore = ts;
+ }
+
+ protected void tokenStoreRemoved(TokenStore ts) {
+ this.tokenStore = null;
+ }
+
+ protected void authenticationServiceAdded(AuthenticationService as) {
+ this.authenticationService = as;
+ }
+
+ protected void authenticationServiceRemoved(AuthenticationService as) {
+ this.authenticationService = null;
+ }
+
+ protected void credentialAuthAdded(CredentialAuth<PasswordCredentials> da) {
+ this.credentialAuth = da;
+ }
+
+ protected void credentialAuthAddedRemoved(CredentialAuth<PasswordCredentials> da) {
+ this.credentialAuth = null;
+ }
+
+ public List<TokenAuth> getTokenAuthCollection() {
+ return tokenAuthCollection;
+ }
+
+ public void setTokenAuthCollection(List<TokenAuth> tokenAuthCollection) {
+ this.tokenAuthCollection = tokenAuthCollection;
+ }
+
+ public CredentialAuth<PasswordCredentials> getCredentialAuth() {
+ return credentialAuth;
+ }
+
+ public synchronized void setCredentialAuth(CredentialAuth<PasswordCredentials> credentialAuth) {
+ this.credentialAuth = credentialAuth;
+ }
+
+ public TokenStore getTokenStore() {
+ return tokenStore;
+ }
+
+ public void setTokenStore(TokenStore tokenStore) {
+ this.tokenStore = tokenStore;
+ }
+
+ public AuthenticationService getAuthenticationService() {
+ return authenticationService;
+ }
+
+ public void setAuthenticationService(AuthenticationService authenticationService) {
+ this.authenticationService = authenticationService;
+ }
+
+ public IdMService getIdmService() {
+ return idmService;
+ }
+
+ public void setIdmService(IdMService idmService) {
+ this.idmService = idmService;
+ }
+
+ public ClientService getClientService() {
+ return clientService;
+ }
+
+ public void setClientService(ClientService clientService) {
+ this.clientService = clientService;
+ }
+}
diff --git a/odl-aaa-moon/aaa-authn-sts/src/main/java/org/opendaylight/aaa/sts/TokenAuthFilter.java b/odl-aaa-moon/aaa-authn-sts/src/main/java/org/opendaylight/aaa/sts/TokenAuthFilter.java
new file mode 100644
index 00000000..3fa7a66c
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-sts/src/main/java/org/opendaylight/aaa/sts/TokenAuthFilter.java
@@ -0,0 +1,148 @@
+/*
+ * 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.sts;
+
+import com.sun.jersey.spi.container.ContainerRequest;
+import com.sun.jersey.spi.container.ContainerRequestFilter;
+import java.util.List;
+import java.util.Map;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
+import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
+import org.apache.oltu.oauth2.common.message.types.ParameterStyle;
+import org.apache.oltu.oauth2.rs.request.OAuthAccessResourceRequest;
+import org.opendaylight.aaa.api.Authentication;
+import org.opendaylight.aaa.api.AuthenticationException;
+import org.opendaylight.aaa.api.TokenAuth;
+
+/**
+ * A token-based authentication filter for resource providers.
+ *
+ * Deprecated: Use <code>AAAFilter</code> instead.
+ *
+ * @author liemmn
+ *
+ */
+@Deprecated
+public class TokenAuthFilter implements ContainerRequestFilter {
+
+ private final String OPTIONS = "OPTIONS";
+ private final String ACCESS_CONTROL_REQUEST_HEADERS = "Access-Control-Request-Headers";
+ private final String AUTHORIZATION = "authorization";
+
+ @Context
+ private HttpServletRequest httpRequest;
+
+ @Override
+ public ContainerRequest filter(ContainerRequest request) {
+
+ // Do the CORS check first
+ if (checkCORSOptionRequest(request)) {
+ return request;
+ }
+
+ // Are we up yet?
+ if (ServiceLocator.getInstance().getAuthenticationService() == null) {
+ throw new WebApplicationException(
+ Response.status(Status.SERVICE_UNAVAILABLE).type(MediaType.APPLICATION_JSON)
+ .entity("{\"error\":\"Authentication service unavailable\"}").build());
+ }
+
+ // Are we doing authentication or not?
+ if (ServiceLocator.getInstance().getAuthenticationService().isAuthEnabled()) {
+ Map<String, List<String>> headers = request.getRequestHeaders();
+
+ // Go through and invoke other TokenAuth first...
+ List<TokenAuth> tokenAuthCollection = ServiceLocator.getInstance()
+ .getTokenAuthCollection();
+ for (TokenAuth ta : tokenAuthCollection) {
+ try {
+ Authentication auth = ta.validate(headers);
+ if (auth != null) {
+ ServiceLocator.getInstance().getAuthenticationService().set(auth);
+ return request;
+ }
+ } catch (AuthenticationException ae) {
+ throw unauthorized();
+ }
+ }
+
+ // OK, last chance to validate token...
+ try {
+ OAuthAccessResourceRequest or = new OAuthAccessResourceRequest(httpRequest,
+ ParameterStyle.HEADER);
+ validate(or.getAccessToken());
+ } catch (OAuthSystemException | OAuthProblemException e) {
+ throw unauthorized();
+ }
+ }
+
+ return request;
+ }
+
+ /**
+ * CORS access control : when browser sends cross-origin request, it first
+ * sends the OPTIONS method with a list of access control request headers,
+ * which has a list of custom headers and access control method such as GET.
+ * POST etc. You custom header "Authorization will not be present in request
+ * header, instead it will be present as a value inside
+ * Access-Control-Request-Headers. We should not do any authorization
+ * against such request. for more details :
+ * https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
+ */
+
+ private boolean checkCORSOptionRequest(ContainerRequest request) {
+ if (OPTIONS.equals(request.getMethod())) {
+ List<String> headerList = request.getRequestHeader(ACCESS_CONTROL_REQUEST_HEADERS);
+ if (headerList != null && !headerList.isEmpty()) {
+ String header = headerList.get(0);
+ if (header != null && header.toLowerCase().contains(AUTHORIZATION)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ // Validate an ODL token...
+ private Authentication validate(final String token) {
+ Authentication auth = ServiceLocator.getInstance().getTokenStore().get(token);
+ if (auth == null) {
+ throw unauthorized();
+ } else {
+ ServiceLocator.getInstance().getAuthenticationService().set(auth);
+ }
+ return auth;
+ }
+
+ // Houston, we got a problem!
+ private static final WebApplicationException unauthorized() {
+ ServiceLocator.getInstance().getAuthenticationService().clear();
+ return new UnauthorizedException();
+ }
+
+ // A custom 401 web exception that handles http basic response as well
+ static final class UnauthorizedException extends WebApplicationException {
+ private static final long serialVersionUID = -1732363804773027793L;
+ static final String WWW_AUTHENTICATE = "WWW-Authenticate";
+ static final Object OPENDAYLIGHT = "Basic realm=\"opendaylight\"";
+ private static final Response response = Response.status(Status.UNAUTHORIZED)
+ .header(WWW_AUTHENTICATE, OPENDAYLIGHT)
+ .build();
+
+ public UnauthorizedException() {
+ super(response);
+ }
+ }
+}
diff --git a/odl-aaa-moon/aaa-authn-sts/src/main/java/org/opendaylight/aaa/sts/TokenEndpoint.java b/odl-aaa-moon/aaa-authn-sts/src/main/java/org/opendaylight/aaa/sts/TokenEndpoint.java
new file mode 100644
index 00000000..a456d702
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-sts/src/main/java/org/opendaylight/aaa/sts/TokenEndpoint.java
@@ -0,0 +1,242 @@
+/*
+ * 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.sts;
+
+import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
+import static javax.servlet.http.HttpServletResponse.SC_CREATED;
+import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
+import static javax.servlet.http.HttpServletResponse.SC_NOT_IMPLEMENTED;
+import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT;
+import static javax.servlet.http.HttpServletResponse.SC_OK;
+import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.List;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.oltu.oauth2.as.issuer.OAuthIssuer;
+import org.apache.oltu.oauth2.as.issuer.OAuthIssuerImpl;
+import org.apache.oltu.oauth2.as.issuer.UUIDValueGenerator;
+import org.apache.oltu.oauth2.as.response.OAuthASResponse;
+import org.apache.oltu.oauth2.common.OAuth;
+import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
+import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
+import org.apache.oltu.oauth2.common.message.OAuthResponse;
+import org.apache.oltu.oauth2.common.message.types.GrantType;
+import org.apache.oltu.oauth2.common.message.types.TokenType;
+import org.opendaylight.aaa.AuthenticationBuilder;
+import org.opendaylight.aaa.ClaimBuilder;
+import org.opendaylight.aaa.PasswordCredentialBuilder;
+import org.opendaylight.aaa.api.Authentication;
+import org.opendaylight.aaa.api.AuthenticationException;
+import org.opendaylight.aaa.api.Claim;
+import org.opendaylight.aaa.api.PasswordCredentials;
+
+/**
+ * Secure Token Service (STS) endpoint.
+ *
+ * @author liemmn
+ *
+ */
+public class TokenEndpoint extends HttpServlet {
+ private static final long serialVersionUID = 8272453849539659999L;
+
+ private static final String DOMAIN_SCOPE_REQUIRED = "Domain scope required";
+ private static final String NOT_IMPLEMENTED = "not_implemented";
+ private static final String UNAUTHORIZED = "unauthorized";
+
+ static final String TOKEN_GRANT_ENDPOINT = "/token";
+ static final String TOKEN_REVOKE_ENDPOINT = "/revoke";
+ static final String TOKEN_VALIDATE_ENDPOINT = "/validate";
+
+ private transient OAuthIssuer oi;
+
+ @Override
+ public void init(ServletConfig config) throws ServletException {
+ oi = new OAuthIssuerImpl(new UUIDValueGenerator());
+ }
+
+ @Override
+ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ try {
+ if (req.getServletPath().equals(TOKEN_GRANT_ENDPOINT)) {
+ createAccessToken(req, resp);
+ } else if (req.getServletPath().equals(TOKEN_REVOKE_ENDPOINT)) {
+ deleteAccessToken(req, resp);
+ } else if (req.getServletPath().equals(TOKEN_VALIDATE_ENDPOINT)) {
+ validateToken(req, resp);
+ }
+ } catch (AuthenticationException e) {
+ error(resp, SC_UNAUTHORIZED, e.getMessage());
+ } catch (OAuthProblemException oe) {
+ error(resp, oe);
+ } catch (Exception e) {
+ error(resp, e);
+ }
+ }
+
+ private void validateToken(HttpServletRequest req, HttpServletResponse resp)
+ throws IOException, OAuthSystemException {
+ String token = req.getReader().readLine();
+ if (token != null) {
+ Authentication authn = ServiceLocator.getInstance().getTokenStore().get(token.trim());
+ if (authn == null) {
+ throw new AuthenticationException(UNAUTHORIZED);
+ } else {
+ ServiceLocator.getInstance().getAuthenticationService().set(authn);
+ resp.setStatus(SC_OK);
+ }
+ } else {
+ throw new AuthenticationException(UNAUTHORIZED);
+ }
+ }
+
+ // Delete an access token
+ private void deleteAccessToken(HttpServletRequest req, HttpServletResponse resp)
+ throws IOException {
+ String token = req.getReader().readLine();
+ if (token != null) {
+ if (ServiceLocator.getInstance().getTokenStore().delete(token.trim())) {
+ resp.setStatus(SC_NO_CONTENT);
+ } else {
+ throw new AuthenticationException(UNAUTHORIZED);
+ }
+ } else {
+ throw new AuthenticationException(UNAUTHORIZED);
+ }
+ }
+
+ // Create an access token
+ private void createAccessToken(HttpServletRequest req, HttpServletResponse resp)
+ throws OAuthSystemException, OAuthProblemException, IOException {
+ Claim claim = null;
+ String clientId = null;
+
+ OAuthRequest oauthRequest = new OAuthRequest(req);
+ // Any client credentials?
+ clientId = oauthRequest.getClientId();
+ if (clientId != null) {
+ ServiceLocator.getInstance().getClientService()
+ .validate(clientId, oauthRequest.getClientSecret());
+ }
+
+ // Credential request...
+ if (oauthRequest.getParam(OAuth.OAUTH_GRANT_TYPE).equals(GrantType.PASSWORD.toString())) {
+ String domain = oauthRequest.getScopes().iterator().next();
+ PasswordCredentials pc = new PasswordCredentialBuilder().setUserName(
+ oauthRequest.getUsername()).setPassword(oauthRequest.getPassword())
+ .setDomain(domain).build();
+ if (!oauthRequest.getScopes().isEmpty()) {
+ claim = ServiceLocator.getInstance().getCredentialAuth().authenticate(pc);
+ }
+ } else if (oauthRequest.getParam(OAuth.OAUTH_GRANT_TYPE).equals(
+ GrantType.REFRESH_TOKEN.toString())) {
+ // Refresh token...
+ String token = oauthRequest.getRefreshToken();
+ if (!oauthRequest.getScopes().isEmpty()) {
+ String domain = oauthRequest.getScopes().iterator().next();
+ // Authenticate...
+ Authentication auth = ServiceLocator.getInstance().getTokenStore().get(token);
+ if (auth != null && domain != null) {
+ List<String> roles = ServiceLocator.getInstance().getIdmService()
+ .listRoles(auth.userId(), domain);
+ if (!roles.isEmpty()) {
+ ClaimBuilder cb = new ClaimBuilder(auth);
+ cb.setDomain(domain); // scope domain
+ // Add roles for the scoped domain
+ for (String role : roles) {
+ cb.addRole(role);
+ }
+ claim = cb.build();
+ }
+ }
+ } else {
+ error(resp, SC_BAD_REQUEST, DOMAIN_SCOPE_REQUIRED);
+ }
+ } else {
+ // Support authorization code later...
+ error(resp, SC_NOT_IMPLEMENTED, NOT_IMPLEMENTED);
+ }
+
+ // Respond with OAuth token
+ oauthAccessTokenResponse(resp, claim, clientId);
+ }
+
+ // Build OAuth access token response from the given claim
+ private void oauthAccessTokenResponse(HttpServletResponse resp, Claim claim, String clientId)
+ throws OAuthSystemException, IOException {
+ if (claim == null) {
+ throw new AuthenticationException(UNAUTHORIZED);
+ }
+ String token = oi.accessToken();
+
+ // Cache this token...
+ Authentication auth = new AuthenticationBuilder(new ClaimBuilder(claim).setClientId(
+ clientId).build()).setExpiration(tokenExpiration()).build();
+ ServiceLocator.getInstance().getTokenStore().put(token, auth);
+
+ OAuthResponse r = OAuthASResponse.tokenResponse(SC_CREATED).setAccessToken(token)
+ .setTokenType(TokenType.BEARER.toString())
+ .setExpiresIn(Long.toString(auth.expiration()))
+ .buildJSONMessage();
+ write(resp, r);
+ }
+
+ // Token expiration
+ private long tokenExpiration() {
+ return ServiceLocator.getInstance().getTokenStore().tokenExpiration();
+ }
+
+ // Emit an error OAuthResponse with the given HTTP code
+ private void error(HttpServletResponse resp, int httpCode, String error) {
+ try {
+ OAuthResponse r = OAuthResponse.errorResponse(httpCode).setError(error)
+ .buildJSONMessage();
+ write(resp, r);
+ } catch (Exception e1) {
+ // Nothing to do here
+ }
+ }
+
+ // Emit an error OAuthResponse for the given OAuth-related exception
+ private void error(HttpServletResponse resp, OAuthProblemException e) {
+ try {
+ OAuthResponse r = OAuthResponse.errorResponse(SC_BAD_REQUEST).error(e)
+ .buildJSONMessage();
+ write(resp, r);
+ } catch (Exception e1) {
+ // Nothing to do here
+ }
+ }
+
+ // Emit an error OAuthResponse for the given generic exception
+ private void error(HttpServletResponse resp, Exception e) {
+ try {
+ OAuthResponse r = OAuthResponse.errorResponse(SC_INTERNAL_SERVER_ERROR)
+ .setError(e.getClass().getName())
+ .setErrorDescription(e.getMessage()).buildJSONMessage();
+ write(resp, r);
+ } catch (Exception e1) {
+ // Nothing to do here
+ }
+ }
+
+ // Write out an OAuthResponse
+ private void write(HttpServletResponse resp, OAuthResponse r) throws IOException {
+ resp.setStatus(r.getResponseStatus());
+ PrintWriter pw = resp.getWriter();
+ pw.print(r.getBody());
+ pw.flush();
+ pw.close();
+ }
+}
diff --git a/odl-aaa-moon/aaa-authn-sts/src/main/resources/WEB-INF/web.xml b/odl-aaa-moon/aaa-authn-sts/src/main/resources/WEB-INF/web.xml
new file mode 100644
index 00000000..83a9fa51
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-sts/src/main/resources/WEB-INF/web.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+ version="3.0">
+
+ <servlet>
+ <servlet-name>STS</servlet-name>
+ <servlet-class>org.opendaylight.aaa.sts.TokenEndpoint</servlet-class>
+ <load-on-startup>1</load-on-startup>
+ </servlet>
+ <servlet-mapping>
+ <servlet-name>STS</servlet-name>
+ <url-pattern>/token</url-pattern>
+ </servlet-mapping>
+ <servlet-mapping>
+ <servlet-name>STS</servlet-name>
+ <url-pattern>/revoke</url-pattern>
+ </servlet-mapping>
+ <servlet-mapping>
+ <servlet-name>STS</servlet-name>
+ <url-pattern>/validate</url-pattern>
+ </servlet-mapping>
+</web-app>
diff --git a/odl-aaa-moon/aaa-authn-sts/src/test/java/org/opendaylight/aaa/sts/RestFixture.java b/odl-aaa-moon/aaa-authn-sts/src/test/java/org/opendaylight/aaa/sts/RestFixture.java
new file mode 100644
index 00000000..0f806d91
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-sts/src/test/java/org/opendaylight/aaa/sts/RestFixture.java
@@ -0,0 +1,34 @@
+/*
+ * 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.sts;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+
+/**
+ * Fixture for testing RESTful stuff.
+ *
+ * @author liemmn
+ *
+ */
+@Path("test")
+public class RestFixture {
+
+ @Context
+ private HttpServletRequest httpRequest;
+
+ @GET
+ @Produces("text/plain")
+ public String msg() {
+ return "ok";
+ }
+}
diff --git a/odl-aaa-moon/aaa-authn-sts/src/test/java/org/opendaylight/aaa/sts/TokenAuthTest.java b/odl-aaa-moon/aaa-authn-sts/src/test/java/org/opendaylight/aaa/sts/TokenAuthTest.java
new file mode 100644
index 00000000..7f888455
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-sts/src/test/java/org/opendaylight/aaa/sts/TokenAuthTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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.sts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.anyMap;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.UniformInterfaceException;
+import com.sun.jersey.test.framework.JerseyTest;
+import com.sun.jersey.test.framework.WebAppDescriptor;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.aaa.AuthenticationBuilder;
+import org.opendaylight.aaa.ClaimBuilder;
+import org.opendaylight.aaa.api.Authentication;
+import org.opendaylight.aaa.api.AuthenticationService;
+import org.opendaylight.aaa.api.TokenAuth;
+import org.opendaylight.aaa.api.TokenStore;
+import org.opendaylight.aaa.sts.TokenAuthFilter.UnauthorizedException;
+
+public class TokenAuthTest extends JerseyTest {
+
+ private static final String RS_PACKAGES = "org.opendaylight.aaa.sts";
+ private static final String JERSEY_FILTERS = "com.sun.jersey.spi.container.ContainerRequestFilters";
+ private static final String AUTH_FILTERS = TokenAuthFilter.class.getName();
+
+ private static Authentication auth = new AuthenticationBuilder(new ClaimBuilder().setUserId(
+ "1234").setUser("Bob").addRole("admin").addRole("user").setDomain("tenantX").build()).setExpiration(
+ System.currentTimeMillis() + 1000).build();
+
+ private static final String GOOD_TOKEN = "9b01b7cf-8a49-346d-8c47-6a61193e2b60";
+ private static final String BAD_TOKEN = "9b01b7cf-8a49-346d-8c47-6a611badbeef";
+
+ public TokenAuthTest() throws Exception {
+ super(new WebAppDescriptor.Builder(RS_PACKAGES).initParam(JERSEY_FILTERS, AUTH_FILTERS)
+ .build());
+ }
+
+ @BeforeClass
+ public static void init() {
+ ServiceLocator.getInstance().setAuthenticationService(mock(AuthenticationService.class));
+ ServiceLocator.getInstance().setTokenStore(mock(TokenStore.class));
+ when(ServiceLocator.getInstance().getTokenStore().get(GOOD_TOKEN)).thenReturn(auth);
+ when(ServiceLocator.getInstance().getTokenStore().get(BAD_TOKEN)).thenReturn(null);
+ when(ServiceLocator.getInstance().getAuthenticationService().isAuthEnabled()).thenReturn(
+ Boolean.TRUE);
+ }
+
+ @Test()
+ public void testGetUnauthorized() {
+ try {
+ resource().path("test").get(String.class);
+ fail("Shoulda failed with 401!");
+ } catch (UniformInterfaceException e) {
+ ClientResponse resp = e.getResponse();
+ assertEquals(401, resp.getStatus());
+ assertTrue(resp.getHeaders().get(UnauthorizedException.WWW_AUTHENTICATE)
+ .contains(UnauthorizedException.OPENDAYLIGHT));
+ }
+ }
+
+ @Test
+ public void testGet() {
+ String resp = resource().path("test").header("Authorization", "Bearer " + GOOD_TOKEN)
+ .get(String.class);
+ assertEquals("ok", resp);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testGetWithValidator() {
+ try {
+ // Mock a laxed tokenauth...
+ TokenAuth ta = mock(TokenAuth.class);
+ when(ta.validate(anyMap())).thenReturn(auth);
+ ServiceLocator.getInstance().getTokenAuthCollection().add(ta);
+ testGet();
+ } finally {
+ ServiceLocator.getInstance().getTokenAuthCollection().clear();
+ }
+ }
+
+}
diff --git a/odl-aaa-moon/aaa-authn-sts/src/test/java/org/opendaylight/aaa/sts/TokenEndpointTest.java b/odl-aaa-moon/aaa-authn-sts/src/test/java/org/opendaylight/aaa/sts/TokenEndpointTest.java
new file mode 100644
index 00000000..06dd6302
--- /dev/null
+++ b/odl-aaa-moon/aaa-authn-sts/src/test/java/org/opendaylight/aaa/sts/TokenEndpointTest.java
@@ -0,0 +1,164 @@
+/*
+ * 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.sts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+import org.eclipse.jetty.testing.HttpTester;
+import org.eclipse.jetty.testing.ServletTester;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.aaa.AuthenticationBuilder;
+import org.opendaylight.aaa.ClaimBuilder;
+import org.opendaylight.aaa.api.AuthenticationService;
+import org.opendaylight.aaa.api.Claim;
+import org.opendaylight.aaa.api.ClientService;
+import org.opendaylight.aaa.api.CredentialAuth;
+import org.opendaylight.aaa.api.IdMService;
+import org.opendaylight.aaa.api.PasswordCredentials;
+import org.opendaylight.aaa.api.TokenAuth;
+import org.opendaylight.aaa.api.TokenStore;
+
+/**
+ * A unit test for token endpoint.
+ *
+ * @author liemmn
+ *
+ */
+public class TokenEndpointTest {
+ private static final long TOKEN_TIMEOUT_SECS = 10;
+ private static final String CONTEXT = "/oauth2";
+ private static final String DIRECT_AUTH = "grant_type=password&username=admin&password=admin&scope=pepsi&client_id=dlux&client_secret=secrete";
+ private static final String REFRESH_TOKEN = "grant_type=refresh_token&refresh_token=whateverisgood&scope=pepsi";
+
+ private static final Claim claim = new ClaimBuilder().setUser("bob").setUserId("1234")
+ .addRole("admin").build();
+ private final static ServletTester server = new ServletTester();
+
+ @BeforeClass
+ public static void init() throws Exception {
+ // Set up server
+ server.setContextPath(CONTEXT);
+
+ // Add our servlet under test
+ server.addServlet(TokenEndpoint.class, "/revoke");
+ server.addServlet(TokenEndpoint.class, "/token");
+
+ // Let's do dis
+ server.start();
+ }
+
+ @AfterClass
+ public static void shutdown() throws Exception {
+ server.stop();
+ }
+
+ @Before
+ public void setup() {
+ mockServiceLocator();
+ when(ServiceLocator.getInstance().getTokenStore().tokenExpiration()).thenReturn(
+ TOKEN_TIMEOUT_SECS);
+ }
+
+ @After
+ public void teardown() {
+ ServiceLocator.getInstance().getTokenAuthCollection().clear();
+ }
+
+ @Test
+ public void testCreateToken401() throws Exception {
+ HttpTester req = new HttpTester();
+ req.setMethod("POST");
+ req.setHeader("Content-Type", "application/x-www-form-urlencoded");
+ req.setContent(DIRECT_AUTH);
+ req.setURI(CONTEXT + TokenEndpoint.TOKEN_GRANT_ENDPOINT);
+ req.setVersion("HTTP/1.0");
+
+ HttpTester resp = new HttpTester();
+ resp.parse(server.getResponses(req.generate()));
+ assertEquals(401, resp.getStatus());
+ }
+
+ @Test
+ public void testCreateTokenWithPassword() throws Exception {
+ when(
+ ServiceLocator.getInstance().getCredentialAuth()
+ .authenticate(any(PasswordCredentials.class))).thenReturn(claim);
+
+ HttpTester req = new HttpTester();
+ req.setMethod("POST");
+ req.setHeader("Content-Type", "application/x-www-form-urlencoded");
+ req.setContent(DIRECT_AUTH);
+ req.setURI(CONTEXT + TokenEndpoint.TOKEN_GRANT_ENDPOINT);
+ req.setVersion("HTTP/1.0");
+
+ HttpTester resp = new HttpTester();
+ resp.parse(server.getResponses(req.generate()));
+ assertEquals(201, resp.getStatus());
+ assertTrue(resp.getContent().contains("expires_in\":10"));
+ assertTrue(resp.getContent().contains("Bearer"));
+ }
+
+ @Test
+ public void testCreateTokenWithRefreshToken() throws Exception {
+ when(ServiceLocator.getInstance().getTokenStore().get(anyString())).thenReturn(
+ new AuthenticationBuilder(claim).build());
+ when(ServiceLocator.getInstance().getIdmService().listRoles(anyString(), anyString())).thenReturn(
+ Arrays.asList("admin", "user"));
+
+ HttpTester req = new HttpTester();
+ req.setMethod("POST");
+ req.setHeader("Content-Type", "application/x-www-form-urlencoded");
+ req.setContent(REFRESH_TOKEN);
+ req.setURI(CONTEXT + TokenEndpoint.TOKEN_GRANT_ENDPOINT);
+ req.setVersion("HTTP/1.0");
+
+ HttpTester resp = new HttpTester();
+ resp.parse(server.getResponses(req.generate()));
+ assertEquals(201, resp.getStatus());
+ assertTrue(resp.getContent().contains("expires_in\":10"));
+ assertTrue(resp.getContent().contains("Bearer"));
+ }
+
+ @Test
+ public void testDeleteToken() throws Exception {
+ when(ServiceLocator.getInstance().getTokenStore().delete("token_to_be_deleted")).thenReturn(
+ true);
+
+ HttpTester req = new HttpTester();
+ req.setMethod("POST");
+ req.setHeader("Content-Type", "application/x-www-form-urlencoded");
+ req.setContent("token_to_be_deleted");
+ req.setURI(CONTEXT + TokenEndpoint.TOKEN_REVOKE_ENDPOINT);
+ req.setVersion("HTTP/1.0");
+
+ HttpTester resp = new HttpTester();
+ resp.parse(server.getResponses(req.generate()));
+ assertEquals(204, resp.getStatus());
+ }
+
+ @SuppressWarnings("unchecked")
+ private static void mockServiceLocator() {
+ ServiceLocator.getInstance().setClientService(mock(ClientService.class));
+ ServiceLocator.getInstance().setIdmService(mock(IdMService.class));
+ ServiceLocator.getInstance().setAuthenticationService(mock(AuthenticationService.class));
+ ServiceLocator.getInstance().setTokenStore(mock(TokenStore.class));
+ ServiceLocator.getInstance().setCredentialAuth(mock(CredentialAuth.class));
+ ServiceLocator.getInstance().getTokenAuthCollection().add(mock(TokenAuth.class));
+ }
+}