Repository: cxf-fediz Updated Branches: refs/heads/master b45b3b7b2 -> dd35349be
FEDIZ-172 - OIDC DataProvider should support client_credentials clients Project: http://git-wip-us.apache.org/repos/asf/cxf-fediz/repo Commit: http://git-wip-us.apache.org/repos/asf/cxf-fediz/commit/dd35349b Tree: http://git-wip-us.apache.org/repos/asf/cxf-fediz/tree/dd35349b Diff: http://git-wip-us.apache.org/repos/asf/cxf-fediz/diff/dd35349b Branch: refs/heads/master Commit: dd35349be0e960854774c546713b8a368de39abb Parents: b45b3b7 Author: Colm O hEigeartaigh <[email protected]> Authored: Tue Aug 2 10:24:19 2016 +0100 Committer: Colm O hEigeartaigh <[email protected]> Committed: Tue Aug 2 10:24:19 2016 +0100 ---------------------------------------------------------------------- .../service/oidc/OAuthDataProviderImpl.java | 49 +++- systests/oidc/pom.xml | 42 ++++ .../cxf/fediz/systests/oidc/OIDCTest.java | 21 ++ .../test/resources/oidc/applicationContext.xml | 230 +++++++++++++++++++ .../src/test/resources/oidc/data-manager.xml | 87 +++++++ systests/oidc/src/test/resources/sts.jaas | 10 + 6 files changed, 437 insertions(+), 2 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/dd35349b/services/oidc/src/main/java/org/apache/cxf/fediz/service/oidc/OAuthDataProviderImpl.java ---------------------------------------------------------------------- diff --git a/services/oidc/src/main/java/org/apache/cxf/fediz/service/oidc/OAuthDataProviderImpl.java b/services/oidc/src/main/java/org/apache/cxf/fediz/service/oidc/OAuthDataProviderImpl.java index 16bd697..43bd6de 100644 --- a/services/oidc/src/main/java/org/apache/cxf/fediz/service/oidc/OAuthDataProviderImpl.java +++ b/services/oidc/src/main/java/org/apache/cxf/fediz/service/oidc/OAuthDataProviderImpl.java @@ -21,7 +21,16 @@ package org.apache.cxf.fediz.service.oidc; import java.security.Principal; import java.util.Collections; import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.login.Configuration; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; + +import org.apache.cxf.common.logging.LogUtils; +import org.apache.cxf.interceptor.security.NamePasswordCallbackHandler; import org.apache.cxf.rs.security.oauth2.common.Client; import org.apache.cxf.rs.security.oauth2.common.OAuthPermission; import org.apache.cxf.rs.security.oauth2.grants.code.DefaultEHCacheCodeDataProvider; @@ -31,7 +40,12 @@ import org.apache.cxf.rs.security.oidc.utils.OidcUtils; public class OAuthDataProviderImpl extends DefaultEHCacheCodeDataProvider { + private static final Logger LOG = LogUtils.getL7dLogger(OAuthDataProviderImpl.class); + private boolean checkOnlyRegisteredClients; + private String contextName; + private Configuration loginConfig; + @Override public Client getClient(String clientId) { @@ -76,13 +90,44 @@ public class OAuthDataProviderImpl extends DefaultEHCacheCodeDataProvider { } protected Client authenticateClient(String clientId, String clientSecret) { - // If the authentication is successful: - // return new Client(clientId, clientSecret, true) + if (contextName != null) { + try { + // Login using JAAS + CallbackHandler callbackHandler = + new NamePasswordCallbackHandler(clientId, clientSecret); + LoginContext ctx = new LoginContext(getContextName(), null, callbackHandler, loginConfig); + ctx.login(); + Client client = new Client(clientId, clientSecret, true); + client.setAllowedGrantTypes(Collections.singletonList(OAuthConstants.CLIENT_CREDENTIALS_GRANT)); + ctx.logout(); + return client; + } catch (LoginException ex) { + ex.printStackTrace(); + String errorMessage = "Authentication failed: " + ex.getMessage(); + LOG.log(Level.FINE, errorMessage, ex); + } + } return null; } public void setCheckOnlyRegisteredClients(boolean checkOnlyRegisteredClients) { this.checkOnlyRegisteredClients = checkOnlyRegisteredClients; } + + public String getContextName() { + return contextName; + } + + public void setContextName(String contextName) { + this.contextName = contextName; + } + + public Configuration getLoginConfig() { + return loginConfig; + } + + public void setLoginConfig(Configuration loginConfig) { + this.loginConfig = loginConfig; + } } http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/dd35349b/systests/oidc/pom.xml ---------------------------------------------------------------------- diff --git a/systests/oidc/pom.xml b/systests/oidc/pom.xml index 5639f37..57f3e69 100644 --- a/systests/oidc/pom.xml +++ b/systests/oidc/pom.xml @@ -99,6 +99,16 @@ <version>${cxf.version}</version> <scope>test</scope> </dependency> + <dependency> + <groupId>org.apache.cxf</groupId> + <artifactId>cxf-rt-ws-security</artifactId> + <version>${cxf.version}</version> + </dependency> + <dependency> + <groupId>org.apache.cxf</groupId> + <artifactId>cxf-rt-ws-policy</artifactId> + <version>${cxf.version}</version> + </dependency> </dependencies> <build> <testResources> @@ -197,6 +207,17 @@ </artifactItems> </configuration> </execution> + <execution> + <id>copy-extra-jars-to-oidc</id> + <phase>package</phase> + <goals> + <goal>copy-dependencies</goal> + </goals> + <configuration> + <outputDirectory>${basedir}/target/tomcat/rp/webapps/fediz-oidc/WEB-INF/lib</outputDirectory> + <includeScope>compile</includeScope> + </configuration> + </execution> </executions> </plugin> <plugin> @@ -222,6 +243,26 @@ </resources> </configuration> </execution> + <execution> + <id>copy-entities-to-oidc</id> + <phase>generate-test-sources</phase> + <goals> + <goal>copy-resources</goal> + </goals> + <configuration> + <outputDirectory>${basedir}/target/tomcat/rp/webapps/fediz-oidc/WEB-INF</outputDirectory> + <resources> + <resource> + <directory>${basedir}/src/test/resources/oidc</directory> + <includes> + <include>applicationContext.xml</include> + <include>data-manager.xml</include> + </includes> + <filtering>true</filtering> + </resource> + </resources> + </configuration> + </execution> </executions> </plugin> <plugin> @@ -261,6 +302,7 @@ <idp.https.port>${idp.https.port}</idp.https.port> <rp.https.port>${rp.https.port}</rp.https.port> <java.io.tmpdir>${basedir}/target</java.io.tmpdir> + <java.security.auth.login.config>src/test/resources/sts.jaas</java.security.auth.login.config> </systemPropertyVariables> <includes> <include>**/systests/**</include> http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/dd35349b/systests/oidc/src/test/java/org/apache/cxf/fediz/systests/oidc/OIDCTest.java ---------------------------------------------------------------------- diff --git a/systests/oidc/src/test/java/org/apache/cxf/fediz/systests/oidc/OIDCTest.java b/systests/oidc/src/test/java/org/apache/cxf/fediz/systests/oidc/OIDCTest.java index ccb3bc9..627e44e 100644 --- a/systests/oidc/src/test/java/org/apache/cxf/fediz/systests/oidc/OIDCTest.java +++ b/systests/oidc/src/test/java/org/apache/cxf/fediz/systests/oidc/OIDCTest.java @@ -632,6 +632,27 @@ public class OIDCTest { webClient.close(); } + @org.junit.Test + public void testClientCredentialsSTS() throws Exception { + String url = "https://localhost:" + getRpHttpsPort() + "/fediz-oidc/oauth2/token"; + WebRequest request = new WebRequest(new URL(url), HttpMethod.POST); + + request.setRequestParameters(new ArrayList<NameValuePair>()); + request.getRequestParameters().add(new NameValuePair("client_id", "alice")); + request.getRequestParameters().add(new NameValuePair("client_secret", "ecila")); + request.getRequestParameters().add(new NameValuePair("grant_type", "client_credentials")); + + final WebClient webClient = new WebClient(); + webClient.getOptions().setUseInsecureSSL(true); + webClient.getOptions().setJavaScriptEnabled(false); + final UnexpectedPage responsePage = webClient.getPage(request); + String response = responsePage.getWebResponse().getContentAsString(); + + Assert.assertTrue(response.contains("access_token")); + + webClient.close(); + } + private static WebClient setupWebClient(String user, String password, String idpPort) { final WebClient webClient = new WebClient(); webClient.getOptions().setUseInsecureSSL(true); http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/dd35349b/systests/oidc/src/test/resources/oidc/applicationContext.xml ---------------------------------------------------------------------- diff --git a/systests/oidc/src/test/resources/oidc/applicationContext.xml b/systests/oidc/src/test/resources/oidc/applicationContext.xml new file mode 100644 index 0000000..2eefe27 --- /dev/null +++ b/systests/oidc/src/test/resources/oidc/applicationContext.xml @@ -0,0 +1,230 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. +--> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:cxf="http://cxf.apache.org/core" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:jaxrs="http://cxf.apache.org/jaxrs" + xmlns:util="http://www.springframework.org/schema/util" + xmlns:http="http://cxf.apache.org/transports/http/configuration" + xmlns:sec="http://cxf.apache.org/configuration/security" + xsi:schemaLocation=" + http://cxf.apache.org/core + http://cxf.apache.org/schemas/core.xsd + http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans-3.0.xsd + http://cxf.apache.org/jaxrs + http://cxf.apache.org/schemas/jaxrs.xsd + http://www.springframework.org/schema/util + http://www.springframework.org/schema/util/spring-util.xsd + http://cxf.apache.org/transports/http/configuration + http://cxf.apache.org/schemas/configuration/http-conf.xsd + http://cxf.apache.org/configuration/security + http://cxf.apache.org/schemas/configuration/security.xsd"> + + <cxf:bus> + <cxf:features> + <cxf:logging/> + </cxf:features> + </cxf:bus> + + <import resource="data-manager.xml" /> + + <!-- Supports OIDC Authorization Code flow --> + <bean id="oidcAuthorizationService" class="org.apache.cxf.rs.security.oidc.idp.OidcAuthorizationCodeService"> + <property name="dataProvider" ref="oauthProvider"/> + <property name="subjectCreator" ref="subjectCreator"/> + <property name="skipAuthorizationWithOidcScope" value="true"/> + <!-- + <property name="useAllClientScopes" value="true"/> + --> + <property name="canSupportPublicClients" value="true"/> + </bean> + <!-- Supports OIDC Implicit and Hybrid flows --> + <bean id="oidcHybridService" class="org.apache.cxf.rs.security.oidc.idp.OidcHybridService"> + <property name="dataProvider" ref="oauthProvider"/> + <property name="subjectCreator" ref="subjectCreator"/> + <property name="skipAuthorizationWithOidcScope" value="true"/> + <property name="responseFilter" ref="idTokenFilter"/> + <property name="codeService" ref="oidcAuthorizationService"/> + </bean> + + <util:list id="oidcServices"> + <ref bean="oidcAuthorizationService"/> + <ref bean="oidcHybridService"/> + </util:list> + + <!-- Service which makes Code, Implicit and Hybrid flow available + at the same relative "/authorize" address --> + <bean id="authorizationService" class="org.apache.cxf.rs.security.oauth2.services.AuthorizationService"> + <property name="services" ref="oidcServices"/> + </bean> + + <!-- Service supporting all OIDC Core flows --> + <jaxrs:server address="/idp"> + <jaxrs:serviceBeans> + <ref bean="authorizationService"/> + </jaxrs:serviceBeans> + <jaxrs:providers> + <ref bean="viewProvider"/> + <ref bean="oauthJsonProvider"/> + </jaxrs:providers> + <jaxrs:properties> + <entry key="rs.security.signature.properties" value="rs.security.properties"/> + <entry key="rs.security.signature.key.password.provider" value-ref="keyPasswordProvider"/> + </jaxrs:properties> + </jaxrs:server> + + <!-- + Public JWK Key Service: Disable it if the client secret is used or if + pre-installing public OIDC keys to clients is preferred + --> + <bean id="oidcKeysService" class="org.apache.cxf.rs.security.oidc.idp.OidcKeysService"/> + <jaxrs:server address="/jwk"> + <jaxrs:serviceBeans> + <ref bean="oidcKeysService"/> + </jaxrs:serviceBeans> + <jaxrs:providers> + <bean class="org.apache.cxf.rs.security.jose.jaxrs.JsonWebKeysProvider"/> + </jaxrs:providers> + <jaxrs:properties> + <entry key="rs.security.signature.properties" value="rs.security.properties"/> + <entry key="rs.security.signature.key.password.provider" value-ref="keyPasswordProvider"/> + </jaxrs:properties> + </jaxrs:server> + + + <bean id="oauth2TokenValidationFilter" class="org.apache.cxf.rs.security.oauth2.filters.OAuthRequestFilter"> + <property name="dataProvider" ref="oauthProvider"/> + <property name="audienceIsEndpointAddress" value="false"/> + </bean> + + <!-- User Info Service --> + <bean id="userInfoService" class="org.apache.cxf.rs.security.oidc.idp.UserInfoService"> + <property name="oauthDataProvider" ref="oauthProvider"/> + <property name="jwsRequired" value="false"/> + </bean> + <jaxrs:server address="/users"> + <jaxrs:serviceBeans> + <ref bean="userInfoService"/> + </jaxrs:serviceBeans> + <jaxrs:providers> + <bean class="org.apache.cxf.jaxrs.provider.json.JsonMapObjectProvider"/> + <ref bean="oauth2TokenValidationFilter"/> + </jaxrs:providers> + </jaxrs:server> + + <bean id="keyPasswordProvider" class="org.apache.cxf.fediz.service.oidc.PrivateKeyPasswordProviderImpl"/> + + <!-- Client Registration Service --> + <bean id="clientRegService" init-method="init" + class="org.apache.cxf.fediz.service.oidc.clients.ClientRegistrationService"> + <property name="dataProvider" ref="oauthProvider"/> + <property name="clientProvider" ref="oauthProvider"/> + <!-- + <property name="clientScopes" ref="supportedScopes"/> + --> + <property name="homeRealms"> + <map> + <entry key="urn:org:apache:cxf:fediz:idp:realm-A" value="IDP of Realm A" /> + <entry key="urn:org:apache:cxf:fediz:idp:realm-B" value="IDP of Realm B" /> + </map> + </property> + </bean> + + <!-- Console linking to the client registration service --> + <bean id="consoleService" class="org.apache.cxf.fediz.service.oidc.console.UserConsoleService"> + <property name="clientRegService" ref="clientRegService"/> + </bean> + <jaxrs:server address="/console"> + <jaxrs:serviceBeans> + <ref bean="consoleService"/> + </jaxrs:serviceBeans> + <jaxrs:providers> + <ref bean="viewProvider"/> + </jaxrs:providers> + </jaxrs:server> + + <bean id="viewProvider" class="org.apache.cxf.jaxrs.provider.RequestDispatcherProvider"> + <property name="useClassNames" value="true"/> + <property name="locationPrefix" value="/WEB-INF/views/"/> + <property name="beanName" value="data"/> + <property name="dispatcherName" value="jsp"/> + <property name="resourcePaths"> + <map> + <entry key="/remove" value="/WEB-INF/views/registeredClients.jsp"/> + </map> + </property> + <property name="classResources"> + <map> + <entry key="org.apache.cxf.fediz.service.oidc.clients.InvalidRegistration" value="/WEB-INF/views/invalidRegistration.jsp"/> + </map> + </property> + </bean> + + <!-- AccessTokenService response filter which adds IdTokens to client responses --> + <bean id="idTokenFilter" class="org.apache.cxf.rs.security.oidc.idp.IdTokenResponseFilter"> + <!-- + <property name="signWithClientSecret" value="true"/> + --> + </bean> + <bean id="refreshTokenHandler" class="org.apache.cxf.rs.security.oauth2.grants.refresh.RefreshTokenGrantHandler"> + <property name="dataProvider" ref="oauthProvider"/> + </bean> + <bean id="clientCredsHandler" class="org.apache.cxf.rs.security.oauth2.grants.clientcred.ClientCredentialsGrantHandler"> + <property name="dataProvider" ref="oauthProvider"/> + </bean> + <!-- Access Token service --> + <bean id="accessTokenService" class="org.apache.cxf.rs.security.oauth2.services.AccessTokenService"> + <property name="dataProvider" ref="oauthProvider"/> + <property name="responseFilter" ref="idTokenFilter"/> + <property name="grantHandler" ref="clientCredsHandler"/> + <property name="canSupportPublicClients" value="true"/> + </bean> + <!-- Access Token Introspection service --> + <bean id="accessTokenIntrospectionService" class="org.apache.cxf.rs.security.oauth2.services.TokenIntrospectionService"> + <property name="dataProvider" ref="oauthProvider"/> + <property name="blockUnauthorizedRequests" value="false"/> + </bean> + <bean id="oauthJsonProvider" class="org.apache.cxf.rs.security.oauth2.provider.OAuthJSONProvider"/> + <jaxrs:server address="/oauth2"> + <jaxrs:serviceBeans> + <ref bean="accessTokenService"/> + <ref bean="accessTokenIntrospectionService"/> + </jaxrs:serviceBeans> + <jaxrs:providers> + <ref bean="oauthJsonProvider"/> + </jaxrs:providers> + <jaxrs:properties> + <entry key="rs.security.signature.properties" value="rs.security.properties"/> + <entry key="rs.security.signature.key.password.provider" value-ref="keyPasswordProvider"/> + </jaxrs:properties> + </jaxrs:server> + + <http:conduit name="*.http-conduit"> + <http:tlsClientParameters + disableCNCheck="true"> + <sec:trustManagers> + <sec:keyStore type="jks" password="tompass" resource="server.jks" /> + </sec:trustManagers> + </http:tlsClientParameters> + </http:conduit> + +</beans> + http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/dd35349b/systests/oidc/src/test/resources/oidc/data-manager.xml ---------------------------------------------------------------------- diff --git a/systests/oidc/src/test/resources/oidc/data-manager.xml b/systests/oidc/src/test/resources/oidc/data-manager.xml new file mode 100644 index 0000000..3397962 --- /dev/null +++ b/systests/oidc/src/test/resources/oidc/data-manager.xml @@ -0,0 +1,87 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. +--> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:util="http://www.springframework.org/schema/util" + xsi:schemaLocation=" + http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans-3.0.xsd + http://www.springframework.org/schema/util + http://www.springframework.org/schema/util/spring-util.xsd + "> + + <bean id="applicationContextProvider" class="org.apache.cxf.fediz.service.oidc.handler.hrd.ApplicationContextProvider"/> + + <!-- List of accepted scopes --> + <util:map id="supportedScopes"> + <entry key="openid" value="Access the authentication claims" /> + <entry key="refreshToken" value="Refresh access tokens" /> + </util:map> + + <!-- + List of required scopes that must be available in request URIs when + client redirects users to OIDC + --> + <util:list id="coreScopes"> + <value>openid</value> + </util:list> + + <!-- + Typically the scopes authorized by the user will be reported back to the client, + reporting an approved refreshToken scope is currently disabled + --> + <util:list id="invisibleToClientScopes"> + <value>refreshToken</value> + </util:list> + + <!-- + To support the alternative data persistence strategies: either register a custom + AbstractCodeDataProvider extension or implement AuthorizationCodeDataProvider directly + --> + <bean id="oauthProvider" + class="org.apache.cxf.fediz.service.oidc.OAuthDataProviderImpl" + init-method="init" destroy-method="close"> + <!-- List of accepted scopes --> + <property name="supportedScopes" ref="supportedScopes"/> + <!-- + List of scopes that the consent/authorization form should make + selected by default. For example, asking a user to do an extra click + to approve an "oidc" scope is a redundant operation because this scope + is required anyway. + --> + <property name="defaultScopes" ref="coreScopes"/> + + <property name="invisibleToClientScopes" ref="invisibleToClientScopes"/> + <!-- + <property name="accessTokenLifetime" value="3600"/> + --> + <!-- + <property name="supportPreauthorizedTokens" value="true"/> + --> + <property name="contextName" value="sts"/> + </bean> + + <!-- Custom SubjectCreator where IdToken is created --> + <bean id="subjectCreator" class="org.apache.cxf.fediz.service.oidc.FedizSubjectCreator"> + <property name="idTokenIssuer" value="accounts.fediz.com"/> + </bean> + +</beans> + http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/dd35349b/systests/oidc/src/test/resources/sts.jaas ---------------------------------------------------------------------- diff --git a/systests/oidc/src/test/resources/sts.jaas b/systests/oidc/src/test/resources/sts.jaas new file mode 100644 index 0000000..fdb759e --- /dev/null +++ b/systests/oidc/src/test/resources/sts.jaas @@ -0,0 +1,10 @@ + +sts { + org.apache.cxf.ws.security.trust.STSLoginModule required + require.roles="true" + disable.on.behalf.of="true" + wsdl.location="https://localhost:${idp.https.port}/fediz-idp-sts/REALMA/STSServiceTransportUT?wsdl" + service.name="{http://docs.oasis-open.org/ws-sx/ws-trust/200512/}SecurityTokenService" + endpoint.name="{http://docs.oasis-open.org/ws-sx/ws-trust/200512/}TransportUT_Port"; +}; +
