//------------------------------------------------------------------------------
// 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
{
///
/// 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.
///
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 _identityCollection = new List();
//
// Add an issuer to specify that this is a IDFx issued AuthorizationPolicy.
//
SysClaimSet _issuer = SysClaimSet.System;
string _id = UniqueId.CreateUniqueId();
///
/// Initializes an instance of
///
public AuthorizationPolicy()
{
}
///
/// Initializes an instance of
///
/// ClaimsIdentity for the AuthorizationPolicy.
/// One of the input argument is null.
public AuthorizationPolicy(ClaimsIdentity identity)
{
if (identity == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("identity");
}
_identityCollection.Add(identity);
}
///
/// Initializes an instance of
///
/// Collection of identities.
public AuthorizationPolicy(IEnumerable identityCollection)
{
if (identityCollection == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("identityCollection");
}
List collection = new List();
foreach (ClaimsIdentity identity in identityCollection)
{
collection.Add(identity);
}
_identityCollection = collection;
}
///
/// Gets a ClaimsIdentity collection.
///
public ReadOnlyCollection IdentityCollection
{
get
{
return _identityCollection.AsReadOnly();
}
}
#region IAuthorizationPolicy Members
///
/// Evaluates the current Policy. This is provided for backward compatibility
/// of WCF Claims model. We always return true without affecting the EvaluationContext.
///
/// The current EvaluationContext.
/// The reference state object.
/// True if the Policy was successfully applied.
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 identities = new List();
foreach (ClaimsIdentity ici in _identityCollection)
{
identities.Add(ici);
}
evaluationContext.Properties.Add(IdentitiesKey, identities);
}
else
{
List identities;
identities = identitiesObj as List;
foreach (ClaimsIdentity ici in _identityCollection)
{
identities.Add(ici);
}
}
return true;
}
private static ClaimsPrincipal CreateClaimsPrincipalFromIdentities(IEnumerable 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;
}
///
/// 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.
///
/// An implementation of IIdentity
/// A claims-based principal.
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));
}
///
/// This method iterates through the collection of ClaimsIdentities
/// and determines which identity must be used as the primary one.
///
///
/// If the identities collection contains a WindowsClaimsIdentity, it is the most preferred.
/// If the identities collection contains an RsaClaimsIdentity, it is the least preferred.
///
private static ClaimsIdentity SelectPrimaryIdentity(IEnumerable 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;
}
///
/// Gets the Issuer Claimset. This will return a DefaultClaimSet with just one claim
/// whose ClaimType is http://schemas.microsoft.com/claims/identityclaim.
///
public SysClaimSet Issuer
{
get
{
return _issuer;
}
}
#endregion
#region IAuthorizationComponent Members
///
/// Returns an Id for the ClaimsPrincipal.
///
public string Id
{
get
{
return _id;
}
}
#endregion
}
}