public void Saml2ConditionsExtensions_ToXElement_OnlyNotOnOrAfter() { var conditions = new Saml2Conditions() { NotOnOrAfter = new DateTime(2099, 07, 25, 19, 52, 42, DateTimeKind.Utc) }; var actual = conditions.ToXElement(); actual.Name.Should().Be(Saml2Namespaces.Saml2 + "Conditions"); actual.Attribute("NotOnOrAfter").Value.Should().Be("2099-07-25T19:52:42Z"); }
public void Saml2ConditionsExtensions_ToXElement_OnlyAudienceRestriction() { var conditions = new Saml2Conditions(); conditions.AudienceRestrictions.Add(new Saml2AudienceRestriction(new[] { new Uri("http://foo1"), new Uri("http://foo2") })); conditions.AudienceRestrictions.Add(new Saml2AudienceRestriction(new Uri("http://bar"))); var actual = conditions.ToXElement(); var expected = new XElement(Saml2Namespaces.Saml2 + "Conditions", new XElement(Saml2Namespaces.Saml2 + "AudienceRestriction", new XElement(Saml2Namespaces.Saml2 + "Audience", "http://foo1"), new XElement(Saml2Namespaces.Saml2 + "Audience", "http://foo2")), new XElement(Saml2Namespaces.Saml2 + "AudienceRestriction", new XElement(Saml2Namespaces.Saml2 + "Audience", "http://bar"))); actual.Should().BeEquivalentTo(expected); }
protected override void ValidateConditions(Saml2Conditions conditions, bool enforceAudienceRestriction) { Saml2ConditionsDelegateWrapper delegateConditions = conditions as Saml2ConditionsDelegateWrapper; ValidateDelegates(delegateConditions.Delegates); base.ValidateConditions(conditions, enforceAudienceRestriction); }
/// <summary> /// Writes the <saml:Conditions> element. /// </summary> /// <param name="writer">A <see cref="XmlWriter"/> to serialize the <see cref="Saml2Conditions"/>.</param> /// <param name="data">The <see cref="Saml2Conditions"/> to serialize.</param> protected virtual void WriteConditions(XmlWriter writer, Saml2Conditions data) { if (null == writer) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer"); } if (null == data) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("data"); } // <Conditions> writer.WriteStartElement(Saml2Constants.Elements.Conditions, Saml2Constants.Namespace); // @NotBefore - optional if (null != data.NotBefore) { writer.WriteAttributeString(Saml2Constants.Attributes.NotBefore, XmlConvert.ToString(data.NotBefore.Value.ToUniversalTime(), DateTimeFormats.Generated)); } // @NotOnOrAfter - optional if (null != data.NotOnOrAfter) { writer.WriteAttributeString(Saml2Constants.Attributes.NotOnOrAfter, XmlConvert.ToString(data.NotOnOrAfter.Value.ToUniversalTime(), DateTimeFormats.Generated)); } // <AudienceRestriction> 0-OO foreach (Saml2AudienceRestriction audienceRestriction in data.AudienceRestrictions) { this.WriteAudienceRestriction(writer, audienceRestriction); } // <OneTimeUse> - limited to one in SAML spec if (data.OneTimeUse) { writer.WriteStartElement(Saml2Constants.Elements.OneTimeUse, Saml2Constants.Namespace); writer.WriteEndElement(); } // <ProxyRestriction> - limited to one in SAML spec if (null != data.ProxyRestriction) { this.WriteProxyRestriction(writer, data.ProxyRestriction); } // </Conditions> writer.WriteEndElement(); }
/// <summary> /// Reads the <saml:Conditions> element. /// </summary> /// <remarks> /// To handle custom <saml:Condition> elements, override this /// method. /// </remarks> /// <param name="reader">A <see cref="XmlReader"/> positioned at a <see cref="Saml2Conditions"/> element.</param> /// <returns>A <see cref="Saml2Conditions"/> instance.</returns> protected virtual Saml2Conditions ReadConditions(XmlReader reader) { if (null == reader) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader"); } // throw if wrong element if (!reader.IsStartElement(Saml2Constants.Elements.Conditions, Saml2Constants.Namespace)) { reader.ReadStartElement(Saml2Constants.Elements.Conditions, Saml2Constants.Namespace); } try { Saml2Conditions conditions = new Saml2Conditions(); bool isEmpty = reader.IsEmptyElement; // @attributes string value; // @xsi:type XmlUtil.ValidateXsiType(reader, Saml2Constants.Types.ConditionsType, Saml2Constants.Namespace); // @NotBefore - optional value = reader.GetAttribute(Saml2Constants.Attributes.NotBefore); if (!string.IsNullOrEmpty(value)) { conditions.NotBefore = XmlConvert.ToDateTime(value, DateTimeFormats.Accepted); } // @NotOnOrAfter - optional value = reader.GetAttribute(Saml2Constants.Attributes.NotOnOrAfter); if (!string.IsNullOrEmpty(value)) { conditions.NotOnOrAfter = XmlConvert.ToDateTime(value, DateTimeFormats.Accepted); } // Content reader.ReadStartElement(); if (!isEmpty) { // <Condition|AudienceRestriction|OneTimeUse|ProxyRestriction>, 0-OO while (reader.IsStartElement()) { // <Condition> - 0-OO if (reader.IsStartElement(Saml2Constants.Elements.Condition, Saml2Constants.Namespace)) { // Since Condition is abstract, must process based on xsi:type XmlQualifiedName declaredType = XmlUtil.GetXsiType(reader); // No type, throw if (null == declaredType || XmlUtil.EqualsQName(declaredType, Saml2Constants.Types.ConditionAbstractType, Saml2Constants.Namespace)) { throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4104, reader.LocalName, reader.NamespaceURI)); } else if (XmlUtil.EqualsQName(declaredType, Saml2Constants.Types.AudienceRestrictionType, Saml2Constants.Namespace)) { conditions.AudienceRestrictions.Add(this.ReadAudienceRestriction(reader)); } else if (XmlUtil.EqualsQName(declaredType, Saml2Constants.Types.OneTimeUseType, Saml2Constants.Namespace)) { if (conditions.OneTimeUse) { throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4115, Saml2Constants.Elements.OneTimeUse)); } ReadEmptyContentElement(reader); conditions.OneTimeUse = true; } else if (XmlUtil.EqualsQName(declaredType, Saml2Constants.Types.ProxyRestrictionType, Saml2Constants.Namespace)) { if (null != conditions.ProxyRestriction) { throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4115, Saml2Constants.Elements.ProxyRestricton)); } conditions.ProxyRestriction = this.ReadProxyRestriction(reader); } else { // Unknown type - Instruct the user to override to handle custom <Condition> throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4113)); } } else if (reader.IsStartElement(Saml2Constants.Elements.AudienceRestriction, Saml2Constants.Namespace)) { conditions.AudienceRestrictions.Add(this.ReadAudienceRestriction(reader)); } else if (reader.IsStartElement(Saml2Constants.Elements.OneTimeUse, Saml2Constants.Namespace)) { if (conditions.OneTimeUse) { throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4115, Saml2Constants.Elements.OneTimeUse)); } ReadEmptyContentElement(reader); conditions.OneTimeUse = true; } else if (reader.IsStartElement(Saml2Constants.Elements.ProxyRestricton, Saml2Constants.Namespace)) { if (null != conditions.ProxyRestriction) { throw DiagnosticUtility.ThrowHelperXml(reader, SR.GetString(SR.ID4115, Saml2Constants.Elements.ProxyRestricton)); } conditions.ProxyRestriction = this.ReadProxyRestriction(reader); } else { break; } } reader.ReadEndElement(); } return conditions; } catch (Exception e) { if (System.Runtime.Fx.IsFatal(e)) throw; Exception wrapped = TryWrapReadException(reader, e); if (null == wrapped) { throw; } else { throw wrapped; } } }
/// <summary> /// Rejects tokens that are not valid. /// </summary> /// <remarks> /// The token may not be valid for a number of reasons. For example, the /// current time may not be within the token's validity period, the /// token may contain data that is contradictory or not valid, or the token /// may contain unsupported SAML2 elements. /// </remarks> /// <param name="conditions">SAML 2.0 condition to be validated.</param> /// <param name="enforceAudienceRestriction">True to check for Audience Restriction condition.</param> protected virtual void ValidateConditions(Saml2Conditions conditions, bool enforceAudienceRestriction) { if (conditions != null) { DateTime now = DateTime.UtcNow; if (conditions.NotBefore != null && DateTimeUtil.Add(now, Configuration.MaxClockSkew) < conditions.NotBefore.Value) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenNotYetValidException(SR.GetString(SR.ID4147, conditions.NotBefore.Value, now))); } if (conditions.NotOnOrAfter != null && DateTimeUtil.Add(now, Configuration.MaxClockSkew.Negate()) >= conditions.NotOnOrAfter.Value) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenExpiredException(SR.GetString(SR.ID4148, conditions.NotOnOrAfter.Value, now))); } if (conditions.OneTimeUse) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenValidationException(SR.GetString(SR.ID4149))); } if (conditions.ProxyRestriction != null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenValidationException(SR.GetString(SR.ID4150))); } } if (enforceAudienceRestriction) { if (this.Configuration == null || this.Configuration.AudienceRestriction.AllowedAudienceUris.Count == 0) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID1032))); } if (conditions == null || conditions.AudienceRestrictions.Count == 0) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new AudienceUriValidationFailedException(SR.GetString(SR.ID1035))); } else { foreach (Saml2AudienceRestriction audienceRestriction in conditions.AudienceRestrictions) { SamlSecurityTokenRequirement.ValidateAudienceRestriction(this.Configuration.AudienceRestriction.AllowedAudienceUris, audienceRestriction.Audiences); } } } }
/// <summary> /// Creates the conditions for the assertion. /// </summary> /// <remarks> /// <para> /// Generally, conditions should be included in assertions to limit the /// impact of misuse of the assertion. Specifying the NotBefore and /// NotOnOrAfter conditions can limit the period of vulnerability in /// the case of a compromised assertion. The AudienceRestrictionCondition /// can be used to explicitly state the intended relying party or parties /// of the assertion, which coupled with appropriate audience restriction /// enforcement at relying parties can help to mitigate spoofing attacks /// between relying parties. /// </para> /// <para> /// The default implementation creates NotBefore and NotOnOrAfter conditions /// based on the tokenDescriptor.Lifetime. It will also generate an /// AudienceRestrictionCondition limiting consumption of the assertion to /// tokenDescriptor.Scope.Address. /// </para> /// </remarks> /// <param name="tokenLifetime">Lifetime of the Token.</param> /// <param name="relyingPartyAddress">The endpoint address to who the token is created. The address /// is modeled as an AudienceRestriction condition.</param> /// <param name="tokenDescriptor">The token descriptor.</param> /// <returns>A Saml2Conditions object.</returns> protected virtual Saml2Conditions CreateConditions(Lifetime tokenLifetime, string relyingPartyAddress, SecurityTokenDescriptor tokenDescriptor) { bool hasLifetime = null != tokenLifetime; bool hasScope = !string.IsNullOrEmpty(relyingPartyAddress); if (!hasLifetime && !hasScope) { return null; } Saml2Conditions conditions = new Saml2Conditions(); if (hasLifetime) { conditions.NotBefore = tokenLifetime.Created; conditions.NotOnOrAfter = tokenLifetime.Expires; } if (hasScope) { conditions.AudienceRestrictions.Add(new Saml2AudienceRestriction(new Uri(relyingPartyAddress))); } return conditions; }
private void WriteConditionDelegates(XmlWriter writer, Saml2Conditions data) { const string xsi = "http://www.w3.org/2001/XMLSchema-instance"; writer.WriteStartElement("Condition", "urn:oasis:names:tc:SAML:2.0:assertion"); writer.WriteAttributeString("xmlns", "del", "http://www.w3.org/2000/xmlns/", "urn:oasis:names:tc:SAML:2.0:conditions:delegation"); writer.WriteAttributeString("xmlns", "xsi", "http://www.w3.org/2000/xmlns/", xsi); writer.WriteAttributeString("xsi", "type", xsi, "del:DelegationRestrictionType"); // Check if there are delegates within an incoming assertion Saml2ConditionsDelegateWrapper delegateData = data as Saml2ConditionsDelegateWrapper; if (delegateData != null && delegateData.Delegates != null) { // Add the delegates in the DelegationRestrictionType foreach (DelegateType del in delegateData.Delegates.Delegate) { writer.WriteStartElement("del", "Delegate", "urn:oasis:names:tc:SAML:2.0:conditions:delegation"); writer.WriteAttributeString("DelegationInstant", XmlConvert.ToString(del.DelegationInstant, DateTimeFormats.Generated)); writer.WriteElementString("NameID", "urn:oasis:names:tc:SAML:2.0:assertion", del.Item.ToString()); writer.WriteEndElement(); } } // Condition writer.WriteEndElement(); }
protected override void WriteConditions(XmlWriter writer, Saml2Conditions data) { if (writer == null) { throw new ArgumentNullException("writer"); } if (data == null) { throw new ArgumentNullException("data"); } writer.WriteStartElement("Conditions", "urn:oasis:names:tc:SAML:2.0:assertion"); if (data.NotBefore.HasValue) { writer.WriteAttributeString("NotBefore", XmlConvert.ToString(data.NotBefore.Value.ToUniversalTime(), DateTimeFormats.Generated)); } if (data.NotOnOrAfter.HasValue) { writer.WriteAttributeString("NotOnOrAfter", XmlConvert.ToString(data.NotOnOrAfter.Value.ToUniversalTime(), DateTimeFormats.Generated)); } foreach (Saml2AudienceRestriction restriction in data.AudienceRestrictions) { this.WriteAudienceRestriction(writer, restriction); } if (data.OneTimeUse) { writer.WriteStartElement("OneTimeUse", "urn:oasis:names:tc:SAML:2.0:assertion"); writer.WriteEndElement(); } if (data.ProxyRestriction != null) { this.WriteProxyRestriction(writer, data.ProxyRestriction); } // GFIPM S2S 8.8.2.6.d Condition/Delegates WriteConditionDelegates(writer, data); writer.WriteEndElement(); }
/// <summary> /// Validate the conditions of the token. /// </summary> /// <param name="conditions">Conditions to check</param> /// <param name="enforceAudienceRestriction">Should the audience restriction be enforced?</param> public new void ValidateConditions(Saml2Conditions conditions, bool enforceAudienceRestriction) { base.ValidateConditions(conditions, enforceAudienceRestriction); }