File: System\IdentityModel\Tokens\AuthorizationPolicy.cs
Project: ndp\cdf\src\WCF\IdentityModel\System.IdentityModel.csproj (System.IdentityModel)
//------------------------------------------------------------------------------
//     Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------------------------
 
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IdentityModel.Diagnostics;
using System.IdentityModel.Policy;
using System.Security.Claims;
using System.Security.Principal;
 
using SysClaimSet = System.IdentityModel.Claims.ClaimSet;
 
namespace System.IdentityModel.Tokens
{
    /// <summary>
    /// Defines an AuthorizationPolicy that carries the IDFx Claims. When IDFx is enabled 
    /// a new set of Security Token Authenticators are added to the system. These Authenticators 
    /// will generate the new Claims defined in System.Security.Claims.
    /// </summary>
    internal class AuthorizationPolicy : IAuthorizationPolicy
    {
#pragma warning disable 1591
        public const string ClaimsPrincipalKey = "ClaimsPrincipal"; // This key must be different from "Principal". "Principal" is reserved for Custom mode.
        public const string IdentitiesKey = "Identities";
#pragma warning restore 1591
 
        List<ClaimsIdentity> _identityCollection = new List<ClaimsIdentity>();
 
        //
        // Add an issuer to specify that this is a IDFx issued AuthorizationPolicy.
        //
        SysClaimSet _issuer = SysClaimSet.System;
        string _id = UniqueId.CreateUniqueId();
 
        /// <summary>
        /// Initializes an instance of <see cref="AuthorizationPolicy"/>
        /// </summary>
        public AuthorizationPolicy()
        {
        }
 
        /// <summary>
        /// Initializes an instance of <see cref="AuthorizationPolicy"/>
        /// </summary>
        /// <param name="identity">ClaimsIdentity for the AuthorizationPolicy.</param>
        /// <exception cref="ArgumentNullException">One of the input argument is null.</exception>
        public AuthorizationPolicy(ClaimsIdentity identity)
        {
            if (identity == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("identity");
            }
 
            _identityCollection.Add(identity);
        }
 
        /// <summary>
        /// Initializes an instance of <see cref="AuthorizationPolicy"/>
        /// </summary>
        /// <param name="identityCollection">Collection of identities.</param>
        public AuthorizationPolicy(IEnumerable<ClaimsIdentity> identityCollection)
        {
            if (identityCollection == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("identityCollection");
            }
 
            List<ClaimsIdentity> collection = new List<ClaimsIdentity>();
            foreach (ClaimsIdentity identity in identityCollection)
            {
                collection.Add(identity);
            }
 
            _identityCollection = collection;
        }
 
        /// <summary>
        /// Gets a ClaimsIdentity collection.
        /// </summary>
        public ReadOnlyCollection<ClaimsIdentity> IdentityCollection
        {
            get
            {
                return _identityCollection.AsReadOnly();
            }
        }
 
        #region IAuthorizationPolicy Members
 
        /// <summary>
        /// Evaluates the current Policy. This is provided for backward compatibility
        /// of WCF Claims model. We always return true without affecting the EvaluationContext.
        /// </summary>
        /// <param name="evaluationContext">The current EvaluationContext.</param>
        /// <param name="state">The reference state object.</param>
        /// <returns>True if the Policy was successfully applied.</returns>
        public bool Evaluate(EvaluationContext evaluationContext, ref object state)
        {
            if (null == evaluationContext || null == evaluationContext.Properties)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("evaluationContext");
            }
 
            if (0 == _identityCollection.Count)
            {
                //
                // Nothing to do here.
                //
                return true;
            }
 
            //
            // Locate or create the ClaimsPrincipal
            //
            object principalObj = null;
            if (!evaluationContext.Properties.TryGetValue(ClaimsPrincipalKey, out principalObj))
            {
                ClaimsPrincipal principalToAdd = CreateClaimsPrincipalFromIdentities(_identityCollection);
 
                evaluationContext.Properties.Add(ClaimsPrincipalKey, principalToAdd);
 
                if (DiagnosticUtility.ShouldTrace(TraceEventType.Information))
                {
                    TraceUtility.TraceEvent(
                        TraceEventType.Information,
                        TraceCode.Diagnostics,
                        SR.GetString(SR.TraceSetPrincipalOnEvaluationContext),
                        new ClaimsPrincipalTraceRecord(principalToAdd),
                        null,
                        null);
                }
            }
            else
            {
                ClaimsPrincipal principal = principalObj as ClaimsPrincipal;
                if (null != principal && null != principal.Identities)
                {
                    principal.AddIdentities(_identityCollection);
                }
                else
                {
                    //
                    // Someone stomped on our ClaimsPrincipal property key in the properties collection
                    // Just trace this for now.
                    //
                    if (DiagnosticUtility.ShouldTrace(TraceEventType.Error))
                    {
                        TraceUtility.TraceString(
                            TraceEventType.Error,
                            SR.GetString(SR.ID8004,
                            ClaimsPrincipalKey));
                    }
                }
            }
 
