Adding a new cxf-rt-security-saml module to remove OpenSAML dependencies from cxf-rt-security
Project: http://git-wip-us.apache.org/repos/asf/cxf/repo Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/c04c2720 Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/c04c2720 Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/c04c2720 Branch: refs/heads/master Commit: c04c27200226b564108b4fe58b8ac70ca5ec7638 Parents: 71e9ada Author: Colm O hEigeartaigh <[email protected]> Authored: Mon Apr 13 13:04:26 2015 +0100 Committer: Colm O hEigeartaigh <[email protected]> Committed: Mon Apr 13 13:04:26 2015 +0100 ---------------------------------------------------------------------- .../features/src/main/resources/features.xml | 10 +- rt/pom.xml | 1 + .../grants/saml/Saml2BearerGrantHandler.java | 2 +- .../saml/sso/AbstractServiceProviderFilter.java | 10 +- .../authorization/ClaimsAuthorizingFilter.java | 4 +- .../SecurityContextProviderImpl.java | 6 +- rt/security-saml/pom.xml | 60 ++++ .../cxf/rt/security/saml/claims/ClaimBean.java | 51 +++ .../cxf/rt/security/saml/claims/SAMLClaim.java | 68 ++++ .../saml/claims/SAMLSecurityContext.java | 104 +++++++ .../ClaimsAuthorizingInterceptor.java | 244 +++++++++++++++ .../cxf/rt/security/saml/utils/SAMLUtils.java | 141 +++++++++ .../AbstractXACMLAuthorizingInterceptor.java | 170 ++++++++++ .../security/saml/xacml/CXFMessageParser.java | 97 ++++++ .../saml/xacml/DefaultXACMLRequestBuilder.java | 217 +++++++++++++ .../saml/xacml/RequestComponentBuilder.java | 183 +++++++++++ .../saml/xacml/SamlRequestComponentBuilder.java | 118 +++++++ .../saml/xacml/XACMLAuthorizingInterceptor.java | 86 ++++++ .../rt/security/saml/xacml/XACMLConstants.java | 206 +++++++++++++ .../saml/xacml/XACMLRequestBuilder.java | 62 ++++ .../saml/xacml/pdp/api/PolicyDecisionPoint.java | 35 +++ .../rt/security/saml/claims/SAMLClaimsTest.java | 212 +++++++++++++ .../saml/claims/SamlCallbackHandler.java | 99 ++++++ .../ClaimsAuthorizingInterceptorTest.java | 298 ++++++++++++++++++ .../cxf/rt/security/saml/xacml/DummyPDP.java | 153 +++++++++ .../saml/xacml/RequestComponentBuilderTest.java | 222 ++++++++++++++ .../xacml/SamlRequestComponentBuilderTest.java | 153 +++++++++ .../xacml/XACMLAuthorizingInterceptorTest.java | 127 ++++++++ .../saml/xacml/XACMLRequestBuilderTest.java | 307 +++++++++++++++++++ rt/security/pom.xml | 10 - .../cxf/rt/security/claims/ClaimBean.java | 51 --- .../claims/ClaimsAuthorizingInterceptor.java | 242 --------------- .../cxf/rt/security/claims/SAMLClaim.java | 66 ---- .../rt/security/saml/SAMLSecurityContext.java | 104 ------- .../apache/cxf/rt/security/saml/SAMLUtils.java | 141 --------- .../AbstractXACMLAuthorizingInterceptor.java | 170 ---------- .../cxf/rt/security/xacml/CXFMessageParser.java | 97 ------ .../xacml/DefaultXACMLRequestBuilder.java | 217 ------------- .../security/xacml/RequestComponentBuilder.java | 183 ----------- .../xacml/SamlRequestComponentBuilder.java | 118 ------- .../xacml/XACMLAuthorizingInterceptor.java | 87 ------ .../cxf/rt/security/xacml/XACMLConstants.java | 206 ------------- .../rt/security/xacml/XACMLRequestBuilder.java | 62 ---- .../xacml/pdp/api/PolicyDecisionPoint.java | 35 --- .../ClaimsAuthorizingInterceptorTest.java | 295 ------------------ .../cxf/rt/security/saml/SAMLClaimsTest.java | 212 ------------- .../rt/security/saml/SamlCallbackHandler.java | 99 ------ .../apache/cxf/rt/security/xacml/DummyPDP.java | 153 --------- .../xacml/RequestComponentBuilderTest.java | 222 -------------- .../xacml/SamlRequestComponentBuilderTest.java | 153 --------- .../xacml/XACMLAuthorizingInterceptorTest.java | 127 -------- .../security/xacml/XACMLRequestBuilderTest.java | 307 ------------------- rt/ws/security/pom.xml | 2 +- .../trust/AuthPolicyValidatingInterceptor.java | 5 +- .../cxf/ws/security/trust/STSLoginModule.java | 3 +- .../wss4j/StaxSecurityContextInInterceptor.java | 4 +- .../wss4j/UsernameTokenInterceptor.java | 4 +- .../ws/security/wss4j/WSS4JInInterceptor.java | 5 +- .../token/validator/DefaultSAMLRoleParser.java | 4 +- .../security/oauth2/SamlCallbackHandler.java | 2 +- .../security/oauth2/SamlCallbackHandler2.java | 2 +- .../security/saml/SamlCallbackHandler.java | 2 +- .../ws/saml/PolicyDecisionPointMockImpl.java | 4 +- .../org/apache/cxf/systest/ws/saml/server.xml | 2 +- .../apache/cxf/systest/ws/saml/stax-server.xml | 2 +- 65 files changed, 3451 insertions(+), 3393 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cxf/blob/c04c2720/osgi/karaf/features/src/main/resources/features.xml ---------------------------------------------------------------------- diff --git a/osgi/karaf/features/src/main/resources/features.xml b/osgi/karaf/features/src/main/resources/features.xml index 33b418d..0705424 100644 --- a/osgi/karaf/features/src/main/resources/features.xml +++ b/osgi/karaf/features/src/main/resources/features.xml @@ -119,14 +119,18 @@ <feature version="${project.version}">cxf-ws-addr</feature> <bundle start-level="40" dependency="true">mvn:net.sf.ehcache/ehcache/${cxf.ehcache.version}</bundle> <bundle start-level="40">mvn:org.apache.cxf/cxf-rt-ws-security/${project.version}</bundle> - <bundle start-level="40">mvn:org.apache.cxf/cxf-rt-security/${project.version}</bundle> + <bundle start-level="40">mvn:org.apache.cxf/cxf-rt-security-saml/${project.version}</bundle> </feature> <feature name="cxf-rt-security" version="${project.version}" resolver="(obr)"> <feature version="${project.version}">cxf-core</feature> <feature version="${project.version}">cxf-wsdl</feature> - <feature version="${cxf.wss4j.version}">wss4j</feature> <bundle start-level="40">mvn:org.apache.cxf/cxf-rt-security/${project.version}</bundle> </feature> + <feature name="cxf-rt-security-saml" version="${project.version}" resolver="(obr)"> + <feature version="${project.version}">cxf-rt-security</feature> + <feature version="${cxf.wss4j.version}">wss4j</feature> + <bundle start-level="40">mvn:org.apache.cxf/cxf-rt-security-saml/${project.version}</bundle> + </feature> <feature name="cxf-http" version="${project.version}" resolver="(obr)"> <feature version="${project.version}">cxf-core</feature> <bundle start-level="40">mvn:${cxf.servlet-api.group}/${cxf.servlet-api.artifact}/${cxf.servlet-api.version}</bundle> @@ -189,7 +193,7 @@ </feature> <feature name="cxf-rs-security-xml" version="${project.version}" resolver="(obr)"> <feature version="${project.version}">cxf-jaxrs</feature> - <feature version="${project.version}">cxf-rt-security</feature> + <feature version="${project.version}">cxf-rt-security-saml</feature> <bundle start-level="40">mvn:org.apache.cxf/cxf-rt-rs-security-xml/${project.version}</bundle> </feature> <feature name="cxf-rs-security-sso-saml" version="${project.version}" resolver="(obr)"> http://git-wip-us.apache.org/repos/asf/cxf/blob/c04c2720/rt/pom.xml ---------------------------------------------------------------------- diff --git a/rt/pom.xml b/rt/pom.xml index fd9da64..2458834 100644 --- a/rt/pom.xml +++ b/rt/pom.xml @@ -75,5 +75,6 @@ <module>management-web</module> <module>javascript</module> <module>security</module> + <module>security-saml</module> </modules> </project> http://git-wip-us.apache.org/repos/asf/cxf/blob/c04c2720/rt/rs/security/oauth-parent/oauth2-saml/src/main/java/org/apache/cxf/rs/security/oauth2/grants/saml/Saml2BearerGrantHandler.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-saml/src/main/java/org/apache/cxf/rs/security/oauth2/grants/saml/Saml2BearerGrantHandler.java b/rt/rs/security/oauth-parent/oauth2-saml/src/main/java/org/apache/cxf/rs/security/oauth2/grants/saml/Saml2BearerGrantHandler.java index 9dbc021..6a29910 100644 --- a/rt/rs/security/oauth-parent/oauth2-saml/src/main/java/org/apache/cxf/rs/security/oauth2/grants/saml/Saml2BearerGrantHandler.java +++ b/rt/rs/security/oauth-parent/oauth2-saml/src/main/java/org/apache/cxf/rs/security/oauth2/grants/saml/Saml2BearerGrantHandler.java @@ -52,7 +52,7 @@ import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants; import org.apache.cxf.rs.security.oauth2.utils.OAuthUtils; import org.apache.cxf.rs.security.saml.authorization.SecurityContextProvider; import org.apache.cxf.rs.security.saml.authorization.SecurityContextProviderImpl; -import org.apache.cxf.rt.security.saml.SAMLSecurityContext; +import org.apache.cxf.rt.security.saml.claims.SAMLSecurityContext; import org.apache.cxf.security.SecurityContext; import org.apache.cxf.security.transport.TLSSessionInfo; import org.apache.cxf.staxutils.StaxUtils; http://git-wip-us.apache.org/repos/asf/cxf/blob/c04c2720/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractServiceProviderFilter.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractServiceProviderFilter.java b/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractServiceProviderFilter.java index d3ccfac..b4afac4 100644 --- a/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractServiceProviderFilter.java +++ b/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/AbstractServiceProviderFilter.java @@ -55,7 +55,7 @@ import org.apache.cxf.rs.security.saml.assertion.Subject; import org.apache.cxf.rs.security.saml.sso.state.RequestState; import org.apache.cxf.rs.security.saml.sso.state.ResponseState; import org.apache.cxf.rt.security.claims.ClaimCollection; -import org.apache.cxf.rt.security.saml.SAMLSecurityContext; +import org.apache.cxf.rt.security.saml.claims.SAMLSecurityContext; import org.apache.cxf.security.SecurityContext; import org.apache.cxf.staxutils.StaxUtils; import org.apache.cxf.ws.security.SecurityConstants; @@ -193,16 +193,16 @@ public abstract class AbstractServiceProviderFilter extends AbstractSSOSpHandler roleAttributeName = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role"; } ClaimCollection claims = - org.apache.cxf.rt.security.saml.SAMLUtils.getClaims(assertionWrapper); + org.apache.cxf.rt.security.saml.utils.SAMLUtils.getClaims(assertionWrapper); Set<Principal> roles = - org.apache.cxf.rt.security.saml.SAMLUtils.parseRolesFromClaims( + org.apache.cxf.rt.security.saml.utils.SAMLUtils.parseRolesFromClaims( claims, roleAttributeName, null); SAMLSecurityContext context = new SAMLSecurityContext(new SimplePrincipal(name), roles, claims); - context.setIssuer(org.apache.cxf.rt.security.saml.SAMLUtils.getIssuer(assertionWrapper)); + context.setIssuer(org.apache.cxf.rt.security.saml.utils.SAMLUtils.getIssuer(assertionWrapper)); context.setAssertionElement( - org.apache.cxf.rt.security.saml.SAMLUtils.getAssertionElement(assertionWrapper)); + org.apache.cxf.rt.security.saml.utils.SAMLUtils.getAssertionElement(assertionWrapper)); m.put(SecurityContext.class, context); } } http://git-wip-us.apache.org/repos/asf/cxf/blob/c04c2720/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/authorization/ClaimsAuthorizingFilter.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/authorization/ClaimsAuthorizingFilter.java b/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/authorization/ClaimsAuthorizingFilter.java index f373d91..09c3d26 100644 --- a/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/authorization/ClaimsAuthorizingFilter.java +++ b/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/authorization/ClaimsAuthorizingFilter.java @@ -28,8 +28,8 @@ import javax.ws.rs.core.Response; import org.apache.cxf.interceptor.security.AccessDeniedException; import org.apache.cxf.jaxrs.utils.JAXRSUtils; import org.apache.cxf.message.Message; -import org.apache.cxf.rt.security.claims.ClaimBean; -import org.apache.cxf.rt.security.claims.ClaimsAuthorizingInterceptor; +import org.apache.cxf.rt.security.saml.claims.ClaimBean; +import org.apache.cxf.rt.security.saml.interceptor.ClaimsAuthorizingInterceptor; public class ClaimsAuthorizingFilter implements ContainerRequestFilter { http://git-wip-us.apache.org/repos/asf/cxf/blob/c04c2720/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/authorization/SecurityContextProviderImpl.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/authorization/SecurityContextProviderImpl.java b/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/authorization/SecurityContextProviderImpl.java index 604efb5..9899085 100644 --- a/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/authorization/SecurityContextProviderImpl.java +++ b/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/authorization/SecurityContextProviderImpl.java @@ -25,9 +25,9 @@ import org.w3c.dom.Element; import org.apache.cxf.message.Message; import org.apache.cxf.rs.security.saml.assertion.Subject; import org.apache.cxf.rt.security.claims.ClaimCollection; -import org.apache.cxf.rt.security.claims.SAMLClaim; -import org.apache.cxf.rt.security.saml.SAMLSecurityContext; -import org.apache.cxf.rt.security.saml.SAMLUtils; +import org.apache.cxf.rt.security.saml.claims.SAMLClaim; +import org.apache.cxf.rt.security.saml.claims.SAMLSecurityContext; +import org.apache.cxf.rt.security.saml.utils.SAMLUtils; import org.apache.cxf.security.SecurityContext; import org.apache.wss4j.common.saml.SamlAssertionWrapper; import org.apache.wss4j.common.saml.builder.SAML2Constants; http://git-wip-us.apache.org/repos/asf/cxf/blob/c04c2720/rt/security-saml/pom.xml ---------------------------------------------------------------------- diff --git a/rt/security-saml/pom.xml b/rt/security-saml/pom.xml new file mode 100644 index 0000000..d740e27 --- /dev/null +++ b/rt/security-saml/pom.xml @@ -0,0 +1,60 @@ +<?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. +--> +<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/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <artifactId>cxf-rt-security-saml</artifactId> + <packaging>bundle</packaging> + <name>Apache CXF Runtime SAML Security functionality</name> + <description>Apache CXF Runtime SAML Security functionality</description> + <url>http://cxf.apache.org</url> + <parent> + <groupId>org.apache.cxf</groupId> + <artifactId>cxf-parent</artifactId> + <version>3.1.0-SNAPSHOT</version> + <relativePath>../../parent/pom.xml</relativePath> + </parent> + <properties> + <cxf.osgi.import> + org.opensaml*, + </cxf.osgi.import> + </properties> + <dependencies> + <dependency> + <groupId>org.apache.cxf</groupId> + <artifactId>cxf-rt-security</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.wss4j</groupId> + <artifactId>wss4j-ws-security-common</artifactId> + <version>${cxf.wss4j.version}</version> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-jdk14</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + </dependencies> +</project> http://git-wip-us.apache.org/repos/asf/cxf/blob/c04c2720/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/claims/ClaimBean.java ---------------------------------------------------------------------- diff --git a/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/claims/ClaimBean.java b/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/claims/ClaimBean.java new file mode 100644 index 0000000..4d9606f --- /dev/null +++ b/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/claims/ClaimBean.java @@ -0,0 +1,51 @@ +/** + * 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.cxf.rt.security.saml.claims; + +import org.apache.cxf.security.claims.authorization.ClaimMode; + +public class ClaimBean { + private SAMLClaim claim; + private ClaimMode claimMode; + private boolean matchAll; + + public ClaimBean(SAMLClaim claim) { + this.claim = claim; + } + + public ClaimBean(SAMLClaim claim, + ClaimMode claimMode, + boolean matchAll) { + this.claim = claim; + this.claimMode = claimMode; + this.matchAll = matchAll; + } + + public SAMLClaim getClaim() { + return claim; + } + + public boolean isMatchAll() { + return matchAll; + } + + public ClaimMode getClaimMode() { + return claimMode; + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/c04c2720/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/claims/SAMLClaim.java ---------------------------------------------------------------------- diff --git a/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/claims/SAMLClaim.java b/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/claims/SAMLClaim.java new file mode 100644 index 0000000..5fb080d --- /dev/null +++ b/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/claims/SAMLClaim.java @@ -0,0 +1,68 @@ +/** + * 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.cxf.rt.security.saml.claims; + +import org.apache.cxf.rt.security.claims.Claim; + + +/** + * This represents a Claim that is coupled to a SAML Assertion + */ +public class SAMLClaim extends Claim { + + /** + * This configuration tag specifies the default attribute name where the roles are present + * The default is "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role". + */ + public static final String SAML_ROLE_ATTRIBUTENAME_DEFAULT = + "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role"; + + private static final long serialVersionUID = 5530712294179589442L; + + private String nameFormat; + private String name; + private String friendlyName; + + public String getNameFormat() { + return nameFormat; + } + + public void setNameFormat(String nameFormat) { + this.nameFormat = nameFormat; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getFriendlyName() { + return friendlyName; + } + + public void setFriendlyName(String friendlyName) { + this.friendlyName = friendlyName; + } + + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/c04c2720/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/claims/SAMLSecurityContext.java ---------------------------------------------------------------------- diff --git a/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/claims/SAMLSecurityContext.java b/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/claims/SAMLSecurityContext.java new file mode 100644 index 0000000..b9b012a --- /dev/null +++ b/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/claims/SAMLSecurityContext.java @@ -0,0 +1,104 @@ +/** + * 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.cxf.rt.security.saml.claims; + +import java.security.Principal; +import java.util.Set; + +import org.w3c.dom.Element; +import org.apache.cxf.rt.security.claims.ClaimCollection; +import org.apache.cxf.rt.security.claims.ClaimsSecurityContext; + +public class SAMLSecurityContext implements ClaimsSecurityContext { + + private final Principal principal; + private Set<Principal> roles; + private Element assertionElement; + private String issuer; + private ClaimCollection claims; + + public SAMLSecurityContext(Principal principal) { + this(principal, null); + } + + public SAMLSecurityContext( + Principal principal, + Set<Principal> roles + ) { + this(principal, roles, null); + } + + public SAMLSecurityContext( + Principal principal, + Set<Principal> roles, + ClaimCollection claims + ) { + this.principal = principal; + this.roles = roles; + this.claims = claims; + } + + public ClaimCollection getClaims() { + return claims; + } + + public Principal getUserPrincipal() { + return principal; + } + + public boolean isUserInRole(String role) { + if (roles == null) { + return false; + } + for (Principal principalRole : roles) { + if (principalRole.getName().equals(role)) { + return true; + } + } + return false; + } + + public javax.security.auth.Subject getSubject() { + return null; + } + + public void setUserRoles(Set<Principal> userRoles) { + this.roles = userRoles; + } + + public Set<Principal> getUserRoles() { + return roles; + } + + public void setAssertionElement(Element assertionElement) { + this.assertionElement = assertionElement; + } + + public Element getAssertionElement() { + return assertionElement; + } + + public void setIssuer(String issuer) { + this.issuer = issuer; + } + + public String getIssuer() { + return issuer; + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/c04c2720/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/interceptor/ClaimsAuthorizingInterceptor.java ---------------------------------------------------------------------- diff --git a/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/interceptor/ClaimsAuthorizingInterceptor.java b/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/interceptor/ClaimsAuthorizingInterceptor.java new file mode 100644 index 0000000..c0244ea --- /dev/null +++ b/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/interceptor/ClaimsAuthorizingInterceptor.java @@ -0,0 +1,244 @@ +/** + * 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.cxf.rt.security.saml.interceptor; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Logger; + +import org.apache.cxf.common.logging.LogUtils; +import org.apache.cxf.common.util.ClassHelper; +import org.apache.cxf.interceptor.Fault; +import org.apache.cxf.interceptor.security.AccessDeniedException; +import org.apache.cxf.message.Message; +import org.apache.cxf.phase.AbstractPhaseInterceptor; +import org.apache.cxf.phase.Phase; +import org.apache.cxf.rt.security.saml.claims.ClaimBean; +import org.apache.cxf.rt.security.saml.claims.SAMLClaim; +import org.apache.cxf.rt.security.saml.claims.SAMLSecurityContext; +import org.apache.cxf.security.SecurityContext; +import org.apache.cxf.security.claims.authorization.Claim; +import org.apache.cxf.security.claims.authorization.ClaimMode; +import org.apache.cxf.security.claims.authorization.Claims; +import org.apache.cxf.service.Service; +import org.apache.cxf.service.invoker.MethodDispatcher; +import org.apache.cxf.service.model.BindingOperationInfo; + + +public class ClaimsAuthorizingInterceptor extends AbstractPhaseInterceptor<Message> { + + private static final Logger LOG = LogUtils.getL7dLogger(ClaimsAuthorizingInterceptor.class); + + private static final Set<String> SKIP_METHODS; + static { + SKIP_METHODS = new HashSet<>(); + SKIP_METHODS.addAll(Arrays.asList( + new String[] {"wait", "notify", "notifyAll", + "equals", "toString", "hashCode"})); + } + + private Map<String, List<ClaimBean>> claims = new HashMap<>(); + private Map<String, String> nameAliases = Collections.emptyMap(); + private Map<String, String> formatAliases = Collections.emptyMap(); + + public ClaimsAuthorizingInterceptor() { + super(Phase.PRE_INVOKE); + } + + public void handleMessage(Message message) throws Fault { + SecurityContext sc = message.get(SecurityContext.class); + if (!(sc instanceof SAMLSecurityContext)) { + throw new AccessDeniedException("Security Context is unavailable or unrecognized"); + } + + Method method = getTargetMethod(message); + + if (authorize((SAMLSecurityContext)sc, method)) { + return; + } + + throw new AccessDeniedException("Unauthorized"); + } + + public void setClaims(Map<String, List<ClaimBean>> claimsMap) { + claims.putAll(claimsMap); + } + + protected Method getTargetMethod(Message m) { + BindingOperationInfo bop = m.getExchange().get(BindingOperationInfo.class); + if (bop != null) { + MethodDispatcher md = (MethodDispatcher) + m.getExchange().get(Service.class).get(MethodDispatcher.class.getName()); + return md.getMethod(bop); + } + Method method = (Method)m.get("org.apache.cxf.resource.method"); + if (method != null) { + return method; + } + throw new AccessDeniedException("Method is not available : Unauthorized"); + } + + protected boolean authorize(SAMLSecurityContext sc, Method method) { + List<ClaimBean> list = claims.get(method.getName()); + org.apache.cxf.rt.security.claims.ClaimCollection actualClaims = sc.getClaims(); + + for (ClaimBean claimBean : list) { + org.apache.cxf.rt.security.claims.Claim claim = claimBean.getClaim(); + org.apache.cxf.rt.security.claims.Claim matchingClaim = null; + for (org.apache.cxf.rt.security.claims.Claim cl : actualClaims) { + if (cl instanceof SAMLClaim + && ((SAMLClaim)cl).getName().equals(((SAMLClaim)claim).getName()) + && ((SAMLClaim)cl).getNameFormat().equals(((SAMLClaim)claim).getNameFormat())) { + matchingClaim = cl; + break; + } + } + if (matchingClaim == null) { + if (claimBean.getClaimMode() == ClaimMode.STRICT) { + return false; + } else { + continue; + } + } + List<Object> claimValues = claim.getValues(); + List<Object> matchingClaimValues = matchingClaim.getValues(); + if (claimBean.isMatchAll() + && !matchingClaimValues.containsAll(claimValues)) { + return false; + } else { + boolean matched = false; + for (Object value : matchingClaimValues) { + if (claimValues.contains(value)) { + matched = true; + break; + } + } + if (!matched) { + return false; + } + } + } + return true; + } + + public void setSecuredObject(Object object) { + Class<?> cls = ClassHelper.getRealClass(object); + findClaims(cls); + if (claims.isEmpty()) { + LOG.warning("The claims list is empty, the service object is not protected"); + } + } + + protected void findClaims(Class<?> cls) { + if (cls == null || cls == Object.class) { + return; + } + List<ClaimBean> clsClaims = + getClaims(cls.getAnnotation(Claims.class), cls.getAnnotation(Claim.class)); + for (Method m : cls.getMethods()) { + if (SKIP_METHODS.contains(m.getName())) { + continue; + } + List<ClaimBean> methodClaims = + getClaims(m.getAnnotation(Claims.class), m.getAnnotation(Claim.class)); + + List<ClaimBean> allClaims = new ArrayList<>(methodClaims); + for (ClaimBean bean : clsClaims) { + if (isClaimOverridden(bean, methodClaims)) { + continue; + } + allClaims.add(bean); + } + + claims.put(m.getName(), allClaims); + } + if (!claims.isEmpty()) { + return; + } + + findClaims(cls.getSuperclass()); + + if (!claims.isEmpty()) { + return; + } + + for (Class<?> interfaceCls : cls.getInterfaces()) { + findClaims(interfaceCls); + } + } + + private static boolean isClaimOverridden(ClaimBean bean, List<ClaimBean> mClaims) { + for (ClaimBean methodBean : mClaims) { + if (bean.getClaim().getName().equals(methodBean.getClaim().getName()) + && bean.getClaim().getNameFormat().equals(methodBean.getClaim().getNameFormat())) { + return true; + } + } + return false; + } + + private List<ClaimBean> getClaims( + Claims claimsAnn, Claim claimAnn) { + List<ClaimBean> claimsList = new ArrayList<>(); + + List<Claim> annClaims = new ArrayList<>(); + if (claimsAnn != null) { + annClaims.addAll(Arrays.asList(claimsAnn.value())); + } else if (claimAnn != null) { + annClaims.add(claimAnn); + } + for (Claim ann : annClaims) { + SAMLClaim claim = new SAMLClaim(); + + String claimName = ann.name(); + if (nameAliases.containsKey(claimName)) { + claimName = nameAliases.get(claimName); + } + String claimFormat = ann.format(); + if (formatAliases.containsKey(claimFormat)) { + claimFormat = formatAliases.get(claimFormat); + } + + claim.setName(claimName); + claim.setNameFormat(claimFormat); + for (String value : ann.value()) { + claim.addValue(value); + } + + claimsList.add(new ClaimBean(claim, ann.mode(), ann.matchAll())); + } + return claimsList; + } + + public void setNameAliases(Map<String, String> nameAliases) { + this.nameAliases = nameAliases; + } + + public void setFormatAliases(Map<String, String> formatAliases) { + this.formatAliases = formatAliases; + } + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/c04c2720/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/utils/SAMLUtils.java ---------------------------------------------------------------------- diff --git a/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/utils/SAMLUtils.java b/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/utils/SAMLUtils.java new file mode 100644 index 0000000..5cb5bd6 --- /dev/null +++ b/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/utils/SAMLUtils.java @@ -0,0 +1,141 @@ +/** + * 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.cxf.rt.security.saml.utils; + +import java.net.URI; +import java.security.Principal; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.w3c.dom.Element; +import org.apache.cxf.common.security.SimpleGroup; +import org.apache.cxf.rt.security.claims.Claim; +import org.apache.cxf.rt.security.claims.ClaimCollection; +import org.apache.cxf.rt.security.saml.claims.SAMLClaim; +import org.apache.wss4j.common.saml.SamlAssertionWrapper; +import org.opensaml.core.xml.XMLObject; +import org.opensaml.saml.common.SAMLVersion; +import org.opensaml.saml.saml2.core.Attribute; +import org.opensaml.saml.saml2.core.AttributeStatement; + +public final class SAMLUtils { + + private SAMLUtils() { + + } + + /** + * Extract Claims from a SAML Assertion + */ + public static ClaimCollection getClaims(SamlAssertionWrapper assertion) { + ClaimCollection claims = new ClaimCollection(); + + if (assertion.getSamlVersion().equals(SAMLVersion.VERSION_20)) { + List<AttributeStatement> statements = assertion.getSaml2().getAttributeStatements(); + for (AttributeStatement as : statements) { + for (Attribute atr : as.getAttributes()) { + SAMLClaim claim = new SAMLClaim(); + claim.setClaimType(URI.create(atr.getName())); + + claim.setName(atr.getName()); + claim.setNameFormat(atr.getNameFormat()); + claim.setFriendlyName(atr.getFriendlyName()); + + for (XMLObject o : atr.getAttributeValues()) { + String attrValue = o.getDOM().getTextContent(); + claim.getValues().add(attrValue); + } + + claims.add(claim); + } + } + } else { + List<org.opensaml.saml.saml1.core.AttributeStatement> attributeStatements = + assertion.getSaml1().getAttributeStatements(); + + for (org.opensaml.saml.saml1.core.AttributeStatement statement : attributeStatements) { + for (org.opensaml.saml.saml1.core.Attribute atr : statement.getAttributes()) { + SAMLClaim claim = new SAMLClaim(); + + String claimType = atr.getAttributeName(); + if (atr.getAttributeNamespace() != null) { + claimType = atr.getAttributeNamespace() + "/" + claimType; + } + claim.setClaimType(URI.create(claimType)); + + claim.setName(atr.getAttributeName()); + claim.setNameFormat(atr.getAttributeNamespace()); + + for (XMLObject o : atr.getAttributeValues()) { + String attrValue = o.getDOM().getTextContent(); + claim.getValues().add(attrValue); + } + + claims.add(claim); + } + } + } + + return claims; + } + + /** + * Extract roles from the given Claims + */ + public static Set<Principal> parseRolesFromClaims( + ClaimCollection claims, + String name, + String nameFormat + ) { + String roleAttributeName = name; + if (roleAttributeName == null) { + roleAttributeName = SAMLClaim.SAML_ROLE_ATTRIBUTENAME_DEFAULT; + } + + Set<Principal> roles = new HashSet<>(); + + for (Claim claim : claims) { + if (claim instanceof SAMLClaim && ((SAMLClaim)claim).getName().equals(name) + && (nameFormat == null + || claim instanceof SAMLClaim && nameFormat.equals(((SAMLClaim)claim).getNameFormat()))) { + for (Object claimValue : claim.getValues()) { + if (claimValue instanceof String) { + roles.add(new SimpleGroup((String)claimValue)); + } + } + if (claim.getValues().size() > 1) { + // Don't search for other attributes with the same name if > 1 claim value + break; + } + } + } + + return roles; + } + + public static String getIssuer(Object assertion) { + return ((SamlAssertionWrapper)assertion).getIssuerString(); + } + + public static Element getAssertionElement(Object assertion) { + return ((SamlAssertionWrapper)assertion).getElement(); + } + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/c04c2720/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/xacml/AbstractXACMLAuthorizingInterceptor.java ---------------------------------------------------------------------- diff --git a/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/xacml/AbstractXACMLAuthorizingInterceptor.java b/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/xacml/AbstractXACMLAuthorizingInterceptor.java new file mode 100644 index 0000000..590b324 --- /dev/null +++ b/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/xacml/AbstractXACMLAuthorizingInterceptor.java @@ -0,0 +1,170 @@ +/** + * 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.cxf.rt.security.saml.xacml; + +import java.security.Principal; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import org.apache.cxf.common.logging.LogUtils; +import org.apache.cxf.helpers.DOMUtils; +import org.apache.cxf.interceptor.Fault; +import org.apache.cxf.interceptor.security.AccessDeniedException; +import org.apache.cxf.message.Message; +import org.apache.cxf.phase.AbstractPhaseInterceptor; +import org.apache.cxf.phase.Phase; +import org.apache.cxf.security.LoginSecurityContext; +import org.apache.cxf.security.SecurityContext; +import org.apache.wss4j.common.saml.OpenSAMLUtil; +import org.apache.wss4j.common.util.DOM2Writer; +import org.opensaml.xacml.ctx.DecisionType.DECISION; +import org.opensaml.xacml.ctx.RequestType; +import org.opensaml.xacml.ctx.ResponseType; +import org.opensaml.xacml.ctx.ResultType; +import org.opensaml.xacml.ctx.StatusType; + + +/** + * An abstract interceptor to perform an XACML authorization request to a remote PDP, + * and make an authorization decision based on the response. It takes the principal and roles + * from the SecurityContext, and uses the XACMLRequestBuilder to construct an XACML Request + * statement. + * + * This class must be subclassed to actually perform the request to the PDP. + * + * @deprecated: Use XACMLAuthorizingInterceptor instead + */ +@Deprecated +public abstract class AbstractXACMLAuthorizingInterceptor extends AbstractPhaseInterceptor<Message> { + + private static final Logger LOG = LogUtils.getL7dLogger(AbstractXACMLAuthorizingInterceptor.class); + + private XACMLRequestBuilder requestBuilder = new DefaultXACMLRequestBuilder(); + + public AbstractXACMLAuthorizingInterceptor() { + super(Phase.PRE_INVOKE); + org.apache.wss4j.common.saml.OpenSAMLUtil.initSamlEngine(); + } + + public void handleMessage(Message message) throws Fault { + SecurityContext sc = message.get(SecurityContext.class); + + if (sc instanceof LoginSecurityContext) { + Principal principal = sc.getUserPrincipal(); + + LoginSecurityContext loginSecurityContext = (LoginSecurityContext)sc; + Set<Principal> principalRoles = loginSecurityContext.getUserRoles(); + List<String> roles = new ArrayList<>(); + if (principalRoles != null) { + for (Principal p : principalRoles) { + if (p != principal) { + roles.add(p.getName()); + } + } + } + + try { + if (authorize(principal, roles, message)) { + return; + } + } catch (Exception e) { + LOG.log(Level.FINE, "Unauthorized: " + e.getMessage(), e); + throw new AccessDeniedException("Unauthorized"); + } + } else { + LOG.log( + Level.FINE, + "The SecurityContext was not an instance of LoginSecurityContext. No authorization " + + "is possible as a result" + ); + } + + throw new AccessDeniedException("Unauthorized"); + } + + public XACMLRequestBuilder getRequestBuilder() { + return requestBuilder; + } + + public void setRequestBuilder(XACMLRequestBuilder requestBuilder) { + this.requestBuilder = requestBuilder; + } + + /** + * Perform a (remote) authorization decision and return a boolean depending on the result + */ + protected boolean authorize( + Principal principal, List<String> roles, Message message + ) throws Exception { + RequestType request = requestBuilder.createRequest(principal, roles, message); + if (LOG.isLoggable(Level.FINE)) { + Document doc = DOMUtils.createDocument(); + Element requestElement = OpenSAMLUtil.toDom(request, doc); + LOG.log(Level.FINE, DOM2Writer.nodeToString(requestElement)); + } + + ResponseType response = performRequest(request, message); + + List<ResultType> results = response.getResults(); + + if (results == null) { + return false; + } + + for (ResultType result : results) { + // Handle any Obligations returned by the PDP + handleObligations(request, principal, message, result); + + DECISION decision = result.getDecision() != null ? result.getDecision().getDecision() : DECISION.Deny; + String code = ""; + String statusMessage = ""; + if (result.getStatus() != null) { + StatusType status = result.getStatus(); + code = status.getStatusCode() != null ? status.getStatusCode().getValue() : ""; + statusMessage = status.getStatusMessage() != null ? status.getStatusMessage().getValue() : ""; + } + LOG.fine("XACML authorization result: " + decision + ", code: " + code + ", message: " + statusMessage); + return decision == DECISION.Permit; + } + + return false; + } + + public abstract ResponseType performRequest(RequestType request, Message message) throws Exception; + + /** + * Handle any Obligations returned by the PDP + */ + protected void handleObligations( + RequestType request, + Principal principal, + Message message, + ResultType result + ) throws Exception { + // Do nothing by default + } + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/c04c2720/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/xacml/CXFMessageParser.java ---------------------------------------------------------------------- diff --git a/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/xacml/CXFMessageParser.java b/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/xacml/CXFMessageParser.java new file mode 100644 index 0000000..1af0f5e --- /dev/null +++ b/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/xacml/CXFMessageParser.java @@ -0,0 +1,97 @@ +/** + * 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.cxf.rt.security.saml.xacml; + +import javax.xml.namespace.QName; + +import org.w3c.dom.Element; +import org.apache.cxf.message.Message; +import org.apache.cxf.rt.security.saml.claims.SAMLSecurityContext; +import org.apache.cxf.security.SecurityContext; +import org.apache.wss4j.common.ext.WSSecurityException; +import org.apache.wss4j.common.saml.SamlAssertionWrapper; + +public class CXFMessageParser { + private Message message; + + /** + * @param message + */ + public CXFMessageParser(Message message) { + this.message = message; + } + + public boolean isSOAPService() { + return getWSDLOperation() != null; + } + + public QName getWSDLOperation() { + if (message != null && message.get(Message.WSDL_OPERATION) != null) { + return (QName)message.get(Message.WSDL_OPERATION); + } + return null; + } + + public QName getWSDLService() { + if (message != null && message.get(Message.WSDL_SERVICE) != null) { + return (QName)message.get(Message.WSDL_SERVICE); + } + return null; + } + + /** + * @param fullRequestURL Whether to send the full Request URL as the resource or not. If set to true, the + * full Request URL will be sent for both a JAX-WS and JAX-RS service. If set to false (the + * default), a JAX-WS service will send the "{namespace}operation" QName, and a JAX-RS service + * will send the RequestURI (i.e. minus the initial https:<ip> prefix) + */ + public String getResourceURI(boolean fullRequestURL) { + String property = fullRequestURL ? Message.REQUEST_URL : Message.REQUEST_URI; + if (message != null && message.get(property) != null) { + return (String)message.get(property); + } + return null; + } + + public String getAction(String defaultSOAPAction) { + String actionToUse = defaultSOAPAction; + // For REST use the HTTP Verb + if (message.get(Message.WSDL_OPERATION) == null && message.get(Message.HTTP_REQUEST_METHOD) != null) { + actionToUse = (String)message.get(Message.HTTP_REQUEST_METHOD); + } + return actionToUse; + } + + /** + * Get the Issuer of the SAML Assertion + */ + public String getIssuer() throws WSSecurityException { + SecurityContext sc = message.get(SecurityContext.class); + + if (sc instanceof SAMLSecurityContext) { + Element assertionElement = ((SAMLSecurityContext)sc).getAssertionElement(); + if (assertionElement != null) { + SamlAssertionWrapper wrapper = new SamlAssertionWrapper(assertionElement); + return wrapper.getIssuerString(); + } + } + + return null; + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/c04c2720/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/xacml/DefaultXACMLRequestBuilder.java ---------------------------------------------------------------------- diff --git a/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/xacml/DefaultXACMLRequestBuilder.java b/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/xacml/DefaultXACMLRequestBuilder.java new file mode 100644 index 0000000..02d115f --- /dev/null +++ b/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/xacml/DefaultXACMLRequestBuilder.java @@ -0,0 +1,217 @@ +/** + * 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.cxf.rt.security.saml.xacml; + +import java.security.Principal; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import javax.xml.namespace.QName; + +import org.apache.cxf.message.Message; +import org.joda.time.DateTime; +import org.opensaml.xacml.ctx.ActionType; +import org.opensaml.xacml.ctx.AttributeType; +import org.opensaml.xacml.ctx.AttributeValueType; +import org.opensaml.xacml.ctx.EnvironmentType; +import org.opensaml.xacml.ctx.RequestType; +import org.opensaml.xacml.ctx.ResourceType; +import org.opensaml.xacml.ctx.SubjectType; + +/** + * This class constructs an XACML Request given a Principal, list of roles and MessageContext, + * following the SAML 2.0 profile of XACML 2.0. The principal name is inserted as the Subject ID, + * and the list of roles associated with that principal are inserted as Subject roles. The action + * to send defaults to "execute". + * + * For a SOAP Service, the resource-id Attribute refers to the + * "{serviceNamespace}serviceName#{operationNamespace}operationName" String (shortened to + * "{serviceNamespace}serviceName#operationName" if the namespaces are identical). The + * "{serviceNamespace}serviceName", "{operationNamespace}operationName" and resource URI are also + * sent to simplify processing at the PDP side. + * + * For a REST service the request URL is the resource. You can also configure the ability to + * send the truncated request URI instead for a SOAP or REST service. The current DateTime is + * also sent in an Environment, however this can be disabled via configuration. + */ +public class DefaultXACMLRequestBuilder implements XACMLRequestBuilder { + + private boolean sendDateTime = true; + private String action = "execute"; + private boolean sendFullRequestURL = true; + + /** + * Create an XACML Request given a Principal, list of roles and Message. + */ + public RequestType createRequest(Principal principal, List<String> roles, Message message) + throws Exception { + CXFMessageParser messageParser = new CXFMessageParser(message); + String issuer = messageParser.getIssuer(); + + String actionToUse = messageParser.getAction(action); + + SubjectType subjectType = createSubjectType(principal, roles, issuer); + ResourceType resourceType = createResourceType(messageParser); + AttributeType actionAttribute = createAttribute(XACMLConstants.ACTION_ID, XACMLConstants.XS_STRING, + null, actionToUse); + ActionType actionType = RequestComponentBuilder.createActionType(Collections.singletonList(actionAttribute)); + + return RequestComponentBuilder.createRequestType(Collections.singletonList(subjectType), + Collections.singletonList(resourceType), + actionType, + createEnvironmentType()); + } + + private ResourceType createResourceType(CXFMessageParser messageParser) { + List<AttributeType> attributes = new ArrayList<>(); + + // Resource-id + String resourceId = null; + boolean isSoapService = messageParser.isSOAPService(); + if (isSoapService) { + QName serviceName = messageParser.getWSDLService(); + QName operationName = messageParser.getWSDLOperation(); + + if (serviceName != null) { + resourceId = serviceName.toString() + "#"; + if (serviceName.getNamespaceURI() != null + && serviceName.getNamespaceURI().equals(operationName.getNamespaceURI())) { + resourceId += operationName.getLocalPart(); + } else { + resourceId += operationName.toString(); + } + } else { + resourceId = operationName.toString(); + } + } else { + resourceId = messageParser.getResourceURI(sendFullRequestURL); + } + + attributes.add(createAttribute(XACMLConstants.RESOURCE_ID, XACMLConstants.XS_STRING, null, + resourceId)); + + if (isSoapService) { + // WSDL Service + QName wsdlService = messageParser.getWSDLService(); + if (wsdlService != null) { + attributes.add(createAttribute(XACMLConstants.RESOURCE_WSDL_SERVICE_ID, XACMLConstants.XS_STRING, null, + wsdlService.toString())); + } + + // WSDL Operation + QName wsdlOperation = messageParser.getWSDLOperation(); + attributes.add(createAttribute(XACMLConstants.RESOURCE_WSDL_OPERATION_ID, XACMLConstants.XS_STRING, null, + wsdlOperation.toString())); + + // WSDL Endpoint + String endpointURI = messageParser.getResourceURI(sendFullRequestURL); + attributes.add(createAttribute(XACMLConstants.RESOURCE_WSDL_ENDPOINT, XACMLConstants.XS_STRING, null, + endpointURI)); + } + + return RequestComponentBuilder.createResourceType(attributes, null); + } + + private EnvironmentType createEnvironmentType() { + if (sendDateTime) { + List<AttributeType> attributes = new ArrayList<>(); + AttributeType environmentAttribute = createAttribute(XACMLConstants.CURRENT_DATETIME, + XACMLConstants.XS_DATETIME, null, + new DateTime().toString()); + attributes.add(environmentAttribute); + return RequestComponentBuilder.createEnvironmentType(attributes); + } + + List<AttributeType> attributes = Collections.emptyList(); + return RequestComponentBuilder.createEnvironmentType(attributes); + } + + private SubjectType createSubjectType(Principal principal, List<String> roles, String issuer) { + List<AttributeType> attributes = new ArrayList<>(); + attributes.add(createAttribute(XACMLConstants.SUBJECT_ID, XACMLConstants.XS_STRING, issuer, + principal.getName())); + + if (roles != null) { + List<AttributeValueType> roleAttributes = new ArrayList<>(); + for (String role : roles) { + if (role != null) { + AttributeValueType subjectRoleAttributeValue = + RequestComponentBuilder.createAttributeValueType(role); + roleAttributes.add(subjectRoleAttributeValue); + } + } + + if (!roleAttributes.isEmpty()) { + AttributeType subjectRoleAttribute = + createAttribute( + XACMLConstants.SUBJECT_ROLE, + XACMLConstants.XS_ANY_URI, + issuer, + roleAttributes + ); + attributes.add(subjectRoleAttribute); + } + } + + return RequestComponentBuilder.createSubjectType(attributes, null); + } + + private AttributeType createAttribute(String id, String type, String issuer, List<AttributeValueType> values) { + return RequestComponentBuilder.createAttributeType(id, type, issuer, values); + } + + private AttributeType createAttribute(String id, String type, String issuer, String value) { + return createAttribute(id, type, issuer, + Collections.singletonList(RequestComponentBuilder.createAttributeValueType(value))); + } + + /** + * Set a new Action String to use + */ + public void setAction(String action) { + this.action = action; + } + + public void setSendDateTime(boolean sendDateTime) { + this.sendDateTime = sendDateTime; + } + + /** + * Whether to send the full Request URL as the resource or not. If set to true, + * the full Request URL will be sent for both a JAX-WS and JAX-RS service. If set + * to false (the default), a JAX-WS service will send the "{namespace}operation" QName, + * and a JAX-RS service will send the RequestURI (i.e. minus the initial https:<ip> prefix). + */ + public void setSendFullRequestURL(boolean sendFullRequestURL) { + this.sendFullRequestURL = sendFullRequestURL; + } + + @Override + public List<String> getResources(Message message) { + throw new IllegalAccessError("Deprecated"); + } + + @Override + public String getResource(Message message) { + throw new IllegalAccessError("Deprecated"); + } + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/c04c2720/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/xacml/RequestComponentBuilder.java ---------------------------------------------------------------------- diff --git a/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/xacml/RequestComponentBuilder.java b/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/xacml/RequestComponentBuilder.java new file mode 100644 index 0000000..08f8da8 --- /dev/null +++ b/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/xacml/RequestComponentBuilder.java @@ -0,0 +1,183 @@ +/** + * 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.cxf.rt.security.saml.xacml; + +import java.util.List; + +import org.opensaml.core.xml.XMLObjectBuilderFactory; +import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport; +import org.opensaml.xacml.XACMLObjectBuilder; +import org.opensaml.xacml.ctx.ActionType; +import org.opensaml.xacml.ctx.AttributeType; +import org.opensaml.xacml.ctx.AttributeValueType; +import org.opensaml.xacml.ctx.EnvironmentType; +import org.opensaml.xacml.ctx.RequestType; +import org.opensaml.xacml.ctx.ResourceContentType; +import org.opensaml.xacml.ctx.ResourceType; +import org.opensaml.xacml.ctx.SubjectType; + +/** + * A set of utility methods to construct XACML 2.0 Request statements + */ +public final class RequestComponentBuilder { + private static volatile XACMLObjectBuilder<AttributeValueType> attributeValueTypeBuilder; + + private static volatile XACMLObjectBuilder<AttributeType> attributeTypeBuilder; + + private static volatile XACMLObjectBuilder<SubjectType> subjectTypeBuilder; + + private static volatile XACMLObjectBuilder<ResourceType> resourceTypeBuilder; + + private static volatile XACMLObjectBuilder<ActionType> actionTypeBuilder; + + private static volatile XACMLObjectBuilder<EnvironmentType> environmentTypeBuilder; + + private static volatile XACMLObjectBuilder<RequestType> requestTypeBuilder; + + private static volatile XMLObjectBuilderFactory builderFactory = + XMLObjectProviderRegistrySupport.getBuilderFactory(); + + private RequestComponentBuilder() { + // complete + } + + @SuppressWarnings("unchecked") + public static AttributeValueType createAttributeValueType( + String value + ) { + if (attributeValueTypeBuilder == null) { + attributeValueTypeBuilder = (XACMLObjectBuilder<AttributeValueType>) + builderFactory.getBuilder(AttributeValueType.DEFAULT_ELEMENT_NAME); + } + AttributeValueType attributeValue = attributeValueTypeBuilder.buildObject(); + attributeValue.setValue(value); + + return attributeValue; + } + + @SuppressWarnings("unchecked") + public static AttributeType createAttributeType( + String attributeId, + String dataType, + String issuer, + List<AttributeValueType> attributeValues + ) { + if (attributeTypeBuilder == null) { + attributeTypeBuilder = (XACMLObjectBuilder<AttributeType>) + builderFactory.getBuilder(AttributeType.DEFAULT_ELEMENT_NAME); + } + AttributeType attributeType = attributeTypeBuilder.buildObject(); + attributeType.setAttributeID(attributeId); + attributeType.setDataType(dataType); + attributeType.setIssuer(issuer); + attributeType.getAttributeValues().addAll(attributeValues); + + return attributeType; + } + + @SuppressWarnings("unchecked") + public static SubjectType createSubjectType( + List<AttributeType> attributes, + String subjectCategory + ) { + if (subjectTypeBuilder == null) { + subjectTypeBuilder = (XACMLObjectBuilder<SubjectType>) + builderFactory.getBuilder(SubjectType.DEFAULT_ELEMENT_NAME); + } + SubjectType subject = subjectTypeBuilder.buildObject(); + if (attributes != null) { + subject.getAttributes().addAll(attributes); + } + subject.setSubjectCategory(subjectCategory); + + return subject; + } + + @SuppressWarnings("unchecked") + public static ResourceType createResourceType( + List<AttributeType> attributes, + ResourceContentType resourceContent + ) { + if (resourceTypeBuilder == null) { + resourceTypeBuilder = (XACMLObjectBuilder<ResourceType>) + builderFactory.getBuilder(ResourceType.DEFAULT_ELEMENT_NAME); + } + ResourceType resource = resourceTypeBuilder.buildObject(); + if (attributes != null) { + resource.getAttributes().addAll(attributes); + } + resource.setResourceContent(resourceContent); + + return resource; + } + + @SuppressWarnings("unchecked") + public static ActionType createActionType( + List<AttributeType> attributes + ) { + if (actionTypeBuilder == null) { + actionTypeBuilder = (XACMLObjectBuilder<ActionType>) + builderFactory.getBuilder(ActionType.DEFAULT_ELEMENT_NAME); + } + ActionType action = actionTypeBuilder.buildObject(); + if (attributes != null) { + action.getAttributes().addAll(attributes); + } + + return action; + } + + @SuppressWarnings("unchecked") + public static EnvironmentType createEnvironmentType( + List<AttributeType> attributes + ) { + if (environmentTypeBuilder == null) { + environmentTypeBuilder = (XACMLObjectBuilder<EnvironmentType>) + builderFactory.getBuilder(EnvironmentType.DEFAULT_ELEMENT_NAME); + } + EnvironmentType enviroment = environmentTypeBuilder.buildObject(); + if (attributes != null) { + enviroment.getAttributes().addAll(attributes); + } + + return enviroment; + } + + @SuppressWarnings("unchecked") + public static RequestType createRequestType( + List<SubjectType> subjects, + List<ResourceType> resources, + ActionType action, + EnvironmentType environment + ) { + if (requestTypeBuilder == null) { + requestTypeBuilder = (XACMLObjectBuilder<RequestType>) + builderFactory.getBuilder(RequestType.DEFAULT_ELEMENT_NAME); + } + RequestType request = requestTypeBuilder.buildObject(); + request.getSubjects().addAll(subjects); + request.getResources().addAll(resources); + request.setAction(action); + request.setEnvironment(environment); + + return request; + } + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/c04c2720/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/xacml/SamlRequestComponentBuilder.java ---------------------------------------------------------------------- diff --git a/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/xacml/SamlRequestComponentBuilder.java b/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/xacml/SamlRequestComponentBuilder.java new file mode 100644 index 0000000..c0d8255 --- /dev/null +++ b/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/xacml/SamlRequestComponentBuilder.java @@ -0,0 +1,118 @@ +/** + * 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.cxf.rt.security.saml.xacml; + +import java.util.UUID; + +import org.joda.time.DateTime; +import org.opensaml.core.xml.XMLObjectBuilderFactory; +import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport; +import org.opensaml.saml.common.SAMLObjectBuilder; +import org.opensaml.saml.common.SAMLVersion; +import org.opensaml.saml.saml2.core.Issuer; +import org.opensaml.xacml.XACMLObjectBuilder; +import org.opensaml.xacml.ctx.RequestType; +import org.opensaml.xacml.profile.saml.SAMLProfileConstants; +import org.opensaml.xacml.profile.saml.XACMLAuthzDecisionQueryType; + +/** + * A set of utility methods to construct XACML SAML Request statements, based on the + * SAML 2.0 profile of XACML v2.0 specification. + */ +public final class SamlRequestComponentBuilder { + private static volatile XACMLObjectBuilder<XACMLAuthzDecisionQueryType> xacmlAuthzDecisionQueryTypeBuilder; + + private static volatile SAMLObjectBuilder<Issuer> issuerBuilder; + + private static volatile XMLObjectBuilderFactory builderFactory = + XMLObjectProviderRegistrySupport.getBuilderFactory(); + + private SamlRequestComponentBuilder() { + // complete + } + + /** + * Create an AuthzDecisionQuery using the defaults + */ + public static XACMLAuthzDecisionQueryType createAuthzDecisionQuery( + String issuerValue, + RequestType request, + String namespace + ) { + return createAuthzDecisionQuery(false, false, issuerValue, request, namespace); + } + + @SuppressWarnings("unchecked") + public static XACMLAuthzDecisionQueryType createAuthzDecisionQuery( + boolean inputContextOnly, + boolean returnContext, + String issuerValue, + RequestType request, + String namespace + ) { + if (xacmlAuthzDecisionQueryTypeBuilder == null) { + xacmlAuthzDecisionQueryTypeBuilder = (XACMLObjectBuilder<XACMLAuthzDecisionQueryType>) + builderFactory.getBuilder(XACMLAuthzDecisionQueryType.DEFAULT_ELEMENT_NAME_XACML20); + } + XACMLAuthzDecisionQueryType authzQuery = + xacmlAuthzDecisionQueryTypeBuilder.buildObject( + namespace, + XACMLAuthzDecisionQueryType.DEFAULT_ELEMENT_LOCAL_NAME, + SAMLProfileConstants.SAML20XACMLPROTOCOL_PREFIX + ); + authzQuery.setID("_" + UUID.randomUUID().toString()); + authzQuery.setVersion(SAMLVersion.VERSION_20); + authzQuery.setIssueInstant(new DateTime()); + authzQuery.setInputContextOnly(Boolean.valueOf(inputContextOnly)); + authzQuery.setReturnContext(Boolean.valueOf(returnContext)); + + if (issuerValue != null) { + Issuer issuer = createIssuer(issuerValue); + authzQuery.setIssuer(issuer); + } + + authzQuery.setRequest(request); + + return authzQuery; + } + + + /** + * Create an Issuer object + * + * @param issuerValue of type String + * @return an Issuer object + */ + @SuppressWarnings("unchecked") + public static Issuer createIssuer(String issuerValue) { + if (issuerBuilder == null) { + issuerBuilder = (SAMLObjectBuilder<Issuer>) + builderFactory.getBuilder(Issuer.DEFAULT_ELEMENT_NAME); + + } + Issuer issuer = issuerBuilder.buildObject(); + // + // The SAML authority that is making the claim(s) in the assertion. The issuer SHOULD + // be unambiguous to the intended relying parties. + issuer.setValue(issuerValue); + return issuer; + } + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/c04c2720/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/xacml/XACMLAuthorizingInterceptor.java ---------------------------------------------------------------------- diff --git a/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/xacml/XACMLAuthorizingInterceptor.java b/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/xacml/XACMLAuthorizingInterceptor.java new file mode 100644 index 0000000..065c5a9 --- /dev/null +++ b/rt/security-saml/src/main/java/org/apache/cxf/rt/security/saml/xacml/XACMLAuthorizingInterceptor.java @@ -0,0 +1,86 @@ +/** + * 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.cxf.rt.security.saml.xacml; + +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMResult; +import javax.xml.transform.dom.DOMSource; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.apache.cxf.helpers.DOMUtils; +import org.apache.cxf.message.Message; +import org.apache.cxf.rt.security.saml.xacml.pdp.api.PolicyDecisionPoint; +import org.apache.wss4j.common.ext.WSSecurityException; +import org.apache.wss4j.common.saml.OpenSAMLUtil; +import org.opensaml.xacml.ctx.RequestType; +import org.opensaml.xacml.ctx.ResponseType; + +/** + * An interceptor to perform an XACML authorization request to a remote PDP, + * and make an authorization decision based on the response. It takes the principal and roles + * from the SecurityContext, and uses the XACMLRequestBuilder to construct an XACML Request + * statement. + */ +@SuppressWarnings("deprecation") +public class XACMLAuthorizingInterceptor extends AbstractXACMLAuthorizingInterceptor { + private PolicyDecisionPoint pdp; + + public XACMLAuthorizingInterceptor(PolicyDecisionPoint pdp) { + super(); + this.pdp = pdp; + } + + @Override + public ResponseType performRequest(RequestType request, Message message) throws Exception { + Source requestSource = requestType2Source(request); + Source responseSource = this.pdp.evaluate(requestSource); + return responseSourceToResponseType(responseSource); + } + + private Source requestType2Source(RequestType request) { + Document doc = DOMUtils.createDocument(); + Element requestElement; + try { + requestElement = OpenSAMLUtil.toDom(request, doc); + } catch (WSSecurityException e) { + throw new RuntimeException("Error converting PDP RequestType to Dom", e); + } + return new DOMSource(requestElement); + } + + private ResponseType responseSourceToResponseType(Source responseSource) { + try { + Transformer trans = TransformerFactory.newInstance().newTransformer(); + DOMResult res = new DOMResult(); + trans.transform(responseSource, res); + Node nd = res.getNode(); + if (nd instanceof Document) { + nd = ((Document)nd).getDocumentElement(); + } + return (ResponseType)OpenSAMLUtil.fromDom((Element)nd); + } catch (Exception e) { + throw new RuntimeException("Error converting pdp response to ResponseType", e); + } + } +}
