File: System\ServiceModel\Security\SctClaimSerializer.cs
Project: ndp\cdf\src\WCF\ServiceModel\System.ServiceModel.csproj (System.ServiceModel)
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------------------------
 
namespace System.ServiceModel.Security
{
    using System;
    using System.ServiceModel;
    using System.Security.Cryptography;
    using System.Security.Cryptography.X509Certificates;
    using System.ServiceModel.Security.Tokens;
    using System.IO;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Net.Mail;
    using System.Xml;
    using System.Runtime.Serialization;
    using System.IdentityModel.Claims;
    using System.IdentityModel.Policy;
    using System.Security.Principal;
 
    static class SctClaimSerializer
    {
        static void SerializeSid(SecurityIdentifier sid, SctClaimDictionary dictionary, XmlDictionaryWriter writer)
        {
            byte[] sidBytes = new byte[sid.BinaryLength];
            sid.GetBinaryForm(sidBytes, 0);
            writer.WriteBase64(sidBytes, 0, sidBytes.Length);
        }
 
        static void WriteRightAttribute(Claim claim, SctClaimDictionary dictionary, XmlDictionaryWriter writer)
        {
            if (Rights.PossessProperty.Equals(claim.Right))
                return;
            writer.WriteAttributeString(dictionary.Right, dictionary.EmptyString, claim.Right);
        }
 
        static string ReadRightAttribute(XmlDictionaryReader reader, SctClaimDictionary dictionary)
        {
            string right = reader.GetAttribute(dictionary.Right, dictionary.EmptyString);
            return String.IsNullOrEmpty(right) ? Rights.PossessProperty : right;
        }
 
        static void WriteSidAttribute(SecurityIdentifier sid, SctClaimDictionary dictionary, XmlDictionaryWriter writer)
        {
            byte[] sidBytes = new byte[sid.BinaryLength];
            sid.GetBinaryForm(sidBytes, 0);
            writer.WriteAttributeString(dictionary.Sid, dictionary.EmptyString, Convert.ToBase64String(sidBytes));
        }
 
        static SecurityIdentifier ReadSidAttribute(XmlDictionaryReader reader, SctClaimDictionary dictionary)
        {
            byte[] sidBytes = Convert.FromBase64String(reader.GetAttribute(dictionary.Sid, dictionary.EmptyString));
            return new SecurityIdentifier(sidBytes, 0);
        }
 