            //
            // Locate or create evaluationContext.Properties[ "Identities" ] with identities
            //
            object identitiesObj = null;
            if (!evaluationContext.Properties.TryGetValue(IdentitiesKey, out identitiesObj))
            {
                List<ClaimsIdentity> identities = new List<ClaimsIdentity>();
                foreach (ClaimsIdentity ici in _identityCollection)
                {
                    identities.Add(ici);
                }
 
                evaluationContext.Properties.Add(IdentitiesKey, identities);
            }
            else
            {
                List<ClaimsIdentity> identities;
                identities = identitiesObj as List<ClaimsIdentity>;
 
                foreach (ClaimsIdentity ici in _identityCollection)
                {
                    identities.Add(ici);
                }
            }
 
 
            return true;
        }
 
        private static ClaimsPrincipal CreateClaimsPrincipalFromIdentities(IEnumerable<ClaimsIdentity> identities)
        {
            ClaimsIdentity selectedClaimsIdentity = SelectPrimaryIdentity(identities);
 
            if (selectedClaimsIdentity == null)
            {
                //return an anonymous identity
                return new ClaimsPrincipal(new ClaimsIdentity());
            }
 
            ClaimsPrincipal principal = CreateFromIdentity(selectedClaimsIdentity);
 
            // Add the remaining identities.
            foreach (ClaimsIdentity identity in identities)
            {
                if (identity != selectedClaimsIdentity)
                {
                    principal.AddIdentity(identity);
                }
            }
 
            return principal;
        }
 
        /// <summary>
        /// Creates the appropriate implementation of an IClaimsPrincipal base on the
        /// type of the specified IIdentity (e.g. WindowsClaimsPrincipal for a WindowsIdentity). 
        /// Note the appropriate IClaimsIdentity is generated based on the specified IIdentity
        /// as well.
        /// </summary>
        /// <param name="identity">An implementation of IIdentity</param>
        /// <returns>A claims-based principal.</returns>
        private static ClaimsPrincipal CreateFromIdentity(IIdentity identity)
        {
            if (null == identity)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("identity");
            }
 
            WindowsIdentity wci = identity as WindowsIdentity;
            if (null != wci)
            {
                return new WindowsPrincipal(wci);
            }
 
            WindowsIdentity wi = identity as WindowsIdentity;
            if (null != wi)
            {
                return new WindowsPrincipal(wi);
            }
 
            ClaimsIdentity ici = identity as ClaimsIdentity;
            if (null != ici)
            {
                return new ClaimsPrincipal(ici);
            }
 
            return new ClaimsPrincipal(new ClaimsIdentity(identity));
        }
 
        /// <summary>
        /// This method iterates through the collection of ClaimsIdentities
        /// and determines which identity must be used as the primary one.
        /// </summary>
        /// <remarks>
        /// If the identities collection contains a WindowsClaimsIdentity, it is the most preferred.
        /// If the identities collection contains an RsaClaimsIdentity, it is the least preferred.
        /// </remarks>
        private static ClaimsIdentity SelectPrimaryIdentity(IEnumerable<ClaimsIdentity> identities)
        {
            if (identities == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("identities");
            }
 
            //
            // Loop through the identities to determine the primary identity.
            //
            ClaimsIdentity selectedClaimsIdentity = null;
 
            foreach (ClaimsIdentity identity in identities)
            {
                if (identity is WindowsIdentity)
                {
                    //
                    // If there is a WindowsIdentity, return that.
                    //
                    selectedClaimsIdentity = identity;
                    break;
                }
                else if (identity.FindFirst(ClaimTypes.Rsa) != null)
                {
                    //this is a RSA identity
                    //it is the least preffered identity
                    if (selectedClaimsIdentity == null)
                    {
                        selectedClaimsIdentity = identity;
                    }
 
                    continue;
                }
                else if (selectedClaimsIdentity == null)
                {
                    //
                    // If no primary identity has been selected yet, choose the current identity.
                    //
                    selectedClaimsIdentity = identity;
                }
            }
 
            return selectedClaimsIdentity;
        }
 
 
        /// <summary>
        /// Gets the Issuer Claimset. This will return a DefaultClaimSet with just one claim 
        /// whose ClaimType is http://schemas.microsoft.com/claims/identityclaim.
        /// </summary>
        public SysClaimSet Issuer
        {
            get
            {
                return _issuer;
            }
        }
 
        #endregion
 
        #region IAuthorizationComponent Members
 
        /// <summary>
        /// Returns an Id for the ClaimsPrincipal.
        /// </summary>
        public string Id
        {
            get
            {
                return _id;
            }
        }
 
        #endregion
    }
 
}