PageRenderTime 47ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/support/cas-server-support-saml-idp-web/src/main/java/org/apereo/cas/support/saml/web/idp/profile/builders/nameid/SamlProfileSamlNameIdBuilder.java

https://github.com/frett/cas
Java | 345 lines | 231 code | 26 blank | 88 comment | 26 complexity | 4ecb58177d0cb1a638509548d19cf443 MD5 | raw file
  1. package org.apereo.cas.support.saml.web.idp.profile.builders.nameid;
  2. import org.apereo.cas.authentication.principal.PersistentIdGenerator;
  3. import org.apereo.cas.support.saml.OpenSamlConfigBean;
  4. import org.apereo.cas.support.saml.SamlException;
  5. import org.apereo.cas.support.saml.SamlIdPUtils;
  6. import org.apereo.cas.support.saml.services.SamlRegisteredService;
  7. import org.apereo.cas.support.saml.services.idp.metadata.SamlRegisteredServiceServiceProviderMetadataFacade;
  8. import org.apereo.cas.support.saml.util.AbstractSaml20ObjectBuilder;
  9. import org.apereo.cas.support.saml.web.idp.profile.builders.SamlProfileObjectBuilder;
  10. import org.apereo.cas.support.saml.web.idp.profile.builders.enc.SamlIdPObjectEncrypter;
  11. import org.apereo.cas.util.CollectionUtils;
  12. import lombok.extern.slf4j.Slf4j;
  13. import lombok.val;
  14. import net.shibboleth.idp.attribute.IdPAttribute;
  15. import net.shibboleth.idp.attribute.StringAttributeValue;
  16. import net.shibboleth.idp.saml.attribute.encoding.impl.SAML2StringNameIDEncoder;
  17. import org.apache.commons.lang3.StringUtils;
  18. import org.jasig.cas.client.authentication.AttributePrincipal;
  19. import org.jasig.cas.client.validation.Assertion;
  20. import org.opensaml.messaging.context.MessageContext;
  21. import org.opensaml.saml.saml2.core.AttributeQuery;
  22. import org.opensaml.saml.saml2.core.AuthnRequest;
  23. import org.opensaml.saml.saml2.core.NameID;
  24. import org.opensaml.saml.saml2.core.NameIDPolicy;
  25. import org.opensaml.saml.saml2.core.NameIDType;
  26. import org.opensaml.saml.saml2.core.RequestAbstractType;
  27. import javax.servlet.http.HttpServletRequest;
  28. import javax.servlet.http.HttpServletResponse;
  29. import java.util.List;
  30. /**
  31. * This is {@link SamlProfileSamlNameIdBuilder}.
  32. *
  33. * @author Misagh Moayyed
  34. * @since 5.0.0
  35. */
  36. @Slf4j
  37. public class SamlProfileSamlNameIdBuilder extends AbstractSaml20ObjectBuilder implements SamlProfileObjectBuilder<NameID> {
  38. private static final long serialVersionUID = -6231886395225437320L;
  39. private final PersistentIdGenerator persistentIdGenerator;
  40. private final transient SamlIdPObjectEncrypter samlObjectEncrypter;
  41. public SamlProfileSamlNameIdBuilder(final OpenSamlConfigBean configBean, final PersistentIdGenerator persistentIdGenerator,
  42. final SamlIdPObjectEncrypter samlObjectEncrypter) {
  43. super(configBean);
  44. this.persistentIdGenerator = persistentIdGenerator;
  45. this.samlObjectEncrypter = samlObjectEncrypter;
  46. }
  47. /**
  48. * Gets supported name id formats.
  49. *
  50. * @param service the service
  51. * @param adaptor the adaptor
  52. * @return the supported name id formats
  53. */
  54. protected static List<String> getSupportedNameIdFormats(final SamlRegisteredService service,
  55. final SamlRegisteredServiceServiceProviderMetadataFacade adaptor) {
  56. val supportedNameFormats = adaptor.getSupportedNameIdFormats();
  57. LOGGER.debug("Metadata for [{}] declares the following NameIDs [{}]", adaptor.getEntityId(), supportedNameFormats);
  58. if (supportedNameFormats.isEmpty()) {
  59. supportedNameFormats.add(NameIDType.TRANSIENT);
  60. LOGGER.debug("No supported nameId formats could be determined from metadata. Added default [{}]", NameIDType.TRANSIENT);
  61. }
  62. if (StringUtils.isNotBlank(service.getRequiredNameIdFormat())) {
  63. val fmt = parseAndBuildRequiredNameIdFormat(service);
  64. supportedNameFormats.add(0, fmt);
  65. LOGGER.debug("Added required nameId format [{}] based on saml service configuration for [{}]", fmt, service.getServiceId());
  66. }
  67. return supportedNameFormats;
  68. }
  69. private static String parseAndBuildRequiredNameIdFormat(final SamlRegisteredService service) {
  70. val fmt = service.getRequiredNameIdFormat().trim();
  71. LOGGER.debug("Required NameID format assigned to service [{}] is [{}]", service.getName(), fmt);
  72. if (StringUtils.containsIgnoreCase(NameIDType.EMAIL, fmt)) {
  73. return NameIDType.EMAIL;
  74. }
  75. if (StringUtils.containsIgnoreCase(NameIDType.TRANSIENT, fmt)) {
  76. return NameIDType.TRANSIENT;
  77. }
  78. if (StringUtils.containsIgnoreCase(NameIDType.UNSPECIFIED, fmt)) {
  79. return NameIDType.UNSPECIFIED;
  80. }
  81. if (StringUtils.containsIgnoreCase(NameIDType.PERSISTENT, fmt)) {
  82. return NameIDType.PERSISTENT;
  83. }
  84. if (StringUtils.containsIgnoreCase(NameIDType.ENTITY, fmt)) {
  85. return NameIDType.ENTITY;
  86. }
  87. if (StringUtils.containsIgnoreCase(NameIDType.X509_SUBJECT, fmt)) {
  88. return NameIDType.X509_SUBJECT;
  89. }
  90. if (StringUtils.containsIgnoreCase(NameIDType.WIN_DOMAIN_QUALIFIED, fmt)) {
  91. return NameIDType.WIN_DOMAIN_QUALIFIED;
  92. }
  93. if (StringUtils.containsIgnoreCase(NameIDType.KERBEROS, fmt)) {
  94. return NameIDType.KERBEROS;
  95. }
  96. if (StringUtils.containsIgnoreCase(NameIDType.ENCRYPTED, fmt)) {
  97. return NameIDType.ENCRYPTED;
  98. }
  99. return fmt;
  100. }
  101. @Override
  102. public NameID build(final RequestAbstractType authnRequest,
  103. final HttpServletRequest request,
  104. final HttpServletResponse response,
  105. final Object assertion,
  106. final SamlRegisteredService service,
  107. final SamlRegisteredServiceServiceProviderMetadataFacade adaptor,
  108. final String binding,
  109. final MessageContext messageContext) throws SamlException {
  110. return buildNameId(authnRequest, assertion, service, adaptor, messageContext);
  111. }
  112. /**
  113. * Build name id.
  114. * If there are no explicitly defined NameIDFormats, include the default format.
  115. * see: http://saml2int.org/profile/current/#section92
  116. *
  117. * @param authnRequest the authn request
  118. * @param assertion the assertion
  119. * @param service the service
  120. * @param adaptor the adaptor
  121. * @return the name id
  122. * @throws SamlException the saml exception
  123. */
  124. private NameID buildNameId(final RequestAbstractType authnRequest,
  125. final Object assertion,
  126. final SamlRegisteredService service,
  127. final SamlRegisteredServiceServiceProviderMetadataFacade adaptor,
  128. final MessageContext messageContext) throws SamlException {
  129. val supportedNameFormats = getSupportedNameIdFormats(service, adaptor);
  130. val requiredNameFormat = getRequiredNameIdFormatIfAny(authnRequest);
  131. validateRequiredNameIdFormatIfAny(authnRequest, adaptor, supportedNameFormats, requiredNameFormat);
  132. val nameid = determineNameId(authnRequest, assertion, supportedNameFormats, service, adaptor);
  133. return finalizeNameId(nameid, authnRequest, assertion, supportedNameFormats, service, adaptor);
  134. }
  135. /**
  136. * Finalize name id name id.
  137. *
  138. * @param nameid the nameid
  139. * @param authnRequest the authn request
  140. * @param assertion the assertion
  141. * @param supportedNameFormats the supported name formats
  142. * @param service the service
  143. * @param adaptor the adaptor
  144. * @return the name id
  145. */
  146. protected NameID finalizeNameId(final NameID nameid,
  147. final RequestAbstractType authnRequest,
  148. final Object assertion,
  149. final List<String> supportedNameFormats,
  150. final SamlRegisteredService service,
  151. final SamlRegisteredServiceServiceProviderMetadataFacade adaptor) {
  152. if (StringUtils.isNotBlank(service.getNameIdQualifier())) {
  153. nameid.setNameQualifier(service.getNameIdQualifier());
  154. } else {
  155. val issuer = SamlIdPUtils.getIssuerFromSamlObject(authnRequest);
  156. nameid.setNameQualifier(issuer);
  157. }
  158. if (StringUtils.isNotBlank(service.getServiceProviderNameIdQualifier())) {
  159. nameid.setSPNameQualifier(service.getServiceProviderNameIdQualifier());
  160. } else {
  161. nameid.setSPNameQualifier(adaptor.getEntityId());
  162. }
  163. return nameid;
  164. }
  165. /**
  166. * Validate required name id format if any.
  167. *
  168. * @param authnRequest the authn request
  169. * @param adaptor the adaptor
  170. * @param supportedNameFormats the supported name formats
  171. * @param requiredNameFormat the required name format
  172. */
  173. protected void validateRequiredNameIdFormatIfAny(final RequestAbstractType authnRequest,
  174. final SamlRegisteredServiceServiceProviderMetadataFacade adaptor,
  175. final List<String> supportedNameFormats,
  176. final String requiredNameFormat) {
  177. if (StringUtils.isNotBlank(requiredNameFormat) && !supportedNameFormats.contains(requiredNameFormat)) {
  178. LOGGER.warn("Required NameID format [{}] in the AuthN request issued by [{}] is not supported based on the metadata for [{}]. "
  179. + "The requested NameID format may not be honored. You should consult the metadata for this service "
  180. + "and ensure the requested NameID format is present in the collection of supported "
  181. + "metadata formats in the metadata, which are the following: [{}]",
  182. requiredNameFormat, SamlIdPUtils.getIssuerFromSamlObject(authnRequest),
  183. adaptor.getEntityId(), adaptor.getSupportedNameIdFormats());
  184. }
  185. }
  186. /**
  187. * Gets required name id format if any.
  188. *
  189. * @param authnRequest the authn request
  190. * @return the required name id format if any
  191. */
  192. protected String getRequiredNameIdFormatIfAny(final RequestAbstractType authnRequest) {
  193. val nameIDPolicy = getNameIDPolicy(authnRequest);
  194. val requiredNameFormat = nameIDPolicy != null ? nameIDPolicy.getFormat() : null;
  195. LOGGER.debug("AuthN request indicates [{}] is the required NameID format", requiredNameFormat);
  196. return requiredNameFormat;
  197. }
  198. private NameIDPolicy getNameIDPolicy(final RequestAbstractType authnRequest) {
  199. if (authnRequest instanceof AuthnRequest) {
  200. return AuthnRequest.class.cast(authnRequest).getNameIDPolicy();
  201. }
  202. return null;
  203. }
  204. /**
  205. * Determine name id name id.
  206. *
  207. * @param authnRequest the authn request
  208. * @param assertion the assertion
  209. * @param supportedNameFormats the supported name formats
  210. * @param service the service
  211. * @param adaptor the adaptor
  212. * @return the name id
  213. */
  214. protected NameID determineNameId(final RequestAbstractType authnRequest,
  215. final Object assertion,
  216. final List<String> supportedNameFormats,
  217. final SamlRegisteredService service,
  218. final SamlRegisteredServiceServiceProviderMetadataFacade adaptor) {
  219. for (val nameFormat : supportedNameFormats) {
  220. LOGGER.debug("Evaluating NameID format [{}]", nameFormat);
  221. val nameid = encodeNameIdBasedOnNameFormat(authnRequest, assertion, nameFormat, service, adaptor);
  222. if (nameid != null) {
  223. LOGGER.debug("Determined NameID based on format [{}] to be [{}]", nameFormat, nameid.getValue());
  224. return nameid;
  225. }
  226. }
  227. LOGGER.warn("No NameID could be determined based on the supported formats [{}]", supportedNameFormats);
  228. return null;
  229. }
  230. /**
  231. * Encode name id based on name format name id.
  232. *
  233. * @param authnRequest the authn request
  234. * @param assertion the assertion
  235. * @param nameFormat the name format
  236. * @param service the service
  237. * @param adaptor the adaptor
  238. * @return the name id
  239. */
  240. protected NameID encodeNameIdBasedOnNameFormat(final RequestAbstractType authnRequest,
  241. final Object assertion,
  242. final String nameFormat,
  243. final SamlRegisteredService service,
  244. final SamlRegisteredServiceServiceProviderMetadataFacade adaptor) {
  245. try {
  246. if (authnRequest instanceof AttributeQuery) {
  247. val query = AttributeQuery.class.cast(authnRequest);
  248. val nameID = query.getSubject().getNameID();
  249. nameID.detach();
  250. LOGGER.debug("Choosing NameID format [{}] with value [{}] for attribute query", nameID.getFormat(), nameID.getValue());
  251. return nameID;
  252. }
  253. val attribute = prepareNameIdAttribute(assertion, nameFormat, adaptor);
  254. val encoder = prepareNameIdEncoder(authnRequest, nameFormat, attribute, service, adaptor);
  255. LOGGER.debug("Encoding NameID based on [{}]", nameFormat);
  256. var nameId = encoder.encode(attribute);
  257. LOGGER.debug("Final NameID encoded with format [{}] has value [{}]", nameId.getFormat(), nameId.getValue());
  258. return nameId;
  259. } catch (final Exception e) {
  260. LOGGER.error(e.getMessage(), e);
  261. }
  262. return null;
  263. }
  264. /**
  265. * Prepare name id attribute id p attribute.
  266. *
  267. * @param casAssertion the assertion
  268. * @param nameFormat the name format
  269. * @param adaptor the adaptor
  270. * @return the idp attribute
  271. */
  272. protected IdPAttribute prepareNameIdAttribute(final Object casAssertion,
  273. final String nameFormat,
  274. final SamlRegisteredServiceServiceProviderMetadataFacade adaptor) {
  275. val assertion = Assertion.class.cast(casAssertion);
  276. val attribute = new IdPAttribute(AttributePrincipal.class.getName());
  277. val principalName = assertion.getPrincipal().getName();
  278. LOGGER.debug("Preparing NameID attribute for principal [{}]", principalName);
  279. val nameIdValue = getNameIdValueFromNameFormat(nameFormat, adaptor, principalName);
  280. val value = new StringAttributeValue(nameIdValue);
  281. LOGGER.debug("NameID attribute value is set to [{}]", value);
  282. attribute.setValues(CollectionUtils.wrap(value));
  283. return attribute;
  284. }
  285. private String getNameIdValueFromNameFormat(final String nameFormat,
  286. final SamlRegisteredServiceServiceProviderMetadataFacade adaptor,
  287. final String principalName) {
  288. if (nameFormat.trim().equalsIgnoreCase(NameIDType.TRANSIENT)) {
  289. val entityId = adaptor.getEntityId();
  290. LOGGER.debug("Generating transient NameID value for principal [{}] and entity id [{}]", principalName, entityId);
  291. return persistentIdGenerator.generate(principalName, entityId);
  292. }
  293. return principalName;
  294. }
  295. /**
  296. * Prepare name id encoder saml 2 string name id encoder.
  297. *
  298. * @param authnRequest the authn request
  299. * @param nameFormat the name format
  300. * @param attribute the attribute
  301. * @param service the service
  302. * @param adaptor the adaptor
  303. * @return the saml 2 string name id encoder
  304. */
  305. protected SAML2StringNameIDEncoder prepareNameIdEncoder(final RequestAbstractType authnRequest,
  306. final String nameFormat,
  307. final IdPAttribute attribute,
  308. final SamlRegisteredService service,
  309. final SamlRegisteredServiceServiceProviderMetadataFacade adaptor) {
  310. val encoder = new SAML2StringNameIDEncoder();
  311. encoder.setNameFormat(nameFormat);
  312. if (getNameIDPolicy(authnRequest) != null) {
  313. val qualifier = getNameIDPolicy(authnRequest).getSPNameQualifier();
  314. LOGGER.debug("NameID qualifier is set to [{}]", qualifier);
  315. encoder.setNameQualifier(qualifier);
  316. }
  317. return encoder;
  318. }
  319. }