        public static void SerializeClaim(Claim claim, SctClaimDictionary dictionary, XmlDictionaryWriter writer, XmlObjectSerializer serializer)
        {
            // the order in which known claim types are checked is optimized for use patterns
            if (claim == null)
            {
                writer.WriteElementString(dictionary.NullValue, dictionary.EmptyString, string.Empty);
                return;
            }
            else if (ClaimTypes.Sid.Equals(claim.ClaimType))
            {
                writer.WriteStartElement(dictionary.WindowsSidClaim, dictionary.EmptyString);
                WriteRightAttribute(claim, dictionary, writer);
                SerializeSid((SecurityIdentifier)claim.Resource, dictionary, writer);
                writer.WriteEndElement();
                return;
            }
            else if (ClaimTypes.DenyOnlySid.Equals(claim.ClaimType))
            {
                writer.WriteStartElement(dictionary.DenyOnlySidClaim, dictionary.EmptyString);
                WriteRightAttribute(claim, dictionary, writer);
                SerializeSid((SecurityIdentifier)claim.Resource, dictionary, writer);
                writer.WriteEndElement();
                return;
            }
            else if (ClaimTypes.X500DistinguishedName.Equals(claim.ClaimType))
            {
                writer.WriteStartElement(dictionary.X500DistinguishedNameClaim, dictionary.EmptyString);
                WriteRightAttribute(claim, dictionary, writer);
                byte[] rawData = ((X500DistinguishedName)claim.Resource).RawData;
                writer.WriteBase64(rawData, 0, rawData.Length);
                writer.WriteEndElement();
                return;
            }
            else if (ClaimTypes.Thumbprint.Equals(claim.ClaimType))
            {
                writer.WriteStartElement(dictionary.X509ThumbprintClaim, dictionary.EmptyString);
                WriteRightAttribute(claim, dictionary, writer);
                byte[] thumbprint = (byte[])claim.Resource;
                writer.WriteBase64(thumbprint, 0, thumbprint.Length);
                writer.WriteEndElement();
                return;
            }
            else if (ClaimTypes.Name.Equals(claim.ClaimType))
            {
                writer.WriteStartElement(dictionary.NameClaim, dictionary.EmptyString);
                WriteRightAttribute(claim, dictionary, writer);
                writer.WriteString((string)claim.Resource);
                writer.WriteEndElement();
                return;
            }
            else if (ClaimTypes.Dns.Equals(claim.ClaimType))
            {
                writer.WriteStartElement(dictionary.DnsClaim, dictionary.EmptyString);
                WriteRightAttribute(claim, dictionary, writer);
                writer.WriteString((string)claim.Resource);
                writer.WriteEndElement();
                return;
            }
            else if (ClaimTypes.Rsa.Equals(claim.ClaimType))
            {
                writer.WriteStartElement(dictionary.RsaClaim, dictionary.EmptyString);
                WriteRightAttribute(claim, dictionary, writer);
                writer.WriteString(((RSA)claim.Resource).ToXmlString(false));
                writer.WriteEndElement();
                return;
            }
            else if (ClaimTypes.Email.Equals(claim.ClaimType))
            {
                writer.WriteStartElement(dictionary.MailAddressClaim, dictionary.EmptyString);
                WriteRightAttribute(claim, dictionary, writer);
                writer.WriteString(((MailAddress)claim.Resource).Address);
                writer.WriteEndElement();
                return;
            }
            else if (claim == Claim.System)
            {
                writer.WriteElementString(dictionary.SystemClaim, dictionary.EmptyString, string.Empty);
                return;
            }
            else if (ClaimTypes.Hash.Equals(claim.ClaimType))
            {
                writer.WriteStartElement(dictionary.HashClaim, dictionary.EmptyString);
                WriteRightAttribute(claim, dictionary, writer);
                byte[] hash = (byte[])claim.Resource;
                writer.WriteBase64(hash, 0, hash.Length);
                writer.WriteEndElement();
                return;
            }
            else if (ClaimTypes.Spn.Equals(claim.ClaimType))
            {
                writer.WriteStartElement(dictionary.SpnClaim, dictionary.EmptyString);
                WriteRightAttribute(claim, dictionary, writer);
                writer.WriteString((string)claim.Resource);
                writer.WriteEndElement();
                return;
            }
            else if (ClaimTypes.Upn.Equals(claim.ClaimType))
            {
                writer.WriteStartElement(dictionary.UpnClaim, dictionary.EmptyString);
                WriteRightAttribute(claim, dictionary, writer);
                writer.WriteString((string)claim.Resource);
                writer.WriteEndElement();
                return;
            }
            else if (ClaimTypes.Uri.Equals(claim.ClaimType))
            {
                writer.WriteStartElement(dictionary.UrlClaim, dictionary.EmptyString);
                WriteRightAttribute(claim, dictionary, writer);
                writer.WriteString(((Uri)claim.Resource).AbsoluteUri);
                writer.WriteEndElement();
                return;
            }
            else
            {
                // this is an extensible claim... need to delegate to xml object serializer
                serializer.WriteObject(writer, claim);
            }
        }
 
        public static void SerializeClaimSet(ClaimSet claimSet, SctClaimDictionary dictionary, XmlDictionaryWriter writer, XmlObjectSerializer serializer, XmlObjectSerializer claimSerializer)
        {
            if (claimSet is X509CertificateClaimSet)
            {
                X509CertificateClaimSet x509ClaimSet = (X509CertificateClaimSet)claimSet;
                writer.WriteStartElement(dictionary.X509CertificateClaimSet, dictionary.EmptyString);
                byte[] rawData = x509ClaimSet.X509Certificate.RawData;
                writer.WriteBase64(rawData, 0, rawData.Length);
                writer.WriteEndElement();
            }
            else if (claimSet == ClaimSet.System)
            {
                writer.WriteElementString(dictionary.SystemClaimSet, dictionary.EmptyString, String.Empty);
            }
            else if (claimSet == ClaimSet.Windows)
            {
                writer.WriteElementString(dictionary.WindowsClaimSet, dictionary.EmptyString, String.Empty);
            }
            else if (claimSet == ClaimSet.Anonymous)
            {
                writer.WriteElementString(dictionary.AnonymousClaimSet, dictionary.EmptyString, String.Empty);
            }
            else if (claimSet is WindowsClaimSet || claimSet is DefaultClaimSet)
            {
                writer.WriteStartElement(dictionary.ClaimSet, dictionary.EmptyString);
                writer.WriteStartElement(dictionary.PrimaryIssuer, dictionary.EmptyString);
                if (claimSet.Issuer == claimSet)
                {
                    writer.WriteElementString(dictionary.NullValue, dictionary.EmptyString, string.Empty);
                }
                else
                {
                    SerializeClaimSet(claimSet.Issuer, dictionary, writer, serializer, claimSerializer);
                }
                writer.WriteEndElement();
 
                foreach (Claim claim in claimSet)
                {
                    writer.WriteStartElement(dictionary.Claim, dictionary.EmptyString);
                    SerializeClaim(claim, dictionary, writer, claimSerializer);
                    writer.WriteEndElement();
                }
                writer.WriteEndElement();
            }
            else
            {
                serializer.WriteObject(writer, claimSet);
            }
        }
 
