Пример #1
0
        private static StructuredTransformer CreateTransformer(TransformBuilderContext context)
        {
            // RequestHeaderOriginalHostKey defaults to false, and CopyRequestHeaders defaults to true.
            // If RequestHeaderOriginalHostKey was not specified then we need to make sure the transform gets
            // added anyways to remove the original host. If CopyRequestHeaders is false then we can omit the
            // transform.
            if (context.CopyRequestHeaders.GetValueOrDefault(true) &&
                !context.RequestTransforms.Any(item => item is RequestHeaderOriginalHostTransform))
            {
                context.AddOriginalHost(false);
            }

            // Add default forwarders only if they haven't already been added or disabled.
            if (context.UseDefaultForwarders.GetValueOrDefault(true))
            {
                context.AddXForwarded();
            }

            return(new StructuredTransformer(
                       context.CopyRequestHeaders,
                       context.CopyResponseHeaders,
                       context.CopyResponseTrailers,
                       context.RequestTransforms,
                       context.ResponseTransforms,
                       context.ResponseTrailersTransforms));
        }
 /// <summary>
 /// Adds the transform that will append or set the query parameter from a route value.
 /// </summary>
 public static TransformBuilderContext AddQueryRouteValue(this TransformBuilderContext context, string queryKey, string routeValueKey, bool append = true)
 {
     context.RequestTransforms.Add(new QueryParameterRouteTransform(
                                       append ? QueryStringTransformMode.Append : QueryStringTransformMode.Set,
                                       queryKey, routeValueKey));
     return(context);
 }
    private static void ValidatePathRouteValues(TransformBuilderContext builderContext)
    {
        var requestTransform         = Assert.Single(builderContext.RequestTransforms);
        var pathRouteValuesTransform = Assert.IsType <PathRouteValuesTransform>(requestTransform);

        Assert.Equal("/path#", pathRouteValuesTransform.Pattern.RawText);
    }
        public bool Build(TransformBuilderContext context, IReadOnlyDictionary <string, string> transformValues)
        {
            if (transformValues.TryGetValue(PathSetKey, out var pathSet))
            {
                TransformHelpers.CheckTooManyParameters(transformValues, expected: 1);
                var path = MakePathString(pathSet);
                context.AddPathSet(path);
            }
            else if (transformValues.TryGetValue(PathPrefixKey, out var pathPrefix))
            {
                TransformHelpers.CheckTooManyParameters(transformValues, expected: 1);
                var path = MakePathString(pathPrefix);
                context.AddPathPrefix(path);
            }
            else if (transformValues.TryGetValue(PathRemovePrefixKey, out var pathRemovePrefix))
            {
                TransformHelpers.CheckTooManyParameters(transformValues, expected: 1);
                var path = MakePathString(pathRemovePrefix);
                context.AddPathRemovePrefix(path);
            }
            else if (transformValues.TryGetValue(PathPatternKey, out var pathPattern))
            {
                TransformHelpers.CheckTooManyParameters(transformValues, expected: 1);
                var path = MakePathString(pathPattern);
                // We don't use the extension here because we want to avoid doing a DI lookup for the binder every time.
                context.RequestTransforms.Add(new PathRouteValuesTransform(path.Value, _binderFactory));
            }
            else
            {
                return(false);
            }

            return(true);
        }
