| /* |
| * 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); |
| } |
| } |