1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.wss4j.common.saml.builder;
21
22 import java.time.Duration;
23 import java.time.Instant;
24 import java.util.ArrayList;
25 import java.util.List;
26
27 import org.apache.wss4j.common.ext.WSSecurityException;
28 import org.apache.wss4j.common.saml.OpenSAMLUtil;
29 import org.apache.wss4j.common.saml.bean.ActionBean;
30 import org.apache.wss4j.common.saml.bean.AdviceBean;
31 import org.apache.wss4j.common.saml.bean.AttributeBean;
32 import org.apache.wss4j.common.saml.bean.AttributeStatementBean;
33 import org.apache.wss4j.common.saml.bean.AudienceRestrictionBean;
34 import org.apache.wss4j.common.saml.bean.AuthDecisionStatementBean;
35 import org.apache.wss4j.common.saml.bean.AuthenticationStatementBean;
36 import org.apache.wss4j.common.saml.bean.ConditionsBean;
37 import org.apache.wss4j.common.saml.bean.KeyInfoBean;
38 import org.apache.wss4j.common.saml.bean.SubjectBean;
39 import org.apache.wss4j.common.saml.bean.SubjectLocalityBean;
40 import org.apache.xml.security.stax.impl.util.IDGenerator;
41 import org.opensaml.core.xml.XMLObject;
42 import org.opensaml.core.xml.XMLObjectBuilderFactory;
43 import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
44 import org.opensaml.core.xml.schema.XSString;
45 import org.opensaml.core.xml.schema.impl.XSStringBuilder;
46 import org.opensaml.saml.common.SAMLObjectBuilder;
47 import org.opensaml.saml.common.SAMLVersion;
48 import org.opensaml.saml.saml1.core.Action;
49 import org.opensaml.saml.saml1.core.Advice;
50 import org.opensaml.saml.saml1.core.Assertion;
51 import org.opensaml.saml.saml1.core.AssertionIDReference;
52 import org.opensaml.saml.saml1.core.Attribute;
53 import org.opensaml.saml.saml1.core.AttributeStatement;
54 import org.opensaml.saml.saml1.core.AttributeValue;
55 import org.opensaml.saml.saml1.core.Audience;
56 import org.opensaml.saml.saml1.core.AudienceRestrictionCondition;
57 import org.opensaml.saml.saml1.core.AuthenticationStatement;
58 import org.opensaml.saml.saml1.core.AuthorizationDecisionStatement;
59 import org.opensaml.saml.saml1.core.Conditions;
60 import org.opensaml.saml.saml1.core.ConfirmationMethod;
61 import org.opensaml.saml.saml1.core.DecisionTypeEnumeration;
62 import org.opensaml.saml.saml1.core.Evidence;
63 import org.opensaml.saml.saml1.core.NameIdentifier;
64 import org.opensaml.saml.saml1.core.Subject;
65 import org.opensaml.saml.saml1.core.SubjectConfirmation;
66 import org.opensaml.saml.saml1.core.SubjectLocality;
67 import org.opensaml.security.credential.BasicCredential;
68 import org.opensaml.security.x509.BasicX509Credential;
69 import org.opensaml.xmlsec.keyinfo.impl.BasicKeyInfoGeneratorFactory;
70 import org.opensaml.xmlsec.keyinfo.impl.X509KeyInfoGeneratorFactory;
71 import org.opensaml.xmlsec.signature.KeyInfo;
72 import org.w3c.dom.Element;
73
74
75
76
77
78 public final class SAML1ComponentBuilder {
79
80 private static volatile SAMLObjectBuilder<Assertion> assertionV1Builder;
81
82 private static volatile SAMLObjectBuilder<Conditions> conditionsV1Builder;
83
84 private static volatile SAMLObjectBuilder<Advice> adviceV1Builder;
85
86 private static volatile SAMLObjectBuilder<AssertionIDReference> assertionIDReferenceBuilder;
87
88 private static volatile SAMLObjectBuilder<AudienceRestrictionCondition> audienceRestrictionV1Builder;
89
90 private static volatile SAMLObjectBuilder<Audience> audienceV1Builder;
91
92 private static volatile SAMLObjectBuilder<AuthenticationStatement> authenticationStatementV1Builder;
93
94 private static volatile SAMLObjectBuilder<Subject> subjectV1Builder;
95
96 private static volatile SAMLObjectBuilder<NameIdentifier> nameIdentifierV1Builder;
97
98 private static volatile SAMLObjectBuilder<SubjectConfirmation>
99 subjectConfirmationV1Builder;
100
101 private static volatile SAMLObjectBuilder<ConfirmationMethod> confirmationMethodV1Builder;
102
103 private static volatile SAMLObjectBuilder<AttributeStatement>
104 attributeStatementV1Builder;
105
106 private static volatile SAMLObjectBuilder<Attribute> attributeV1Builder;
107
108 private static volatile XSStringBuilder stringBuilder;
109
110 private static volatile SAMLObjectBuilder<AuthorizationDecisionStatement>
111 authorizationDecisionStatementV1Builder;
112
113 private static volatile SAMLObjectBuilder<Action> actionElementV1Builder;
114
115 private static volatile XMLObjectBuilderFactory builderFactory =
116 XMLObjectProviderRegistrySupport.getBuilderFactory();
117
118 private static volatile SAMLObjectBuilder<SubjectLocality> subjectLocalityBuilder;
119
120 private SAML1ComponentBuilder() {
121
122 }
123
124
125
126
127
128
129
130 @SuppressWarnings("unchecked")
131 public static Assertion createSamlv1Assertion(String issuer) {
132 if (assertionV1Builder == null) {
133 assertionV1Builder = (SAMLObjectBuilder<Assertion>)
134 builderFactory.getBuilder(Assertion.DEFAULT_ELEMENT_NAME);
135 if (assertionV1Builder == null) {
136 throw new IllegalStateException(
137 "OpenSaml engine not initialized. Please make sure to initialize the OpenSaml "
138 + "engine prior using it"
139 );
140 }
141 }
142 Assertion assertion =
143 assertionV1Builder.buildObject(
144 Assertion.DEFAULT_ELEMENT_NAME,
145 Assertion.TYPE_NAME
146 );
147 assertion.setVersion(SAMLVersion.VERSION_11);
148 assertion.setIssuer(issuer);
149 assertion.setIssueInstant(Instant.now());
150 assertion.setID(IDGenerator.generateID("_"));
151 return assertion;
152 }
153
154
155
156
157
158
159
160
161 @SuppressWarnings("unchecked")
162 public static Subject createSaml1v1Subject(SubjectBean subjectBean)
163 throws org.opensaml.security.SecurityException, WSSecurityException {
164 if (subjectV1Builder == null) {
165 subjectV1Builder = (SAMLObjectBuilder<Subject>)
166 builderFactory.getBuilder(Subject.DEFAULT_ELEMENT_NAME);
167 }
168 if (nameIdentifierV1Builder == null) {
169 nameIdentifierV1Builder = (SAMLObjectBuilder<NameIdentifier>)
170 builderFactory.getBuilder(NameIdentifier.DEFAULT_ELEMENT_NAME);
171 }
172 if (subjectConfirmationV1Builder == null) {
173 subjectConfirmationV1Builder = (SAMLObjectBuilder<SubjectConfirmation>)
174 builderFactory.getBuilder(SubjectConfirmation.DEFAULT_ELEMENT_NAME);
175
176 }
177 if (confirmationMethodV1Builder == null) {
178 confirmationMethodV1Builder = (SAMLObjectBuilder<ConfirmationMethod>)
179 builderFactory.getBuilder(ConfirmationMethod.DEFAULT_ELEMENT_NAME);
180 }
181
182 Subject subject = subjectV1Builder.buildObject();
183 NameIdentifier nameIdentifier = nameIdentifierV1Builder.buildObject();
184 SubjectConfirmation subjectConfirmation = subjectConfirmationV1Builder.buildObject();
185 ConfirmationMethod confirmationMethod = confirmationMethodV1Builder.buildObject();
186
187 nameIdentifier.setNameQualifier(subjectBean.getSubjectNameQualifier());
188 nameIdentifier.setValue(subjectBean.getSubjectName());
189 nameIdentifier.setFormat(subjectBean.getSubjectNameIDFormat());
190 String confirmationMethodStr = subjectBean.getSubjectConfirmationMethod();
191
192 if (confirmationMethodStr == null) {
193 confirmationMethodStr = SAML1Constants.CONF_SENDER_VOUCHES;
194 }
195
196 confirmationMethod.setURI(confirmationMethodStr);
197 subjectConfirmation.getConfirmationMethods().add(confirmationMethod);
198 if (subjectBean.getKeyInfo() != null) {
199 KeyInfo keyInfo = createKeyInfo(subjectBean.getKeyInfo());
200 subjectConfirmation.setKeyInfo(keyInfo);
201 }
202 subject.setNameIdentifier(nameIdentifier);
203 subject.setSubjectConfirmation(subjectConfirmation);
204
205 return subject;
206 }
207
208
209
210
211
212
213
214 public static KeyInfo createKeyInfo(KeyInfoBean keyInfo)
215 throws org.opensaml.security.SecurityException, WSSecurityException {
216 if (keyInfo.getElement() != null) {
217 return (KeyInfo)OpenSAMLUtil.fromDom(keyInfo.getElement());
218 } else {
219
220 if (keyInfo.getCertificate() != null) {
221 BasicCredential keyInfoCredential = new BasicX509Credential(keyInfo.getCertificate());
222
223
224 X509KeyInfoGeneratorFactory kiFactory = new X509KeyInfoGeneratorFactory();
225 KeyInfoBean.CERT_IDENTIFIER certIdentifier = keyInfo.getCertIdentifer();
226 switch (certIdentifier) {
227 case X509_CERT:
228 kiFactory.setEmitEntityCertificate(true);
229 break;
230 case KEY_VALUE:
231 kiFactory.setEmitPublicKeyValue(true);
232 break;
233 case X509_ISSUER_SERIAL:
234 kiFactory.setEmitX509IssuerSerial(true);
235 break;
236 }
237 return kiFactory.newInstance().generate(keyInfoCredential);
238
239 } else if (keyInfo.getPublicKey() != null) {
240 BasicCredential keyInfoCredential = new BasicCredential(keyInfo.getPublicKey());
241 BasicKeyInfoGeneratorFactory kiFactory = new BasicKeyInfoGeneratorFactory();
242 kiFactory.setEmitPublicKeyValue(true);
243 return kiFactory.newInstance().generate(keyInfoCredential);
244 }
245 }
246
247 return null;
248 }
249
250
251
252
253
254
255
256 @SuppressWarnings("unchecked")
257 public static Conditions createSamlv1Conditions(ConditionsBean conditionsBean) {
258 if (conditionsV1Builder == null) {
259 conditionsV1Builder = (SAMLObjectBuilder<Conditions>)
260 builderFactory.getBuilder(Conditions.DEFAULT_ELEMENT_NAME);
261
262 }
263 Conditions conditions = conditionsV1Builder.buildObject();
264
265 if (conditionsBean == null) {
266 Instant newNotBefore = Instant.now();
267 conditions.setNotBefore(newNotBefore);
268 conditions.setNotOnOrAfter(newNotBefore.plus(Duration.ofMinutes(5)));
269 return conditions;
270 }
271
272 long tokenPeriodSeconds = conditionsBean.getTokenPeriodSeconds();
273 Instant notBefore = conditionsBean.getNotBefore();
274 Instant notAfter = conditionsBean.getNotAfter();
275
276 if (notBefore != null && notAfter != null) {
277 if (notBefore.isAfter(notAfter)) {
278 throw new IllegalStateException(
279 "The value of notBefore may not be after the value of notAfter"
280 );
281 }
282 conditions.setNotBefore(notBefore);
283 conditions.setNotOnOrAfter(notAfter);
284 } else {
285 Instant newNotBefore = Instant.now();
286 conditions.setNotBefore(newNotBefore);
287 if (tokenPeriodSeconds <= 0) {
288 tokenPeriodSeconds = 5L * 60L;
289 }
290 Instant notOnOrAfter = newNotBefore.plusSeconds(tokenPeriodSeconds);
291 conditions.setNotOnOrAfter(notOnOrAfter);
292 }
293
294 if (conditionsBean.getAudienceRestrictions() != null
295 && !conditionsBean.getAudienceRestrictions().isEmpty()) {
296 for (AudienceRestrictionBean audienceRestrictionBean
297 : conditionsBean.getAudienceRestrictions()) {
298 AudienceRestrictionCondition audienceRestriction =
299 createSamlv1AudienceRestriction(audienceRestrictionBean);
300 conditions.getAudienceRestrictionConditions().add(audienceRestriction);
301 }
302 }
303
304 return conditions;
305 }
306
307
308
309
310
311
312
313
314 @SuppressWarnings("unchecked")
315 public static Advice createAdvice(AdviceBean adviceBean) throws WSSecurityException {
316 if (adviceV1Builder == null) {
317 adviceV1Builder = (SAMLObjectBuilder<Advice>)
318 builderFactory.getBuilder(Advice.DEFAULT_ELEMENT_NAME);
319 }
320
321 Advice advice = adviceV1Builder.buildObject();
322
323 if (!adviceBean.getIdReferences().isEmpty()) {
324 if (assertionIDReferenceBuilder == null) {
325 assertionIDReferenceBuilder = (SAMLObjectBuilder<AssertionIDReference>)
326 builderFactory.getBuilder(AssertionIDReference.DEFAULT_ELEMENT_NAME);
327 }
328
329 for (String ref : adviceBean.getIdReferences()) {
330 AssertionIDReference assertionIdReference =
331 assertionIDReferenceBuilder.buildObject();
332 assertionIdReference.setValue(ref);
333 advice.getAssertionIDReferences().add(assertionIdReference);
334 }
335 }
336
337 if (!adviceBean.getAssertions().isEmpty()) {
338 for (Element assertionElement : adviceBean.getAssertions()) {
339 XMLObject xmlObject = OpenSAMLUtil.fromDom(assertionElement);
340 if (xmlObject instanceof Assertion) {
341 Assertion assertion = (Assertion)xmlObject;
342 advice.getAssertions().add(assertion);
343 }
344 }
345 }
346
347 return advice;
348 }
349
350
351
352
353
354
355
356 @SuppressWarnings("unchecked")
357 public static AudienceRestrictionCondition
358 createSamlv1AudienceRestriction(AudienceRestrictionBean audienceRestrictionBean) {
359 if (audienceRestrictionV1Builder == null) {
360 audienceRestrictionV1Builder = (SAMLObjectBuilder<AudienceRestrictionCondition>)
361 builderFactory.getBuilder(AudienceRestrictionCondition.DEFAULT_ELEMENT_NAME);
362 }
363 if (audienceV1Builder == null) {
364 audienceV1Builder = (SAMLObjectBuilder<Audience>)
365 builderFactory.getBuilder(Audience.DEFAULT_ELEMENT_NAME);
366 }
367
368 AudienceRestrictionCondition audienceRestriction =
369 audienceRestrictionV1Builder.buildObject();
370
371 for (String audienceURI : audienceRestrictionBean.getAudienceURIs()) {
372 Audience audience = audienceV1Builder.buildObject();
373 audience.setURI(audienceURI);
374 audienceRestriction.getAudiences().add(audience);
375 }
376 return audienceRestriction;
377 }
378
379
380
381
382
383
384
385 @SuppressWarnings("unchecked")
386 public static List<AuthenticationStatement> createSamlv1AuthenticationStatement(
387 List<AuthenticationStatementBean> authBeans
388 ) throws org.opensaml.security.SecurityException, WSSecurityException {
389 List<AuthenticationStatement> authenticationStatements = new ArrayList<>();
390
391 if (authenticationStatementV1Builder == null) {
392 authenticationStatementV1Builder = (SAMLObjectBuilder<AuthenticationStatement>)
393 builderFactory.getBuilder(AuthenticationStatement.DEFAULT_ELEMENT_NAME);
394 }
395 if (subjectLocalityBuilder == null) {
396 subjectLocalityBuilder = (SAMLObjectBuilder<SubjectLocality>)
397 builderFactory.getBuilder(SubjectLocality.DEFAULT_ELEMENT_NAME);
398 }
399
400 if (authBeans != null && !authBeans.isEmpty()) {
401 for (AuthenticationStatementBean statementBean : authBeans) {
402 AuthenticationStatement authenticationStatement =
403 authenticationStatementV1Builder.buildObject(
404 AuthenticationStatement.DEFAULT_ELEMENT_NAME,
405 AuthenticationStatement.TYPE_NAME
406 );
407 Subject authSubject =
408 SAML1ComponentBuilder.createSaml1v1Subject(statementBean.getSubject());
409 authenticationStatement.setSubject(authSubject);
410
411 if (statementBean.getAuthenticationInstant() != null) {
412 authenticationStatement.setAuthenticationInstant(
413 statementBean.getAuthenticationInstant()
414 );
415 } else {
416 authenticationStatement.setAuthenticationInstant(Instant.now());
417 }
418
419 authenticationStatement.setAuthenticationMethod(
420 transformAuthenticationMethod(statementBean.getAuthenticationMethod())
421 );
422
423 SubjectLocalityBean subjectLocalityBean = statementBean.getSubjectLocality();
424 if (subjectLocalityBean != null) {
425 SubjectLocality subjectLocality = subjectLocalityBuilder.buildObject();
426 subjectLocality.setDNSAddress(subjectLocalityBean.getDnsAddress());
427 subjectLocality.setIPAddress(subjectLocalityBean.getIpAddress());
428
429 authenticationStatement.setSubjectLocality(subjectLocality);
430 }
431
432 authenticationStatements.add(authenticationStatement);
433 }
434 }
435
436 return authenticationStatements;
437 }
438
439
440
441
442
443
444
445
446 private static String transformAuthenticationMethod(String sourceMethod) {
447 String transformedMethod = "";
448
449 if ("Password".equals(sourceMethod)) {
450 transformedMethod = SAML1Constants.AUTH_METHOD_PASSWORD;
451 } else if (sourceMethod != null && sourceMethod.length() != 0) {
452 return sourceMethod;
453 }
454
455 return transformedMethod;
456 }
457
458
459
460
461
462
463
464 @SuppressWarnings("unchecked")
465 public static List<AttributeStatement> createSamlv1AttributeStatement(
466 List<AttributeStatementBean> attributeData
467 ) throws org.opensaml.security.SecurityException, WSSecurityException {
468 if (attributeStatementV1Builder == null) {
469 attributeStatementV1Builder = (SAMLObjectBuilder<AttributeStatement>)
470 builderFactory.getBuilder(AttributeStatement.DEFAULT_ELEMENT_NAME);
471 }
472
473 List<AttributeStatement> attributeStatements = new ArrayList<>();
474
475 if (attributeData != null && !attributeData.isEmpty()) {
476 for (AttributeStatementBean statementBean : attributeData) {
477
478 AttributeStatement attributeStatement = attributeStatementV1Builder.buildObject();
479 Subject attributeSubject =
480 SAML1ComponentBuilder.createSaml1v1Subject(statementBean.getSubject());
481 attributeStatement.setSubject(attributeSubject);
482
483 for (AttributeBean values : statementBean.getSamlAttributes()) {
484 List<Object> attributeValues = values.getAttributeValues();
485
486 Attribute samlAttribute =
487 createSamlv1Attribute(
488 values.getSimpleName(),
489 values.getQualifiedName(),
490 attributeValues
491 );
492 attributeStatement.getAttributes().add(samlAttribute);
493 }
494
495 attributeStatements.add(attributeStatement);
496 }
497 }
498
499 return attributeStatements;
500 }
501
502
503
504
505
506
507
508
509
510 @SuppressWarnings("unchecked")
511 public static Attribute createSamlv1Attribute(
512 String attributeName,
513 String attributeUrn,
514 List<Object> values
515 ) {
516 if (attributeV1Builder == null) {
517 attributeV1Builder = (SAMLObjectBuilder<Attribute>)
518 builderFactory.getBuilder(Attribute.DEFAULT_ELEMENT_NAME);
519 }
520 if (stringBuilder == null) {
521 stringBuilder = (XSStringBuilder)builderFactory.getBuilder(XSString.TYPE_NAME);
522 }
523
524 Attribute attribute = attributeV1Builder.buildObject();
525 attribute.setAttributeName(attributeName);
526 attribute.setAttributeNamespace(attributeUrn);
527
528 for (Object value : values) {
529 if (value instanceof String) {
530 XSString attribute1 =
531 stringBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSString.TYPE_NAME);
532 attribute1.setValue((String)value);
533 attribute.getAttributeValues().add(attribute1);
534 } else if (value instanceof XMLObject) {
535 attribute.getAttributeValues().add((XMLObject)value);
536 }
537 }
538
539 return attribute;
540 }
541
542
543
544
545
546
547
548 @SuppressWarnings("unchecked")
549 public static List<AuthorizationDecisionStatement> createSamlv1AuthorizationDecisionStatement(
550 List<AuthDecisionStatementBean> decisionData)
551 throws org.opensaml.security.SecurityException, WSSecurityException {
552 List<AuthorizationDecisionStatement> authDecisionStatements = new ArrayList<>();
553 if (authorizationDecisionStatementV1Builder == null) {
554 authorizationDecisionStatementV1Builder =
555 (SAMLObjectBuilder<AuthorizationDecisionStatement>)
556 builderFactory.getBuilder(AuthorizationDecisionStatement.DEFAULT_ELEMENT_NAME);
557
558 }
559
560 if (decisionData != null && !decisionData.isEmpty()) {
561 for (AuthDecisionStatementBean decisionStatementBean : decisionData) {
562 AuthorizationDecisionStatement authDecision =
563 authorizationDecisionStatementV1Builder.buildObject();
564 Subject authDecisionSubject =
565 SAML1ComponentBuilder.createSaml1v1Subject(decisionStatementBean.getSubject());
566 authDecision.setSubject(authDecisionSubject);
567
568 authDecision.setResource(decisionStatementBean.getResource());
569 authDecision.setDecision(transformDecisionType(decisionStatementBean.getDecision()));
570
571 for (ActionBean actionBean : decisionStatementBean.getActions()) {
572 Action actionElement = createSamlv1Action(actionBean);
573 authDecision.getActions().add(actionElement);
574 }
575
576 if (decisionStatementBean.getEvidence() instanceof Evidence) {
577 authDecision.setEvidence((Evidence)decisionStatementBean.getEvidence());
578 }
579
580 authDecisionStatements.add(authDecision);
581 }
582 }
583
584 return authDecisionStatements;
585 }
586
587
588
589
590
591
592
593 @SuppressWarnings("unchecked")
594 public static Action createSamlv1Action(ActionBean actionBean) {
595 if (actionElementV1Builder == null) {
596 actionElementV1Builder = (SAMLObjectBuilder<Action>)
597 builderFactory.getBuilder(Action.DEFAULT_ELEMENT_NAME);
598 }
599
600 Action actionElement = actionElementV1Builder.buildObject();
601 actionElement.setNamespace(actionBean.getActionNamespace());
602 actionElement.setValue(actionBean.getContents());
603
604 return actionElement;
605 }
606
607
608
609
610
611
612
613 private static DecisionTypeEnumeration transformDecisionType(
614 AuthDecisionStatementBean.Decision decision
615 ) {
616 DecisionTypeEnumeration decisionTypeEnum = DecisionTypeEnumeration.DENY;
617 if (decision.equals(AuthDecisionStatementBean.Decision.PERMIT)) {
618 decisionTypeEnum = DecisionTypeEnumeration.PERMIT;
619 } else if (decision.equals(AuthDecisionStatementBean.Decision.INDETERMINATE)) {
620 decisionTypeEnum = DecisionTypeEnumeration.INDETERMINATE;
621 }
622
623 return decisionTypeEnum;
624 }
625
626 }