File: System\IdentityModel\Claims\ClaimComparer.cs
Web Access
Project: src\src\System.ServiceModel.Primitives\src\System.ServiceModel.Primitives.csproj (System.ServiceModel.Primitives)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
 
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Principal;
using System.ServiceModel;
 
namespace System.IdentityModel.Claims
{
    internal class ClaimComparer : IEqualityComparer<Claim>
    {
        private static IEqualityComparer<Claim> s_defaultComparer;
        private static IEqualityComparer<Claim> s_hashComparer;
        private static IEqualityComparer<Claim> s_dnsComparer;
        private static IEqualityComparer<Claim> s_rsaComparer;
        private static IEqualityComparer<Claim> s_thumbprintComparer;
        private static IEqualityComparer<Claim> s_upnComparer;
        private static IEqualityComparer<Claim> s_x500DistinguishedNameComparer;
        private IEqualityComparer _resourceComparer;
 
        private ClaimComparer(IEqualityComparer resourceComparer)
        {
            _resourceComparer = resourceComparer;
        }
 
        public static IEqualityComparer<Claim> GetComparer(string claimType)
        {
            if (claimType == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(claimType));
            }
 
            if (claimType == ClaimTypes.Dns)
            {
                return Dns;
            }
 
            if (claimType == ClaimTypes.Hash)
            {
                return Hash;
            }
 
            if (claimType == ClaimTypes.Rsa)
            {
                return Rsa;
            }
 
            if (claimType == ClaimTypes.Thumbprint)
            {
                return Thumbprint;
            }
 
            if (claimType == ClaimTypes.Upn)
            {
                return Upn;
            }
 
            if (claimType == ClaimTypes.X500DistinguishedName)
            {
                return X500DistinguishedName;
            }
 
            return Default;
        }
 
        public static IEqualityComparer<Claim> Default
        {
            get
            {
                if (s_defaultComparer == null)
                {
                    s_defaultComparer = new ClaimComparer(new ObjectComparer());
                }
                return s_defaultComparer;
            }
        }
 
        public static IEqualityComparer<Claim> Dns
        {
            get
            {
                if (s_dnsComparer == null)
                {
                    s_dnsComparer = new ClaimComparer(StringComparer.OrdinalIgnoreCase);
                }
                return s_dnsComparer;
            }
        }
 
        public static IEqualityComparer<Claim> Hash
        {
            get
            {
                if (s_hashComparer == null)
                {
                    s_hashComparer = new ClaimComparer(new BinaryObjectComparer());
                }
                return s_hashComparer;
            }
        }
 
        public static IEqualityComparer<Claim> Rsa
        {
            get
            {
                if (s_rsaComparer == null)
                {
                    s_rsaComparer = new ClaimComparer(new RsaObjectComparer());
                }
                return s_rsaComparer;
            }
        }
 
        public static IEqualityComparer<Claim> Thumbprint
        {
            get
            {
                if (s_thumbprintComparer == null)
                {
                    s_thumbprintComparer = new ClaimComparer(new BinaryObjectComparer());
                }
                return s_thumbprintComparer;
            }
        }
 
        public static IEqualityComparer<Claim> Upn
        {
            get
            {
                if (s_upnComparer == null)
                {
                    s_upnComparer = new ClaimComparer(new UpnObjectComparer());
                }
                return s_upnComparer;
            }
        }
 
        public static IEqualityComparer<Claim> X500DistinguishedName
        {
            get
            {
                if (s_x500DistinguishedNameComparer == null)
                {
                    s_x500DistinguishedNameComparer = new ClaimComparer(new X500DistinguishedNameObjectComparer());
                }
                return s_x500DistinguishedNameComparer;
            }
        }
 
        // we still need to review how the default equals works, this is not how Doug envisioned it.
        public bool Equals(Claim claim1, Claim claim2)
        {
            if (ReferenceEquals(claim1, claim2))
            {
                return true;
            }
 
            if (claim1 == null || claim2 == null)
            {
                return false;
            }
 
            if (claim1.ClaimType != claim2.ClaimType || claim1.Right != claim2.Right)
            {
                return false;
            }
 
            return _resourceComparer.Equals(claim1.Resource, claim2.Resource);
        }
 
        public int GetHashCode(Claim claim)
        {
            if (claim == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(claim));
            }
 
