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(); }
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; }
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); }
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))); }
internal LicensePage(ImageflowMiddlewareOptions options) { this.options = options; }
public static IApplicationBuilder UseImageflow(this IApplicationBuilder builder, ImageflowMiddlewareOptions options) { return(builder.UseMiddleware <ImageflowMiddleware>(options)); }
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; }
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 }); }
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)); } }