View Javadoc
1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements. See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License. You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied. See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
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.Collections;
26  import java.util.List;
27  
28  import org.apache.wss4j.common.ext.WSSecurityException;
29  import org.apache.wss4j.common.saml.OpenSAMLUtil;
30  import org.apache.wss4j.common.saml.bean.ActionBean;
31  import org.apache.wss4j.common.saml.bean.AdviceBean;
32  import org.apache.wss4j.common.saml.bean.AttributeBean;
33  import org.apache.wss4j.common.saml.bean.AttributeStatementBean;
34  import org.apache.wss4j.common.saml.bean.AudienceRestrictionBean;
35  import org.apache.wss4j.common.saml.bean.AuthDecisionStatementBean;
36  import org.apache.wss4j.common.saml.bean.AuthenticationStatementBean;
37  import org.apache.wss4j.common.saml.bean.ConditionsBean;
38  import org.apache.wss4j.common.saml.bean.DelegateBean;
39  import org.apache.wss4j.common.saml.bean.KeyInfoBean;
40  import org.apache.wss4j.common.saml.bean.NameIDBean;
41  import org.apache.wss4j.common.saml.bean.ProxyRestrictionBean;
42  import org.apache.wss4j.common.saml.bean.SubjectBean;
43  import org.apache.wss4j.common.saml.bean.SubjectConfirmationDataBean;
44  import org.apache.wss4j.common.saml.bean.SubjectLocalityBean;
45  import org.apache.xml.security.stax.impl.util.IDGenerator;
46  import org.opensaml.core.xml.XMLObject;
47  import org.opensaml.core.xml.XMLObjectBuilderFactory;
48  import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
49  import org.opensaml.core.xml.schema.XSString;
50  import org.opensaml.core.xml.schema.impl.XSStringBuilder;
51  import org.opensaml.saml.common.SAMLObjectBuilder;
52  import org.opensaml.saml.common.SAMLVersion;
53  import org.opensaml.saml.ext.saml2delrestrict.Delegate;
54  import org.opensaml.saml.ext.saml2delrestrict.DelegationRestrictionType;
55  import org.opensaml.saml.saml2.core.Action;
56  import org.opensaml.saml.saml2.core.Advice;
57  import org.opensaml.saml.saml2.core.Assertion;
58  import org.opensaml.saml.saml2.core.AssertionIDRef;
59  import org.opensaml.saml.saml2.core.AssertionURIRef;
60  import org.opensaml.saml.saml2.core.Attribute;
61  import org.opensaml.saml.saml2.core.AttributeStatement;
62  import org.opensaml.saml.saml2.core.AttributeValue;
63  import org.opensaml.saml.saml2.core.Audience;
64  import org.opensaml.saml.saml2.core.AudienceRestriction;
65  import org.opensaml.saml.saml2.core.AuthnContext;
66  import org.opensaml.saml.saml2.core.AuthnContextClassRef;
67  import org.opensaml.saml.saml2.core.AuthnStatement;
68  import org.opensaml.saml.saml2.core.AuthzDecisionStatement;
69  import org.opensaml.saml.saml2.core.Conditions;
70  import org.opensaml.saml.saml2.core.DecisionTypeEnumeration;
71  import org.opensaml.saml.saml2.core.Evidence;
72  import org.opensaml.saml.saml2.core.Issuer;
73  import org.opensaml.saml.saml2.core.KeyInfoConfirmationDataType;
74  import org.opensaml.saml.saml2.core.NameID;
75  import org.opensaml.saml.saml2.core.OneTimeUse;
76  import org.opensaml.saml.saml2.core.ProxyRestriction;
77  import org.opensaml.saml.saml2.core.Subject;
78  import org.opensaml.saml.saml2.core.SubjectConfirmation;
79  import org.opensaml.saml.saml2.core.SubjectConfirmationData;
80  import org.opensaml.saml.saml2.core.SubjectLocality;
81  import org.opensaml.xmlsec.signature.KeyInfo;
82  import org.w3c.dom.Element;
83  
84  /**
85   * Class SAML2ComponentBuilder provides builder methods that can be used
86   * to construct SAML v2.0 statements using the OpenSaml library.
87   */
88  public final class SAML2ComponentBuilder {
89  
90      private static final transient org.slf4j.Logger LOG =
91              org.slf4j.LoggerFactory.getLogger(SAML2ComponentBuilder.class);
92  
93      private static volatile SAMLObjectBuilder<Assertion> assertionBuilder;
94  
95      private static volatile SAMLObjectBuilder<Issuer> issuerBuilder;
96  
97      private static volatile SAMLObjectBuilder<Subject> subjectBuilder;
98  
99      private static volatile SAMLObjectBuilder<NameID> nameIdBuilder;
100 
101     private static volatile SAMLObjectBuilder<SubjectConfirmation> subjectConfirmationBuilder;
102 
103     private static volatile SAMLObjectBuilder<OneTimeUse> oneTimeUseBuilder;
104 
105     private static volatile SAMLObjectBuilder<ProxyRestriction> proxyRestrictionBuilder;
106 
107     private static volatile SAMLObjectBuilder<Conditions> conditionsBuilder;
108 
109     private static volatile SAMLObjectBuilder<Advice> adviceBuilder;
110 
111     private static volatile SAMLObjectBuilder<AssertionIDRef> assertionIDRefBuilder;
112 
113     private static volatile SAMLObjectBuilder<AssertionURIRef> assertionURIRefBuilder;
114 
115     private static volatile SAMLObjectBuilder<SubjectConfirmationData> subjectConfirmationDataBuilder;
116 
117     private static volatile SAMLObjectBuilder<KeyInfoConfirmationDataType> keyInfoConfirmationDataBuilder;
118 
119     private static volatile SAMLObjectBuilder<AuthnStatement> authnStatementBuilder;
120 
121     private static volatile SAMLObjectBuilder<AuthnContext> authnContextBuilder;
122 
123     private static volatile SAMLObjectBuilder<AuthnContextClassRef> authnContextClassRefBuilder;
124 
125     private static volatile SAMLObjectBuilder<AttributeStatement> attributeStatementBuilder;
126 
127     private static volatile SAMLObjectBuilder<Attribute> attributeBuilder;
128 
129     private static volatile XSStringBuilder stringBuilder;
130 
131     private static volatile SAMLObjectBuilder<AudienceRestriction> audienceRestrictionBuilder;
132 
133     private static volatile SAMLObjectBuilder<DelegationRestrictionType> delegationRestrictionBuilder;
134 
135     private static volatile SAMLObjectBuilder<Audience> audienceBuilder;
136 
137     private static volatile SAMLObjectBuilder<Delegate> delegateBuilder;
138 
139     private static volatile SAMLObjectBuilder<AuthzDecisionStatement> authorizationDecisionStatementBuilder;
140 
141     private static volatile SAMLObjectBuilder<Action> actionElementBuilder;
142 
143     private static volatile XMLObjectBuilderFactory builderFactory =
144         XMLObjectProviderRegistrySupport.getBuilderFactory();
145 
146     private static volatile SAMLObjectBuilder<SubjectLocality> subjectLocalityBuilder;
147 
148     private SAML2ComponentBuilder() {
149         // Complete
150     }
151 
152     /**
153      * Create a SAML 2 assertion
154      *
155      * @return a SAML 2 assertion
156      */
157     @SuppressWarnings("unchecked")
158     public static Assertion createAssertion() {
159         if (assertionBuilder == null) {
160             assertionBuilder = (SAMLObjectBuilder<Assertion>)
161                 builderFactory.getBuilder(Assertion.DEFAULT_ELEMENT_NAME);
162             if (assertionBuilder == null) {
163                 throw new IllegalStateException(
164                     "OpenSaml engine not initialized. Please make sure to initialize the OpenSaml engine "
165                     + "prior using it"
166                 );
167             }
168         }
169         Assertion assertion =
170             assertionBuilder.buildObject(Assertion.DEFAULT_ELEMENT_NAME, Assertion.TYPE_NAME);
171         assertion.setID(IDGenerator.generateID("_"));
172         assertion.setVersion(SAMLVersion.VERSION_20);
173         assertion.setIssueInstant(Instant.now());
174         return assertion;
175     }
176 
177     /**
178      * Create an Issuer object
179      *
180      * @param issuerValue of type String
181      * @param issuerFormat of type String
182      * @param issuerQualifier of type String
183      * @return an Issuer object
184      */
185     @SuppressWarnings("unchecked")
186     public static Issuer createIssuer(String issuerValue, String issuerFormat, String issuerQualifier) {
187         if (issuerBuilder == null) {
188             issuerBuilder = (SAMLObjectBuilder<Issuer>)
189                 builderFactory.getBuilder(Issuer.DEFAULT_ELEMENT_NAME);
190 
191         }
192         Issuer issuer = issuerBuilder.buildObject();
193         //
194         // The SAML authority that is making the claim(s) in the assertion. The issuer SHOULD
195         // be unambiguous to the intended relying parties.
196         issuer.setValue(issuerValue);
197         issuer.setFormat(issuerFormat);
198         issuer.setNameQualifier(issuerQualifier);
199         return issuer;
200     }
201 
202     /**
203      * Create a Conditions object
204      *
205      * @param conditionsBean A ConditionsBean object
206      * @return a Conditions object
207      */
208     @SuppressWarnings("unchecked")
209     public static Conditions createConditions(ConditionsBean conditionsBean) {
210         if (conditionsBuilder == null) {
211             conditionsBuilder = (SAMLObjectBuilder<Conditions>)
212                 builderFactory.getBuilder(Conditions.DEFAULT_ELEMENT_NAME);
213         }
214 
215         Conditions conditions = conditionsBuilder.buildObject();
216 
217         if (conditionsBean == null) {
218             Instant newNotBefore = Instant.now();
219             conditions.setNotBefore(newNotBefore);
220             conditions.setNotOnOrAfter(newNotBefore.plus(Duration.ofMinutes(5)));
221             return conditions;
222         }
223 
224         long tokenPeriodSeconds = conditionsBean.getTokenPeriodSeconds();
225         Instant notBefore = conditionsBean.getNotBefore();
226         Instant notAfter = conditionsBean.getNotAfter();
227 
228         if (notBefore != null && notAfter != null) {
229             if (notBefore.isAfter(notAfter)) {
230                 throw new IllegalStateException(
231                     "The value of notBefore may not be after the value of notAfter"
232                 );
233             }
234             conditions.setNotBefore(notBefore);
235             conditions.setNotOnOrAfter(notAfter);
236         } else {
237             Instant newNotBefore = Instant.now();
238             conditions.setNotBefore(newNotBefore);
239             if (tokenPeriodSeconds <= 0) {
240                 tokenPeriodSeconds = 5L * 60L;
241             }
242             Instant notOnOrAfter = newNotBefore.plusSeconds(tokenPeriodSeconds);
243             conditions.setNotOnOrAfter(notOnOrAfter);
244         }
245 
246         if (conditionsBean.getAudienceRestrictions() != null
247             && !conditionsBean.getAudienceRestrictions().isEmpty()) {
248             for (AudienceRestrictionBean audienceRestrictionBean
249                 : conditionsBean.getAudienceRestrictions()) {
250                 AudienceRestriction audienceRestriction =
251                         createAudienceRestriction(audienceRestrictionBean);
252                 conditions.getAudienceRestrictions().add(audienceRestriction);
253             }
254         }
255 
256         if (conditionsBean.isOneTimeUse()) {
257             conditions.getConditions().add(createOneTimeUse());
258         }
259 
260         if (conditionsBean.getProxyRestriction() != null) {
261             conditions.getConditions().add(createProxyRestriction(conditionsBean.getProxyRestriction()));
262         }
263 
264         if (conditionsBean.getDelegates() != null && !conditionsBean.getDelegates().isEmpty()) {
265             DelegationRestrictionType delegationRestriction =
266                 createDelegationRestriction(conditionsBean.getDelegates());
267             conditions.getConditions().add(delegationRestriction);
268         }
269 
270         return conditions;
271     }
272 
273     /**
274      * Create a Advice object
275      *
276      * @param adviceBean A AdviceBean object
277      * @return a Advice object
278      * @throws WSSecurityException
279      */
280     @SuppressWarnings("unchecked")
281     public static Advice createAdvice(AdviceBean adviceBean) throws WSSecurityException {
282         if (adviceBuilder == null) {
283             adviceBuilder = (SAMLObjectBuilder<Advice>)
284                 builderFactory.getBuilder(Advice.DEFAULT_ELEMENT_NAME);
285         }
286 
287         Advice advice = adviceBuilder.buildObject();
288 
289         if (!adviceBean.getIdReferences().isEmpty()) {
290             if (assertionIDRefBuilder == null) {
291                 assertionIDRefBuilder = (SAMLObjectBuilder<AssertionIDRef>)
292                     builderFactory.getBuilder(AssertionIDRef.DEFAULT_ELEMENT_NAME);
293             }
294 
295             for (String ref : adviceBean.getIdReferences()) {
296                 AssertionIDRef assertionIdRef = assertionIDRefBuilder.buildObject();
297                 assertionIdRef.setValue(ref);
298                 advice.getAssertionIDReferences().add(assertionIdRef);
299             }
300         }
301 
302         if (!adviceBean.getUriReferences().isEmpty()) {
303             if (assertionURIRefBuilder == null) {
304                 assertionURIRefBuilder = (SAMLObjectBuilder<AssertionURIRef>)
305                     builderFactory.getBuilder(AssertionURIRef.DEFAULT_ELEMENT_NAME);
306             }
307 
308             for (String ref : adviceBean.getUriReferences()) {
309                 AssertionURIRef assertionURIRef = assertionURIRefBuilder.buildObject();
310                 assertionURIRef.setURI(ref);
311                 advice.getAssertionURIReferences().add(assertionURIRef);
312             }
313         }
314 
315         if (!adviceBean.getAssertions().isEmpty()) {
316             for (Element assertionElement : adviceBean.getAssertions()) {
317                 XMLObject xmlObject = OpenSAMLUtil.fromDom(assertionElement);
318                 if (xmlObject instanceof Assertion) {
319                     Assertion assertion = (Assertion)xmlObject;
320                     advice.getAssertions().add(assertion);
321                 }
322             }
323         }
324 
325         return advice;
326     }
327 
328     /**
329      * Create an AudienceRestriction object
330      *
331      * @param audienceRestrictionBean of type AudienceRestrictionBean
332      * @return an AudienceRestriction object
333      */
334     @SuppressWarnings("unchecked")
335     public static AudienceRestriction createAudienceRestriction(
336         AudienceRestrictionBean audienceRestrictionBean
337     ) {
338         if (audienceRestrictionBuilder == null) {
339             audienceRestrictionBuilder = (SAMLObjectBuilder<AudienceRestriction>)
340                 builderFactory.getBuilder(AudienceRestriction.DEFAULT_ELEMENT_NAME);
341         }
342         if (audienceBuilder == null) {
343             audienceBuilder = (SAMLObjectBuilder<Audience>)
344                 builderFactory.getBuilder(Audience.DEFAULT_ELEMENT_NAME);
345         }
346 
347         AudienceRestriction audienceRestriction = audienceRestrictionBuilder.buildObject();
348 
349         for (String audienceURI : audienceRestrictionBean.getAudienceURIs()) {
350             Audience audience = audienceBuilder.buildObject();
351             audience.setURI(audienceURI);
352             audienceRestriction.getAudiences().add(audience);
353         }
354         return audienceRestriction;
355     }
356 
357     /**
358      * Create an DelegationRestrictionType object
359      *
360      * @param delegates of type List<DelegateBean>
361      * @return a DelegationRestrictionType object
362      */
363     @SuppressWarnings("unchecked")
364     public static DelegationRestrictionType createDelegationRestriction(
365         List<DelegateBean> delegates
366     ) {
367         if (delegationRestrictionBuilder == null) {
368             delegationRestrictionBuilder = (SAMLObjectBuilder<DelegationRestrictionType>)
369                 builderFactory.getBuilder(DelegationRestrictionType.TYPE_NAME);
370         }
371         DelegationRestrictionType delegationRestriction = delegationRestrictionBuilder.buildObject();
372 
373         if (delegateBuilder == null) {
374             delegateBuilder = (SAMLObjectBuilder<Delegate>)
375                 builderFactory.getBuilder(Delegate.DEFAULT_ELEMENT_NAME);
376         }
377 
378         for (DelegateBean delegateBean : delegates) {
379             Delegate delegate = delegateBuilder.buildObject();
380             delegate.setConfirmationMethod(delegateBean.getConfirmationMethod());
381             delegate.setDelegationInstant(delegateBean.getDelegationInstant());
382 
383             if (delegateBean.getNameIDBean() == null) {
384                 throw new IllegalStateException(
385                    "The value of NameIDBean in DelegateBean may not be null"
386                 );
387             }
388             NameID nameID = createNameID(delegateBean.getNameIDBean());
389             delegate.setNameID(nameID);
390             delegationRestriction.getDelegates().add(delegate);
391         }
392 
393         return delegationRestriction;
394     }
395 
396     /**
397      * Create a OneTimeUse object
398      *
399      * @return a OneTimeUse object
400      */
401     @SuppressWarnings("unchecked")
402     public static OneTimeUse createOneTimeUse() {
403         if (oneTimeUseBuilder == null) {
404             oneTimeUseBuilder = (SAMLObjectBuilder<OneTimeUse>)
405                 builderFactory.getBuilder(OneTimeUse.DEFAULT_ELEMENT_NAME);
406         }
407 
408         return oneTimeUseBuilder.buildObject();
409     }
410 
411     /**
412      * Create a ProxyRestriction object
413      *
414      * @return a ProxyRestriction object
415      */
416     @SuppressWarnings("unchecked")
417     public static ProxyRestriction createProxyRestriction(ProxyRestrictionBean proxyRestrictionBean) {
418         if (proxyRestrictionBuilder == null) {
419             proxyRestrictionBuilder = (SAMLObjectBuilder<ProxyRestriction>)
420                 builderFactory.getBuilder(ProxyRestriction.DEFAULT_ELEMENT_NAME);
421         }
422 
423         ProxyRestriction proxyRestriction = proxyRestrictionBuilder.buildObject();
424         if (proxyRestrictionBean.getCount() > 0) {
425             proxyRestriction.setProxyCount(proxyRestrictionBean.getCount());
426         }
427 
428         if (!proxyRestrictionBean.getAudienceURIs().isEmpty()) {
429             if (audienceBuilder == null) {
430                 audienceBuilder = (SAMLObjectBuilder<Audience>)
431                     builderFactory.getBuilder(Audience.DEFAULT_ELEMENT_NAME);
432             }
433             for (String audienceURI : proxyRestrictionBean.getAudienceURIs()) {
434                 Audience audience = audienceBuilder.buildObject();
435                 audience.setURI(audienceURI);
436                 proxyRestriction.getAudiences().add(audience);
437             }
438         }
439 
440         return proxyRestriction;
441     }
442 
443     /**
444      * Create SAML 2 Authentication Statement(s).
445      *
446      * @param authBeans A list of AuthenticationStatementBean instances
447      * @return SAML 2 Authentication Statement(s).
448      */
449     @SuppressWarnings("unchecked")
450     public static List<AuthnStatement> createAuthnStatement(
451         List<AuthenticationStatementBean> authBeans
452     ) {
453         List<AuthnStatement> authnStatements = new ArrayList<>();
454 
455         if (authnStatementBuilder == null) {
456             authnStatementBuilder = (SAMLObjectBuilder<AuthnStatement>)
457                 builderFactory.getBuilder(AuthnStatement.DEFAULT_ELEMENT_NAME);
458         }
459         if (authnContextBuilder == null) {
460             authnContextBuilder = (SAMLObjectBuilder<AuthnContext>)
461                 builderFactory.getBuilder(AuthnContext.DEFAULT_ELEMENT_NAME);
462         }
463         if (authnContextClassRefBuilder == null) {
464             authnContextClassRefBuilder = (SAMLObjectBuilder<AuthnContextClassRef>)
465                 builderFactory.getBuilder(AuthnContextClassRef.DEFAULT_ELEMENT_NAME);
466         }
467         if (subjectLocalityBuilder == null) {
468             subjectLocalityBuilder = (SAMLObjectBuilder<SubjectLocality>)
469             builderFactory.getBuilder(SubjectLocality.DEFAULT_ELEMENT_NAME);
470         }
471 
472         if (authBeans != null && !authBeans.isEmpty()) {
473             for (AuthenticationStatementBean statementBean : authBeans) {
474                 AuthnStatement authnStatement = authnStatementBuilder.buildObject();
475                 Instant authInstant = statementBean.getAuthenticationInstant();
476                 if (authInstant == null) {
477                     authInstant = Instant.now();
478                 }
479                 authnStatement.setAuthnInstant(authInstant);
480 
481                 Instant sessionNotOnOrAfter = statementBean.getSessionNotOnOrAfter();
482                 if (sessionNotOnOrAfter != null) {
483                     authnStatement.setSessionNotOnOrAfter(sessionNotOnOrAfter);
484                 }
485 
486                 if (statementBean.getSessionIndex() != null) {
487                     authnStatement.setSessionIndex(statementBean.getSessionIndex());
488                 }
489 
490                 AuthnContextClassRef authnContextClassRef = authnContextClassRefBuilder.buildObject();
491                 authnContextClassRef.setURI(
492                     transformAuthenticationMethod(statementBean.getAuthenticationMethod())
493                 );
494                 AuthnContext authnContext = authnContextBuilder.buildObject();
495                 authnContext.setAuthnContextClassRef(authnContextClassRef);
496                 authnStatement.setAuthnContext(authnContext);
497 
498                 SubjectLocalityBean subjectLocalityBean = statementBean.getSubjectLocality();
499                 if (subjectLocalityBean != null) {
500                     SubjectLocality subjectLocality = subjectLocalityBuilder.buildObject();
501                     subjectLocality.setDNSName(subjectLocalityBean.getDnsAddress());
502                     subjectLocality.setAddress(subjectLocalityBean.getIpAddress());
503 
504                     authnStatement.setSubjectLocality(subjectLocality);
505                 }
506 
507                 authnStatements.add(authnStatement);
508             }
509         }
510 
511         return authnStatements;
512     }
513 
514     /**
515      * Transform the user-supplied authentication method value into one of the supported
516      * specification-compliant values.
517      *
518      * @param sourceMethod of type String
519      * @return String
520      */
521     private static String transformAuthenticationMethod(String sourceMethod) {
522         String transformedMethod = "";
523 
524         if ("Password".equalsIgnoreCase(sourceMethod)) {
525             transformedMethod = SAML2Constants.AUTH_CONTEXT_CLASS_REF_PASSWORD;
526         } else if (sourceMethod != null && sourceMethod.length() != 0) {
527             return sourceMethod;
528         }
529 
530         return transformedMethod;
531     }
532 
533     /**
534      * Create a SAML2 Attribute
535      *
536      * @param friendlyName of type String
537      * @param name         of type String
538      * @param nameFormat   of type String
539      * @param values       of type ArrayList
540      * @return a SAML2 Attribute
541      */
542     public static Attribute createAttribute(
543         String friendlyName, String name, String nameFormat, List<Object> values
544     ) {
545         if (stringBuilder == null) {
546             stringBuilder = (XSStringBuilder)builderFactory.getBuilder(XSString.TYPE_NAME);
547         }
548         Attribute attribute = createAttribute(friendlyName, name, nameFormat);
549 
550         for (Object value : values) {
551             if (value instanceof String) {
552                 XSString attributeValue =
553                     stringBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSString.TYPE_NAME);
554                 attributeValue.setValue((String)value);
555                 attribute.getAttributeValues().add(attributeValue);
556             } else if (value instanceof XMLObject) {
557                 attribute.getAttributeValues().add((XMLObject)value);
558             }
559         }
560 
561         return attribute;
562     }
563 
564     /**
565      * Create a Subject.
566      *
567      * @param subjectBean of type SubjectBean
568      * @return a Subject
569      */
570     @SuppressWarnings("unchecked")
571     public static Subject createSaml2Subject(SubjectBean subjectBean)
572         throws org.opensaml.security.SecurityException, WSSecurityException {
573         if (subjectBuilder == null) {
574             subjectBuilder = (SAMLObjectBuilder<Subject>)
575                 builderFactory.getBuilder(Subject.DEFAULT_ELEMENT_NAME);
576         }
577         Subject subject = subjectBuilder.buildObject();
578 
579         NameID nameID = SAML2ComponentBuilder.createNameID(subjectBean);
580         subject.setNameID(nameID);
581 
582         SubjectConfirmationData subjectConfData = null;
583         if (subjectBean.getKeyInfo() != null || subjectBean.getSubjectConfirmationData() != null) {
584             subjectConfData =
585                 SAML2ComponentBuilder.createSubjectConfirmationData(
586                     subjectBean.getSubjectConfirmationData(),
587                     subjectBean.getKeyInfo()
588                 );
589         }
590 
591         NameID subjectConfNameId = null;
592         if (subjectBean.getSubjectConfirmationNameID() != null) {
593             subjectConfNameId = SAML2ComponentBuilder.createNameID(subjectBean.getSubjectConfirmationNameID());
594         }
595 
596         String confirmationMethodStr = subjectBean.getSubjectConfirmationMethod();
597         if (confirmationMethodStr == null) {
598             confirmationMethodStr = SAML2Constants.CONF_SENDER_VOUCHES;
599         }
600         SubjectConfirmation subjectConfirmation =
601             SAML2ComponentBuilder.createSubjectConfirmation(
602                 confirmationMethodStr, subjectConfData, subjectConfNameId
603             );
604 
605         subject.getSubjectConfirmations().add(subjectConfirmation);
606         return subject;
607     }
608 
609     /**
610      * Create a SubjectConfirmationData object
611      *
612      * @param subjectConfirmationDataBean of type SubjectConfirmationDataBean
613      * @param keyInfoBean of type KeyInfoBean
614      * @return a SubjectConfirmationData object
615      */
616     @SuppressWarnings("unchecked")
617     public static SubjectConfirmationData createSubjectConfirmationData(
618         SubjectConfirmationDataBean subjectConfirmationDataBean,
619         KeyInfoBean keyInfoBean
620     ) throws org.opensaml.security.SecurityException, WSSecurityException {
621         SubjectConfirmationData subjectConfirmationData = null;
622         KeyInfo keyInfo = null;
623         if (keyInfoBean == null) {
624             if (subjectConfirmationDataBuilder == null) {
625                 subjectConfirmationDataBuilder = (SAMLObjectBuilder<SubjectConfirmationData>)
626                     builderFactory.getBuilder(SubjectConfirmationData.DEFAULT_ELEMENT_NAME);
627             }
628             subjectConfirmationData = subjectConfirmationDataBuilder.buildObject();
629         } else {
630             if (keyInfoConfirmationDataBuilder == null) {
631                 keyInfoConfirmationDataBuilder = (SAMLObjectBuilder<KeyInfoConfirmationDataType>)
632                     builderFactory.getBuilder(KeyInfoConfirmationDataType.TYPE_NAME);
633             }
634             subjectConfirmationData = keyInfoConfirmationDataBuilder.buildObject();
635             keyInfo = SAML1ComponentBuilder.createKeyInfo(keyInfoBean);
636             ((KeyInfoConfirmationDataType)subjectConfirmationData).getKeyInfos().add(keyInfo);
637         }
638 
639         if (subjectConfirmationDataBean != null) {
640             if (subjectConfirmationDataBean.getInResponseTo() != null) {
641                 subjectConfirmationData.setInResponseTo(subjectConfirmationDataBean.getInResponseTo());
642             }
643             if (subjectConfirmationDataBean.getRecipient() != null) {
644                 subjectConfirmationData.setRecipient(subjectConfirmationDataBean.getRecipient());
645             }
646             if (subjectConfirmationDataBean.getAddress() != null) {
647                 subjectConfirmationData.setAddress(subjectConfirmationDataBean.getAddress());
648             }
649             if (subjectConfirmationDataBean.getNotAfter() != null) {
650                 subjectConfirmationData.setNotOnOrAfter(subjectConfirmationDataBean.getNotAfter());
651             }
652             if (subjectConfirmationDataBean.getNotBefore() != null) {
653                 subjectConfirmationData.setNotBefore(subjectConfirmationDataBean.getNotBefore());
654             }
655             if (subjectConfirmationDataBean.getAny() != null) {
656                 List<XMLObject> unknownObjects = subjectConfirmationData.getUnknownXMLObjects();
657                 for (Object obj : subjectConfirmationDataBean.getAny()) {
658                     if (obj == null) {
659                         LOG.warn("Ignore <null> object in SubjectConfirmationData.any");
660                     } else if (obj instanceof XMLObject) {
661                         unknownObjects.add((XMLObject) obj);
662                     } else if (obj instanceof AttributeStatementBean) {
663                         unknownObjects.addAll(createAttributeStatement(Collections.singletonList((AttributeStatementBean) obj)));
664                     } else {
665                         LOG.warn("Ignore object of the unsupported type {} in SubjectConfirmationData.any", obj.getClass());
666                     }
667                 }
668             }
669         }
670 
671         return subjectConfirmationData;
672     }
673 
674     /**
675      * Create a SubjectConfirmation object
676      * One of the following subject confirmation methods MUST be used:
677      *   urn:oasis:names:tc:SAML:2.0:cm:holder-of-key
678      *   urn:oasis:names:tc:SAML:2.0:cm:sender-vouches
679      *   urn:oasis:names:tc:SAML:2.0:cm:bearer
680      *
681      * @param method of type String
682      * @param subjectConfirmationData of type SubjectConfirmationData
683      * @return a SubjectConfirmation object
684      */
685     @SuppressWarnings("unchecked")
686     public static SubjectConfirmation createSubjectConfirmation(
687         String method,
688         SubjectConfirmationData subjectConfirmationData,
689         NameID subjectConfirmationNameId
690     ) {
691         if (subjectConfirmationBuilder == null) {
692             subjectConfirmationBuilder = (SAMLObjectBuilder<SubjectConfirmation>)
693                 builderFactory.getBuilder(SubjectConfirmation.DEFAULT_ELEMENT_NAME);
694         }
695 
696         SubjectConfirmation subjectConfirmation = subjectConfirmationBuilder.buildObject();
697         subjectConfirmation.setMethod(method);
698         subjectConfirmation.setSubjectConfirmationData(subjectConfirmationData);
699         subjectConfirmation.setNameID(subjectConfirmationNameId);
700         return subjectConfirmation;
701     }
702 
703     /**
704      * Create a NameID object
705      * One of the following formats MUST be used:
706      *   urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
707      *   urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
708      *   urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName
709      *   urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName
710      *   urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos
711      *   urn:oasis:names:tc:SAML:2.0:nameid-format:entity
712      *   urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
713      *   urn:oasis:names:tc:SAML:2.0:nameid-format:transient
714      *
715      * @param subject A SubjectBean instance
716      * @return NameID
717      */
718     public static NameID createNameID(SubjectBean subject) {
719         NameIDBean nameIDBean = new NameIDBean();
720         nameIDBean.setNameIDFormat(subject.getSubjectNameIDFormat());
721         nameIDBean.setNameQualifier(subject.getSubjectNameQualifier());
722         nameIDBean.setSPNameQualifier(subject.getSubjectNameSPNameQualifier());
723         nameIDBean.setSPProvidedID(subject.getSubjectNameSPProvidedID());
724         nameIDBean.setNameValue(subject.getSubjectName());
725         return createNameID(nameIDBean);
726     }
727 
728     @SuppressWarnings("unchecked")
729     public static NameID createNameID(NameIDBean nameIDBean) {
730         if (nameIdBuilder == null) {
731             nameIdBuilder = (SAMLObjectBuilder<NameID>)
732                 builderFactory.getBuilder(NameID.DEFAULT_ELEMENT_NAME);
733         }
734         NameID nameID = nameIdBuilder.buildObject();
735         nameID.setNameQualifier(nameIDBean.getNameQualifier());
736         nameID.setFormat(nameIDBean.getNameIDFormat());
737         nameID.setValue(nameIDBean.getNameValue());
738         nameID.setSPNameQualifier(nameIDBean.getSPNameQualifier());
739         nameID.setSPProvidedID(nameIDBean.getSPProvidedID());
740         return nameID;
741     }
742 
743     /**
744      * Create SAML2 Attribute Statement(s)
745      *
746      * @param attributeData A list of AttributeStatementBean instances
747      * @return SAML2 Attribute Statement(s)
748      */
749     @SuppressWarnings("unchecked")
750     public static List<AttributeStatement> createAttributeStatement(
751         List<AttributeStatementBean> attributeData
752     ) {
753         List<AttributeStatement> attributeStatements = new ArrayList<>();
754         if (attributeStatementBuilder == null) {
755             attributeStatementBuilder = (SAMLObjectBuilder<AttributeStatement>)
756             builderFactory.getBuilder(AttributeStatement.DEFAULT_ELEMENT_NAME);
757         }
758 
759         if (attributeData != null && !attributeData.isEmpty()) {
760             for (AttributeStatementBean statementBean : attributeData) {
761                 AttributeStatement attributeStatement = attributeStatementBuilder.buildObject();
762                 for (AttributeBean values : statementBean.getSamlAttributes()) {
763                     List<Object> attributeValues = values.getAttributeValues();
764                     Attribute samlAttribute =
765                         createAttribute(
766                             values.getSimpleName(),
767                             values.getQualifiedName(),
768                             values.getNameFormat(),
769                             attributeValues
770                         );
771                     attributeStatement.getAttributes().add(samlAttribute);
772                 }
773                 // Add the completed attribute statementBean to the collection
774                 attributeStatements.add(attributeStatement);
775             }
776         }
777 
778         return attributeStatements;
779     }
780 
781     /**
782      * Create an Attribute object.
783      *
784      * @param friendlyName of type String
785      * @param name of type String
786      * @param nameFormat of type String
787      * @return an Attribute object
788      */
789     @SuppressWarnings("unchecked")
790     public static Attribute createAttribute(String friendlyName, String name, String nameFormat) {
791         if (attributeBuilder == null) {
792             attributeBuilder = (SAMLObjectBuilder<Attribute>)
793                 builderFactory.getBuilder(Attribute.DEFAULT_ELEMENT_NAME);
794         }
795 
796         Attribute attribute = attributeBuilder.buildObject();
797         attribute.setFriendlyName(friendlyName);
798         if (nameFormat == null) {
799             attribute.setNameFormat(SAML2Constants.ATTRNAME_FORMAT_URI);
800         } else {
801             attribute.setNameFormat(nameFormat);
802         }
803         attribute.setName(name);
804         return attribute;
805     }
806 
807     /**
808      * Create SAML2 AuthorizationDecisionStatement(s)
809      *
810      * @param decisionData A list of AuthDecisionStatementBean instances
811      * @return SAML2 AuthorizationDecisionStatement(s)
812      */
813     @SuppressWarnings("unchecked")
814     public static List<AuthzDecisionStatement> createAuthorizationDecisionStatement(
815         List<AuthDecisionStatementBean> decisionData
816     ) {
817         List<AuthzDecisionStatement> authDecisionStatements = new ArrayList<>();
818         if (authorizationDecisionStatementBuilder == null) {
819             authorizationDecisionStatementBuilder =
820                 (SAMLObjectBuilder<AuthzDecisionStatement>)
821                     builderFactory.getBuilder(AuthzDecisionStatement.DEFAULT_ELEMENT_NAME);
822         }
823 
824         if (decisionData != null && !decisionData.isEmpty()) {
825             for (AuthDecisionStatementBean decisionStatementBean : decisionData) {
826                 AuthzDecisionStatement authDecision =
827                     authorizationDecisionStatementBuilder.buildObject();
828                 authDecision.setResource(decisionStatementBean.getResource());
829                 authDecision.setDecision(
830                     transformDecisionType(decisionStatementBean.getDecision())
831                 );
832 
833                 for (ActionBean actionBean : decisionStatementBean.getActions()) {
834                     Action actionElement = createSamlAction(actionBean);
835                     authDecision.getActions().add(actionElement);
836                 }
837 
838                 if (decisionStatementBean.getEvidence() instanceof Evidence) {
839                     authDecision.setEvidence((Evidence)decisionStatementBean.getEvidence());
840                 }
841 
842                 authDecisionStatements.add(authDecision);
843             }
844         }
845 
846         return authDecisionStatements;
847     }
848 
849 
850     /**
851      * Create an Action object
852      *
853      * @param actionBean An ActionBean instance
854      * @return an Action object
855      */
856     @SuppressWarnings("unchecked")
857     public static Action createSamlAction(ActionBean actionBean) {
858         if (actionElementBuilder == null) {
859             actionElementBuilder = (SAMLObjectBuilder<Action>)
860                 builderFactory.getBuilder(Action.DEFAULT_ELEMENT_NAME);
861         }
862         Action actionElement = actionElementBuilder.buildObject();
863         actionElement.setNamespace(actionBean.getActionNamespace());
864         if (actionBean.getActionNamespace() == null) {
865             actionElement.setNamespace("urn:oasis:names:tc:SAML:1.0:action:rwedc-negation");
866         }
867         actionElement.setValue(actionBean.getContents());
868 
869         return actionElement;
870     }
871 
872     /**
873      * Create a DecisionTypeEnumeration object
874      *
875      * @param decision of type Decision
876      * @return a DecisionTypeEnumeration object
877      */
878     private static DecisionTypeEnumeration transformDecisionType(
879         AuthDecisionStatementBean.Decision decision
880     ) {
881         DecisionTypeEnumeration decisionTypeEnum = DecisionTypeEnumeration.DENY;
882         if (decision.equals(AuthDecisionStatementBean.Decision.PERMIT)) {
883             decisionTypeEnum = DecisionTypeEnumeration.PERMIT;
884         } else if (decision.equals(AuthDecisionStatementBean.Decision.INDETERMINATE)) {
885             decisionTypeEnum = DecisionTypeEnumeration.INDETERMINATE;
886         }
887 
888         return decisionTypeEnum;
889     }
890 
891 }