// ==++==
// 
//   Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// ==--==
// <OWNER>Microsoft</OWNER>
// 

//
// ClaimsIdentity.cs
//

namespace System.Security.Claims
{
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Diagnostics.Contracts;
    using System.IO;
    using System.Runtime.CompilerServices;
    using System.Runtime.InteropServices;
    using System.Runtime.Serialization;
    using System.Runtime.Serialization.Formatters.Binary;
    using System.Security.Permissions;
    using System.Security.Principal;

    /// <summary>
    /// An Identity that is represented by a set of claims.
    /// </summary>
    [Serializable]
    [ComVisible(true)]
    public class ClaimsIdentity : IIdentity
    {
        private enum SerializationMask
        {
            None = 0,
            AuthenticationType = 1,
            BootstrapConext = 2,
            NameClaimType = 4,
            RoleClaimType = 8,
            HasClaims = 16,
            HasLabel = 32,
            Actor = 64,
            UserData = 128,
        }

        [NonSerialized]
        private byte[] m_userSerializationData;

        [NonSerialized]
        const string PreFix = "System.Security.ClaimsIdentity.";
        [NonSerialized]
        const string ActorKey = PreFix + "actor";        
        [NonSerialized]
        const string AuthenticationTypeKey = PreFix + "authenticationType";        
        [NonSerialized]
        const string BootstrapContextKey = PreFix + "bootstrapContext";
        [NonSerialized]
        const string ClaimsKey = PreFix + "claims";
        [NonSerialized]
        const string LabelKey = PreFix + "label";
        [NonSerialized]
        const string NameClaimTypeKey = PreFix + "nameClaimType";
        [NonSerialized]
        const string RoleClaimTypeKey = PreFix + "roleClaimType";
        [NonSerialized]
        const string VersionKey = PreFix + "version";
        [NonSerialized]
        public const string DefaultIssuer = @"LOCAL AUTHORITY";
        [NonSerialized]
        public const string DefaultNameClaimType = ClaimTypes.Name;
        [NonSerialized]
        public const string DefaultRoleClaimType = ClaimTypes.Role;
        // === Important
        //
        // adding claims to this list will affect the Authorization for this Identity 
        // originally marked this as SecurityCritical, however because enumerators access it
        // we would need to extend SecuritySafeCritical to the enumerator methods AND the constructors.
        // In the end, this requires additional [SecuritySafeCritical] attributes.  So if any additional access
        // is added to 'm_instanceClaims' then this must be carefully monitored. This is equivalent to adding sids to the 
        // NTToken and will be used up the stack to make Authorization decisions.
        //

        // these are claims that are added by using the AddClaim, AddClaims methods or passed in the constructor.
        [NonSerialized]
        List<Claim> m_instanceClaims = new List<Claim>();

        // These are claims that are external to the identity. .Net runtime attaches roles owned by principals GenericPrincpal and RolePrincipal here. 
        // They are not serialized OR remembered when cloned. Access through public method: ClaimProviders.
        [NonSerialized]
        Collection<IEnumerable<Claim>> m_externalClaims = new Collection<IEnumerable<Claim>>();

        [NonSerialized]
        string m_nameType = DefaultNameClaimType;
        
        [NonSerialized]
        string m_roleType = DefaultRoleClaimType;
        
        [OptionalField(VersionAdded=2)]
        string m_version = "1.0";

        [OptionalField(VersionAdded = 2)]
        ClaimsIdentity m_actor;

        [OptionalField(VersionAdded = 2)]
        string m_authenticationType;

        [OptionalField(VersionAdded = 2)]
        object m_bootstrapContext;

        [OptionalField(VersionAdded = 2)]
        string m_label;

        [OptionalField(VersionAdded = 2)]
        string m_serializedNameType;

        [OptionalField(VersionAdded = 2)]
        string m_serializedRoleType;

        [OptionalField(VersionAdded = 2)]
        string m_serializedClaims;
        
        #region ClaimsIdentity Constructors

        /// <summary>
        /// Initializes an instance of <see cref="ClaimsIdentity"/> with an empty claims collection.
        /// </summary>
        /// <remarks>
        /// <see cref="Identity.AuthenticationType"/> is set to null.
        /// </remarks>
        public ClaimsIdentity()
            : this((Claim[])null)
        {
        }

        /// <summary>
        /// Initializes an instance of <see cref="ClaimsIdentity"/> using the name and authentication type from
        /// an <see cref="IIdentity"/> instance.
        /// </summary>
        /// <param name="identity"><see cref="IIdentity"/> to draw the name and authentication type from.</param>
        /// <exception cref="ArgumentNullException"> if <paramref name="identity"/> is null.</exception>
        public ClaimsIdentity(IIdentity identity)
            : this(identity, (IEnumerable<Claim>)null)
        {
        }

        /// <summary>
        /// Initializes an instance of <see cref="Identity"/> using an enumerated collection of 
        /// <see cref="Claim"/> objects.
        /// </summary>
        /// <param name="claims">
        /// The collection of <see cref="Claim"/> objects to populate <see cref="Identity.Claims"/> with.
        /// </param>
        /// <remarks>
        /// <see cref="Identity.AuthenticationType"/> is set to null.
        /// </remarks>
        public ClaimsIdentity(IEnumerable<Claim> claims)
            : this((IIdentity) null, claims, null, null, null)
        {
        }

