public virtual SamlAttribute LoadAttribute(XmlDictionaryReader reader, SecurityTokenSerializer keyInfoSerializer, SecurityTokenResolver outOfBandTokenResolver) { SamlAttribute attribute = new SamlAttribute(); attribute.ReadXml(reader, this, keyInfoSerializer, outOfBandTokenResolver); return(attribute); }
public static IList<string> GetValuesFor(SamlAttribute attribute) { IList<string> values = new List<string>(); foreach (string value in attribute.AttributeValues) { values.Add(value); } return values; }
public override void ReadXml(XmlDictionaryReader reader, SamlSerializer samlSerializer, SecurityTokenSerializer keyInfoSerializer, SecurityTokenResolver outOfBandTokenResolver) { if (reader == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("reader")); } if (samlSerializer == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("samlSerializer")); } #pragma warning suppress 56506 // samlSerializer.DictionaryManager is never null. SamlDictionary dictionary = samlSerializer.DictionaryManager.SamlDictionary; reader.MoveToContent(); reader.Read(); if (reader.IsStartElement(dictionary.Subject, dictionary.Namespace)) { SamlSubject subject = new SamlSubject(); subject.ReadXml(reader, samlSerializer, keyInfoSerializer, outOfBandTokenResolver); base.SamlSubject = subject; } else { // SAML Subject is a required Attribute Statement clause. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.SAMLAttributeStatementMissingSubjectOnRead))); } while (reader.IsStartElement()) { if (reader.IsStartElement(dictionary.Attribute, dictionary.Namespace)) { // SAML Attribute is a extensibility point. So ask the SAML serializer // to load this part. SamlAttribute attribute = samlSerializer.LoadAttribute(reader, keyInfoSerializer, outOfBandTokenResolver); if (attribute == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.SAMLUnableToLoadAttribute))); } this.attributes.Add(attribute); } else { break; } } if (this.attributes.Count == 0) { // Each Attribute statement should have at least one attribute. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.SAMLAttributeStatementMissingAttributeOnRead))); } reader.MoveToContent(); reader.ReadEndElement(); }
public void WriteXml1 () { SamlAttribute attr = new SamlAttribute (Claim.CreateNameClaim ("myname")); SamlSubject subject = new SamlSubject ( SamlConstants.UserNameNamespace, "urn:myqualifier", "myname"); SamlAttributeStatement s = new SamlAttributeStatement ( subject, new SamlAttribute [] {attr}); StringWriter sw = new StringWriter (); using (XmlDictionaryWriter dw = CreateWriter (sw)) { s.WriteXml (dw, new SamlSerializer (), null); } Assert.AreEqual (String.Format ("<?xml version=\"1.0\" encoding=\"utf-16\"?><saml:AttributeStatement xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\"><saml:Subject><saml:NameIdentifier Format=\"urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName\" NameQualifier=\"urn:myqualifier\">myname</saml:NameIdentifier></saml:Subject><saml:Attribute AttributeName=\"name\" AttributeNamespace=\"{0}\"><saml:AttributeValue>myname</saml:AttributeValue></saml:Attribute></saml:AttributeStatement>", "http://schemas.xmlsoap.org/ws/2005/05/identity/claims"), sw.ToString ()); }
public AttributeKey(SamlAttribute attribute) { if (attribute == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("attribute"); } _friendlyName = String.Empty; _name = attribute.Name; _nameFormat = String.Empty; _namespace = attribute.Namespace ?? String.Empty; _valueType = attribute.AttributeValueXsiType ?? String.Empty; _originalIssuer = attribute.OriginalIssuer ?? String.Empty; ComputeHashCode(); }
public override void ReadXml(XmlDictionaryReader reader, SamlSerializer samlSerializer, SecurityTokenSerializer keyInfoSerializer, SecurityTokenResolver outOfBandTokenResolver) { if (reader == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("reader")); } if (samlSerializer == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("samlSerializer")); } SamlDictionary samlDictionary = samlSerializer.DictionaryManager.SamlDictionary; reader.MoveToContent(); reader.Read(); if (!reader.IsStartElement(samlDictionary.Subject, samlDictionary.Namespace)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(System.IdentityModel.SR.GetString("SAMLAttributeStatementMissingSubjectOnRead"))); } SamlSubject subject = new SamlSubject(); subject.ReadXml(reader, samlSerializer, keyInfoSerializer, outOfBandTokenResolver); base.SamlSubject = subject; while (reader.IsStartElement()) { if (!reader.IsStartElement(samlDictionary.Attribute, samlDictionary.Namespace)) { break; } SamlAttribute item = samlSerializer.LoadAttribute(reader, keyInfoSerializer, outOfBandTokenResolver); if (item == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(System.IdentityModel.SR.GetString("SAMLUnableToLoadAttribute"))); } this.attributes.Add(item); } if (this.attributes.Count == 0) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(System.IdentityModel.SR.GetString("SAMLAttributeStatementMissingAttributeOnRead"))); } reader.MoveToContent(); reader.ReadEndElement(); }
SamlSecurityToken GetSamlToken () { SamlAssertion a = new SamlAssertion (); SamlSubject subject = new SamlSubject ( SamlConstants.UserNameNamespace, "urn:myqualifier", "myname"); SamlAttribute attr = new SamlAttribute (Claim.CreateNameClaim ("myname")); SamlAttributeStatement statement = new SamlAttributeStatement (subject, new SamlAttribute [] {attr}); a.Statements.Add (statement); a.Issuer = "my_hero"; X509Certificate2 cert = new X509Certificate2 ("Test/Resources/test.pfx", "mono"); X509AsymmetricSecurityKey key = new X509AsymmetricSecurityKey (cert); a.SigningCredentials = new SigningCredentials (key, SecurityAlgorithms.HmacSha1Signature, SecurityAlgorithms.Sha256Digest); XmlDocument doc = new XmlDocument (); XmlWriter w = doc.CreateNavigator ().AppendChild (); using (XmlDictionaryWriter dw = XmlDictionaryWriter.CreateDictionaryWriter (w)) { a.WriteXml (dw, new SamlSerializer (), new MySecurityTokenSerializer ()); } Console.Error.WriteLine (doc.OuterXml); return new SamlSecurityToken (a); }
public void ConstructorNullSubject () { SamlAttribute attr = new SamlAttribute (Claim.CreateNameClaim ("myname")); new SamlAttributeStatement (null, new SamlAttribute [] {attr}); }
/// <summary> /// Generates a SamlAttribute from a claim. /// </summary> /// <param name="claim">Claim from which to generate a SamlAttribute.</param> /// <param name="tokenDescriptor">Contains all the information that is used in token issuance.</param> /// <returns>The SamlAttribute.</returns> /// <exception cref="ArgumentNullException">The parameter 'claim' is null.</exception> protected virtual SamlAttribute CreateAttribute(Claim claim, SecurityTokenDescriptor tokenDescriptor) { if (claim == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("claim"); } int lastSlashIndex = claim.Type.LastIndexOf('/'); string attributeNamespace = null; string attributeName = null; if ((lastSlashIndex == 0) || (lastSlashIndex == -1)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("claimType", SR.GetString(SR.ID4216, claim.Type)); } else if (lastSlashIndex == claim.Type.Length - 1) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("claimType", SR.GetString(SR.ID4216, claim.Type)); } else { attributeNamespace = claim.Type.Substring(0, lastSlashIndex); // // The WCF SamlAttribute requires that the attributeNamespace and attributeName are both non-null and non-empty. // Furthermore, on deserialization / construction it considers the claimType associated with the SamlAttribute to be attributeNamespace + "/" + attributeName. // // IDFX extends the WCF SamlAttribute and hence has to work with an attributeNamespace and attributeName that are both non-null and non-empty. // On serialization, we identify the last slash in the claimtype, and treat everything before the slash as the attributeNamespace and everything after the slash as the attributeName. // On deserialization, we don't always insert a "/" between the attributeNamespace and attributeName (like WCF does); we only do so if the attributeNamespace doesn't have a trailing slash. // // Send Receive Behavior // ============================= // WCF WCF Works as expected // // WCF IDFX In the common case (http://www.claimtypes.com/foo), WCF will not send a trailing slash in the attributeNamespace. IDFX will add one upon deserialization. // In the edge case (http://www.claimtypes.com//foo), WCF will send a trailing slash in the attributeNamespace. IDFX will not add one upon deserialization. // // IDFX WCF In the common case (http://www.claimtypes.com/foo), IDFX will not send a trailing slash. WCF will add one upon deserialization. // In the edge case (http://www.claimtypes.com//foo), IDFX will throw (which is what the fix for FIP 6301 is about). // // IDFX IDFX Works as expected // if (attributeNamespace.EndsWith("/", StringComparison.Ordinal)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("claim", SR.GetString(SR.ID4213, claim.Type)); } attributeName = claim.Type.Substring(lastSlashIndex + 1, claim.Type.Length - (lastSlashIndex + 1)); } SamlAttribute attribute = new SamlAttribute(attributeNamespace, attributeName, new string[] { claim.Value }); if (!StringComparer.Ordinal.Equals(ClaimsIdentity.DefaultIssuer, claim.OriginalIssuer)) { attribute.OriginalIssuer = claim.OriginalIssuer; } attribute.AttributeValueXsiType = claim.ValueType; return attribute; }
/// <summary> /// Writes the saml:Attribute value. /// </summary> /// <param name="writer">XmlWriter to which to write.</param> /// <param name="value">Attribute value to be written.</param> /// <param name="attribute">The SAML attribute whose value is being written.</param> /// <remarks>By default the method writes the value as a string.</remarks> /// <exception cref="ArgumentNullException">The input parameter 'writer' is null.</exception> protected virtual void WriteAttributeValue(XmlWriter writer, string value, SamlAttribute attribute) { if (writer == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer"); } writer.WriteString(value); }
/// <summary> /// Serializes a given SamlAttribute. /// </summary> /// <param name="writer">XmlWriter to serialize the SamlAttribute.</param> /// <param name="attribute">SamlAttribute to be serialized.</param> /// <exception cref="ArgumentNullException">The input parameter 'writer' or 'attribute' is null.</exception> protected virtual void WriteAttribute(XmlWriter writer, SamlAttribute attribute) { if (writer == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writer"); } if (attribute == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("attribute"); } writer.WriteStartElement(SamlConstants.Prefix, SamlConstants.ElementNames.Attribute, SamlConstants.Namespace); writer.WriteAttributeString(SamlConstants.AttributeNames.AttributeName, null, attribute.Name); writer.WriteAttributeString(SamlConstants.AttributeNames.AttributeNamespace, null, attribute.Namespace); SamlAttribute SamlAttribute = attribute as SamlAttribute; if ((SamlAttribute != null) && (SamlAttribute.OriginalIssuer != null)) { writer.WriteAttributeString(SamlConstants.AttributeNames.OriginalIssuer, ClaimType2009Namespace, SamlAttribute.OriginalIssuer); } string xsiTypePrefix = null; string xsiTypeSuffix = null; if (SamlAttribute != null && !StringComparer.Ordinal.Equals(SamlAttribute.AttributeValueXsiType, ClaimValueTypes.String)) { // ClaimValueTypes are URIs of the form prefix#suffix, while xsi:type should be a QName. // Hence, the tokens-to-claims spec requires that ClaimValueTypes be serialized as xmlns:tn="prefix" xsi:type="tn:suffix" int indexOfHash = SamlAttribute.AttributeValueXsiType.IndexOf('#'); xsiTypePrefix = SamlAttribute.AttributeValueXsiType.Substring(0, indexOfHash); xsiTypeSuffix = SamlAttribute.AttributeValueXsiType.Substring(indexOfHash + 1); } for (int i = 0; i < attribute.AttributeValues.Count; i++) { if (attribute.AttributeValues[i] == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.ID4096))); } writer.WriteStartElement(SamlConstants.Prefix, SamlConstants.ElementNames.AttributeValue, SamlConstants.Namespace); if ((xsiTypePrefix != null) && (xsiTypeSuffix != null)) { writer.WriteAttributeString("xmlns", ProductConstants.ClaimValueTypeSerializationPrefix, null, xsiTypePrefix); writer.WriteAttributeString("type", XmlSchema.InstanceNamespace, String.Concat(ProductConstants.ClaimValueTypeSerializationPrefixWithColon, xsiTypeSuffix)); } WriteAttributeValue(writer, attribute.AttributeValues[i], attribute); writer.WriteEndElement(); } writer.WriteEndElement(); }
/// <summary> /// Reads an attribute value. /// </summary> /// <param name="reader">XmlReader to read from.</param> /// <param name="attribute">The current attribute that is being read.</param> /// <returns>The attribute value as a string.</returns> /// <exception cref="ArgumentNullException">The input parameter 'reader' is null.</exception> protected virtual string ReadAttributeValue(XmlReader reader, SamlAttribute attribute) { // This code was designed realizing that the writter of the xml controls how our // reader will report the NodeType. A completely differnet system (IBM, etc) could write the values. // Considering NodeType is important, because we need to read the entire value, end element and not loose anything significant. // // Couple of cases to help understand the design choices. // // 1. // "<MyElement xmlns=""urn:mynamespace""><another>complex</another></MyElement><sibling>value</sibling>" // Could result in the our reader reporting the NodeType as Text OR Element, depending if '<' was entitized to '<' // // 2. // " <MyElement xmlns=""urn:mynamespace""><another>complex</another></MyElement><sibling>value</sibling>" // Could result in the our reader reporting the NodeType as Text OR Whitespace. Post Whitespace processing, the NodeType could be // reported as Text or Element, depending if '<' was entitized to '<' // // 3. // "/r/n/t " // Could result in the our reader reporting the NodeType as whitespace. // // Since an AttributeValue with ONLY Whitespace and a complex Element proceeded by whitespace are reported as the same NodeType (2. and 3.) // the whitespace is remembered and discarded if an found is found, otherwise it becomes the value. This is to help users who accidently put a space when adding claims in ADFS // If we just skipped the Whitespace, then an AttributeValue that started with Whitespace would loose that part and claims generated from the AttributeValue // would be missing that part. // if (reader == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader"); } string result = String.Empty; string whiteSpace = String.Empty; reader.ReadStartElement(Saml2Constants.Elements.AttributeValue, SamlConstants.Namespace); while (reader.NodeType == XmlNodeType.Whitespace) { whiteSpace += reader.Value; reader.Read(); } reader.MoveToContent(); if (reader.NodeType == XmlNodeType.Element) { while (reader.NodeType == XmlNodeType.Element) { result += reader.ReadOuterXml(); reader.MoveToContent(); } } else { result = whiteSpace; result += reader.ReadContentAsString(); } reader.ReadEndElement(); return result; }
/// <summary> /// Read an saml:Attribute element. /// </summary> /// <param name="reader">XmlReader positioned at a saml:Attribute element.</param> /// <returns>SamlAttribute</returns> /// <exception cref="ArgumentNullException">The input parameter 'reader' is null.</exception> /// <exception cref="XmlException">The XmlReader is not positioned on a valid saml:Attribute element.</exception> protected virtual SamlAttribute ReadAttribute(XmlReader reader) { if (reader == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader"); } SamlAttribute attribute = new SamlAttribute(); attribute.Name = reader.GetAttribute(SamlConstants.AttributeNames.AttributeName, null); if (string.IsNullOrEmpty(attribute.Name)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4094))); } attribute.Namespace = reader.GetAttribute(SamlConstants.AttributeNames.AttributeNamespace, null); if (string.IsNullOrEmpty(attribute.Namespace)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4095))); } // // OriginalIssuer is an optional attribute. // We are lax on read here, and will accept the following namespaces for original issuer, in order: // http://schemas.xmlsoap.org/ws/2009/09/identity/claims // http://schemas.microsoft.com/ws/2008/06/identity // string originalIssuer = reader.GetAttribute(SamlConstants.AttributeNames.OriginalIssuer, ClaimType2009Namespace); if (originalIssuer == null) { originalIssuer = reader.GetAttribute(SamlConstants.AttributeNames.OriginalIssuer, ProductConstants.NamespaceUri); } if (originalIssuer == String.Empty) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4252))); } attribute.OriginalIssuer = originalIssuer; reader.MoveToContent(); reader.Read(); while (reader.IsStartElement(SamlConstants.ElementNames.AttributeValue, SamlConstants.Namespace)) { // FIP 9570 - ENTERPRISE SCENARIO: Saml11SecurityTokenHandler.ReadAttribute is not checking the AttributeValue XSI type correctly. // Lax on receive. If we dont find the AttributeValueXsiType in the format we are looking for in the xml, we default to string. // Read the xsi:type. We are expecting a value of the form "some-non-empty-string" or "some-non-empty-local-prefix:some-non-empty-string". // ":some-non-empty-string" and "some-non-empty-string:" are edge-cases where defaulting to string is reasonable. string attributeValueXsiTypePrefix = null; string attributeValueXsiTypeSuffix = null; string attributeValueXsiTypeSuffixWithLocalPrefix = reader.GetAttribute("type", XmlSchema.InstanceNamespace); if (!string.IsNullOrEmpty(attributeValueXsiTypeSuffixWithLocalPrefix)) { if (attributeValueXsiTypeSuffixWithLocalPrefix.IndexOf(":", StringComparison.Ordinal) == -1) // "some-non-empty-string" case { attributeValueXsiTypePrefix = reader.LookupNamespace(String.Empty); attributeValueXsiTypeSuffix = attributeValueXsiTypeSuffixWithLocalPrefix; } else if (attributeValueXsiTypeSuffixWithLocalPrefix.IndexOf(":", StringComparison.Ordinal) > 0 && attributeValueXsiTypeSuffixWithLocalPrefix.IndexOf(":", StringComparison.Ordinal) < attributeValueXsiTypeSuffixWithLocalPrefix.Length - 1) // "some-non-empty-local-prefix:some-non-empty-string" case { string localPrefix = attributeValueXsiTypeSuffixWithLocalPrefix.Substring(0, attributeValueXsiTypeSuffixWithLocalPrefix.IndexOf(":", StringComparison.Ordinal)); attributeValueXsiTypePrefix = reader.LookupNamespace(localPrefix); // For attributeValueXsiTypeSuffix, we want the portion after the local prefix in "some-non-empty-local-prefix:some-non-empty-string" attributeValueXsiTypeSuffix = attributeValueXsiTypeSuffixWithLocalPrefix.Substring(attributeValueXsiTypeSuffixWithLocalPrefix.IndexOf(":", StringComparison.Ordinal) + 1); } } if (attributeValueXsiTypePrefix != null && attributeValueXsiTypeSuffix != null) { attribute.AttributeValueXsiType = String.Concat(attributeValueXsiTypePrefix, "#", attributeValueXsiTypeSuffix); } if (reader.IsEmptyElement) { reader.Read(); attribute.AttributeValues.Add(string.Empty); } else { attribute.AttributeValues.Add(ReadAttributeValue(reader, attribute)); } } if (attribute.AttributeValues.Count == 0) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.GetString(SR.ID4212))); } reader.MoveToContent(); reader.ReadEndElement(); return attribute; }
/// <summary> /// This method gets called when a special type of SamlAttribute is detected. The SamlAttribute passed in wraps a SamlAttribute /// that contains a collection of AttributeValues, each of which are mapped to a claim. All of the claims will be returned /// in an ClaimsIdentity with the specified issuer. /// </summary> /// <param name="attribute">The SamlAttribute to be processed.</param> /// <param name="subject">The identity that should be modified to reflect the SamlAttribute.</param> /// <param name="issuer">Issuer Identity.</param> /// <exception cref="InvalidOperationException">Will be thrown if the SamlAttribute does not contain any valid SamlAttributeValues.</exception> protected virtual void SetDelegateFromAttribute(SamlAttribute attribute, ClaimsIdentity subject, string issuer) { // bail here nothing to add. if (subject == null || attribute == null || attribute.AttributeValues == null || attribute.AttributeValues.Count < 1) { return; } Collection<Claim> claims = new Collection<Claim>(); SamlAttribute actingAsAttribute = null; foreach (string attributeValue in attribute.AttributeValues) { if (attributeValue != null && attributeValue.Length > 0) { using (XmlDictionaryReader xmlReader = XmlDictionaryReader.CreateTextReader(Encoding.UTF8.GetBytes(attributeValue), XmlDictionaryReaderQuotas.Max)) { xmlReader.MoveToContent(); xmlReader.ReadStartElement(Actor); while (xmlReader.IsStartElement(Attribute)) { SamlAttribute innerAttribute = ReadAttribute(xmlReader); if (innerAttribute != null) { string claimType = string.IsNullOrEmpty(innerAttribute.Namespace) ? innerAttribute.Name : innerAttribute.Namespace + "/" + innerAttribute.Name; if (claimType == ClaimTypes.Actor) { // In this case we have two delegates acting as an identity, we do not allow this if (actingAsAttribute != null) { throw DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4034)); } actingAsAttribute = innerAttribute; } else { string claimValueType = ClaimValueTypes.String; string originalIssuer = null; SamlAttribute SamlAttribute = innerAttribute as SamlAttribute; if (SamlAttribute != null) { claimValueType = SamlAttribute.AttributeValueXsiType; originalIssuer = SamlAttribute.OriginalIssuer; } for (int k = 0; k < innerAttribute.AttributeValues.Count; ++k) { Claim claim = null; if (string.IsNullOrEmpty(originalIssuer)) { claim = new Claim(claimType, innerAttribute.AttributeValues[k], claimValueType, issuer); } else { claim = new Claim(claimType, innerAttribute.AttributeValues[k], claimValueType, issuer, originalIssuer); } claims.Add(claim); } } } } xmlReader.ReadEndElement(); // Actor } } } subject.Actor = new ClaimsIdentity(claims, AuthenticationTypes.Federation); SetDelegateFromAttribute(actingAsAttribute, subject.Actor, issuer); }
public virtual SamlAttribute LoadAttribute(XmlDictionaryReader reader, SecurityTokenSerializer keyInfoSerializer, SecurityTokenResolver outOfBandTokenResolver) { // We will load all attributes as string values. SamlAttribute attribute = new SamlAttribute(); attribute.ReadXml(reader, this, keyInfoSerializer, outOfBandTokenResolver); return attribute; }
public void WriteXmlValid () { SamlAssertion a = new SamlAssertion (); SamlSubject subject = new SamlSubject ( SamlConstants.UserNameNamespace, "urn:myqualifier", "myname"); SamlAttribute attr = new SamlAttribute (Claim.CreateNameClaim ("myname")); SamlAttributeStatement statement = new SamlAttributeStatement (subject, new SamlAttribute [] {attr}); a.Advice = new SamlAdvice (new string [] {"urn:testadvice1"}); DateTime notBefore = DateTime.SpecifyKind (new DateTime (2000, 1, 1), DateTimeKind.Utc); DateTime notOnAfter = DateTime.SpecifyKind (new DateTime (2006, 1, 1), DateTimeKind.Utc); a.Conditions = new SamlConditions (notBefore, notOnAfter); a.Statements.Add (statement); a.Issuer = "my_hero"; StringWriter sw = new StringWriter (); string id = a.AssertionId; DateTime instant = a.IssueInstant; using (XmlDictionaryWriter dw = CreateWriter (sw)) { a.WriteXml (dw, new SamlSerializer (), null); } string expected = String.Format ("<?xml version=\"1.0\" encoding=\"utf-16\"?><saml:Assertion MajorVersion=\"1\" MinorVersion=\"1\" AssertionID=\"{0}\" Issuer=\"my_hero\" IssueInstant=\"{1}\" xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\"><saml:Conditions NotBefore=\"{3}\" NotOnOrAfter=\"{4}\" /><saml:Advice><saml:AssertionIDReference>urn:testadvice1</saml:AssertionIDReference></saml:Advice><saml:AttributeStatement><saml:Subject><saml:NameIdentifier Format=\"urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName\" NameQualifier=\"urn:myqualifier\">myname</saml:NameIdentifier></saml:Subject><saml:Attribute AttributeName=\"name\" AttributeNamespace=\"{2}\"><saml:AttributeValue>myname</saml:AttributeValue></saml:Attribute></saml:AttributeStatement></saml:Assertion>", id, instant.ToString ("yyyy-MM-ddTHH:mm:ss.fff'Z'", CultureInfo.InvariantCulture), "http://schemas.xmlsoap.org/ws/2005/05/identity/claims", notBefore.ToString ("yyyy-MM-ddTHH:mm:ss.fff'Z'", CultureInfo.InvariantCulture), notOnAfter.ToString ("yyyy-MM-ddTHH:mm:ss.fff'Z'", CultureInfo.InvariantCulture)); Assert.AreEqual (expected, sw.ToString ()); }