XML as a SAML Attribute Value

Most SAML attribute values are simple string values. For example:

<saml:Attribute Name=“support-email”>
saml:AttributeValuesupport@componentspace.com</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name=“sales-email”>
saml:AttributeValuesales@componentspace.com</saml:AttributeValue>
</saml:Attribute>



However, it’s also possible to specify XML as the SAML attribute value. For example:

<saml:Attribute Name=“email-addresses”>
saml:AttributeValue
support@componentspace.com
sales@componentspace.com
</saml:AttributeValue>
</saml:Attribute>



The following code calls the SAML high-level API and includes an attribute whose value is XML.

var attributes = new Dictionary<string, string>();

var xmlDocument = new XmlDocument();
xmlDocument.PreserveWhitespace = true;
xmlDocument.LoadXml(“support@componentspace.comsales@componentspace.com”);

attributes[“email-addresses”] = xmlDocument.DocumentElement.FirstChild.OuterXml;

SAMLIdentityProvider.SendSSO(Response, userName, attributes);



The equivalent code using the SAML low-level API is:


var attributeStatement = new AttributeStatement();
SAMLAttribute.RegisterAttributeValueSerializer(“email-addresses”, null, new XmlAttributeValueSerializer());
var samlAttribute = new SAMLAttribute(“email-addresses”, null, null);

var xmlDocument = new XmlDocument();
xmlDocument.PreserveWhitespace = true;
xmlDocument.LoadXml(“support@componentspace.comsales@componentspace.com”);

samlAttribute.Values.Add(new AttributeValue(xmlDocument.DocumentElement.FirstChild));
attributeStatement.Attributes.Add(samlAttribute);


[quote]
ComponentSpace - 9/30/2016
I want to generate saml as below and I tried code for such requirement-I want such list structure to generate as mentioned below in SAMl I want) as mentioned in earlier post from your Forum--https://www.componentspace.com/Forums/5463/XML-as-a-SAML-Attribute-Value


<saml:Assertion Version="2.0" ID="12345" IssueInstant="2018-04-28T17:10:44Z"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">



Employee1
Department1


Employee2
Department2





1. For this I am generating list in my custom object first from data.
I have class (Custom)as below
public class SAMLModuleAttribute

{
public SAMLModuleAttribute()
{

}
public SAMLModuleAttribute(string attributename, string friendlyname, string attributevalue)
{
this.AttributeName = attributename;
this.FriendlyName = friendlyname;
this.AttributeValue = attributevalue;
}

public string AttributeName { get; set; }

public string FriendlyName { get; set; }

public string AttributeValue { get; set; }

public List AttributeValues { get; set; }
}

Then I am using it to generate below Object structure

var lstAttributes =List();
var samlAttribute = new SAMLModuleAttribute("Employees", "","");
samlAttribute.AttributeValues = new List();

foreach (var employee in employeeList)
{
samlAttribute.AttributeValues.Add(new SAMLModuleAttributeValue
{
Name = "Employee",
Value = "Employee1"
});
samlAttribute.AttributeValues.Add(new SAMLModuleAttributeValue
{
Name = "Department",
Value = "Department1"
});
}
lstAttributes.Add(samlAttribute);

2. Then I am passing it to first fill it in Saml low lecvel api of componenet space using AttributeStatement

AttributeStatement attStatement = new AttributeStatement();
CreateMultiValueSAMLAttributes(lstAttributes,attStatement);