        /// <summary>
        /// Initializes an instance of <see cref="Identity"/> with an empty <see cref="Claim"/> collection
        /// and the specified authentication type.
        /// </summary>
        /// <param name="authenticationType">The type of authentication used.</param>
        public ClaimsIdentity(string authenticationType)
            : this((IIdentity) null, (IEnumerable<Claim>)null, authenticationType, (string)null, (string)null)
        {
        }

        /// <summary>
        /// Initializes an instance of <see cref="Identity"/> using an enumerated collection of 
        /// <see cref="Claim"/> objects.
        /// </summary>
        /// <param name="claims">
        /// The collection of <see cref="Claim"/> objects to populate <see cref="Identity.Claims"/> with.
        /// </param>
        /// <param name="authenticationType">The type of authentication used.</param>
        /// <remarks>
        /// <see cref="Identity.AuthenticationType"/> is set to null.
        /// </remarks>
        public ClaimsIdentity(IEnumerable<Claim> claims, string authenticationType)
            : this((IIdentity)null, claims, authenticationType, null, null)
        {
        }

        /// <summary>
        /// Initializes an instance of <see cref="ClaimsIdentity"/> using the name and authentication type from
        /// an <see cref="IIdentity"/> instance.
        /// </summary>
        /// <param name="identity"><see cref="IIdentity"/> to draw the name and authentication type from.</param>
        /// <exception cref="ArgumentNullException"> if <paramref name="identity"/> is null.</exception>
        public ClaimsIdentity(IIdentity identity, IEnumerable<Claim> claims)
            : this(identity, claims, (string)null, (string)null, (string)null)
        {
        }

        /// <summary>
        /// Initializes an instance of <see cref="Identity"/> with an empty <see cref="Claim"/> collection,
        /// the specified authentication type, name claim type, and role claim type.
        /// </summary>
        /// <param name="authenticationType">The type of authentication used.</param>
        /// <param name="nameType">The claim type to use for <see cref="Identity.Name"/>.</param>
        /// <param name="roleType">The claim type to use for IClaimsPrincipal.IsInRole(string).</param>
        public ClaimsIdentity(string authenticationType, string nameType, string roleType )
            : this((IIdentity) null, (IEnumerable<Claim>)null, authenticationType, nameType, roleType)
        {
        }

        /// <summary>
        /// Initializes an instance of <see cref="ClaimsIdentity"/> using an enumeration of type 
        /// <see cref="Claim"/>, authentication type, name claim type, role claim type, and bootstrapContext.
        /// </summary>
        /// <param name="claims">An enumeration of type <see cref="Claim"/> to initialize this identity</param>
        /// <param name="authenticationType">The type of authentication used.</param>
        /// <param name="nameType">The claim type to identify NameClaims.</param>
        /// <param name="roleType">The claim type to identify RoleClaims.</param>
        public ClaimsIdentity(IEnumerable<Claim> claims, string authenticationType, string nameType, string roleType)
            : this((IIdentity)null, claims, authenticationType, nameType, roleType)
        {
        }

        /// <summary>
        /// Initializes an instance of <see cref="ClaimsIdentity"/> using an enumeration of type 
        /// <see cref="Claim"/>, authentication type, name claim type, role claim type, and bootstrapContext.
        /// </summary>
        /// <param name="identity">The initial identity to base this identity from.</param>
        /// <param name="claims">An enumeration of type <see cref="Claim"/> to initialize this identity.</param>
        /// <param name="authenticationType">The type of authentication used.</param>
        /// <param name="nameType">The claim type to identify NameClaims.</param>
        /// <param name="roleType">The claim type to identify RoleClaims.</param>
        public ClaimsIdentity(IIdentity identity, IEnumerable<Claim> claims, string authenticationType, string nameType, string roleType)
            : this(identity, claims, authenticationType, nameType, roleType, true)
        {

        }
       
