209 lines
9.1 KiB
C#
209 lines
9.1 KiB
C#
|
//------------------------------------------------------------------------------
|
|||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|||
|
//------------------------------------------------------------------------------
|
|||
|
|
|||
|
using System;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.Collections.ObjectModel;
|
|||
|
using System.IdentityModel;
|
|||
|
using System.IdentityModel.Selectors;
|
|||
|
using System.IdentityModel.Tokens;
|
|||
|
using System.ServiceModel.Security.Tokens;
|
|||
|
|
|||
|
using SystemUniqueId = System.Xml.UniqueId;
|
|||
|
using SR = System.ServiceModel.SR;
|
|||
|
|
|||
|
namespace System.ServiceModel.Security
|
|||
|
{
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The purpose of this class is to provide an ISecurityContextSecurityTokenCache contract over a SecurityTokenCache.
|
|||
|
/// This allows for a consistent interface for the SecurityContextSecurityTokenHandler and a SessionSecurityTokenHandler.
|
|||
|
/// The SecurityTokenCache can be passed to the SecurityContextSecurityTokenHandler and wrapped to expose an ISecurityContextSecurityTokenCache
|
|||
|
/// that can be set to the be the token cache for WCF context tokens
|
|||
|
/// </summary>
|
|||
|
class WrappedTokenCache : SecurityTokenResolver, ISecurityContextSecurityTokenCache
|
|||
|
{
|
|||
|
SessionSecurityTokenCache _tokenCache;
|
|||
|
SctClaimsHandler _claimsHandler;
|
|||
|
|
|||
|
public WrappedTokenCache(SessionSecurityTokenCache tokenCache, SctClaimsHandler sctClaimsHandler)
|
|||
|
{
|
|||
|
if (tokenCache == null)
|
|||
|
{
|
|||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokenCache");
|
|||
|
}
|
|||
|
|
|||
|
if (sctClaimsHandler == null)
|
|||
|
{
|
|||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("sctClaimsHandler");
|
|||
|
}
|
|||
|
|
|||
|
_tokenCache = tokenCache;
|
|||
|
_claimsHandler = sctClaimsHandler;
|
|||
|
}
|
|||
|
|
|||
|
#region ISecurityContextSecurityTokenCache Members
|
|||
|
|
|||
|
public void AddContext(SecurityContextSecurityToken token)
|
|||
|
{
|
|||
|
//
|
|||
|
// WCF will cache the token first before calling the WrappedSessionSecurityTokenHandler.OnTokenIssued.
|
|||
|
// We need to map the claims here so we will be caching the correct token with Geneva Claims substitued
|
|||
|
// in place of the WCF claims.
|
|||
|
//
|
|||
|
_claimsHandler.SetPrincipalBootstrapTokensAndBindIdfxAuthPolicy(token);
|
|||
|
|
|||
|
SessionSecurityTokenCacheKey key = new SessionSecurityTokenCacheKey(_claimsHandler.EndpointId, token.ContextId, token.KeyGeneration);
|
|||
|
SessionSecurityToken sessionToken = SecurityContextSecurityTokenHelper.ConvertSctToSessionToken(token, SecureConversationVersion.Default);
|
|||
|
DateTime expiryTime = DateTimeUtil.Add(sessionToken.ValidTo, _claimsHandler.SecurityTokenHandlerCollection.Configuration.MaxClockSkew);
|
|||
|
_tokenCache.AddOrUpdate(key, sessionToken, expiryTime);
|
|||
|
}
|
|||
|
|
|||
|
public void ClearContexts()
|
|||
|
{
|
|||
|
_tokenCache.RemoveAll(_claimsHandler.EndpointId);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Called to retrieve all tokens that match a particular contextId. WCF will call this
|
|||
|
/// </summary>
|
|||
|
/// <param name="contextId"></param>
|
|||
|
/// <returns></returns>
|
|||
|
public Collection<SecurityContextSecurityToken> GetAllContexts(System.Xml.UniqueId contextId)
|
|||
|
{
|
|||
|
Collection<SecurityContextSecurityToken> tokens = new Collection<SecurityContextSecurityToken>();
|
|||
|
|
|||
|
IEnumerable<SessionSecurityToken> cachedTokens = _tokenCache.GetAll(_claimsHandler.EndpointId, contextId);
|
|||
|
if (cachedTokens != null)
|
|||
|
{
|
|||
|
foreach (SessionSecurityToken sessionSct in cachedTokens)
|
|||
|
{
|
|||
|
if (sessionSct != null && sessionSct.IsSecurityContextSecurityTokenWrapper)
|
|||
|
{
|
|||
|
SecurityContextSecurityToken sctToken = SecurityContextSecurityTokenHelper.ConvertSessionTokenToSecurityContextSecurityToken(sessionSct);
|
|||
|
tokens.Add(sctToken);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return tokens;
|
|||
|
}
|
|||
|
|
|||
|
public SecurityContextSecurityToken GetContext(System.Xml.UniqueId contextId, System.Xml.UniqueId generation)
|
|||
|
{
|
|||
|
SessionSecurityToken token = null;
|
|||
|
SessionSecurityTokenCacheKey key = new SessionSecurityTokenCacheKey(_claimsHandler.EndpointId, contextId, generation);
|
|||
|
token = _tokenCache.Get(key);
|
|||
|
|
|||
|
SecurityContextSecurityToken sctToken = null;
|
|||
|
|
|||
|
if (token != null && token.IsSecurityContextSecurityTokenWrapper)
|
|||
|
{
|
|||
|
sctToken = SecurityContextSecurityTokenHelper.ConvertSessionTokenToSecurityContextSecurityToken(token);
|
|||
|
}
|
|||
|
|
|||
|
return sctToken;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Removes all the tokens that match the contextId.
|
|||
|
/// </summary>
|
|||
|
/// <param name="contextId">The context id.</param>
|
|||
|
/// <remarks>
|
|||
|
/// When WCF renews a token, its context id is the same as the issuedToken. The only
|
|||
|
/// difference is in the generationId. When WCF closes the session channel, all the tokens that
|
|||
|
/// were issued need to be removed that match the contextId.
|
|||
|
/// </remarks>
|
|||
|
public void RemoveAllContexts(System.Xml.UniqueId contextId)
|
|||
|
{
|
|||
|
_tokenCache.RemoveAll(_claimsHandler.EndpointId, contextId);
|
|||
|
}
|
|||
|
|
|||
|
public void RemoveContext(System.Xml.UniqueId contextId, System.Xml.UniqueId generation)
|
|||
|
{
|
|||
|
SessionSecurityTokenCacheKey key = new SessionSecurityTokenCacheKey(_claimsHandler.EndpointId, contextId, generation);
|
|||
|
_tokenCache.Remove(key);
|
|||
|
}
|
|||
|
|
|||
|
public bool TryAddContext(SecurityContextSecurityToken token)
|
|||
|
{
|
|||
|
//
|
|||
|
// WCF will cache the token first before calling the WrappedSessionSecurityTokenHandler.OnTokenIssued.
|
|||
|
// We need to map the claims here so we will be caching the correct token with Geneva Claims substitued
|
|||
|
// in place of the WCF claims.
|
|||
|
//
|
|||
|
_claimsHandler.SetPrincipalBootstrapTokensAndBindIdfxAuthPolicy(token);
|
|||
|
|
|||
|
SessionSecurityTokenCacheKey key = new SessionSecurityTokenCacheKey(_claimsHandler.EndpointId, token.ContextId, token.KeyGeneration);
|
|||
|
SessionSecurityToken sessionToken = SecurityContextSecurityTokenHelper.ConvertSctToSessionToken(token, SecureConversationVersion.Default);
|
|||
|
DateTime expiryTime = DateTimeUtil.Add(token.ValidTo, _claimsHandler.SecurityTokenHandlerCollection.Configuration.MaxClockSkew);
|
|||
|
_tokenCache.AddOrUpdate(key, sessionToken, expiryTime);
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
public void UpdateContextCachingTime(SecurityContextSecurityToken token, DateTime expirationTime)
|
|||
|
{
|
|||
|
if (token.ValidTo <= expirationTime.ToUniversalTime())
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
SessionSecurityTokenCacheKey key = new SessionSecurityTokenCacheKey(_claimsHandler.EndpointId, token.ContextId, token.KeyGeneration);
|
|||
|
SessionSecurityToken sessionToken = SecurityContextSecurityTokenHelper.ConvertSctToSessionToken(token, SecureConversationVersion.Default);
|
|||
|
DateTime expiryTime = DateTimeUtil.Add(sessionToken.ValidTo, _claimsHandler.SecurityTokenHandlerCollection.Configuration.MaxClockSkew);
|
|||
|
if (_tokenCache.Get(key) == null)
|
|||
|
{
|
|||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID4285, sessionToken.ContextId.ToString()));
|
|||
|
}
|
|||
|
_tokenCache.AddOrUpdate(key, sessionToken, expiryTime);
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
// these are not needed as this will never be used as an SecurityTokenResolver.
|
|||
|
protected override bool TryResolveSecurityKeyCore(SecurityKeyIdentifierClause keyIdentifierClause, out SecurityKey key)
|
|||
|
{
|
|||
|
SecurityToken sct;
|
|||
|
if (TryResolveTokenCore(keyIdentifierClause, out sct))
|
|||
|
{
|
|||
|
key = ((SecurityContextSecurityToken)sct).SecurityKeys[0];
|
|||
|
return true;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
key = null;
|
|||
|
return false;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
protected override bool TryResolveTokenCore(SecurityKeyIdentifierClause keyIdentifierClause, out SecurityToken token)
|
|||
|
{
|
|||
|
SecurityContextKeyIdentifierClause sctSkiClause = keyIdentifierClause as SecurityContextKeyIdentifierClause;
|
|||
|
if (sctSkiClause != null)
|
|||
|
{
|
|||
|
token = GetContext(sctSkiClause.ContextId, sctSkiClause.Generation) as SecurityToken;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
token = null;
|
|||
|
}
|
|||
|
return (token != null);
|
|||
|
}
|
|||
|
|
|||
|
protected override bool TryResolveTokenCore(SecurityKeyIdentifier keyIdentifier, out SecurityToken token)
|
|||
|
{
|
|||
|
SecurityContextKeyIdentifierClause sctSkiClause;
|
|||
|
if (keyIdentifier.TryFind<SecurityContextKeyIdentifierClause>(out sctSkiClause))
|
|||
|
{
|
|||
|
return TryResolveTokenCore(sctSkiClause, out token);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
token = null;
|
|||
|
return false;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|