private void CreateMultiValueSAMLAttributes(List lstAttributes, AttributeStatement attStatement)
SAMLAttribute.RegisterAttributeValueSerializer("Employees", null, new XmlAttributeValueSerializer());
foreach (SAMLModuleAttribute attb in lstAttributes)
{

SAMLAttribute attrib = new SAMLAttribute(attb.AttributeName, SAMLIdentifiers.AttributeNameFormats.Basic,
attb.FriendlyName, attb.AttributeValue);

if (attb.AttributeValues != null && attb.AttributeValues.Count > 0)
{
XmlDocument doc = new XmlDocument();
XmlDeclaration xmlDeclaration = doc.CreateXmlDeclaration("1.0", "UTF-8", null);
XmlElement root = doc.DocumentElement;
doc.InsertBefore(xmlDeclaration, root);

XmlElement employeeElement = doc.CreateElement(string.Empty, "Employees", string.Empty);
doc.AppendChild(employeeElement);

foreach (var attrinuteValue in attb.AttributeValues)
{
XmlElement attributeValueNode = doc.CreateElement(string.Empty, attrinuteValue.Name, string.Empty);
XmlText attributeValue = doc.CreateTextNode(attrinuteValue.Value);
attributeValueNode.AppendChild(attributeValue);

accountElement.AppendChild(attributeValueNode);

///The Above code will add Xml Strucure like when loop completes Employee1 Department1 Employee2 Department2
// inside parent node

XmlElement employeeNode = doc.CreateElement(string.Empty, "Employee", string.Empty);
XmlText attributeValue = doc.CreateTextNode("Employee1";
employeeNode.AppendChild(attributeValue);


XmlElement deptNode = doc.CreateElement(string.Empty, "Department", string.Empty);
XmlText attributeValue = doc.CreateTextNode("Department1";
deptNode.AppendChild(attributeValue);
}

var xmlDocument = new XmlDocument();
xmldocument.PreserveWhitespace = true;
xmldocument.LoadXml(doc.DocumentElement.OuterXml);

attrib.Values.Add(new AttributeValue(xmldocument.DocumentElement));

}
attStatement.Attributes.Add(attrib);
}
}

And finally I am using To Xml on SamlAssertion to get Saml string from it
SAMLAssertion samlAssertion = new SAMLAssertion();
--code to fill Saml Assertion

samlAssertion.Statements.Add(attStatement);

string samlString= samlAssertion.ToXml().OuterXml.ToString();

But above line generates saml as below

Actual SAMl I am getting

<saml:Assertion Version="2.0" ID="12345" IssueInstant="2018-04-28T17:10:44Z"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">



System.Xml.XmlElement




Instead of below which is expected

<saml:Assertion Version="2.0" ID="12345" IssueInstant="2018-04-28T17:10:44Z"
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">



Employee1
Department1


Employee2
Department2






It seems it cannot serlize it properly the InnerXml under parent EMployees node and genartes incorrect xml as System.Xml.XmlElement

Can you please help to resolve this.

The following code generates the required XML.
For brevity, it loads the attribute values XML from strings but you can construct these however you wish.


// Employees attribute values are to be serialized as XML.
SAMLAttribute.RegisterAttributeValueSerializer(“Employees”, null, new XmlAttributeValueSerializer());

// Load some test employee records.
var xmlDocument = new XmlDocument()
{
PreserveWhitespace = true
};

xmlDocument.LoadXml(“Employee1Department1”);
var employee1 = xmlDocument.DocumentElement.ChildNodes;

xmlDocument.LoadXml(“Employee2Department2”);
var employee2 = xmlDocument.DocumentElement.ChildNodes;

// Create the example SAML assertion.
var samlAssertion = new SAMLAssertion();
samlAssertion.Statements.Add(new AttributeStatement()
{
Attributes = new ArrayList()
{
new SAMLAttribute()
{
Name = “Employees”,
FriendlyName = “Employees”,
NameFormat = SAMLIdentifiers.AttributeNameFormats.Basic,
Values = new List()
{
new AttributeValue()
{
Data = employee1
},
new AttributeValue()
{
Data = employee2
}
}
}
}
});

// Serialize the SAMl assertion to an XML string.
var xmlText = samlAssertion.ToString();


[quote]
ComponentSpace - 4/28/2018
The following code generates the required XML.
For brevity, it loads the attribute values XML from strings but you can construct these however you wish.


// Employees attribute values are to be serialized as XML.
SAMLAttribute.RegisterAttributeValueSerializer("Employees", null, new XmlAttributeValueSerializer());

// Load some test employee records.
var xmlDocument = new XmlDocument()
{
PreserveWhitespace = true
};

xmldocument.LoadXml("Employee1Department1");
var employee1 = xmldocument.DocumentElement.ChildNodes;

xmldocument.LoadXml("Employee2Department2");
var employee2 = xmldocument.DocumentElement.ChildNodes;

// Create the example SAML assertion.
var samlAssertion = new SAMLAssertion();
samlAssertion.Statements.Add(new AttributeStatement()
{
Attributes = new ArrayList()
{
new SAMLAttribute()
{
Name = "Employees",
FriendlyName = "Employees",
NameFormat = SAMLIdentifiers.AttributeNameFormats.Basic,
Values = new List()
{
new AttributeValue()
{
Data = employee1
},
new AttributeValue()
{
Data = employee2
}
}
}
}
});

// Serialize the SAMl assertion to an XML string.
var xmlText = samlAssertion.ToString();


[/quote]

Hi Mitchell ,

I tried it but the last line var xmlText = samlAssertion.ToString(); gives me output ComponentSpace.SAML2.Assertions.SAMLAssertion.
And when I tried samlAssertion.ToXml() or samlAssertion.ToXml().OuterXml it gives below exception during Serlization

"Unable to cast object of type 'System.Xml.XmlChildNodes' to type 'System.Xml.XmlElement'."}System.Exception {System.InvalidCastException
'samlAssertion.ToXml()' threw an exception of type 'ComponentSpace.SAML2.SAMLSerializationException'

It seems it cannot serilize it properly.
If you run the code you shared you will get the above output i.e.
xmlText = samlAssertion.ToString(); gives me output ComponentSpace.SAML2.Assertions.SAMLAssertion.
And
xmlText = samlAssertion.ToXml(); gives exception
"Unable to cast object of type 'System.Xml.XmlChildNodes' to type 'System.Xml.XmlElement'."}System.Exception {System.InvalidCastException

Please help me to resolve this.

Thanks,
Amol




What version of the product are you using?
https://www.componentspace.com/Forums/31/Determining-the-Component-Version-and-License

[quote]
ComponentSpace - 4/29/2018
[/quote]

I am using 2.1.0.4

That version is almost 10 years old.
You should upgrade to pick up the latest functionality.
https://www.componentspace.com/documentation/saml-for-asp-net/ComponentSpace%20SAML%20for%20ASP.NET%20Release%20Notes.pdf