        /// <summary>
        /// This constructor was added so that the WindowsIdentity could control if the authenticationType should be checked. For WindowsIdentities this
        /// leads to a priviledged call and will fail where the caller has low priviledge.
        /// </summary>
        /// <param name="identity">The initial identity to base this identity from.</param>
        /// <param name="claims">An enumeration of type <see cref="Claim"/> to initialize this identity.</param>
        /// <param name="authenticationType">The type of authentication used.</param>
        /// <param name="nameType">The claim type to identify NameClaims.</param>
        /// <param name="roleType">The claim type to identify RoleClaims.</param>
        /// <param name="checkAuthType">This boolean flag controls if we blindly set the authenticationType, since call WindowsIdentity.AuthenticationType is a priviledged call.</param>
        internal ClaimsIdentity(IIdentity identity, IEnumerable<Claim> claims, string authenticationType, string nameType, string roleType, bool checkAuthType)
        {
            bool nameTypeSet = false;
            bool roleTypeSet = false;

            // move the authtype, nameType and roleType over from the identity ONLY if they weren't specifically set.
            if(checkAuthType && null != identity && string.IsNullOrEmpty(authenticationType)) 
            {
                // can safely ignore UnauthorizedAccessException from WindowsIdentity, 
                // LSA didn't allow the call and WindowsIdentity throws if property is never accessed, no reason to fail.
                if (identity is WindowsIdentity)
                {
                    try
                    {
                        m_authenticationType = identity.AuthenticationType;
                    }
                    catch (UnauthorizedAccessException)
                    {
                        m_authenticationType = null;
                    }
                }
                else
                {
                    m_authenticationType = identity.AuthenticationType;
                }
            }
            else
            {
                m_authenticationType = authenticationType;
            }

            if(!string.IsNullOrEmpty(nameType))
            {
                m_nameType = nameType;
                nameTypeSet = true;
            }

            if(!string.IsNullOrEmpty(roleType))
            {
                m_roleType = roleType;
                roleTypeSet = true;
            }

            ClaimsIdentity claimsIdentity = identity as ClaimsIdentity;

            if (claimsIdentity != null)
            {
                m_label = claimsIdentity.m_label;

                // give preference to parameters
                if (!nameTypeSet)
                {
                    m_nameType = claimsIdentity.m_nameType;
                }

                if (!roleTypeSet)
                {
                    m_roleType = claimsIdentity.m_roleType;
                }

                m_bootstrapContext = claimsIdentity.m_bootstrapContext;

                if (claimsIdentity.Actor != null)
                {
                    //
                    // Check if the Actor is circular before copying. That check is done while setting
                    // the Actor property and so not really needed here. But checking just for sanity sake
                    //
                    if(!IsCircular(claimsIdentity.Actor))
                    {
                        if (!AppContextSwitches.SetActorAsReferenceWhenCopyingClaimsIdentity)
                        {
                            m_actor = claimsIdentity.Actor.Clone();
                        }
                        else
                        {
                            m_actor = claimsIdentity.Actor;
                        }
                    }
                    else
                    {
                        throw new InvalidOperationException(Environment.GetResourceString("InvalidOperationException_ActorGraphCircular"));
                    }
                }

                // We can only copy over the claims we own, it is up to the derived 
                // to copy over claims they own.
                // BUT we need to special case WindowsIdentity as it keeps its own claims.
                // In the case where we are not a windowsIdentity and the claimsIdentity is
                // we need to copy the claims

                if ((claimsIdentity is WindowsIdentity) && (!(this is WindowsIdentity)))
                    SafeAddClaims(claimsIdentity.Claims);
                else
                    SafeAddClaims(claimsIdentity.m_instanceClaims);

                if (claimsIdentity.m_userSerializationData != null)
                {
                    m_userSerializationData = claimsIdentity.m_userSerializationData.Clone() as byte[];
                }
            }
            else
            {
                if (identity != null && !string.IsNullOrEmpty(identity.Name))
                {
                    SafeAddClaim(new Claim(m_nameType, identity.Name, ClaimValueTypes.String, DefaultIssuer, DefaultIssuer, this));
                }
            }

            if (claims != null)
            {
                SafeAddClaims(claims);
            }
        }

        /// Initializes an instance of <see cref="ClaimsIdentity"/> using a <see cref="BinaryReader"/>.
        /// Normally the reader is constructed from the bytes returned from <see cref="WriteTo"/>
        /// </summary>
        /// <param name="reader">a <see cref="BinaryReader"/> pointing to a <see cref="ClaimsIdentity"/>.</param>
        /// <exception cref="ArgumentNullException">if 'reader' is null.</exception>
        public ClaimsIdentity(BinaryReader reader)
        {
            if (reader == null)
                throw new ArgumentNullException("reader");

            Initialize(reader);
        }

        /// <summary>
        /// Copy constructor.
        /// </summary>
        /// <param name="other"><see cref="ClaimsIdentity"/> to copy.</param>
        /// <exception cref="ArgumentNullException">if 'other' is null.</exception>
        protected ClaimsIdentity(ClaimsIdentity other)
        {
            if (other == null)
            {
                throw new ArgumentNullException("other");
            }

            if (other.m_actor != null)
            {
                m_actor = other.m_actor.Clone();
            }

            m_authenticationType = other.m_authenticationType;
            m_bootstrapContext = other.m_bootstrapContext;
            m_label = other.m_label;
            m_nameType = other.m_nameType;
            m_roleType = other.m_roleType;
            if (other.m_userSerializationData != null)
            {
                m_userSerializationData = other.m_userSerializationData.Clone() as byte[];
            }

            SafeAddClaims(other.m_instanceClaims);
        }

        /// <summary>
        /// Initializes an instance of <see cref="Identity"/> from a serialized stream created via 
        /// <see cref="ISerializable"/>.
        /// </summary>
        /// <param name="info">
        /// The <see cref="SerializationInfo"/> to read from.
        /// </param>
        /// <param name="context">The <see cref="StreamingContext"/> for serialization. Can be null.</param>
        /// <exception cref="ArgumentNullException">Thrown is the <paramref name="info"/> is null.</exception>
        [SecurityCritical]
        protected ClaimsIdentity(SerializationInfo info, StreamingContext context)
        {
            if (null == info)
            {
                throw new ArgumentNullException("info");
            }

            Deserialize(info, context, true);
        }