        public static Claim DeserializeClaim(XmlDictionaryReader reader, SctClaimDictionary dictionary, XmlObjectSerializer serializer)
        {
            if (reader.IsStartElement(dictionary.NullValue, dictionary.EmptyString))
            {
                reader.ReadElementString();
                return null;
            }
            else if (reader.IsStartElement(dictionary.WindowsSidClaim, dictionary.EmptyString))
            {
                string right = ReadRightAttribute(reader, dictionary);
                reader.ReadStartElement();
                byte[] sidBytes = reader.ReadContentAsBase64();
                reader.ReadEndElement();
                return new Claim(ClaimTypes.Sid, new SecurityIdentifier(sidBytes, 0), right);
            }
            else if (reader.IsStartElement(dictionary.DenyOnlySidClaim, dictionary.EmptyString))
            {
                string right = ReadRightAttribute(reader, dictionary);
                reader.ReadStartElement();
                byte[] sidBytes = reader.ReadContentAsBase64();
                reader.ReadEndElement();
                return new Claim(ClaimTypes.DenyOnlySid, new SecurityIdentifier(sidBytes, 0), right);
            }
            else if (reader.IsStartElement(dictionary.X500DistinguishedNameClaim, dictionary.EmptyString))
            {
                string right = ReadRightAttribute(reader, dictionary);
                reader.ReadStartElement();
                byte[] rawData = reader.ReadContentAsBase64();
                reader.ReadEndElement();
                return new Claim(ClaimTypes.X500DistinguishedName, new X500DistinguishedName(rawData), right);
            }
            else if (reader.IsStartElement(dictionary.X509ThumbprintClaim, dictionary.EmptyString))
            {
                string right = ReadRightAttribute(reader, dictionary);
                reader.ReadStartElement();
                byte[] thumbprint = reader.ReadContentAsBase64();
                reader.ReadEndElement();
                return new Claim(ClaimTypes.Thumbprint, thumbprint, right);
            }
            else if (reader.IsStartElement(dictionary.NameClaim, dictionary.EmptyString))
            {
                string right = ReadRightAttribute(reader, dictionary);
                reader.ReadStartElement();
                string name = reader.ReadString();
                reader.ReadEndElement();
                return new Claim(ClaimTypes.Name, name, right);
            }
            else if (reader.IsStartElement(dictionary.DnsClaim, dictionary.EmptyString))
            {
                string right = ReadRightAttribute(reader, dictionary);
                reader.ReadStartElement();
                string dns = reader.ReadString();
                reader.ReadEndElement();
                return new Claim(ClaimTypes.Dns, dns, right);
            }
            else if (reader.IsStartElement(dictionary.RsaClaim, dictionary.EmptyString))
            {
                string right = ReadRightAttribute(reader, dictionary);
                reader.ReadStartElement();
                string rsaXml = reader.ReadString();
                reader.ReadEndElement();
 
                System.Security.Cryptography.RSACryptoServiceProvider rsa = new System.Security.Cryptography.RSACryptoServiceProvider();
                rsa.FromXmlString(rsaXml);
                return new Claim(ClaimTypes.Rsa, rsa, right);
            }
            else if (reader.IsStartElement(dictionary.MailAddressClaim, dictionary.EmptyString))
            {
                string right = ReadRightAttribute(reader, dictionary);
                reader.ReadStartElement();
                string address = reader.ReadString();
                reader.ReadEndElement();
                return new Claim(ClaimTypes.Email, new System.Net.Mail.MailAddress(address), right);
            }
            else if (reader.IsStartElement(dictionary.SystemClaim, dictionary.EmptyString))
            {
                reader.ReadElementString();
                return Claim.System;
            }
            else if (reader.IsStartElement(dictionary.HashClaim, dictionary.EmptyString))
            {
                string right = ReadRightAttribute(reader, dictionary);
                reader.ReadStartElement();
                byte[] hash = reader.ReadContentAsBase64();
                reader.ReadEndElement();
                return new Claim(ClaimTypes.Hash, hash, right);
            }
            else if (reader.IsStartElement(dictionary.SpnClaim, dictionary.EmptyString))
            {
                string right = ReadRightAttribute(reader, dictionary);
                reader.ReadStartElement();
                string spn = reader.ReadString();
                reader.ReadEndElement();
                return new Claim(ClaimTypes.Spn, spn, right);
            }
            else if (reader.IsStartElement(dictionary.UpnClaim, dictionary.EmptyString))
            {
                string right = ReadRightAttribute(reader, dictionary);
                reader.ReadStartElement();
                string upn = reader.ReadString();
                reader.ReadEndElement();
                return new Claim(ClaimTypes.Upn, upn, right);
            }
            else if (reader.IsStartElement(dictionary.UrlClaim, dictionary.EmptyString))
            {
                string right = ReadRightAttribute(reader, dictionary);
                reader.ReadStartElement();
                string url = reader.ReadString();
                reader.ReadEndElement();
                return new Claim(ClaimTypes.Uri, new Uri(url), right);
            }
            else
            {
                return (Claim)serializer.ReadObject(reader);
            }
        }
 
