1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
|
//------------------------------------------------------------------------------
// 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;
}
}
}
}
|