| | | 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 System.Linq; |
| | | 8 | | using System.Reflection; |
| | | 9 | | using System.Threading.Tasks; |
| | | 10 | | using CoreWCF.Channels; |
| | | 11 | | using CoreWCF.IdentityModel.Policy; |
| | | 12 | | using CoreWCF.Security; |
| | | 13 | | |
| | | 14 | | namespace CoreWCF |
| | | 15 | | { |
| | | 16 | | public class ServiceAuthorizationManager |
| | | 17 | | { |
| | | 18 | | private readonly bool _isAsyncImplementation; |
| | | 19 | | |
| | 7 | 20 | | public ServiceAuthorizationManager() |
| | | 21 | | { |
| | 7 | 22 | | Type implementorType = GetType(); |
| | 7 | 23 | | var methods = implementorType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instanc |
| | | 24 | | |
| | 7 | 25 | | var checkAccessCoreAsyncMethodInfo = (from method in methods |
| | 91 | 26 | | where method.Name == nameof(CheckAccessCoreAsync) |
| | 7 | 27 | | let parameters = method.GetParameters() |
| | 7 | 28 | | where parameters.Length == 1 |
| | 7 | 29 | | let firstParameter = parameters[0] |
| | 7 | 30 | | where firstParameter.ParameterType == typeof(OperationContext) |
| | 14 | 31 | | select method).SingleOrDefault(); |
| | | 32 | | |
| | 7 | 33 | | var baseCheckAccessCoreAsyncMethodInfo = checkAccessCoreAsyncMethodInfo!.GetBaseDefinition(); |
| | | 34 | | |
| | 7 | 35 | | bool isCheckAccessCoreAsyncOverridden = baseCheckAccessCoreAsyncMethodInfo.DeclaringType != checkAccessCoreA |
| | | 36 | | |
| | 7 | 37 | | var checkAccessAsyncWithSingleParameterMethodInfo = (from method in methods |
| | 91 | 38 | | where method.Name == nameof(CheckAccessAsync) |
| | 14 | 39 | | let parameters = method.GetParameters() |
| | 14 | 40 | | where parameters.Length == 1 |
| | 7 | 41 | | let firstParameter = parameters[0] |
| | 7 | 42 | | where firstParameter.ParameterType == typeof(OperationContext) |
| | 14 | 43 | | select method).SingleOrDefault(); |
| | | 44 | | |
| | 7 | 45 | | var baseCheckAccessAsyncWithSingleParameterMethodInfo = |
| | 7 | 46 | | checkAccessAsyncWithSingleParameterMethodInfo!.GetBaseDefinition(); |
| | | 47 | | |
| | 7 | 48 | | bool isCheckAccessAsyncWithSingleParameterOverridden = |
| | 7 | 49 | | baseCheckAccessAsyncWithSingleParameterMethodInfo.DeclaringType != checkAccessAsyncWithSingleParameterMe |
| | | 50 | | |
| | 7 | 51 | | _isAsyncImplementation = isCheckAccessCoreAsyncOverridden || isCheckAccessAsyncWithSingleParameterOverridden |
| | 7 | 52 | | } |
| | | 53 | | |
| | | 54 | | // This is the API called by framework to perform CheckAccess. |
| | | 55 | | // The API is responsible for ... |
| | | 56 | | // 1) Evaluate all policies (Forward\Backward) |
| | | 57 | | // 2) Optionally wire up the resulting AuthorizationContext |
| | | 58 | | // to ServiceSecurityContext. |
| | | 59 | | // 3) An availability of message content to make an authoritive decision. |
| | | 60 | | // 4) Return the authoritive decision true/false (allow/deny). |
| | | 61 | | [Obsolete("Implementers should override CheckAccessAsync.")] |
| | 15 | 62 | | public virtual bool CheckAccess(OperationContext operationContext, ref Message message) => CheckAccess(operation |
| | | 63 | | |
| | | 64 | | public virtual async ValueTask<(bool isAuthorized, Message message)> CheckAccessAsync(OperationContext operation |
| | | 65 | | { |
| | 17 | 66 | | if (_isAsyncImplementation) |
| | | 67 | | { |
| | 2 | 68 | | var isAuthorized = await CheckAccessAsync(operationContext); |
| | 2 | 69 | | return (isAuthorized, message); |
| | | 70 | | } |
| | | 71 | | |
| | | 72 | | // delegate to matching sync call overload |
| | 15 | 73 | | bool checkAccessResult = CheckAccess(operationContext, ref message); |
| | 15 | 74 | | return (checkAccessResult, message); |
| | 17 | 75 | | } |
| | | 76 | | |
| | | 77 | | [Obsolete("Implementers should override CheckAccessAsync.")] |
| | | 78 | | public virtual bool CheckAccess(OperationContext operationContext) |
| | | 79 | | { |
| | 15 | 80 | | if (operationContext == null) |
| | | 81 | | { |
| | 0 | 82 | | throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(operationContext)); |
| | | 83 | | } |
| | | 84 | | |
| | | 85 | | // default to forward-chaining implementation |
| | | 86 | | // 1) Get policies that will participate in chain process. |
| | | 87 | | // We provide a safe default policies set below. |
| | 15 | 88 | | ReadOnlyCollection<IAuthorizationPolicy> authorizationPolicies = GetAuthorizationPolicies(operationContext); |
| | | 89 | | |
| | | 90 | | // 2) Do forward chaining and wire the new ServiceSecurityContext |
| | 15 | 91 | | operationContext.IncomingMessageProperties.Security.ServiceSecurityContext = |
| | 15 | 92 | | new ServiceSecurityContext(authorizationPolicies ?? EmptyReadOnlyCollection<IAuthorizationPolicy>.Instan |
| | | 93 | | |
| | | 94 | | // 3) Call the CheckAccessCore(OperationContext operationContext) |
| | 15 | 95 | | return CheckAccessCore(operationContext); |
| | | 96 | | } |
| | | 97 | | |
| | | 98 | | public virtual async ValueTask<bool> CheckAccessAsync(OperationContext operationContext) |
| | | 99 | | { |
| | 2 | 100 | | if (operationContext == null) |
| | | 101 | | { |
| | 0 | 102 | | throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(operationContext)); |
| | | 103 | | } |
| | | 104 | | |
| | | 105 | | // default to forward-chaining implementation |
| | | 106 | | // 1) Get policies that will participate in chain process. |
| | | 107 | | // We provide a safe default policies set below. |
| | 2 | 108 | | ReadOnlyCollection<IAuthorizationPolicy> authorizationPolicies = GetAuthorizationPolicies(operationContext); |
| | | 109 | | |
| | | 110 | | // 2) Do forward chaining and wire the new ServiceSecurityContext |
| | 2 | 111 | | operationContext.IncomingMessageProperties.Security.ServiceSecurityContext = |
| | 2 | 112 | | new ServiceSecurityContext(authorizationPolicies ?? EmptyReadOnlyCollection<IAuthorizationPolicy>.Instan |
| | | 113 | | |
| | | 114 | | // 3) Call the CheckAccessCoreAsync(OperationContext operationContext) |
| | 2 | 115 | | return await CheckAccessCoreAsync(operationContext); |
| | 2 | 116 | | } |
| | | 117 | | |
| | | 118 | | // Define the set of policies taking part in chaining. We will provide |
| | | 119 | | // the safe default set (primary token + all supporting tokens except token with |
| | | 120 | | // with SecurityTokenAttachmentMode.Signed + transport token). Implementor |
| | | 121 | | // can override and provide different selection of policies set. |
| | | 122 | | protected virtual ReadOnlyCollection<IAuthorizationPolicy> GetAuthorizationPolicies(OperationContext operationCo |
| | | 123 | | { |
| | 17 | 124 | | SecurityMessageProperty security = operationContext.IncomingMessageProperties.Security; |
| | 17 | 125 | | if (security == null) |
| | | 126 | | { |
| | 0 | 127 | | return EmptyReadOnlyCollection<IAuthorizationPolicy>.Instance; |
| | | 128 | | } |
| | | 129 | | |
| | 17 | 130 | | ReadOnlyCollection<IAuthorizationPolicy> externalPolicies = security.ExternalAuthorizationPolicies; |
| | 17 | 131 | | if (security.ServiceSecurityContext == null) |
| | | 132 | | { |
| | 0 | 133 | | return externalPolicies ?? EmptyReadOnlyCollection<IAuthorizationPolicy>.Instance; |
| | | 134 | | } |
| | | 135 | | |
| | 17 | 136 | | ReadOnlyCollection<IAuthorizationPolicy> authorizationPolicies = security.ServiceSecurityContext.Authorizati |
| | 17 | 137 | | if (externalPolicies == null || externalPolicies.Count <= 0) |
| | | 138 | | { |
| | 0 | 139 | | return authorizationPolicies; |
| | | 140 | | } |
| | | 141 | | |
| | | 142 | | // Combine |
| | 17 | 143 | | List<IAuthorizationPolicy> policies = new(authorizationPolicies); |
| | 17 | 144 | | policies.AddRange(externalPolicies); |
| | 17 | 145 | | return policies.AsReadOnly(); |
| | | 146 | | } |
| | | 147 | | |
| | | 148 | | [Obsolete("Implementers should override CheckAccessCoreAsync.")] |
| | 13 | 149 | | protected virtual bool CheckAccessCore(OperationContext operationContext) => true; |
| | | 150 | | |
| | | 151 | | // Implementor overrides this API to make authoritive decision. |
| | | 152 | | // The AuthorizationContext in opContext is generally the result from forward chain. |
| | 0 | 153 | | protected virtual ValueTask<bool> CheckAccessCoreAsync(OperationContext operationContext) => new (true); |
| | | 154 | | |
| | | 155 | | } |
| | | 156 | | } |