        public static ClaimSet DeserializeClaimSet(XmlDictionaryReader reader, SctClaimDictionary dictionary, XmlObjectSerializer serializer, XmlObjectSerializer claimSerializer)
        {
            if (reader.IsStartElement(dictionary.NullValue, dictionary.EmptyString))
            {
                reader.ReadElementString();
                return null;
            }
            else if (reader.IsStartElement(dictionary.X509CertificateClaimSet, dictionary.EmptyString))
            {
                reader.ReadStartElement();
                byte[] rawData = reader.ReadContentAsBase64();
                reader.ReadEndElement();
                return new X509CertificateClaimSet(new X509Certificate2(rawData), false);
            }
            else if (reader.IsStartElement(dictionary.SystemClaimSet, dictionary.EmptyString))
            {
                reader.ReadElementString();
                return ClaimSet.System;
            }
            else if (reader.IsStartElement(dictionary.WindowsClaimSet, dictionary.EmptyString))
            {
                reader.ReadElementString();
                return ClaimSet.Windows;
            }
            else if (reader.IsStartElement(dictionary.AnonymousClaimSet, dictionary.EmptyString))
            {
                reader.ReadElementString();
                return ClaimSet.Anonymous;
            }
            else if (reader.IsStartElement(dictionary.ClaimSet, dictionary.EmptyString))
            {
                ClaimSet issuer = null;
                List<Claim> claims = new List<Claim>();
                reader.ReadStartElement();
 
                if (reader.IsStartElement(dictionary.PrimaryIssuer, dictionary.EmptyString))
                {
                    reader.ReadStartElement();
                    issuer = DeserializeClaimSet(reader, dictionary, serializer, claimSerializer);
                    reader.ReadEndElement();
                }
 
                while (reader.IsStartElement())
                {
                    reader.ReadStartElement();
                    claims.Add(DeserializeClaim(reader, dictionary, claimSerializer));
                    reader.ReadEndElement();
                }
 
                reader.ReadEndElement();
                return issuer != null ? new DefaultClaimSet(issuer, claims) : new DefaultClaimSet(claims);
            }
            else
            {
                return (ClaimSet)serializer.ReadObject(reader);
            }
        }
 
        public static void SerializeIdentities(AuthorizationContext authContext, SctClaimDictionary dictionary, XmlDictionaryWriter writer, XmlObjectSerializer serializer)
        {
            object obj;
            IList<IIdentity> identities;
            if (authContext.Properties.TryGetValue(SecurityUtils.Identities, out obj))
            {
                identities = obj as IList<IIdentity>;
                if (identities != null && identities.Count > 0)
                {
                    writer.WriteStartElement(dictionary.Identities, dictionary.EmptyString);
                    for (int i = 0; i < identities.Count; ++i)
                    {
                        SerializePrimaryIdentity(identities[i], dictionary, writer, serializer);
                    }
                    writer.WriteEndElement();
                }
            }
        }
 
