blob: 0f98133d2a96fa6075bffc418c3d6f55233f25b1 [file] [log] [blame]
/*
* 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.meecrowave.oauth2;
import org.apache.cxf.common.classloader.ClassLoaderUtils;
import org.apache.cxf.message.Message;
import org.apache.cxf.rs.security.jose.jwa.KeyAlgorithm;
import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm;
import org.apache.cxf.rs.security.oauth2.common.ClientAccessToken;
import org.apache.cxf.rs.security.oauth2.common.OAuthAuthorizationData;
import org.apache.cxf.rs.security.oauth2.provider.OAuthJSONProvider;
import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants;
import org.apache.meecrowave.Meecrowave;
import org.apache.meecrowave.junit.MeecrowaveRule;
import org.apache.meecrowave.oauth2.provider.JCacheCodeDataProvider;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import javax.cache.Cache;
import javax.cache.CacheManager;
import javax.cache.Caching;
import javax.cache.configuration.MutableConfiguration;
import javax.cache.spi.CachingProvider;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Form;
import javax.ws.rs.core.Response;
import java.io.File;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import static java.util.Collections.singletonList;
import static javax.ws.rs.client.Entity.entity;
import static javax.ws.rs.core.MediaType.APPLICATION_FORM_URLENCODED_TYPE;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE;
import static javax.xml.bind.DatatypeConverter.printBase64Binary;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
public class OAuth2Test {
private static final File KEYSTORE = new File("target/OAuth2Test/keystore.jceks");
@ClassRule
public static final MeecrowaveRule MEECROWAVE = new MeecrowaveRule(
new Meecrowave.Builder().randomHttpPort()
.user("test", "pwd").role("test", "admin")
// auth code support is optional so activate it
.property("oauth2-authorization-code-support", "true")
// auth code jose setup to store the tokens
.property("oauth2.cxf.rs.security.keystore.type", "jks")
.property("oauth2.cxf.rs.security.keystore.file", KEYSTORE.getAbsolutePath())
.property("oauth2.cxf.rs.security.keystore.password", "password")
.property("oauth2.cxf.rs.security.keystore.alias", "alice")
.property("oauth2.cxf.rs.security.key.password", "pwd")
// auth code needs a logged pcp to work
.loginConfig(new Meecrowave.LoginConfigBuilder().basic())
.securityConstraints(new Meecrowave.SecurityConstaintBuilder()
.authConstraint(true)
.addAuthRole("**")
.addCollection("secured", "/oauth2/authorize")
.addCollection("secured", "/oauth2/authorize/decision")), "");
@BeforeClass
public static void createKeyStore() throws Exception {
Keystores.create(KEYSTORE);
}
@Test
public void getPasswordTokenNoClient() {
final Client client = ClientBuilder.newClient().register(new OAuthJSONProvider());
try {
final ClientAccessToken token = client.target("http://localhost:" + MEECROWAVE.getConfiguration().getHttpPort())
.path("oauth2/token")
.request(APPLICATION_JSON_TYPE)
.post(entity(
new Form()
.param("grant_type", "password")
.param("username", "test")
.param("password", "pwd"), APPLICATION_FORM_URLENCODED_TYPE), ClientAccessToken.class);
assertNotNull(token);
assertEquals("Bearer", token.getTokenType());
assertNotNull(token.getTokenKey());
assertEquals(3600, token.getExpiresIn());
assertNotEquals(0, token.getIssuedAt());
assertNotNull(token.getRefreshToken());
} finally {
client.close();
}
}
@Test
public void getRefreshTokenNoClient() {
final Client client = ClientBuilder.newClient().register(new OAuthJSONProvider());
try {
// password
final ClientAccessToken primary = client.target("http://localhost:" + MEECROWAVE.getConfiguration().getHttpPort())
.path("oauth2/token")
.request(APPLICATION_JSON_TYPE)
.post(entity(
new Form()
.param("grant_type", "password")
.param("username", "test")
.param("password", "pwd"), APPLICATION_FORM_URLENCODED_TYPE), ClientAccessToken.class);
// refresh
final ClientAccessToken token = client.target("http://localhost:" + MEECROWAVE.getConfiguration().getHttpPort())
.path("oauth2/token")
.request(APPLICATION_JSON_TYPE)
.post(entity(
new Form()
.param("grant_type", "refresh_token")
.param("refresh_token", primary.getRefreshToken()), APPLICATION_FORM_URLENCODED_TYPE), ClientAccessToken.class);
assertNotNull(token);
assertEquals("Bearer", token.getTokenType());
assertNotNull(token.getTokenKey());
assertEquals(3600, token.getExpiresIn());
assertNotEquals(0, token.getIssuedAt());
assertNotNull(token.getRefreshToken());
} finally {
client.close();
}
}
@Test
public void authorizationCode() throws URISyntaxException {
final int httpPort = MEECROWAVE.getConfiguration().getHttpPort();
createRedirectedClient(httpPort);
final Client client = ClientBuilder.newClient()
.property(Message.MAINTAIN_SESSION, true)
.register(new OAuthJSONProvider());
try {
final WebTarget target = client.target("http://localhost:" + httpPort);
final Response authorization = target
.path("oauth2/authorize")
.queryParam(OAuthConstants.GRANT_TYPE, OAuthConstants.AUTHORIZATION_CODE_GRANT)
.queryParam(OAuthConstants.RESPONSE_TYPE, OAuthConstants.CODE_RESPONSE_TYPE)
.queryParam(OAuthConstants.CLIENT_ID, "c1")
.queryParam(OAuthConstants.CLIENT_SECRET, "cpwd")
.queryParam(OAuthConstants.REDIRECT_URI, "http://localhost:" + httpPort + "/redirected")
.request(APPLICATION_JSON_TYPE)
.header("authorization", "Basic " + printBase64Binary("test:pwd".getBytes(StandardCharsets.UTF_8)))
.get();
final OAuthAuthorizationData data = authorization.readEntity(OAuthAuthorizationData.class);
assertNotNull(data.getAuthenticityToken());
assertEquals("c1", data.getClientId());
assertEquals("http://localhost:" + httpPort + "/oauth2/authorize/decision", data.getReplyTo());
assertEquals("code", data.getResponseType());
assertEquals("http://localhost:" + httpPort + "/redirected", data.getRedirectUri());
final Response decision = target
.path("oauth2/authorize/decision")
.queryParam(OAuthConstants.SESSION_AUTHENTICITY_TOKEN, data.getAuthenticityToken())
.queryParam(OAuthConstants.AUTHORIZATION_DECISION_KEY, "allow")
.request(APPLICATION_JSON_TYPE)
.cookie(authorization.getCookies().get("JSESSIONID"))
.header("authorization", "Basic " + printBase64Binary("test:pwd".getBytes(StandardCharsets.UTF_8)))
.get();
assertEquals(Response.Status.SEE_OTHER.getStatusCode(), decision.getStatus());
assertTrue(decision.getLocation().toASCIIString(), decision.getLocation().toASCIIString().startsWith("http://localhost:" + httpPort + "/redirected?code="));
final ClientAccessToken token = target
.path("oauth2/token")
.request(APPLICATION_JSON_TYPE)
.post(entity(
new Form()
.param(OAuthConstants.GRANT_TYPE, OAuthConstants.AUTHORIZATION_CODE_GRANT)
.param(OAuthConstants.CODE_RESPONSE_TYPE, decision.getLocation().getRawQuery().substring("code=".length()))
.param(OAuthConstants.REDIRECT_URI, "http://localhost:" + httpPort + "/redirected")
.param(OAuthConstants.CLIENT_ID, "c1")
.param(OAuthConstants.CLIENT_SECRET, "cpwd"), APPLICATION_FORM_URLENCODED_TYPE), ClientAccessToken.class);
assertNotNull(token);
assertEquals("Bearer", token.getTokenType());
assertNotNull(token.getTokenKey());
assertEquals(3600, token.getExpiresIn());
assertNotEquals(0, token.getIssuedAt());
assertNotNull(token.getRefreshToken());
} finally {
client.close();
}
}
private void createRedirectedClient(final int httpPort) throws URISyntaxException {
final CachingProvider provider = Caching.getCachingProvider();
final CacheManager cacheManager = provider.getCacheManager(
ClassLoaderUtils.getResource("default-oauth2.jcs", OAuth2Test.class).toURI(),
Thread.currentThread().getContextClassLoader());
Cache<String, org.apache.cxf.rs.security.oauth2.common.Client> cache;
try {
cache = cacheManager
.createCache(JCacheCodeDataProvider.CLIENT_CACHE_KEY, new MutableConfiguration<String, org.apache.cxf.rs.security.oauth2.common.Client>()
.setTypes(String.class, org.apache.cxf.rs.security.oauth2.common.Client.class)
.setStoreByValue(true));
} catch (final RuntimeException re) {
cache = cacheManager.getCache(JCacheCodeDataProvider.CLIENT_CACHE_KEY, String.class, org.apache.cxf.rs.security.oauth2.common.Client.class);
}
final org.apache.cxf.rs.security.oauth2.common.Client value = new org.apache.cxf.rs.security.oauth2.common.Client("c1", "cpwd", true);
value.setRedirectUris(singletonList("http://localhost:" + httpPort + "/redirected"));
cache.put("c1", value);
}
}