public GlobalInfoProvider(ImageflowMiddlewareOptions options, IWebHostEnvironment env, ILogger <ImageflowMiddleware> logger,
                                  IStreamCache streamCache,
                                  IClassicDiskCache diskCache, IList <IBlobProvider> blobProviders)
        {
            this.env         = env;
            this.streamCache = streamCache;
            this.options     = options;
            var plugins = new List <object>()
            {
                logger, streamCache, diskCache
            }.Concat(blobProviders).ToList();

            infoProviders = plugins.OfType <IInfoProvider>().ToList();

            pluginNames = plugins
                          .Where(p => p != null)
                          .Select(p =>
            {
                var t = p.GetType();
                if (t.Namespace != null &&
                    (t.Namespace.StartsWith("Imazen") ||
                     t.Namespace.StartsWith("Imageflow") ||
                     t.Namespace.StartsWith("Microsoft.Extensions.Logging") ||
                     t.Namespace.StartsWith("Microsoft.Extensions.Caching")))
                {
                    return(t.Name);
                }
                else
                {
                    return(t.FullName);
                }
            }).ToList();
        }
예제 #2
0
        public ImageflowMiddleware(
            RequestDelegate next,
            IWebHostEnvironment env,
            IEnumerable <ILogger <ImageflowMiddleware> > logger,
            IEnumerable <IMemoryCache> memoryCache,
            IEnumerable <IDistributedCache> distributedCache,
            IEnumerable <ISqliteCache> sqliteCaches,
            IEnumerable <IClassicDiskCache> diskCache,
            IEnumerable <IBlobProvider> blobProviders,
            ImageflowMiddlewareOptions options)
        {
            this.next             = next;
            this.options          = options;
            this.env              = env;
            this.logger           = logger.FirstOrDefault();
            this.memoryCache      = memoryCache.FirstOrDefault();
            this.diskCache        = diskCache.FirstOrDefault();
            this.distributedCache = distributedCache.FirstOrDefault();
            this.sqliteCache      = sqliteCaches.FirstOrDefault();
            var providers   = blobProviders.ToList();
            var mappedPaths = options.MappedPaths.ToList();

            if (options.MapWebRoot)
            {
                if (this.env?.WebRootPath == null)
                {
                    throw new InvalidOperationException("Cannot call MapWebRoot if env.WebRootPath is null");
                }
                mappedPaths.Add(new PathMapping("/", this.env.WebRootPath));
            }

            blobProvider    = new BlobProvider(providers, mappedPaths);
            diagnosticsPage = new DiagnosticsPage(env, this.logger, this.memoryCache, this.distributedCache, this.diskCache, providers);
        }
        private bool VerifySignature(HttpContext context, ImageflowMiddlewareOptions options)
        {
            var pathAndQuery = context.Request.PathBase.HasValue
                ? "/" + context.Request.PathBase.Value.TrimStart('/')
                : "";

            pathAndQuery += context.Request.Path.ToString() + context.Request.QueryString.ToString();

            pathAndQuery = Signatures.NormalizePathAndQueryForSigning(pathAndQuery);
            if (context.Request.Query.TryGetValue("signature", out var actualSignature))
            {
                foreach (var key in options.SigningKeys)
                {
                    var expectedSignature = Signatures.SignString(pathAndQuery, key, 16);
                    if (expectedSignature == actualSignature)
                    {
                        return(true);
                    }
                }

                AuthorizedMessage = "Image signature does not match request, or used an invalid signing key.";
                return(false);
            }

            // A missing signature is only a problem if they are required
            if (!options.RequireRequestSignature)
            {
                return(true);
            }

            AuthorizedMessage = "Image requests must be signed. No &signature query key found. ";
            return(false);
        }
 internal DiagnosticsPage(ImageflowMiddlewareOptions options, IWebHostEnvironment env, ILogger <ImageflowMiddleware> logger,
                          IStreamCache streamCache,
                          IClassicDiskCache diskCache, IList <IBlobProvider> blobProviders)
 {
     this.options       = options;
     this.env           = env;
     this.streamCache   = streamCache;
     this.diskCache     = diskCache;
     this.blobProviders = blobProviders;
 }