        static void SerializePrimaryIdentity(IIdentity identity, SctClaimDictionary dictionary, XmlDictionaryWriter writer, XmlObjectSerializer serializer)
        {
            if (identity != null && identity != SecurityUtils.AnonymousIdentity)
            {
                writer.WriteStartElement(dictionary.PrimaryIdentity, dictionary.EmptyString);
                if (identity is WindowsIdentity)
                {
                    WindowsIdentity wid = (WindowsIdentity)identity;
                    writer.WriteStartElement(dictionary.WindowsSidIdentity, dictionary.EmptyString);
                    WriteSidAttribute(wid.User, dictionary, writer);
 
                    // This is to work around WOW64 bug Windows OS 1491447
                    string authenticationType = null;
                    using (WindowsIdentity self = WindowsIdentity.GetCurrent())
                    {
                        // is owner or admin?  AuthenticationType could throw un-authorized exception
                        if ((self.User == wid.Owner) || 
                            (wid.Owner != null && self.Groups.Contains(wid.Owner)) || 
                            (wid.Owner != SecurityUtils.AdministratorsSid && self.Groups.Contains(SecurityUtils.AdministratorsSid)))
                        {
                            authenticationType = wid.AuthenticationType;
                        }
                    }
                    if (!String.IsNullOrEmpty(authenticationType))
                        writer.WriteAttributeString(dictionary.AuthenticationType, dictionary.EmptyString, authenticationType);
                    writer.WriteString(wid.Name);
                    writer.WriteEndElement();
                }
                else if (identity is WindowsSidIdentity)
                {
                    WindowsSidIdentity wsid = (WindowsSidIdentity)identity;
                    writer.WriteStartElement(dictionary.WindowsSidIdentity, dictionary.EmptyString);
                    WriteSidAttribute(wsid.SecurityIdentifier, dictionary, writer);
                    if (!String.IsNullOrEmpty(wsid.AuthenticationType))
                        writer.WriteAttributeString(dictionary.AuthenticationType, dictionary.EmptyString, wsid.AuthenticationType);
                    writer.WriteString(wsid.Name);
                    writer.WriteEndElement();
                }
                else if (identity is GenericIdentity)
                {
                    GenericIdentity genericIdentity = (GenericIdentity)identity;
                    writer.WriteStartElement(dictionary.GenericIdentity, dictionary.EmptyString);
                    if (!String.IsNullOrEmpty(genericIdentity.AuthenticationType))
                        writer.WriteAttributeString(dictionary.AuthenticationType, dictionary.EmptyString, genericIdentity.AuthenticationType);
                    writer.WriteString(genericIdentity.Name);
                    writer.WriteEndElement();
                }
                else
                {
                    serializer.WriteObject(writer, identity);
                }
                writer.WriteEndElement();
            }
        }
 
        public static IList<IIdentity> DeserializeIdentities(XmlDictionaryReader reader, SctClaimDictionary dictionary, XmlObjectSerializer serializer)
        {
            List<IIdentity> identities = null;
            if (reader.IsStartElement(dictionary.Identities, dictionary.EmptyString))
            {
                identities = new List<IIdentity>();
                reader.ReadStartElement();
                while (reader.IsStartElement(dictionary.PrimaryIdentity, dictionary.EmptyString))
                {
                    IIdentity identity = DeserializePrimaryIdentity(reader, dictionary, serializer);
                    if (identity != null && identity != SecurityUtils.AnonymousIdentity)
                    {
                        identities.Add(identity);
                    }
                }
                reader.ReadEndElement();
            }
            return identities;
        }
 
        static IIdentity DeserializePrimaryIdentity(XmlDictionaryReader reader, SctClaimDictionary dictionary, XmlObjectSerializer serializer)
        {
            IIdentity identity = null;
            if (reader.IsStartElement(dictionary.PrimaryIdentity, dictionary.EmptyString))
            {
                reader.ReadStartElement();
                if (reader.IsStartElement(dictionary.WindowsSidIdentity, dictionary.EmptyString))
                {
                    SecurityIdentifier sid = ReadSidAttribute(reader, dictionary);
                    string authenticationType = reader.GetAttribute(dictionary.AuthenticationType, dictionary.EmptyString);
                    reader.ReadStartElement();
                    string name = reader.ReadContentAsString();
                    identity = new WindowsSidIdentity(sid, name, authenticationType ?? String.Empty);
                    reader.ReadEndElement();
                }
                else if (reader.IsStartElement(dictionary.GenericIdentity, dictionary.EmptyString))
                {
                    string authenticationType = reader.GetAttribute(dictionary.AuthenticationType, dictionary.EmptyString);
                    reader.ReadStartElement();
                    string name = reader.ReadContentAsString();
                    identity = SecurityUtils.CreateIdentity(name, authenticationType ?? String.Empty);
                    reader.ReadEndElement();
                }
                else
                {
                    identity = (IIdentity)serializer.ReadObject(reader);
                }
                reader.ReadEndElement();
            }
            return identity;
        }
    }
}