1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.wss4j.policy.stax.assertionStates;
20
21 import org.apache.wss4j.common.ext.WSSecurityException;
22 import org.apache.wss4j.common.saml.SamlAssertionWrapper;
23 import org.apache.wss4j.common.WSSPolicyException;
24 import org.apache.wss4j.policy.model.AbstractSecurityAssertion;
25 import org.apache.wss4j.policy.model.AbstractToken;
26 import org.apache.wss4j.policy.model.IssuedToken;
27 import org.apache.wss4j.policy.stax.PolicyAsserter;
28 import org.apache.wss4j.stax.ext.WSSConstants;
29 import org.apache.wss4j.stax.securityEvent.KerberosTokenSecurityEvent;
30 import org.apache.wss4j.stax.securityEvent.SamlTokenSecurityEvent;
31 import org.apache.xml.security.exceptions.XMLSecurityException;
32 import org.apache.xml.security.stax.securityEvent.SecurityEventConstants;
33 import org.apache.xml.security.stax.securityEvent.TokenSecurityEvent;
34 import org.apache.xml.security.stax.securityToken.SecurityToken;
35 import org.apache.wss4j.stax.securityEvent.IssuedTokenSecurityEvent;
36 import org.apache.wss4j.stax.securityEvent.WSSecurityEventConstants;
37 import org.opensaml.saml.common.SAMLVersion;
38 import org.w3c.dom.Element;
39 import org.w3c.dom.Node;
40
41 import java.net.URI;
42 import java.security.Key;
43 import java.security.PublicKey;
44 import java.security.cert.X509Certificate;
45 import java.util.List;
46 import java.util.Map;
47
48
49
50
51
52 public class IssuedTokenAssertionState extends TokenAssertionState {
53
54 private static final String DEFAULT_CLAIMS_NAMESPACE =
55 "http://schemas.xmlsoap.org/ws/2005/05/identity";
56
57 public IssuedTokenAssertionState(AbstractSecurityAssertion assertion, boolean asserted,
58 PolicyAsserter policyAsserter, boolean initiator) {
59 super(assertion, asserted, policyAsserter, initiator);
60 }
61
62 @Override
63 public SecurityEventConstants.Event[] getSecurityEventType() {
64 return new SecurityEventConstants.Event[]{
65 WSSecurityEventConstants.KERBEROS_TOKEN,
66 WSSecurityEventConstants.REL_TOKEN,
67 WSSecurityEventConstants.SAML_TOKEN,
68 WSSecurityEventConstants.SECURITY_CONTEXT_TOKEN,
69 };
70 }
71
72 @Override
73 public boolean assertToken(TokenSecurityEvent<? extends SecurityToken> tokenSecurityEvent,
74 AbstractToken abstractToken) throws WSSPolicyException {
75 if (!(tokenSecurityEvent instanceof IssuedTokenSecurityEvent)) {
76 throw new WSSPolicyException("Expected a IssuedTokenSecurityEvent but got " + tokenSecurityEvent.getClass().getName());
77 }
78
79 IssuedToken issuedToken = (IssuedToken) abstractToken;
80 IssuedTokenSecurityEvent<? extends SecurityToken> issuedTokenSecurityEvent
81 = (IssuedTokenSecurityEvent<? extends SecurityToken>) tokenSecurityEvent;
82 try {
83 if (issuedToken.getIssuerName() != null
84 && !issuedToken.getIssuerName().equals(issuedTokenSecurityEvent.getIssuerName())) {
85 setErrorMessage("IssuerName in Policy (" + issuedToken.getIssuerName()
86 + ") didn't match with the one in the IssuedToken (" + issuedTokenSecurityEvent.getIssuerName() + ")");
87 getPolicyAsserter().unassertPolicy(getAssertion(), getErrorMessage());
88 return false;
89 }
90 if (issuedToken.getRequestSecurityTokenTemplate() != null) {
91 if (issuedTokenSecurityEvent instanceof SamlTokenSecurityEvent) {
92 SamlTokenSecurityEvent samlTokenSecurityEvent = (SamlTokenSecurityEvent) issuedTokenSecurityEvent;
93 String errorMsg = checkIssuedTokenTemplate(issuedToken.getRequestSecurityTokenTemplate(), samlTokenSecurityEvent);
94 if (errorMsg != null) {
95 setErrorMessage(errorMsg);
96 getPolicyAsserter().unassertPolicy(getAssertion(), getErrorMessage());
97 return false;
98 }
99 } else if (issuedTokenSecurityEvent instanceof KerberosTokenSecurityEvent) {
100 KerberosTokenSecurityEvent kerberosTokenSecurityEvent = (KerberosTokenSecurityEvent) issuedTokenSecurityEvent;
101 String errorMsg = checkIssuedTokenTemplate(issuedToken.getRequestSecurityTokenTemplate(), kerberosTokenSecurityEvent);
102 if (errorMsg != null) {
103 setErrorMessage(errorMsg);
104 getPolicyAsserter().unassertPolicy(getAssertion(), getErrorMessage());
105 return false;
106 }
107 }
108 }
109
110 Element claims = issuedToken.getClaims();
111 if (claims != null && issuedTokenSecurityEvent instanceof SamlTokenSecurityEvent) {
112 String errorMsg =
113 validateClaims((Element) claims, (SamlTokenSecurityEvent)issuedTokenSecurityEvent);
114 if (errorMsg != null) {
115 setErrorMessage(errorMsg);
116 getPolicyAsserter().unassertPolicy(getAssertion(), getErrorMessage());
117 return false;
118 }
119 }
120 } catch (XMLSecurityException e) {
121 getPolicyAsserter().unassertPolicy(getAssertion(), getErrorMessage());
122 throw new WSSPolicyException(e.getMessage(), e);
123 }
124
125
126
127 getPolicyAsserter().assertPolicy(getAssertion());
128 return true;
129 }
130
131
132
133
134 protected String checkIssuedTokenTemplate(Element template, SamlTokenSecurityEvent samlTokenSecurityEvent) throws XMLSecurityException {
135 Node child = template.getFirstChild();
136 while (child != null) {
137 if (child.getNodeType() != Node.ELEMENT_NODE) {
138 child = child.getNextSibling();
139 continue;
140 }
141 if ("TokenType".equals(child.getLocalName())) {
142 String content = child.getTextContent();
143 final SAMLVersion samlVersion = samlTokenSecurityEvent.getSamlAssertionWrapper().getSamlVersion();
144 if (WSSConstants.NS_SAML11_TOKEN_PROFILE_TYPE.equals(content)
145 && samlVersion != SAMLVersion.VERSION_11) {
146 return "Policy enforces SAML V1.1 token but got " + samlVersion.toString();
147 } else if (WSSConstants.NS_SAML20_TOKEN_PROFILE_TYPE.equals(content)
148 && samlVersion != SAMLVersion.VERSION_20) {
149 return "Policy enforces SAML V2.0 token but got " + samlVersion.toString();
150 }
151 } else if ("KeyType".equals(child.getLocalName())) {
152 String content = child.getTextContent();
153 if (content.endsWith("SymmetricKey")) {
154 Map<String, Key> subjectKeys = samlTokenSecurityEvent.getSecurityToken().getSecretKey();
155 if (subjectKeys.isEmpty()) {
156 return "Policy enforces SAML token with a symmetric key";
157 }
158 } else if (content.endsWith("PublicKey")) {
159 PublicKey publicKey = samlTokenSecurityEvent.getSecurityToken().getPublicKey();
160 X509Certificate[] x509Certificate = samlTokenSecurityEvent.getSecurityToken().getX509Certificates();
161 if (publicKey == null && x509Certificate == null) {
162 return "Policy enforces SAML token with an asymmetric key";
163 }
164 }
165 } else if ("Claims".equals(child.getLocalName())) {
166 String errorMsg = validateClaims((Element) child, samlTokenSecurityEvent);
167 if (errorMsg != null) {
168 return errorMsg;
169 }
170 }
171 child = child.getNextSibling();
172 }
173 return null;
174 }
175
176
177
178
179 private String checkIssuedTokenTemplate(Element template, KerberosTokenSecurityEvent kerberosTokenSecurityEvent) {
180 Node child = template.getFirstChild();
181 while (child != null) {
182 if (child.getNodeType() != Node.ELEMENT_NODE) {
183 child = child.getNextSibling();
184 continue;
185 }
186 if ("TokenType".equals(child.getLocalName())) {
187 String content = child.getTextContent();
188 String valueType = kerberosTokenSecurityEvent.getKerberosTokenValueType();
189 if (!content.equals(valueType)) {
190 return "Policy enforces Kerberos token of type " + content + " but got " + valueType;
191 }
192 }
193 child = child.getNextSibling();
194 }
195 return null;
196 }
197
198
199
200 protected String validateClaims(Element claimsPolicy, SamlTokenSecurityEvent samlTokenSecurityEvent) throws WSSecurityException {
201 String dialect = claimsPolicy.getAttributeNS(null, "Dialect");
202 if (!DEFAULT_CLAIMS_NAMESPACE.equals(dialect)) {
203 return null;
204 }
205
206 Node child = claimsPolicy.getFirstChild();
207 while (child != null) {
208 if (child.getNodeType() != Node.ELEMENT_NODE) {
209 child = child.getNextSibling();
210 continue;
211 }
212
213 if ("ClaimType".equals(child.getLocalName())) {
214 Element claimType = (Element) child;
215 String claimTypeUri = claimType.getAttributeNS(null, "Uri");
216 String claimTypeOptional = claimType.getAttributeNS(null, "Optional");
217
218 if (claimTypeOptional.length() == 0 || !Boolean.parseBoolean(claimTypeOptional)) {
219 String errorMsg = findClaimInAssertion(samlTokenSecurityEvent.getSamlAssertionWrapper(), URI.create(claimTypeUri));
220 if (errorMsg != null) {
221 return errorMsg;
222 }
223 }
224 }
225 child = child.getNextSibling();
226 }
227 return null;
228 }
229
230 protected String findClaimInAssertion(SamlAssertionWrapper samlAssertionWrapper, URI claimURI) {
231 if (samlAssertionWrapper.getSaml1() != null) {
232 return findClaimInAssertion(samlAssertionWrapper.getSaml1(), claimURI);
233 } else if (samlAssertionWrapper.getSaml2() != null) {
234 return findClaimInAssertion(samlAssertionWrapper.getSaml2(), claimURI);
235 }
236 return "Unsupported SAML version";
237 }
238
239 protected String findClaimInAssertion(org.opensaml.saml.saml2.core.Assertion assertion, URI claimURI) {
240 List<org.opensaml.saml.saml2.core.AttributeStatement> attributeStatements =
241 assertion.getAttributeStatements();
242 if (attributeStatements == null || attributeStatements.isEmpty()) {
243 return "Attribute " + claimURI + " not found in the SAMLAssertion";
244 }
245
246 for (org.opensaml.saml.saml2.core.AttributeStatement statement : attributeStatements) {
247 List<org.opensaml.saml.saml2.core.Attribute> attributes = statement.getAttributes();
248 for (org.opensaml.saml.saml2.core.Attribute attribute : attributes) {
249
250 if (attribute.getName().equals(claimURI.toString())
251 && attribute.getAttributeValues() != null && !attribute.getAttributeValues().isEmpty()) {
252 return null;
253 }
254 }
255 }
256 return "Attribute " + claimURI + " not found in the SAMLAssertion";
257 }
258
259 protected String findClaimInAssertion(org.opensaml.saml.saml1.core.Assertion assertion, URI claimURI) {
260 List<org.opensaml.saml.saml1.core.AttributeStatement> attributeStatements =
261 assertion.getAttributeStatements();
262 if (attributeStatements == null || attributeStatements.isEmpty()) {
263 return "Attribute " + claimURI + " not found in the SAMLAssertion";
264 }
265
266 for (org.opensaml.saml.saml1.core.AttributeStatement statement : attributeStatements) {
267
268 List<org.opensaml.saml.saml1.core.Attribute> attributes = statement.getAttributes();
269 for (org.opensaml.saml.saml1.core.Attribute attribute : attributes) {
270
271 URI attributeNamespace = URI.create(attribute.getAttributeNamespace());
272 String desiredRole = attributeNamespace.relativize(claimURI).toString();
273 if (attribute.getAttributeName().equals(desiredRole)
274 && attribute.getAttributeValues() != null && !attribute.getAttributeValues().isEmpty()) {
275 return null;
276 }
277 }
278 }
279 return "Attribute " + claimURI + " not found in the SAMLAssertion";
280 }
281 }