예제 #5
0
        public ImageJobInfo(HttpContext context, ImageflowMiddlewareOptions options, BlobProvider blobProvider)
        {
            this.options = options;
            Authorized   = ProcessRewritesAndAuthorization(context, options);

            if (!Authorized)
            {
                return;
            }

            HasParams = PathHelpers.SupportedQuerystringKeys.Any(FinalQuery.ContainsKey);


            var extension = Path.GetExtension(FinalVirtualPath);

            if (FinalQuery.TryGetValue("format", out var newExtension))
            {
                extension = newExtension;
            }

            EstimatedFileExtension = PathHelpers.SanitizeImageExtension(extension);

            primaryBlob = new BlobFetchCache(FinalVirtualPath, blobProvider);
            allBlobs    = new List <BlobFetchCache>(1)
            {
                primaryBlob
            };

            if (HasParams)
            {
                CommandString = PathHelpers.SerializeCommandString(FinalQuery);

                // Look up watermark names
                if (FinalQuery.TryGetValue("watermark", out var watermarkValues))
                {
                    var watermarkNames = watermarkValues.Split(",").Select(s => s.Trim(' '));

                    appliedWatermarks = new List <NamedWatermark>();
                    foreach (var name in watermarkNames)
                    {
                        var watermark = options.NamedWatermarks.FirstOrDefault(w =>
                                                                               w.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
                        if (watermark == null)
                        {
                            throw new InvalidOperationException($"watermark {name} was referenced from the querystring but no watermark by that name is registered with the middleware");
                        }

                        appliedWatermarks.Add(watermark);
                        allBlobs.Add(new BlobFetchCache(watermark.VirtualPath, blobProvider));
                    }
                }
            }

            provider = blobProvider;
        }
        private bool VerifySignature(HttpContext context, ImageflowMiddlewareOptions middlewareOptions)
        {
            if (middlewareOptions.RequestSignatureOptions == null)
            {
                return(true);
            }

            var(requirement, signingKeys) = middlewareOptions.RequestSignatureOptions
                                            .GetRequirementForPath(context.Request.Path.Value);

            var queryString = context.Request.QueryString.ToString();

            var pathAndQuery = context.Request.PathBase.HasValue
                ? "/" + context.Request.PathBase.Value.TrimStart('/')
                : "";

            pathAndQuery += context.Request.Path.ToString() + queryString;

            pathAndQuery = Signatures.NormalizePathAndQueryForSigning(pathAndQuery);
            if (context.Request.Query.TryGetValue("signature", out var actualSignature))
            {
                foreach (var key in signingKeys)
                {
                    var expectedSignature = Signatures.SignString(pathAndQuery, key, 16);
                    if (expectedSignature == actualSignature)
                    {
                        return(true);
                    }
                }

                AuthorizedMessage = "Image signature does not match request, or used an invalid signing key.";
                return(false);
            }

            if (requirement == SignatureRequired.Never)
            {
                return(true);
            }
            if (requirement == SignatureRequired.ForQuerystringRequests)
            {
                if (queryString.Length <= 0)
                {
                    return(true);
                }

                AuthorizedMessage = "Image processing requests must be signed. No &signature query key found. ";
                return(false);
            }
            AuthorizedMessage = "Image requests must be signed. No &signature query key found. ";
            return(false);
        }
예제 #7
0
        internal void Initialize(ImageflowMiddlewareOptions middlewareOptions)
        {
            options = middlewareOptions;
            mgr.MonitorLicenses(this);
            mgr.MonitorHeartbeat(this);

            // Ensure our cache is appropriately invalidated
            cachedResult = null;
            mgr.AddLicenseChangeHandler(this, (me, manager) => me.cachedResult = null);

            // And repopulated, so that errors show up.
            if (Result == null)
            {
                throw new ApplicationException("Failed to populate license result");
            }
        }
        public static bool ShouldHandleRequest(HttpContext context, ImageflowMiddlewareOptions options)
        {
            // If the path is empty or null we don't handle it
            var pathValue = context.Request.Path;

            if (pathValue == null || !pathValue.HasValue)
            {
                return(false);
            }

            var path = pathValue.Value;

            if (path == null)
            {
                return(false);
            }

            // We handle image request extensions
            if (PathHelpers.IsImagePath(path))
            {
                return(true);
            }

            // Don't do string parsing unless there are actually prefixes configured
            if (options.ExtensionlessPaths.Count == 0)
            {
                return(false);
            }

            // If there's no extension, then we can see if it's one of the prefixes we should handle
            var extension = Path.GetExtension(path);

            // If there's a non-image extension, we shouldn't handle the request
            if (!string.IsNullOrEmpty(extension))
            {
                return(false);
            }

            // Return true if any of the prefixes match
            return(options.ExtensionlessPaths
                   .Any(extensionlessPath => path.StartsWith(extensionlessPath.Prefix, extensionlessPath.PrefixComparison)));
        }
예제 #9
0
 internal LicensePage(ImageflowMiddlewareOptions options)
 {
     this.options = options;
 }
예제 #10
0
 public static IApplicationBuilder UseImageflow(this IApplicationBuilder builder, ImageflowMiddlewareOptions options)
 {
     return(builder.UseMiddleware <ImageflowMiddleware>(options));
 }
예제 #11
0
        private bool ProcessRewritesAndAuthorization(HttpContext context, ImageflowMiddlewareOptions options)
        {
            var path = context.Request.Path.Value;
            var args = new UrlEventArgs(context, context.Request.Path.Value, PathHelpers.ToQueryDictionary(context.Request.Query));

            foreach (var handler in options.PreRewriteAuthorization)
            {
                var matches = string.IsNullOrEmpty(handler.PathPrefix) ||
                              path.StartsWith(handler.PathPrefix, StringComparison.OrdinalIgnoreCase);
                if (matches && !handler.Handler(args))
                {
                    return(false);
                }
            }

            if (options.UsePresetsExclusively)
            {
                var firstKey = args.Query.FirstOrDefault().Key;

                if (args.Query.Count > 1 || (firstKey != null && firstKey != "preset"))
                {
                    return(false);
                }
            }

            // Parse and apply presets before rewriting
            if (args.Query.TryGetValue("preset", out var presetNames))
            {
                var presetNamesList = presetNames
                                      .Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                foreach (var presetName in presetNamesList)
                {
                    if (options.Presets.TryGetValue(presetName, out var presetOptions))
                    {
                        foreach (var pair in presetOptions.pairs)
                        {
                            if (presetOptions.Priority == PresetPriority.OverrideQuery ||
                                !args.Query.ContainsKey(pair.Key))
                            {
                                args.Query[pair.Key] = pair.Value;
                            }
                        }
                    }
                    else
                    {
                        throw new InvalidOperationException($"The image preset {presetName} was referenced from the querystring but is not registered.");
                    }
                }
            }

            // Apply rewrite handlers
            foreach (var handler in options.Rewrite)
            {
                var matches = string.IsNullOrEmpty(handler.PathPrefix) ||
                              path.StartsWith(handler.PathPrefix, StringComparison.OrdinalIgnoreCase);
                if (matches)
                {
                    handler.Handler(args);
                    path = args.VirtualPath;
                }
            }

            // Set defaults if keys are missing, but at least 1 supported key is present
            if (PathHelpers.SupportedQuerystringKeys.Any(args.Query.ContainsKey))
            {
                foreach (var pair in options.CommandDefaults)
                {
                    if (!args.Query.ContainsKey(pair.Key))
                    {
                        args.Query[pair.Key] = pair.Value;
                    }
                }
            }

            // Run post-rewrite authorization
            foreach (var handler in options.PostRewriteAuthorization)
            {
                var matches = string.IsNullOrEmpty(handler.PathPrefix) ||
                              path.StartsWith(handler.PathPrefix, StringComparison.OrdinalIgnoreCase);
                if (matches && !handler.Handler(args))
                {
                    return(false);
                }
            }

            FinalVirtualPath = args.VirtualPath;
            FinalQuery       = args.Query;
            return(true);
        }
        public ImageJobInfo(HttpContext context, ImageflowMiddlewareOptions options, BlobProvider blobProvider)
        {
            this.options = options;
            Authorized   = ProcessRewritesAndAuthorization(context, options);

            if (!Authorized)
            {
                return;
            }

            HasParams = PathHelpers.SupportedQuerystringKeys.Any(FinalQuery.ContainsKey);


            var extension = Path.GetExtension(FinalVirtualPath);

            if (FinalQuery.TryGetValue("format", out var newExtension))
            {
                extension = newExtension;
            }

            EstimatedFileExtension = PathHelpers.SanitizeImageExtension(extension) ?? "jpg";

            primaryBlob = new BlobFetchCache(FinalVirtualPath, blobProvider);
            allBlobs    = new List <BlobFetchCache>(1)
            {
                primaryBlob
            };

            appliedWatermarks = new List <NamedWatermark>();

            if (HasParams)
            {
                CommandString = PathHelpers.SerializeCommandString(FinalQuery);

                // Look up watermark names
                if (FinalQuery.TryGetValue("watermark", out var watermarkValues))
                {
                    var watermarkNames = watermarkValues.Split(",").Select(s => s.Trim(' '));
                    foreach (var name in watermarkNames)
                    {
                        var watermark = options.NamedWatermarks.FirstOrDefault(w =>
                                                                               w.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
                        if (watermark == null)
                        {
                            throw new InvalidOperationException(
                                      $"watermark {name} was referenced from the querystring but no watermark by that name is registered with the middleware");
                        }

                        appliedWatermarks.Add(watermark);
                    }
                }
            }

            // After we've populated the defaults, run the event handlers for custom watermarking logic
            var args = new WatermarkingEventArgs(context, FinalVirtualPath, FinalQuery, appliedWatermarks);

            foreach (var handler in options.Watermarking)
            {
                var matches = string.IsNullOrEmpty(handler.PathPrefix) ||
                              FinalVirtualPath.StartsWith(handler.PathPrefix, StringComparison.OrdinalIgnoreCase);
                if (matches)
                {
                    handler.Handler(args);
                }
            }
            appliedWatermarks = args.AppliedWatermarks;

            // Add the watermark source files
            foreach (var w in appliedWatermarks)
            {
                allBlobs.Add(new BlobFetchCache(w.VirtualPath, blobProvider));
            }

            provider = blobProvider;
        }
예제 #13
0
        public ImageflowMiddleware(
            RequestDelegate next,
            IWebHostEnvironment env,
            IEnumerable <ILogger <ImageflowMiddleware> > logger,
            IEnumerable <IClassicDiskCache> diskCaches,
            IEnumerable <IStreamCache> streamCaches,
            IEnumerable <IBlobProvider> blobProviders,
            ImageflowMiddlewareOptions options)
        {
            this.next = next;
            options.Licensing ??= new Licensing(LicenseManagerSingleton.GetOrCreateSingleton(
                                                    "imageflow_", new[] { env.ContentRootPath, Path.GetTempPath() }));
            this.options = options;
            this.env     = env;
            this.logger  = logger.FirstOrDefault();
            diskCache    = diskCaches.FirstOrDefault();

            var streamCacheArray = streamCaches.ToArray();

            if (streamCacheArray.Count() > 1)
            {
                throw new InvalidOperationException("Only 1 IStreamCache instance can be registered at a time");
            }

            streamCache = streamCacheArray.FirstOrDefault();


            var providers   = blobProviders.ToList();
            var mappedPaths = options.MappedPaths.ToList();

            if (options.MapWebRoot)
            {
                if (this.env?.WebRootPath == null)
                {
                    throw new InvalidOperationException("Cannot call MapWebRoot if env.WebRootPath is null");
                }
                mappedPaths.Add(new PathMapping("/", this.env.WebRootPath));
            }

            //Determine the active cache backend
            var streamCacheEnabled = streamCache != null && options.AllowCaching;
            var diskCacheEnabled   = this.diskCache != null && options.AllowDiskCaching;

            if (streamCacheEnabled)
            {
                options.ActiveCacheBackend = CacheBackend.StreamCache;
            }
            else if (diskCacheEnabled)
            {
                options.ActiveCacheBackend = CacheBackend.ClassicDiskCache;
            }
            else
            {
                options.ActiveCacheBackend = CacheBackend.NoCache;
            }


            options.Licensing.Initialize(this.options);

            blobProvider       = new BlobProvider(providers, mappedPaths);
            diagnosticsPage    = new DiagnosticsPage(options, env, this.logger, streamCache, this.diskCache, providers);
            licensePage        = new LicensePage(options);
            globalInfoProvider = new GlobalInfoProvider(options, env, this.logger, streamCache, this.diskCache, providers);

            options.Licensing.FireHeartbeat();
            GlobalPerf.Singleton.SetInfoProviders(new List <IInfoProvider>()
            {
                globalInfoProvider
            });
        }
예제 #14
0
        public ImageJobInfo(HttpContext context, ImageflowMiddlewareOptions options, BlobProvider blobProvider)
        {
            this.options = options;
            Authorized   = ProcessRewritesAndAuthorization(context, options);

            if (!Authorized)
            {
                return;
            }

            HasParams = PathHelpers.SupportedQuerystringKeys.Any(FinalQuery.ContainsKey);

            // Get the image and page domains
            ImageDomain = context.Request.Host.Host;
            var referer = context.Request.Headers["Referer"].ToString();

            if (!string.IsNullOrEmpty(referer) && Uri.TryCreate(referer, UriKind.Absolute, out var result))
            {
                PageDomain = result.DnsSafeHost;
            }

            var extension = Path.GetExtension(FinalVirtualPath);

            if (FinalQuery.TryGetValue("format", out var newExtension))
            {
                extension = newExtension;
            }

            EstimatedFileExtension = PathHelpers.SanitizeImageExtension(extension) ?? "jpg";

            primaryBlob = new BlobFetchCache(FinalVirtualPath, blobProvider);
            allBlobs    = new List <BlobFetchCache>(1)
            {
                primaryBlob
            };

            appliedWatermarks = new List <NamedWatermark>();

            if (HasParams)
            {
                if (options.Licensing.RequestNeedsEnforcementAction(context.Request))
                {
                    if (options.EnforcementMethod == EnforceLicenseWith.RedDotWatermark)
                    {
                        FinalQuery["watermark_red_dot"] = "true";
                    }
                    LicenseError = true;
                }

                CommandString = PathHelpers.SerializeCommandString(FinalQuery);

                // Look up watermark names
                if (FinalQuery.TryGetValue("watermark", out var watermarkValues))
                {
                    var watermarkNames = watermarkValues.Split(",").Select(s => s.Trim(' '));
                    foreach (var name in watermarkNames)
                    {
                        var watermark = options.NamedWatermarks.FirstOrDefault(w =>
                                                                               w.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
                        if (watermark == null)
                        {
                            throw new InvalidOperationException(
                                      $"watermark {name} was referenced from the querystring but no watermark by that name is registered with the middleware");
                        }

                        appliedWatermarks.Add(watermark);
                    }
                }
            }

            // After we've populated the defaults, run the event handlers for custom watermarking logic
            var args = new WatermarkingEventArgs(context, FinalVirtualPath, FinalQuery, appliedWatermarks);

            foreach (var handler in options.Watermarking)
            {
                var matches = string.IsNullOrEmpty(handler.PathPrefix) ||
                              (FinalVirtualPath != null &&
                               FinalVirtualPath.StartsWith(handler.PathPrefix, StringComparison.OrdinalIgnoreCase));
                if (matches)
                {
                    handler.Handler(args);
                }
            }
            appliedWatermarks = args.AppliedWatermarks;
            if (appliedWatermarks.Count > 0)
            {
                HasParams = true;
            }

            // Add the watermark source files
            foreach (var w in appliedWatermarks)
            {
                allBlobs.Add(new BlobFetchCache(w.VirtualPath, blobProvider));
            }
        }