        /// <summary>
        /// Initializes an instance of <see cref="Identity"/> from a serialized stream created via 
        /// <see cref="ISerializable"/>.
        /// </summary>
        /// <param name="info">
        /// The <see cref="SerializationInfo"/> to read from.
        /// </param>
        /// <exception cref="ArgumentNullException">Thrown is the <paramref name="info"/> is null.</exception>
        [SecurityCritical]
        protected ClaimsIdentity(SerializationInfo info)
        {
            if (null == info)
            {
                throw new ArgumentNullException("info");
            }

            StreamingContext sc = new StreamingContext();
            Deserialize(info, sc, false);
        }

        #endregion

        /// <summary>
        /// Gets the authentication type.
        /// </summary>
        public virtual string AuthenticationType
        {
            get { return m_authenticationType; }
        }

        /// <summary>
        /// Gets a value that indicates whether the user has been authenticated.
        /// </summary>
        public virtual bool IsAuthenticated
        {
            get { return !string.IsNullOrEmpty(m_authenticationType); }
        }

        /// <summary>
        /// Gets or sets a <see cref="ClaimsIdentity"/> that was granted delegation rights.
        /// </summary>
        public ClaimsIdentity Actor
        {
            get { return m_actor; }
            set
            {
                if(value != null)
                {
                    if(IsCircular(value))
                    {
                        throw new InvalidOperationException(Environment.GetResourceString("InvalidOperationException_ActorGraphCircular")); 
                    }
                }
                m_actor = value;
            }
        }
        
        /// <summary>
        /// Gets or sets a context that was used to create this <see cref="ClaimsIdentity"/>.
        /// </summary>
        public object BootstrapContext
        {
            get { return m_bootstrapContext; }
            
            [SecurityCritical]
            set { m_bootstrapContext = value; }
        }

