| | | 1 | | // Licensed to the .NET Foundation under one or more agreements. |
| | | 2 | | // The .NET Foundation licenses this file to you under the MIT license. |
| | | 3 | | |
| | | 4 | | using System; |
| | | 5 | | using System.Collections.Generic; |
| | | 6 | | using System.Collections.ObjectModel; |
| | | 7 | | using CoreWCF.IdentityModel; |
| | | 8 | | using CoreWCF.IdentityModel.Selectors; |
| | | 9 | | using CoreWCF.IdentityModel.Tokens; |
| | | 10 | | using CoreWCF.Security.Tokens; |
| | | 11 | | |
| | | 12 | | namespace CoreWCF.Security |
| | | 13 | | { |
| | | 14 | | /// <summary> |
| | | 15 | | /// The purpose of this class is to provide an ISecurityContextSecurityTokenCache contract over a SecurityTokenCache |
| | | 16 | | /// This allows for a consistent interface for the SecurityContextSecurityTokenHandler and a SessionSecurityTokenHan |
| | | 17 | | /// The SecurityTokenCache can be passed to the SecurityContextSecurityTokenHandler and wrapped to expose an ISecuri |
| | | 18 | | /// that can be set to the be the token cache for WCF context tokens |
| | | 19 | | /// </summary> |
| | | 20 | | internal class WrappedTokenCache : SecurityTokenResolver, ISecurityContextSecurityTokenCache |
| | | 21 | | { |
| | | 22 | | private readonly SessionSecurityTokenCache _tokenCache; |
| | | 23 | | private readonly SctClaimsHandler _claimsHandler; |
| | | 24 | | |
| | 0 | 25 | | public WrappedTokenCache(SessionSecurityTokenCache tokenCache, SctClaimsHandler sctClaimsHandler) |
| | | 26 | | { |
| | 0 | 27 | | _tokenCache = tokenCache ?? throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(tokenCac |
| | 0 | 28 | | _claimsHandler = sctClaimsHandler ?? throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof |
| | 0 | 29 | | } |
| | | 30 | | |
| | | 31 | | #region ISecurityContextSecurityTokenCache Members |
| | | 32 | | |
| | | 33 | | public void AddContext(SecurityContextSecurityToken token) |
| | | 34 | | { |
| | | 35 | | // |
| | | 36 | | // WCF will cache the token first before calling the WrappedSessionSecurityTokenHandler.OnTokenIssued. |
| | | 37 | | // We need to map the claims here so we will be caching the correct token with Geneva Claims substitued |
| | | 38 | | // in place of the WCF claims. |
| | | 39 | | // |
| | 0 | 40 | | _claimsHandler.SetPrincipalBootstrapTokensAndBindIdfxAuthPolicy(token); |
| | | 41 | | |
| | 0 | 42 | | SessionSecurityTokenCacheKey key = new SessionSecurityTokenCacheKey(_claimsHandler.EndpointId, token.Context |
| | 0 | 43 | | SessionSecurityToken sessionToken = SecurityContextSecurityTokenHelper.ConvertSctToSessionToken(token, Secur |
| | 0 | 44 | | DateTime expiryTime = DateTimeUtil.Add(sessionToken.ValidTo, _claimsHandler.SecurityTokenHandlerCollection.C |
| | 0 | 45 | | _tokenCache.AddOrUpdate(key, sessionToken, expiryTime); |
| | 0 | 46 | | } |
| | | 47 | | |
| | | 48 | | public void ClearContexts() |
| | | 49 | | { |
| | 0 | 50 | | _tokenCache.RemoveAll(_claimsHandler.EndpointId); |
| | 0 | 51 | | } |
| | | 52 | | |
| | | 53 | | /// <summary> |
| | | 54 | | /// Called to retrieve all tokens that match a particular contextId. WCF will call this |
| | | 55 | | /// </summary> |
| | | 56 | | /// <param name="contextId"></param> |
| | | 57 | | /// <returns></returns> |
| | | 58 | | public Collection<SecurityContextSecurityToken> GetAllContexts(System.Xml.UniqueId contextId) |
| | | 59 | | { |
| | 0 | 60 | | Collection<SecurityContextSecurityToken> tokens = new Collection<SecurityContextSecurityToken>(); |
| | | 61 | | |
| | 0 | 62 | | IEnumerable<SessionSecurityToken> cachedTokens = _tokenCache.GetAll(_claimsHandler.EndpointId, contextId); |
| | 0 | 63 | | if (cachedTokens != null) |
| | | 64 | | { |
| | 0 | 65 | | foreach (SessionSecurityToken sessionSct in cachedTokens) |
| | | 66 | | { |
| | 0 | 67 | | if (sessionSct != null && sessionSct.IsSecurityContextSecurityTokenWrapper) |
| | | 68 | | { |
| | 0 | 69 | | SecurityContextSecurityToken sctToken = SecurityContextSecurityTokenHelper.ConvertSessionTokenTo |
| | 0 | 70 | | tokens.Add(sctToken); |
| | | 71 | | } |
| | | 72 | | } |
| | | 73 | | } |
| | | 74 | | |
| | 0 | 75 | | return tokens; |
| | | 76 | | } |
| | | 77 | | |
| | | 78 | | public SecurityContextSecurityToken GetContext(System.Xml.UniqueId contextId, System.Xml.UniqueId generation) |
| | | 79 | | { |
| | 0 | 80 | | SessionSecurityToken token = null; |
| | 0 | 81 | | SessionSecurityTokenCacheKey key = new SessionSecurityTokenCacheKey(_claimsHandler.EndpointId, contextId, ge |
| | 0 | 82 | | token = _tokenCache.Get(key); |
| | | 83 | | |
| | 0 | 84 | | SecurityContextSecurityToken sctToken = null; |
| | | 85 | | |
| | 0 | 86 | | if (token != null && token.IsSecurityContextSecurityTokenWrapper) |
| | | 87 | | { |
| | 0 | 88 | | sctToken = SecurityContextSecurityTokenHelper.ConvertSessionTokenToSecurityContextSecurityToken(token); |
| | | 89 | | } |
| | | 90 | | |
| | 0 | 91 | | return sctToken; |
| | | 92 | | } |
| | | 93 | | |
| | | 94 | | /// <summary> |
| | | 95 | | /// Removes all the tokens that match the contextId. |
| | | 96 | | /// </summary> |
| | | 97 | | /// <param name="contextId">The context id.</param> |
| | | 98 | | /// <remarks> |
| | | 99 | | /// When WCF renews a token, its context id is the same as the issuedToken. The only |
| | | 100 | | /// difference is in the generationId. When WCF closes the session channel, all the tokens that |
| | | 101 | | /// were issued need to be removed that match the contextId. |
| | | 102 | | /// </remarks> |
| | | 103 | | public void RemoveAllContexts(System.Xml.UniqueId contextId) |
| | | 104 | | { |
| | 0 | 105 | | _tokenCache.RemoveAll(_claimsHandler.EndpointId, contextId); |
| | 0 | 106 | | } |
| | | 107 | | |
| | | 108 | | public void RemoveContext(System.Xml.UniqueId contextId, System.Xml.UniqueId generation) |
| | | 109 | | { |
| | 0 | 110 | | SessionSecurityTokenCacheKey key = new SessionSecurityTokenCacheKey(_claimsHandler.EndpointId, contextId, ge |
| | 0 | 111 | | _tokenCache.Remove(key); |
| | 0 | 112 | | } |
| | | 113 | | |
| | | 114 | | public bool TryAddContext(SecurityContextSecurityToken token) |
| | | 115 | | { |
| | | 116 | | // |
| | | 117 | | // WCF will cache the token first before calling the WrappedSessionSecurityTokenHandler.OnTokenIssued. |
| | | 118 | | // We need to map the claims here so we will be caching the correct token with Geneva Claims substitued |
| | | 119 | | // in place of the WCF claims. |
| | | 120 | | // |
| | 0 | 121 | | _claimsHandler.SetPrincipalBootstrapTokensAndBindIdfxAuthPolicy(token); |
| | | 122 | | |
| | 0 | 123 | | SessionSecurityTokenCacheKey key = new SessionSecurityTokenCacheKey(_claimsHandler.EndpointId, token.Context |
| | 0 | 124 | | SessionSecurityToken sessionToken = SecurityContextSecurityTokenHelper.ConvertSctToSessionToken(token, Secur |
| | 0 | 125 | | DateTime expiryTime = DateTimeUtil.Add(token.ValidTo, _claimsHandler.SecurityTokenHandlerCollection.Configur |
| | 0 | 126 | | _tokenCache.AddOrUpdate(key, sessionToken, expiryTime); |
| | 0 | 127 | | return true; |
| | | 128 | | } |
| | | 129 | | |
| | | 130 | | public void UpdateContextCachingTime(SecurityContextSecurityToken token, DateTime expirationTime) |
| | | 131 | | { |
| | 0 | 132 | | if (token.ValidTo <= expirationTime.ToUniversalTime()) |
| | | 133 | | { |
| | 0 | 134 | | return; |
| | | 135 | | } |
| | | 136 | | |
| | 0 | 137 | | SessionSecurityTokenCacheKey key = new SessionSecurityTokenCacheKey(_claimsHandler.EndpointId, token.Context |
| | 0 | 138 | | SessionSecurityToken sessionToken = SecurityContextSecurityTokenHelper.ConvertSctToSessionToken(token, Secur |
| | 0 | 139 | | DateTime expiryTime = DateTimeUtil.Add(sessionToken.ValidTo, _claimsHandler.SecurityTokenHandlerCollection.C |
| | 0 | 140 | | if (_tokenCache.Get(key) == null) |
| | | 141 | | { |
| | 0 | 142 | | throw DiagnosticUtility.ExceptionUtility.ThrowHelperInvalidOperation(SR.Format(SR.ID4285, sessionToken.C |
| | | 143 | | } |
| | 0 | 144 | | _tokenCache.AddOrUpdate(key, sessionToken, expiryTime); |
| | 0 | 145 | | } |
| | | 146 | | |
| | | 147 | | #endregion |
| | | 148 | | |
| | | 149 | | // these are not needed as this will never be used as an SecurityTokenResolver. |
| | | 150 | | protected override bool TryResolveSecurityKeyCore(SecurityKeyIdentifierClause keyIdentifierClause, out SecurityK |
| | | 151 | | { |
| | | 152 | | SecurityToken sct; |
| | 0 | 153 | | if (TryResolveTokenCore(keyIdentifierClause, out sct)) |
| | | 154 | | { |
| | 0 | 155 | | key = ((SecurityContextSecurityToken)sct).SecurityKeys[0]; |
| | 0 | 156 | | return true; |
| | | 157 | | } |
| | | 158 | | else |
| | | 159 | | { |
| | 0 | 160 | | key = null; |
| | 0 | 161 | | return false; |
| | | 162 | | } |
| | | 163 | | } |
| | | 164 | | |
| | | 165 | | protected override bool TryResolveTokenCore(SecurityKeyIdentifierClause keyIdentifierClause, out SecurityToken t |
| | | 166 | | { |
| | 0 | 167 | | SecurityContextKeyIdentifierClause sctSkiClause = keyIdentifierClause as SecurityContextKeyIdentifierClause; |
| | 0 | 168 | | if (sctSkiClause != null) |
| | | 169 | | { |
| | 0 | 170 | | token = GetContext(sctSkiClause.ContextId, sctSkiClause.Generation) as SecurityToken; |
| | | 171 | | } |
| | | 172 | | else |
| | | 173 | | { |
| | 0 | 174 | | token = null; |
| | | 175 | | } |
| | 0 | 176 | | return (token != null); |
| | | 177 | | } |
| | | 178 | | |
| | | 179 | | protected override bool TryResolveTokenCore(SecurityKeyIdentifier keyIdentifier, out SecurityToken token) |
| | | 180 | | { |
| | | 181 | | SecurityContextKeyIdentifierClause sctSkiClause; |
| | 0 | 182 | | if (keyIdentifier.TryFind<SecurityContextKeyIdentifierClause>(out sctSkiClause)) |
| | | 183 | | { |
| | 0 | 184 | | return TryResolveTokenCore(sctSkiClause, out token); |
| | | 185 | | } |
| | | 186 | | else |
| | | 187 | | { |
| | 0 | 188 | | token = null; |
| | 0 | 189 | | return false; |
| | | 190 | | } |
| | | 191 | | } |
| | | 192 | | } |
| | | 193 | | } |