            return claim.ClaimType.GetHashCode() ^ claim.Right.GetHashCode()
                   ^ ((claim.Resource == null) ? 0 : _resourceComparer.GetHashCode(claim.Resource));
        }
 
        private class ObjectComparer : IEqualityComparer
        {
            bool IEqualityComparer.Equals(object obj1, object obj2)
            {
                if (obj1 == null && obj2 == null)
                {
                    return true;
                }
 
                if (obj1 == null || obj2 == null)
                {
                    return false;
                }
 
                return obj1.Equals(obj2);
            }
 
            int IEqualityComparer.GetHashCode(object obj)
            {
                if (obj == null)
                {
                    return 0;
                }
 
                return obj.GetHashCode();
            }
        }
 
        private class BinaryObjectComparer : IEqualityComparer
        {
            bool IEqualityComparer.Equals(object obj1, object obj2)
            {
                if (ReferenceEquals(obj1, obj2))
                {
                    return true;
                }
 
                byte[] bytes1 = obj1 as byte[];
                byte[] bytes2 = obj2 as byte[];
                if (bytes1 == null || bytes2 == null)
                {
                    return false;
                }
 
                if (bytes1.Length != bytes2.Length)
                {
                    return false;
                }
 
                for (int i = 0; i < bytes1.Length; ++i)
                {
                    if (bytes1[i] != bytes2[i])
                    {
                        return false;
                    }
                }
 
                return true;
            }
 
            int IEqualityComparer.GetHashCode(object obj)
            {
                byte[] bytes = obj as byte[];
                if (bytes == null)
                {
                    return 0;
                }
 
                int hashCode = 0;
                for (int i = 0; i < bytes.Length && i < 4; ++i)
                {
                    hashCode = (hashCode << 8) | bytes[i];
                }
 
                return hashCode ^ bytes.Length;
            }
        }
 
        private class RsaObjectComparer : IEqualityComparer
        {
            bool IEqualityComparer.Equals(object obj1, object obj2)
            {
                if (ReferenceEquals(obj1, obj2))
                {
                    return true;
                }
 
                throw ExceptionHelper.PlatformNotSupported();
            }
 
            int IEqualityComparer.GetHashCode(object obj)
            {
                throw ExceptionHelper.PlatformNotSupported();
            }
        }
 
        private class X500DistinguishedNameObjectComparer : IEqualityComparer
        {
            private IEqualityComparer _binaryComparer;
 
            public X500DistinguishedNameObjectComparer()
            {
                _binaryComparer = new BinaryObjectComparer();
            }
 
            bool IEqualityComparer.Equals(object obj1, object obj2)
            {
                if (ReferenceEquals(obj1, obj2))
                {
                    return true;
                }
 
                throw ExceptionHelper.PlatformNotSupported();
            }
 
            int IEqualityComparer.GetHashCode(object obj)
            {
                throw ExceptionHelper.PlatformNotSupported();
            }
        }
 
        private class UpnObjectComparer : IEqualityComparer
        {
            bool IEqualityComparer.Equals(object obj1, object obj2)
            {
                if (StringComparer.OrdinalIgnoreCase.Equals(obj1 as string, obj2 as string))
                {
                    return true;
                }
 
                if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                {
                    string upn1 = obj1 as string;
                    string upn2 = obj2 as string;
                    if (upn1 == null || upn2 == null)
                    {
                        return false;
                    }
 
                    SecurityIdentifier sid1;
                    if (!TryLookupSidFromName(upn1, out sid1))
                    {
                        return false;
                    }
 
                    // Normalize to sid
                    SecurityIdentifier sid2;
                    if (!TryLookupSidFromName(upn2, out sid2))
                    {
                        return false;
                    }
 
                    return sid1 == sid2;
                }
                else
                {
                    return false;
                }
            }
 
            int IEqualityComparer.GetHashCode(object obj)
            {
                string upn = obj as string;
                if (upn == null)
                {
                    return 0;
                }
 
                if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                {
                    // Normalize to sid
                    SecurityIdentifier sid;
                    if (TryLookupSidFromName(upn, out sid))
                    {
                        return sid.GetHashCode();
                    }
                }
 
                return StringComparer.OrdinalIgnoreCase.GetHashCode(upn);
            }
 
            [SupportedOSPlatform("windows")]
            private bool TryLookupSidFromName(string upn, out SecurityIdentifier sid)
            {
                sid = null;
                try
                {
                    NTAccount acct = new NTAccount(upn);
                    sid = acct.Translate(typeof(SecurityIdentifier)) as SecurityIdentifier;
                }
                catch (IdentityNotMappedException)
                {
                }
                return sid != null;
            }
        }
    }
}