        /// <summary>
        /// Gets the claims as <see cref="IEnumerable{Claim}"/>, associated with this <see cref="ClaimsIdentity"/>.
        /// </summary>       
        /// <remarks>May contain nulls.</remarks>
        public virtual IEnumerable<Claim> Claims
        {
            get
            {
                for (int i = 0; i < m_instanceClaims.Count; i++)
                {
                    yield return m_instanceClaims[i];
                }

                if (m_externalClaims != null)
                {
                    for (int j = 0; j < m_externalClaims.Count; j++)
                    {
                        if (m_externalClaims[j] != null)
                        {
                            foreach (Claim claim in m_externalClaims[j])
                            {
                                yield return claim;
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Contains any additional data provided by a derived type, typically set when calling <see cref="WriteTo(BinaryWriter, byte[])"/>.</param>
        /// </summary>
        protected virtual byte[] CustomSerializationData
        {
            get
            {
                return m_userSerializationData;
            }
        }

        /// <summary>
        /// Allow the association of claims with this instance of <see cref="ClaimsIdentity"/>. 
        /// The claims will not be serialized or added in Clone(). They will be included in searches, finds and returned from the call to Claims.
        /// It is recommended the creator of the claims ensures the subject of the claims reflects this <see cref="ClaimsIdentity"/>.
        /// </summary>               
        internal Collection<IEnumerable<Claim>> ExternalClaims
        {
            [FriendAccessAllowed]
            get { return m_externalClaims; }
        }

        /// <summary>
        /// Gets or sets the label for this <see cref="Identity"/>
        /// </summary>
        public string Label
        {
            get { return m_label; }
            set { m_label = value; }
        }

        /// <summary>
        /// Gets the value of the first claim that has a type of NameClaimType. If no claim is found, null is returned.
        /// </summary>        
        public virtual string Name
        {
            // just an accessor for getting the name claim
            get
            {
                Claim claim = FindFirst(m_nameType);
                if (claim != null)
                {
                    return claim.Value;
                }

                return null;
            }
        }

        /// <summary>
        /// Gets the claim type used to distinguish claims that refer to the name.
        /// </summary>
        public string NameClaimType
        {
            get { return m_nameType; }
        }

        /// <summary>
        /// Gets the claim type used to distinguish claims that refer to Roles.
        /// </summary>
        public string RoleClaimType
        {
            get { return m_roleType; }
        }

        /// <summary>
        /// Returns a new instance of <see cref="ClaimsIdentity"/> with values copied from this object.
        /// </summary>
        /// <returns>A new <see cref="Identity"/> object copied from this object</returns>
        public virtual ClaimsIdentity Clone()
        {
            ClaimsIdentity newIdentity = new ClaimsIdentity(m_instanceClaims);

            newIdentity.m_authenticationType = this.m_authenticationType;
            newIdentity.m_bootstrapContext = this.m_bootstrapContext;
            newIdentity.m_label = this.m_label;
            newIdentity.m_nameType = this.m_nameType;
            newIdentity.m_roleType = this.m_roleType;
            
            if(this.Actor != null)
            {
                // Check if the Actor is circular before copying. That check is done while setting
                // the Actor property and so not really needed here. But checking just for sanity sake
                if(!IsCircular(this.Actor))
                {
                    if (!AppContextSwitches.SetActorAsReferenceWhenCopyingClaimsIdentity)
                    {
                        newIdentity.Actor = this.Actor.Clone();
                    }
                    else
                    {
                        newIdentity.Actor = this.Actor;
                    }
                }
                else
                {
                    throw new InvalidOperationException(Environment.GetResourceString("InvalidOperationException_ActorGraphCircular"));
                }
            }

            return newIdentity;
        }

        /// <summary>
        /// Adds a single claim to this ClaimsIdentity. The claim is examined and if the subject != this, then a new claim is 
        /// created by calling claim.Clone(this).  This creates a new claim, with the correct subject.
        /// </summary>
        /// <param name="claims">Enumeration of claims to add.</param>
        /// This is SecurityCritical as we need to control who can add claims to the Identity. Futher down the pipe
        /// Authorization decisions will be made based on the claims found in this collection.
        [SecurityCritical]
        public virtual void AddClaim(Claim claim)
        {
            if (claim == null)
            {
                throw new ArgumentNullException("claim");
            }

            Contract.EndContractBlock();

            if(object.ReferenceEquals(claim.Subject, this))
            {
                m_instanceClaims.Add(claim);
            }
            else
            {
                m_instanceClaims.Add(claim.Clone(this));
            }
        }

        /// <summary>
        /// Adds a list of claims to this Claims Identity. Each claim is examined and if the subject != this, then a new claim is 
        /// created by calling claim.Clone(this).  This creates a new claim, with the correct subject.
        /// </summary>
        /// <param name="claims">Enumeration of claims to add.</param>
        /// This is SecurityCritical as we need to control who can add claims to the Identity. Futher down the pipe
        /// Authorization decisions will be made based on the claims found in this collection.        
        [SecurityCritical]
        public virtual void AddClaims(IEnumerable<Claim> claims)
        {
            if (claims == null)
            {
                throw new ArgumentNullException("claims");
            }

            Contract.EndContractBlock();

            foreach (Claim claim in claims)
            {
                if (claim == null)
                {
                    continue;
                }

                AddClaim(claim);
            }
        }

        /// <summary>
        /// Attempts to remove a claim from the identity.  It is possible that the claim cannot be removed since it is not owned
        /// by the identity.  This would be the case for role claims that are owned by the Principal.
        /// Matches by object reference.
        /// <summary/>
        [SecurityCritical]
        public virtual bool TryRemoveClaim(Claim claim)
        {
            bool removed = false;

            for (int i = 0; i < m_instanceClaims.Count; i++)
            {
                if (object.ReferenceEquals(m_instanceClaims[i], claim))
                {
                    m_instanceClaims.RemoveAt(i);
                    removed = true;
                    break;
                }
            }
            return removed;
        }

        [SecurityCritical]
        public virtual void RemoveClaim(Claim claim)
        {
            if (!TryRemoveClaim(claim))
            {
                throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ClaimCannotBeRemoved", claim));
            }
        }

        /// <summary>
        /// Called from constructor, isolated for easy review
        /// This is called from the constructor, this implies that the base class has 
        /// ownership of holding onto the claims.  We can't call AddClaim as that is a virtual and the
        /// Derived class may not be constructed yet.
        /// </summary>
        /// <param name="claims"></param>
        [SecuritySafeCritical]
        void SafeAddClaims(IEnumerable<Claim> claims)
        {
            foreach (Claim claim in claims)
            {
                if (object.ReferenceEquals(claim.Subject, this))
                {
                    m_instanceClaims.Add(claim);
                }
                else
                {
                    m_instanceClaims.Add(claim.Clone(this));
                }
            }
        }

        /// <summary>
        /// Called from constructor, isolated for easy review.
        /// This is called from the constructor, this implies that the base class has 
        /// ownership of holding onto the claims.  We can't call AddClaim as that is a virtual and the
        /// Derived class may not be constructed yet.
        /// </summary>
        /// <param name="claim"></param>
        [SecuritySafeCritical]
        void SafeAddClaim(Claim claim)
        {
            if (object.ReferenceEquals(claim.Subject, this))
            {
                m_instanceClaims.Add(claim);
            }
            else
            {
                m_instanceClaims.Add(claim.Clone(this));
            }
        }

        /// <summary>
        /// Retrieves a <see cref="IEnumerable{Claim}"/> where each claim is matched by <param name="match"/>.
        /// </summary>
        /// <param name="match">The function that performs the matching logic.</param>
        /// <returns>A <see cref="IEnumerable{Claim}"/> of matched claims.</returns>   
        public virtual IEnumerable<Claim> FindAll(Predicate<Claim> match)
        {
            if (match == null)
            {
                throw new ArgumentNullException("match");
            }

            Contract.EndContractBlock();

            List<Claim> claims = new List<Claim>();

            foreach (Claim claim in Claims)
            {
                if (match(claim))
                {
                    claims.Add(claim);
                }
            }

            return claims.AsReadOnly();
        }

        /// <summary>
        /// Retrieves a <see cref="IEnumerable{Claim}"/> where each Claim.Type equals <paramref name="type"/>.
        /// </summary>
        /// <param name="type">The type of the claim to match.</param>
        /// <returns>A <see cref="IEnumerable{Claim}"/> of matched claims.</returns>   
        /// <remarks>Comparison is made using Ordinal case in-sensitive on type.<</remarks>
        public virtual IEnumerable<Claim> FindAll(string type)
        {
            if (type == null)
            {
                throw new ArgumentNullException("type");
            }

            Contract.EndContractBlock();

            List<Claim> claims = new List<Claim>();

            foreach (Claim claim in Claims)
            {
                if (claim != null)
                {
                    if (string.Equals(claim.Type, type, StringComparison.OrdinalIgnoreCase))
                    {
                        claims.Add(claim);
                    }
                }
            }

            return claims.AsReadOnly();
        }

        /// <summary>
        /// Determines if a claim is contained within this ClaimsIdentity.
        /// </summary>
        /// <param name="match">The function that performs the matching logic.</param>
        /// <returns>true if a claim is found, false otherwise.</returns>
        public virtual bool HasClaim(Predicate<Claim> match)
        {
            if (match == null)
            {
                throw new ArgumentNullException("match");
            }

            Contract.EndContractBlock();

            foreach (Claim claim in Claims)
            {
                if (match(claim))
                {
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// Determines if a claim with type AND value is contained in the claims within this ClaimsIdentity.
        /// </summary>
        /// <param name="type"> the type of the claim to match.</param>
        /// <param name="value"> the value of the claim to match.</param>
        /// <returns>true if a claim is matched, false otherwise.</returns>
        /// <remarks>Does not check Issuer or OriginalIssuer.  Comparison is made using Ordinal, case sensitive on value, case in-sensitive on type.</remarks>
        public virtual bool HasClaim(string type, string value)
        {
            if (type == null)
            {
                throw new ArgumentNullException("type");
            }

            if (value == null)
            {
                throw new ArgumentNullException("value");
            }

            Contract.EndContractBlock();

            foreach (Claim claim in Claims)
            {
                if (claim != null)
                {
                    if (claim != null
                         && string.Equals(claim.Type, type, StringComparison.OrdinalIgnoreCase)
                         && string.Equals(claim.Value, value, StringComparison.Ordinal))
                    {
                        return true;
                    }
                }
            }

            return false;
        }

        /// <summary>
        /// Retrieves the first <see cref="Claim"/> that is matched by <param name="match"/>.
        /// </summary>
        /// <param name="match">The function that performs the matching logic.</param>
        /// <returns>A <see cref="Claim"/>, null if nothing matches.</returns>
        /// <remarks>Comparison is made using Ordinal, case in-sensitive.</remarks>
        public virtual Claim FindFirst(Predicate<Claim> match)
        {
            if (match == null)
            {
                throw new ArgumentNullException("match");
            }

            Contract.EndContractBlock();

            foreach (Claim claim in Claims)
            {
                if (match(claim))
                {
                    return claim;
                }
            }

            return null;
        }

        /// <summary>
        /// Retrieves the first <see cref="Claim"/> where Claim.Type equals <paramref name="type"/>.
        /// </summary>
        /// <param name="type">The type of the claim to match.</param>
        /// <returns>A <see cref="Claim"/>, null if nothing matches.</returns>
        /// <remarks>Comparison is made using Ordinal, case in-sensitive.</remarks>
        public virtual Claim FindFirst(string type)
        {
            if (type == null)
            {
                throw new ArgumentNullException("type");
            }

            Contract.EndContractBlock();

            foreach (Claim claim in Claims)
            {
                if (claim != null)
                {
                    if (string.Equals(claim.Type, type, StringComparison.OrdinalIgnoreCase))
                    {
                        return claim;
                    }
                }
            }

            return null;
        }

        [OnSerializing()]
        [SecurityCritical]
        private void OnSerializingMethod(StreamingContext context)
        {
            if (this is ISerializable)
                return;
            
            m_serializedClaims   = SerializeClaims();
            m_serializedNameType = m_nameType;
            m_serializedRoleType = m_roleType;
        }

        [OnDeserialized()]
        [SecurityCritical]
        private void OnDeserializedMethod(StreamingContext context)
        {
            if (this is ISerializable)
                return;

            if (!String.IsNullOrEmpty(m_serializedClaims))
            {
                DeserializeClaims(m_serializedClaims);
                m_serializedClaims = null;
            }

            m_nameType = string.IsNullOrEmpty(m_serializedNameType) ? DefaultNameClaimType : m_serializedNameType;
            m_roleType = string.IsNullOrEmpty(m_serializedRoleType) ? DefaultRoleClaimType : m_serializedRoleType;
        }

        [OnDeserializing()]
        private void OnDeserializingMethod(StreamingContext context)
        {
            if (this is ISerializable)
                return;

            m_instanceClaims = new List<Claim>();

            m_externalClaims = new Collection<IEnumerable<Claim>>();
        }

        /// <summary>
        /// Populates the specified <see cref="SerializationInfo"/> with the serialization data for the ClaimsIdentity
        /// </summary>
        /// <param name="info">The serialization information stream to write to. Satisfies ISerializable contract.</param>
        /// <param name="context">Context for serialization. Can be null.</param>
        /// <exception cref="ArgumentNullException">Thrown if the info parameter is null.</exception>
        [SecurityCritical]
        [SecurityPermission(SecurityAction.Assert, SerializationFormatter = true)]
        protected virtual void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            if (null == info)
            {
                throw new ArgumentNullException("info");
            }
            Contract.EndContractBlock();

            BinaryFormatter formatter = new BinaryFormatter();

            info.AddValue(VersionKey, m_version);
            if (!string.IsNullOrEmpty(m_authenticationType))
            {
                info.AddValue(AuthenticationTypeKey, m_authenticationType);
            }

            info.AddValue(NameClaimTypeKey, m_nameType);
            info.AddValue(RoleClaimTypeKey, m_roleType);

            if (!string.IsNullOrEmpty(m_label))
            {
                info.AddValue(LabelKey, m_label);
            }
            
            //
            // actor
            //
            if (m_actor != null)
            {
                using (MemoryStream ms = new MemoryStream())
                {
                    formatter.Serialize(ms, m_actor, null, false);
                    info.AddValue(ActorKey, Convert.ToBase64String(ms.GetBuffer(), 0, (int)ms.Length));
                }
            }

            //
            // claims
            //
            info.AddValue(ClaimsKey, SerializeClaims());

            //
            // bootstrapContext
            //
            if (m_bootstrapContext != null)
            {
                using (MemoryStream ms = new MemoryStream())
                {
                    formatter.Serialize(ms, m_bootstrapContext, null, false);
                    info.AddValue(BootstrapContextKey, Convert.ToBase64String(ms.GetBuffer(), 0, (int)ms.Length));
                }
            }
        }

        [SecurityCritical]
        private void DeserializeClaims(string serializedClaims)
        {
            if (!string.IsNullOrEmpty(serializedClaims))
            {
                using (MemoryStream stream = new MemoryStream(Convert.FromBase64String(serializedClaims)))
                {
                    m_instanceClaims = (List<Claim>)(new BinaryFormatter()).Deserialize(stream, null, false);
                    for (int i = 0; i < m_instanceClaims.Count; i++)
                    {
                        m_instanceClaims[i].Subject = this;
                    }
                }
            }

            if (m_instanceClaims == null)
            {
                m_instanceClaims = new List<Claim>();
            }
        }

        [SecurityCritical]
        private string SerializeClaims()
        {
            using (MemoryStream ms = new MemoryStream())
            {
                (new BinaryFormatter()).Serialize(ms, m_instanceClaims, null, false);
                return Convert.ToBase64String(ms.GetBuffer(), 0, (int)ms.Length);
            }
        }

        /// <summary>
        /// Checks if a circular reference exists to 'this'
        /// </summary>
        /// <param name="subject"></param>
        /// <returns></returns>
        bool IsCircular(ClaimsIdentity subject)
        {
            if(ReferenceEquals(this, subject))
            {
                return true;
            }

            ClaimsIdentity currSubject = subject;

            while(currSubject.Actor != null)
            {
                if(ReferenceEquals(this, currSubject.Actor))
                {
                    return true;
                }

                currSubject = currSubject.Actor;
            }

            return false;
        }

        /// <summary>
        /// Initializes from a <see cref="BinaryReader"/>. Normally the reader is initialized in the same as the one passed to <see cref="Serialize(BinaryWriter)"/>
        /// </summary>
        /// <param name="reader">a <see cref="BinaryReader"/> pointing to a <see cref="ClaimsIdentity"/>.</param>
        /// <exception cref="ArgumentNullException">if 'reader' is null.</exception>
        private void Initialize(BinaryReader reader)
        {
            if (reader == null)
            {
                throw new ArgumentNullException("reader");
            }

            // 
            SerializationMask mask = (SerializationMask)reader.ReadInt32();

            if ((mask & SerializationMask.AuthenticationType) == SerializationMask.AuthenticationType)
            {
                m_authenticationType = reader.ReadString();
            }

            if ((mask & SerializationMask.BootstrapConext) == SerializationMask.BootstrapConext)
            {
                m_bootstrapContext = reader.ReadString();
            }

            if ((mask & SerializationMask.NameClaimType) == SerializationMask.NameClaimType)
            {
                m_nameType = reader.ReadString();
            }
            else
            {
                m_nameType = ClaimsIdentity.DefaultNameClaimType;
            }

            if ((mask & SerializationMask.RoleClaimType) == SerializationMask.RoleClaimType)
            {
                m_roleType = reader.ReadString();
            }
            else
            {
                m_roleType = ClaimsIdentity.DefaultRoleClaimType;
            }

            if ((mask & SerializationMask.HasClaims) == SerializationMask.HasClaims)
            {
                // 
                int numberOfClaims = reader.ReadInt32();
                for (int index = 0; index < numberOfClaims; ++index)
                {
                    Claim claim = new Claim(reader, this);
                    m_instanceClaims.Add(claim);
                }
            }
        }

        /// <summary>
        /// Provides and extensibility point for derived types to create a custom <see cref="Claim"/>.
        /// </summary>
        /// <param name="reader">the <see cref="BinaryReader"/>that points at the claim.</param>
        /// <returns>a new <see cref="Claim"/>.</returns>
        protected virtual Claim CreateClaim(BinaryReader reader)
        {
            if (reader == null)
            {
                throw new ArgumentNullException("reader");
            }

            return new Claim(reader, this);
        }

        /// <summary>
        /// Serializes using a <see cref="BinaryWriter"/>
        /// </summary>
        /// <param name="writer">the <see cref="BinaryWriter"/> to use for data storage.</param>
        /// <exception cref="ArgumentNullException">if 'writer' is null.</exception>
        public virtual void WriteTo(BinaryWriter writer)
        {
            WriteTo(writer, null);
        }

        /// <summary>
        /// Serializes using a <see cref="BinaryWriter"/>
        /// </summary>
        /// <param name="writer">the <see cref="BinaryWriter"/> to use for data storage.</param>
        /// <param name="userData">additional data provided by derived type.</param>
        /// <exception cref="ArgumentNullException">if 'writer' is null.</exception>
        protected virtual void WriteTo(BinaryWriter writer, byte[] userData)
        {
            if (writer == null)
            {
                throw new ArgumentNullException("writer");
            }

            int numberOfPropertiesWritten = 0;
            var mask = SerializationMask.None;
            if (m_authenticationType != null)
            {
                mask |= SerializationMask.AuthenticationType;
                numberOfPropertiesWritten++;
            }

            if (m_bootstrapContext != null)
            {
                string rawData = m_bootstrapContext as string;
                if (rawData != null)
                {
                    mask |= SerializationMask.BootstrapConext;
                    numberOfPropertiesWritten++;
                }
            }

            if (!string.Equals(m_nameType, ClaimsIdentity.DefaultNameClaimType, StringComparison.Ordinal))
            {
                mask |= SerializationMask.NameClaimType;
                numberOfPropertiesWritten++;
            }

            if (!string.Equals(m_roleType, ClaimsIdentity.DefaultRoleClaimType, StringComparison.Ordinal))
            {
                mask |= SerializationMask.RoleClaimType;
                numberOfPropertiesWritten++;
            }

            if (!string.IsNullOrWhiteSpace(m_label))
            {
                mask |= SerializationMask.HasLabel;
                numberOfPropertiesWritten++;
            }

            if (m_instanceClaims.Count > 0)
            {
                mask |= SerializationMask.HasClaims;
                numberOfPropertiesWritten++;
            }

            if (m_actor != null)
            {
                mask |= SerializationMask.Actor;
                numberOfPropertiesWritten++;
            }

            if (userData != null && userData.Length > 0)
            {
                numberOfPropertiesWritten++;
                mask |= SerializationMask.UserData;
            }

            writer.Write((Int32)mask);
            writer.Write((Int32)numberOfPropertiesWritten);
            if ((mask & SerializationMask.AuthenticationType) == SerializationMask.AuthenticationType)
            {
                writer.Write(m_authenticationType);
            }

            if ((mask & SerializationMask.BootstrapConext) == SerializationMask.BootstrapConext)
            {
                writer.Write(m_bootstrapContext as string);
            }

            if ((mask & SerializationMask.NameClaimType) == SerializationMask.NameClaimType)
            {
                writer.Write(m_nameType);
            }

            if ((mask & SerializationMask.RoleClaimType) == SerializationMask.RoleClaimType)
            {
                writer.Write(m_roleType);
            }

            if ((mask & SerializationMask.HasLabel) == SerializationMask.HasLabel)
            {
                writer.Write(m_label);
            }

            if ((mask & SerializationMask.HasClaims) == SerializationMask.HasClaims)
            {
                writer.Write((Int32)m_instanceClaims.Count);
                foreach (var claim in m_instanceClaims)
                {
                    claim.WriteTo(writer);
                }
            }

            if ((mask & SerializationMask.Actor) == SerializationMask.Actor)
            {
                m_actor.WriteTo(writer);
            }

            if ((mask & SerializationMask.UserData) == SerializationMask.UserData)
            {
                writer.Write((Int32)userData.Length);
                writer.Write(userData);
            }

            writer.Flush();
        }

        // <param name="useContext"></param> The reason for this param is due to WindowsIdentity deciding to have an 
        // api that doesn't pass the context to its internal constructor.
        [SecurityCritical]
        [SecurityPermission(SecurityAction.Assert, SerializationFormatter = true)]
        private void Deserialize(SerializationInfo info, StreamingContext context, bool useContext)
        {

            if (null == info)
            {
                throw new ArgumentNullException("info");
            }

            BinaryFormatter bf;

            if (useContext)
                bf = new BinaryFormatter(null, context);
            else
                bf = new BinaryFormatter();


            SerializationInfoEnumerator enumerator = info.GetEnumerator();
            while (enumerator.MoveNext())
            {
                switch (enumerator.Name)
                {
                    case VersionKey:
                        info.GetString(VersionKey);
                        break;

                    case AuthenticationTypeKey:
                        m_authenticationType = info.GetString(AuthenticationTypeKey);
                        break;

                    case NameClaimTypeKey:
                        m_nameType = info.GetString(NameClaimTypeKey);
                        break;

                    case RoleClaimTypeKey:
                        m_roleType = info.GetString(RoleClaimTypeKey);
                        break;

                    case LabelKey:
                        m_label = info.GetString(LabelKey);
                        break;

                    case ActorKey:
                        using (MemoryStream stream = new MemoryStream(Convert.FromBase64String(info.GetString(ActorKey))))
                        {
                            m_actor = (ClaimsIdentity)bf.Deserialize(stream, null, false);
                        }
                        break;

                    case ClaimsKey:
                        DeserializeClaims(info.GetString(ClaimsKey));
                        break;

                    case BootstrapContextKey:
                        using (MemoryStream ms = new MemoryStream(Convert.FromBase64String(info.GetString(BootstrapContextKey))))
                        {
                            m_bootstrapContext = bf.Deserialize(ms, null, false);
                        }
                        break;

                    default:
                        // Ignore other fields for forward compatability.
                        break;
                }
            }
        }
    }
}