Пример #5
0
 internal static void RemoveAllXForwardedHeaders(TransformBuilderContext context, string prefix)
 {
     context.AddXForwardedFor(prefix + ForwardedTransformFactory.ForKey, ForwardedTransformActions.Remove);
     context.AddXForwardedPrefix(prefix + ForwardedTransformFactory.PrefixKey, ForwardedTransformActions.Remove);
     context.AddXForwardedHost(prefix + ForwardedTransformFactory.HostKey, ForwardedTransformActions.Remove);
     context.AddXForwardedProto(prefix + ForwardedTransformFactory.ProtoKey, ForwardedTransformActions.Remove);
 }
        public bool Build(TransformBuilderContext context, IReadOnlyDictionary <string, string> transformValues)
        {
            if (transformValues.TryGetValue(RequestHeadersCopyKey, out var copyHeaders))
            {
                TransformHelpers.CheckTooManyParameters(transformValues, expected: 1);
                context.CopyRequestHeaders = bool.Parse(copyHeaders);
            }
            else if (transformValues.TryGetValue(RequestHeaderOriginalHostKey, out var originalHost))
            {
                TransformHelpers.CheckTooManyParameters(transformValues, expected: 1);
                context.UseOriginalHost = bool.Parse(originalHost);
            }
            else if (transformValues.TryGetValue(RequestHeaderKey, out var headerName))
            {
                TransformHelpers.CheckTooManyParameters(transformValues, expected: 2);
                if (transformValues.TryGetValue(SetKey, out var setValue))
                {
                    context.AddRequestHeader(headerName, setValue, append: false);
                }
                else if (transformValues.TryGetValue(AppendKey, out var appendValue))
                {
                    context.AddRequestHeader(headerName, appendValue, append: true);
                }
                else
                {
                    throw new ArgumentException($"Unexpected parameters for RequestHeader: {string.Join(';', transformValues.Keys)}. Expected 'Set' or 'Append'");
                }
            }
            else
            {
                return(false);
            }

            return(true);
        }
 public bool Build(TransformBuilderContext context, IReadOnlyDictionary <string, string> transformValues)
 {
     Assert.NotNull(context.Services);
     Assert.NotNull(context.Route);
     BuildCalls++;
     if (transformValues.TryGetValue(_v, out var _))
     {
         context.AddResponseTrailersTransform(context => default);
    private static void ValidatePathRemovePrefix(TransformBuilderContext builderContext)
    {
        var requestTransform    = Assert.Single(builderContext.RequestTransforms);
        var pathStringTransform = Assert.IsType <PathStringTransform>(requestTransform);

        Assert.Equal(PathStringTransform.PathTransformMode.RemovePrefix, pathStringTransform.Mode);
        Assert.Equal("/path#", pathStringTransform.Value.Value);
    }
 /// <summary>
 /// Adds the transform which will add X-Forwarded-* request headers.
 /// </summary>
 public static TransformBuilderContext AddXForwarded(this TransformBuilderContext context, ForwardedTransformActions action = ForwardedTransformActions.Set)
 {
     context.AddXForwardedFor(action: action);
     context.AddXForwardedPrefix(action: action);
     context.AddXForwardedHost(action: action);
     context.AddXForwardedProto(action: action);
     return(context);
 }
    public void Apply(TransformBuilderContext context)
    {
        var options = context.Cluster?.SessionAffinity;

        if (options != null && options.Enabled.GetValueOrDefault())
        {
            var policy = _sessionAffinityPolicies.GetRequiredServiceById(options.Policy, SessionAffinityConstants.Policies.Cookie);
            context.ResponseTransforms.Add(new AffinitizeTransform(policy));
        }
    }
Пример #11
0
        public void Apply(TransformBuilderContext context)
        {
            var options = context.Cluster?.SessionAffinity;

            if ((options?.Enabled).GetValueOrDefault())
            {
                var provider = _sessionAffinityProviders.GetRequiredServiceById(options.Mode, SessionAffinityConstants.Modes.Cookie);
                context.ResponseTransforms.Add(new AffinitizeTransform(provider));
            }
        }
Пример #12
0
 /// <summary>
 /// Adds the transform which will add X-Forwarded-Proto request header.
 /// </summary>
 public static TransformBuilderContext AddXForwardedProto(this TransformBuilderContext context, string headerName = "X-Forwarded-Proto", ForwardedTransformActions action = ForwardedTransformActions.Set)
 {
     context.UseDefaultForwarders = false;
     if (action == ForwardedTransformActions.Off)
     {
         return(context);
     }
     context.RequestTransforms.Add(new RequestHeaderXForwardedProtoTransform(headerName, action));
     return(context);
 }
Пример #13
0
        internal StructuredTransformer CreateInternal(Action <TransformBuilderContext> action)
        {
            var context = new TransformBuilderContext
            {
                Services = _services,
            };

            action(context);

            return(CreateTransformer(context));
        }
Пример #14
0
        /// <summary>
        /// Adds the transform which will add X-Forwarded-* request headers.
        /// </summary>
        public static TransformBuilderContext AddXForwarded(this TransformBuilderContext context, ForwardedTransformActions action = ForwardedTransformActions.Set)
        {
            context.AddXForwardedFor(action: action);
            context.AddXForwardedPrefix(action: action);
            context.AddXForwardedHost(action: action);
            context.AddXForwardedProto(action: action);

            // Remove the Forwarded header when an X-Forwarded transform is enabled
            TransformHelpers.RemoveForwardedHeader(context);
            return(context);
        }
Пример #15
0
    /// <summary>
    /// Clones the route and adds the transform which will set the request path with the given value.
    /// </summary>
    public static TransformBuilderContext AddPathRouteValues(this TransformBuilderContext context, PathString pattern)
    {
        if (pattern.Value is null)
        {
            throw new ArgumentNullException(nameof(pattern));
        }

        var binder = context.Services.GetRequiredService <TemplateBinderFactory>();

        context.RequestTransforms.Add(new PathRouteValuesTransform(pattern.Value, binder));
        return(context);
    }
Пример #16
0
 /// <summary>
 /// Adds the transform which will copy or remove the original host header.
 /// </summary>
 public static TransformBuilderContext AddOriginalHost(this TransformBuilderContext context, bool useOriginal = true)
 {
     if (useOriginal)
     {
         context.RequestTransforms.Add(RequestHeaderOriginalHostTransform.OriginalHost);
     }
     else
     {
         context.RequestTransforms.Add(RequestHeaderOriginalHostTransform.SuppressHost);
     }
     return(context);
 }
Пример #17
0
        public bool Build(TransformBuilderContext context, IReadOnlyDictionary <string, string> transformValues)
        {
            if (transformValues.TryGetValue("CustomTransform", out var value))
            {
                if (string.IsNullOrEmpty(value))
                {
                    throw new ArgumentException("A non-empty CustomTransform value is required");
                }

                context.AddRequestTransform(transformContext =>
                {
                    transformContext.ProxyRequest.Options.Set(new HttpRequestOptionsKey <string>("CustomTransform"), value);
                    return(default);
Пример #18
0
 /// <summary>
 /// Adds the transform which will add the Forwarded header as defined by [RFC 7239](https://tools.ietf.org/html/rfc7239).
 /// </summary>
 public static TransformBuilderContext AddForwarded(this TransformBuilderContext context,
                                                    bool useHost        = true, bool useProto = true, NodeFormat forFormat = NodeFormat.Random,
                                                    NodeFormat byFormat = NodeFormat.Random, bool append = true)
 {
     context.UseDefaultForwarders = false;
     if (byFormat != NodeFormat.None || forFormat != NodeFormat.None || useHost || useProto)
     {
         var random = context.Services.GetRequiredService <IRandomFactory>();
         context.RequestTransforms.Add(new RequestHeaderForwardedTransform(random,
                                                                           forFormat, byFormat, useHost, useProto, append));
     }
     return(context);
 }
    /// <inheritdoc />
    public void Apply(TransformBuilderContext transformBuildContext)
    {
        var routeValue   = transformBuildContext.Route.Metadata?.GetValueOrDefault(Constants.Yarp.TokenTypeMetadata);
        var clusterValue =
            transformBuildContext.Cluster?.Metadata?.GetValueOrDefault(Constants.Yarp.TokenTypeMetadata);

        // no metadata
        if (string.IsNullOrEmpty(routeValue) && string.IsNullOrEmpty(clusterValue))
        {
            return;
        }

        var values = new HashSet <string>();

        if (!string.IsNullOrEmpty(routeValue))
        {
            values.Add(routeValue);
        }
        if (!string.IsNullOrEmpty(clusterValue))
        {
            values.Add(clusterValue);
        }

        if (values.Count > 1)
        {
            throw new ArgumentException(
                      "Mismatching Duende.Bff.Yarp.TokenType route or cluster metadata values found");
        }

        if (!TokenType.TryParse(values.First(), true, out TokenType tokenType))
        {
            throw new ArgumentException("Invalid value for Duende.Bff.Yarp.TokenType metadata");
        }

        transformBuildContext.AddRequestTransform(async transformContext =>
        {
            transformContext.HttpContext.CheckForBffMiddleware(_options);

            var token = await transformContext.HttpContext.GetManagedAccessToken(tokenType);

            if (!string.IsNullOrEmpty(token))
            {
                transformContext.ProxyRequest.Headers.Authorization =
                    new AuthenticationHeaderValue("Bearer", token);
            }
            else
            {
                _logger.AccessTokenMissing(transformBuildContext?.Route?.RouteId ?? "unknown route", tokenType);
            }
        });
    }
Пример #20
0
        public bool Build(TransformBuilderContext context, IReadOnlyDictionary <string, string> transformValues)
        {
            if (transformValues.TryGetValue(RequestHeadersCopyKey, out var copyHeaders))
            {
                TransformHelpers.CheckTooManyParameters(transformValues, expected: 1);
                context.CopyRequestHeaders = bool.Parse(copyHeaders);
            }
            else if (transformValues.TryGetValue(RequestHeaderOriginalHostKey, out var originalHost))
            {
                TransformHelpers.CheckTooManyParameters(transformValues, expected: 1);
                context.AddOriginalHost(bool.Parse(originalHost));
            }
            else if (transformValues.TryGetValue(RequestHeaderKey, out var headerName))
            {
                TransformHelpers.CheckTooManyParameters(transformValues, expected: 2);
                if (transformValues.TryGetValue(SetKey, out var setValue))
                {
                    context.AddRequestHeader(headerName, setValue, append: false);
                }
                else if (transformValues.TryGetValue(AppendKey, out var appendValue))
                {
                    context.AddRequestHeader(headerName, appendValue, append: true);
                }
                else
                {
                    throw new ArgumentException($"Unexpected parameters for RequestHeader: {string.Join(';', transformValues.Keys)}. Expected 'Set' or 'Append'");
                }
            }
            else if (transformValues.TryGetValue(RequestHeaderRemoveKey, out var removeHeaderName))
            {
                TransformHelpers.CheckTooManyParameters(transformValues, expected: 1);
                context.AddRequestHeaderRemove(removeHeaderName);
            }
            else if (transformValues.TryGetValue(RequestHeadersAllowedKey, out var allowedHeaders))
            {
                TransformHelpers.CheckTooManyParameters(transformValues, expected: 1);
                var headersList = allowedHeaders.Split(';', options: StringSplitOptions.RemoveEmptyEntries
#if NET
                                                       | StringSplitOptions.TrimEntries
#endif
                                                       );
                context.AddRequestHeadersAllowed(headersList);
            }
            else
            {
                return(false);
            }

            return(true);
        }
        /// <summary>
        /// Adds a transform Func that runs on each response for the given route.
        /// </summary>
        public static TransformBuilderContext AddResponseTrailersTransform(this TransformBuilderContext context, Func <ResponseTrailersTransformContext, ValueTask> func)
        {
            if (context is null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (func is null)
            {
                throw new ArgumentNullException(nameof(func));
            }

            context.ResponseTrailersTransforms.Add(new ResponseTrailersFuncTransform(func));
            return(context);
        }
Пример #22
0
        public void Apply(TransformBuilderContext transformBuildContext)
        {
            // Check all routes for a custom property and add the associated transform.
            if ((transformBuildContext.Route.Metadata?.TryGetValue("CustomMetadata", out var value) ?? false) ||
                (transformBuildContext.Cluster?.Metadata?.TryGetValue("CustomMetadata", out value) ?? false))
            {
                if (string.IsNullOrEmpty(value))
                {
                    throw new ArgumentException("A non-empty CustomMetadata value is required");
                }

                transformBuildContext.AddRequestTransform(transformContext =>
                {
                    transformContext.ProxyRequest.Options.Set(new HttpRequestOptionsKey <string>("CustomMetadata"), value);
                    return(default);
Пример #23
0
        public bool Build(TransformBuilderContext context, IReadOnlyDictionary <string, string> transformValues)
        {
            if (transformValues.TryGetValue(QueryValueParameterKey, out var queryValueParameter))
            {
                TransformHelpers.CheckTooManyParameters(transformValues, expected: 2);
                if (transformValues.TryGetValue(AppendKey, out var appendValue))
                {
                    context.AddQueryValue(queryValueParameter, appendValue, append: true);
                }
                else if (transformValues.TryGetValue(SetKey, out var setValue))
                {
                    context.AddQueryValue(queryValueParameter, setValue, append: false);
                }
                else
                {
                    throw new NotSupportedException(string.Join(";", transformValues.Keys));
                }
            }
            else if (transformValues.TryGetValue(QueryRouteParameterKey, out var queryRouteParameter))
            {
                TransformHelpers.CheckTooManyParameters(transformValues, expected: 2);
                if (transformValues.TryGetValue(AppendKey, out var routeValueKeyAppend))
                {
                    context.AddQueryRouteValue(queryRouteParameter, routeValueKeyAppend, append: true);
                }
                else if (transformValues.TryGetValue(SetKey, out var routeValueKeySet))
                {
                    context.AddQueryRouteValue(queryRouteParameter, routeValueKeySet, append: false);
                }
                else
                {
                    throw new NotSupportedException(string.Join(";", transformValues.Keys));
                }
            }
            else if (transformValues.TryGetValue(QueryRemoveParameterKey, out var removeQueryParameter))
            {
                TransformHelpers.CheckTooManyParameters(transformValues, expected: 1);
                context.AddQueryRemoveKey(removeQueryParameter);
            }
            else
            {
                return(false);
            }

            return(true);
        }
        public bool Build(TransformBuilderContext context, IReadOnlyDictionary <string, string> transformValues)
        {
            if (transformValues.TryGetValue(HttpMethodChangeKey, out var fromHttpMethod))
            {
                TransformHelpers.CheckTooManyParameters(transformValues, expected: 2);
                if (transformValues.TryGetValue(SetKey, out var toHttpMethod))
                {
                    context.AddHttpMethodChange(fromHttpMethod, toHttpMethod);
                }
            }
            else
            {
                return(false);
            }

            return(true);
        }
Пример #25
0
        public void Apply(TransformBuilderContext context)
        {
            string daprAct = null;

            if (context.Route.Metadata?.TryGetValue(DaprYarpConst.MetaKeys.Dapr, out daprAct) ?? false)
            {
                switch (daprAct)
                {
                case DaprYarpConst.DaprAct.Method:
                    context.AddRequestTransform(transformContext =>
                    {
                        var index   = transformContext.Path.Value !.IndexOf('/', 5);  // format: /api/xxxx
                        var appId   = transformContext.Path.Value.Substring(5, index - 5);
                        var newPath = transformContext.Path.Value.Substring(index);
                        transformContext.ProxyRequest.RequestUri = new Uri($"{transformContext.DestinationPrefix}/v1.0/invoke/{appId}/method{newPath}");
                        return(ValueTask.CompletedTask);
                    });
                    break;
                }
            }
        }
Пример #26
0
        // This is separate from Build for testing purposes.
        internal StructuredTransformer BuildInternal(ProxyRoute route, Cluster cluster)
        {
            var rawTransforms = route.Transforms;

            var context = new TransformBuilderContext
            {
                Services = _services,
                Route    = route,
                Cluster  = cluster,
            };

            if (rawTransforms?.Count > 0)
            {
                foreach (var rawTransform in rawTransforms)
                {
                    var handled = false;
                    foreach (var factory in _factories)
                    {
                        if (factory.Build(context, rawTransform))
                        {
                            handled = true;
                            break;
                        }
                    }

                    if (!handled)
                    {
                        throw new ArgumentException($"Unknown transform: {string.Join(';', rawTransform.Keys)}");
                    }
                }
            }

            // Let the app add any more transforms it wants.
            foreach (var transformProvider in _providers)
            {
                transformProvider.Apply(context);
            }

            return(CreateTransformer(context));
        }
Пример #27
0
 /// <summary>
 /// Adds the transform which will add X-Forwarded-* request headers.
 /// </summary>
 public static TransformBuilderContext AddXForwarded(this TransformBuilderContext context, string headerPrefix = "X-Forwarded-",
                                                     bool useFor = true, bool useHost = true, bool useProto = true, bool usePrefix = true, bool append = true)
 {
     context.UseDefaultForwarders = false;
     if (useFor)
     {
         context.RequestTransforms.Add(new RequestHeaderXForwardedForTransform(headerPrefix + ForwardedTransformFactory.ForKey, append));
     }
     if (useHost)
     {
         context.RequestTransforms.Add(new RequestHeaderXForwardedHostTransform(headerPrefix + ForwardedTransformFactory.HostKey, append));
     }
     if (useProto)
     {
         context.RequestTransforms.Add(new RequestHeaderXForwardedProtoTransform(headerPrefix + ForwardedTransformFactory.ProtoKey, append));
     }
     if (usePrefix)
     {
         context.RequestTransforms.Add(new RequestHeaderXForwardedPrefixTransform(headerPrefix + ForwardedTransformFactory.PrefixKey, append));
     }
     return(context);
 }
Пример #28
0
        /// <summary>
        /// Adds the transform which will add the Forwarded header as defined by [RFC 7239](https://tools.ietf.org/html/rfc7239).
        /// </summary>
        public static TransformBuilderContext AddForwarded(this TransformBuilderContext context,
                                                           bool useHost        = true, bool useProto = true, NodeFormat forFormat = NodeFormat.Random,
                                                           NodeFormat byFormat = NodeFormat.Random, ForwardedTransformActions action = ForwardedTransformActions.Set)
        {
            context.UseDefaultForwarders = false;

            if (action == ForwardedTransformActions.Off)
            {
                return(context);
            }

            if (byFormat != NodeFormat.None || forFormat != NodeFormat.None || useHost || useProto)
            {
                var random = context.Services.GetRequiredService <IRandomFactory>();
                context.RequestTransforms.Add(new RequestHeaderForwardedTransform(random,
                                                                                  forFormat, byFormat, useHost, useProto, action));

                // Remove the X-Forwarded headers when an Forwarded transform is enabled
                TransformHelpers.RemoveAllXForwardedHeaders(context, ForwardedTransformFactory.DefaultXForwardedPrefix);
            }
            return(context);
        }
Пример #29
0
    public void EnableSession_InvalidMode_Fails()
    {
        var affinityPolicy = new Mock <ISessionAffinityPolicy>(MockBehavior.Strict);

        affinityPolicy.SetupGet(p => p.Name).Returns("Policy");

        var transformProvider = new AffinitizeTransformProvider(new[] { affinityPolicy.Object });

        var cluster = new ClusterConfig
        {
            ClusterId       = "cluster1",
            SessionAffinity = new SessionAffinityConfig
            {
                Enabled         = true,
                Policy          = "Invalid",
                AffinityKeyName = "Key1"
            }
        };

        var validationContext = new TransformClusterValidationContext()
        {
            Cluster = cluster,
        };

        transformProvider.ValidateCluster(validationContext);

        var ex = Assert.Single(validationContext.Errors);

        Assert.Equal("No matching ISessionAffinityPolicy found for the session affinity policy 'Invalid' set on the cluster 'cluster1'.", ex.Message);

        var builderContext = new TransformBuilderContext()
        {
            Cluster = cluster,
        };

        ex = Assert.Throws <ArgumentException>(() => transformProvider.Apply(builderContext));
        Assert.Equal($"No {typeof(ISessionAffinityPolicy).FullName} was found for the id 'Invalid'. (Parameter 'id')", ex.Message);
    }
Пример #30
0
    public void EnableSessionAffinity_AddsTransform()
    {
        var affinityPolicy = new Mock <ISessionAffinityPolicy>(MockBehavior.Strict);

        affinityPolicy.SetupGet(p => p.Name).Returns("Policy");

        var transformProvider = new AffinitizeTransformProvider(new[] { affinityPolicy.Object });

        var cluster = new ClusterConfig
        {
            ClusterId       = "cluster1",
            SessionAffinity = new SessionAffinityConfig
            {
                Enabled         = true,
                Policy          = "Policy",
                AffinityKeyName = "Key1"
            }
        };

        var validationContext = new TransformClusterValidationContext()
        {
            Cluster = cluster,
        };

        transformProvider.ValidateCluster(validationContext);

        Assert.Empty(validationContext.Errors);

        var builderContext = new TransformBuilderContext()
        {
            Cluster = cluster,
        };

        transformProvider.Apply(builderContext);

        Assert.IsType <AffinitizeTransform>(builderContext.ResponseTransforms.Single());
    }