From ad5bb5b49af49c4668fd30fb2a0e606dcefe4ec0 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Fri, 27 Oct 2017 19:39:13 -0400 Subject: proxy Pastebin requests through our API instead of third parties, improve error-handling (#358) --- .../Framework/AllowLargePostsAttribute.cs | 52 ++++++++++ .../Framework/ConfigModels/LogParserConfig.cs | 18 ++++ .../Framework/ConfigModels/ModUpdateCheckConfig.cs | 2 +- .../Framework/LogParser/GetPasteResponse.cs | 15 +++ .../Framework/LogParser/PastebinClient.cs | 110 +++++++++++++++++++++ .../Framework/LogParser/SavePasteResponse.cs | 15 +++ 6 files changed, 211 insertions(+), 1 deletion(-) create mode 100644 src/SMAPI.Web/Framework/AllowLargePostsAttribute.cs create mode 100644 src/SMAPI.Web/Framework/ConfigModels/LogParserConfig.cs create mode 100644 src/SMAPI.Web/Framework/LogParser/GetPasteResponse.cs create mode 100644 src/SMAPI.Web/Framework/LogParser/PastebinClient.cs create mode 100644 src/SMAPI.Web/Framework/LogParser/SavePasteResponse.cs (limited to 'src/SMAPI.Web/Framework') diff --git a/src/SMAPI.Web/Framework/AllowLargePostsAttribute.cs b/src/SMAPI.Web/Framework/AllowLargePostsAttribute.cs new file mode 100644 index 00000000..68ead3c2 --- /dev/null +++ b/src/SMAPI.Web/Framework/AllowLargePostsAttribute.cs @@ -0,0 +1,52 @@ +using System; +using Microsoft.AspNetCore.Http.Features; +using Microsoft.AspNetCore.Mvc.Filters; + +namespace StardewModdingAPI.Web.Framework +{ + /// A filter which increases the maximum request size for an endpoint. + /// Derived from . + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] + public class AllowLargePostsAttribute : Attribute, IAuthorizationFilter, IOrderedFilter + { + /********* + ** Properties + *********/ + /// The underlying form options. + private readonly FormOptions FormOptions; + + + /********* + ** Accessors + *********/ + /// The attribute order. + public int Order { get; set; } + + + /********* + ** Public methods + *********/ + /// Construct an instance. + public AllowLargePostsAttribute() + { + this.FormOptions = new FormOptions + { + ValueLengthLimit = 200 * 1024 * 1024 // 200MB + }; + } + + /// Called early in the filter pipeline to confirm request is authorized. + /// The authorisation filter context. + public void OnAuthorization(AuthorizationFilterContext context) + { + IFeatureCollection features = context.HttpContext.Features; + IFormFeature formFeature = features.Get(); + + if (formFeature?.Form == null) + { + // Request form has not been read yet, so set the limits + features.Set(new FormFeature(context.HttpContext.Request, this.FormOptions)); + } + } + } +} diff --git a/src/SMAPI.Web/Framework/ConfigModels/LogParserConfig.cs b/src/SMAPI.Web/Framework/ConfigModels/LogParserConfig.cs new file mode 100644 index 00000000..5cb0cf95 --- /dev/null +++ b/src/SMAPI.Web/Framework/ConfigModels/LogParserConfig.cs @@ -0,0 +1,18 @@ +namespace StardewModdingAPI.Web.Framework.ConfigModels +{ + /// The config settings for the log parser. + internal class LogParserConfig + { + /********* + ** Accessors + *********/ + /// The base URL for the Pastebin API. + public string PastebinBaseUrl { get; set; } + + /// The user agent for the Pastebin API client, where {0} is the SMAPI version. + public string PastebinUserAgent { get; set; } + + /// The developer key used to authenticate with the Pastebin API. + public string PastebinDevKey { get; set; } + } +} diff --git a/src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs b/src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs index 03de639e..2fb5b97e 100644 --- a/src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs +++ b/src/SMAPI.Web/Framework/ConfigModels/ModUpdateCheckConfig.cs @@ -1,7 +1,7 @@ namespace StardewModdingAPI.Web.Framework.ConfigModels { /// The config settings for mod update checks. - public class ModUpdateCheckConfig + internal class ModUpdateCheckConfig { /********* ** Accessors diff --git a/src/SMAPI.Web/Framework/LogParser/GetPasteResponse.cs b/src/SMAPI.Web/Framework/LogParser/GetPasteResponse.cs new file mode 100644 index 00000000..4f8794db --- /dev/null +++ b/src/SMAPI.Web/Framework/LogParser/GetPasteResponse.cs @@ -0,0 +1,15 @@ +namespace StardewModdingAPI.Web.Framework.LogParser +{ + /// The response for a get-paste request. + internal class GetPasteResponse + { + /// Whether the log was successfully fetched. + public bool Success { get; set; } + + /// The fetched paste content (if is true). + public string Content { get; set; } + + /// The error message (if saving failed). + public string Error { get; set; } + } +} diff --git a/src/SMAPI.Web/Framework/LogParser/PastebinClient.cs b/src/SMAPI.Web/Framework/LogParser/PastebinClient.cs new file mode 100644 index 00000000..8536f249 --- /dev/null +++ b/src/SMAPI.Web/Framework/LogParser/PastebinClient.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; +using Pathoschild.Http.Client; + +namespace StardewModdingAPI.Web.Framework.LogParser +{ + /// An API client for Pastebin. + internal class PastebinClient : IDisposable + { + /********* + ** Properties + *********/ + /// The underlying HTTP client. + private readonly IClient Client; + + /// The developer key used to authenticate with the Pastebin API. + private readonly string DevKey; + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// The base URL for the Pastebin API. + /// The user agent for the API client. + /// The developer key used to authenticate with the Pastebin API. + public PastebinClient(string baseUrl, string userAgent, string devKey) + { + this.Client = new FluentClient(baseUrl).SetUserAgent(userAgent); + this.DevKey = devKey; + } + + /// Fetch a saved paste. + /// The paste ID. + public async Task GetAsync(string id) + { + try + { + // get from API + string content = await this.Client + .GetAsync($"raw/{id}") + .AsString(); + + // handle Pastebin errors + if (string.IsNullOrWhiteSpace(content)) + return new GetPasteResponse { Error = "Received an empty response from Pastebin." }; + if (content.StartsWith(" PostAsync(string content) + { + try + { + // validate + if (string.IsNullOrWhiteSpace(content)) + return new SavePasteResponse { Error = "The log content can't be empty." }; + + // post to API + string response = await this.Client + .PostAsync("api/api_post.php") + .WithBodyContent(new FormUrlEncodedContent(new Dictionary + { + ["api_dev_key"] = "b8219d942109d1e60ebb14fbb45f06f9", + ["api_option"] = "paste", + ["api_paste_private"] = "1", + ["api_paste_code"] = content, + ["api_paste_expire_date"] = "1W" + })) + .AsString(); + + // handle Pastebin errors + if (string.IsNullOrWhiteSpace(response)) + return new SavePasteResponse { Error = "Received an empty response from Pastebin." }; + if (response.StartsWith("Bad API request")) + return new SavePasteResponse { Error = response }; + if (!response.Contains("/")) + return new SavePasteResponse { Error = $"Received an unknown response: {response}" }; + + // return paste ID + string pastebinID = response.Split("/").Last(); + return new SavePasteResponse { Success = true, ID = pastebinID }; + } + catch (Exception ex) + { + return new SavePasteResponse { Success = false, Error = ex.ToString() }; + } + } + + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + public void Dispose() + { + this.Client.Dispose(); + } + } +} diff --git a/src/SMAPI.Web/Framework/LogParser/SavePasteResponse.cs b/src/SMAPI.Web/Framework/LogParser/SavePasteResponse.cs new file mode 100644 index 00000000..1c0960a4 --- /dev/null +++ b/src/SMAPI.Web/Framework/LogParser/SavePasteResponse.cs @@ -0,0 +1,15 @@ +namespace StardewModdingAPI.Web.Framework.LogParser +{ + /// The response for a save-log request. + internal class SavePasteResponse + { + /// Whether the log was successfully saved. + public bool Success { get; set; } + + /// The saved paste ID (if is true). + public string ID { get; set; } + + /// The error message (if saving failed). + public string Error { get; set; } + } +} -- cgit From 3f43ebcc0e31db523fa82a163374cebf2f577cde Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Fri, 27 Oct 2017 21:10:36 -0400 Subject: fix issues with subdomain routing in log UI (#358) --- .../Framework/ConfigModels/LogParserConfig.cs | 3 +++ src/SMAPI.Web/Framework/RewriteSubdomainRule.cs | 22 ++++++++++++++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) (limited to 'src/SMAPI.Web/Framework') diff --git a/src/SMAPI.Web/Framework/ConfigModels/LogParserConfig.cs b/src/SMAPI.Web/Framework/ConfigModels/LogParserConfig.cs index 5cb0cf95..18d8ff05 100644 --- a/src/SMAPI.Web/Framework/ConfigModels/LogParserConfig.cs +++ b/src/SMAPI.Web/Framework/ConfigModels/LogParserConfig.cs @@ -6,6 +6,9 @@ namespace StardewModdingAPI.Web.Framework.ConfigModels /********* ** Accessors *********/ + /// The root URL for the log parser controller. + public string SectionUrl { get; set; } + /// The base URL for the Pastebin API. public string PastebinBaseUrl { get; set; } diff --git a/src/SMAPI.Web/Framework/RewriteSubdomainRule.cs b/src/SMAPI.Web/Framework/RewriteSubdomainRule.cs index 5a56844f..cc183fe3 100644 --- a/src/SMAPI.Web/Framework/RewriteSubdomainRule.cs +++ b/src/SMAPI.Web/Framework/RewriteSubdomainRule.cs @@ -1,4 +1,7 @@ using System; +using System.Linq; +using System.Text.RegularExpressions; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Rewrite; namespace StardewModdingAPI.Web.Framework @@ -7,14 +10,29 @@ namespace StardewModdingAPI.Web.Framework /// Derived from . internal class RewriteSubdomainRule : IRule { + /********* + ** Accessors + *********/ + /// The paths (excluding the hostname portion) to not rewrite. + public Regex[] ExceptPaths { get; set; } + + + /********* + ** Public methods + *********/ /// Applies the rule. Implementations of ApplyRule should set the value for (defaults to RuleResult.ContinueRules). /// The rewrite context. public void ApplyRule(RewriteContext context) { context.Result = RuleResult.ContinueRules; + HttpRequest request = context.HttpContext.Request; + + // check ignores + if (this.ExceptPaths?.Any(pattern => pattern.IsMatch(request.Path)) == true) + return; // get host parts - string host = context.HttpContext.Request.Host.Host; + string host = request.Host.Host; string[] parts = host.Split('.'); // validate @@ -24,7 +42,7 @@ namespace StardewModdingAPI.Web.Framework return; // prepend to path - context.HttpContext.Request.Path = $"/{parts[0]}{context.HttpContext.Request.Path}"; + request.Path = $"/{parts[0]}{request.Path}"; } } } -- cgit From c6d8333c7a28b752397e171540306ceccf74ca12 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 28 Oct 2017 11:53:54 -0400 Subject: improve criteria for subdomain rewriting (#358) --- .../ConditionalRewriteSubdomainRule.cs | 48 ++++++++++++++++++++++ src/SMAPI.Web/Framework/RewriteSubdomainRule.cs | 48 ---------------------- 2 files changed, 48 insertions(+), 48 deletions(-) create mode 100644 src/SMAPI.Web/Framework/RewriteRules/ConditionalRewriteSubdomainRule.cs delete mode 100644 src/SMAPI.Web/Framework/RewriteSubdomainRule.cs (limited to 'src/SMAPI.Web/Framework') diff --git a/src/SMAPI.Web/Framework/RewriteRules/ConditionalRewriteSubdomainRule.cs b/src/SMAPI.Web/Framework/RewriteRules/ConditionalRewriteSubdomainRule.cs new file mode 100644 index 00000000..83f23058 --- /dev/null +++ b/src/SMAPI.Web/Framework/RewriteRules/ConditionalRewriteSubdomainRule.cs @@ -0,0 +1,48 @@ +using System; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Rewrite; + +namespace StardewModdingAPI.Web.Framework.RewriteRules +{ + /// Rewrite requests to prepend the subdomain portion (if any) to the path. + /// Derived from . + internal class ConditionalRewriteSubdomainRule : IRule + { + /********* + ** Accessors + *********/ + /// A predicate which indicates when the rule should be applied. + private readonly Func ShouldRewrite; + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// A predicate which indicates when the rule should be applied. + public ConditionalRewriteSubdomainRule(Func shouldRewrite = null) + { + this.ShouldRewrite = shouldRewrite; + } + + /// Applies the rule. Implementations of ApplyRule should set the value for (defaults to RuleResult.ContinueRules). + /// The rewrite context. + public void ApplyRule(RewriteContext context) + { + HttpRequest request = context.HttpContext.Request; + + // check condition + if (this.ShouldRewrite != null && !this.ShouldRewrite(request)) + return; + + // get host parts + string host = request.Host.Host; + string[] parts = host.Split('.'); + if (parts.Length < 2) + return; + + // prepend to path + request.Path = $"/{parts[0]}{request.Path}"; + } + } +} diff --git a/src/SMAPI.Web/Framework/RewriteSubdomainRule.cs b/src/SMAPI.Web/Framework/RewriteSubdomainRule.cs deleted file mode 100644 index cc183fe3..00000000 --- a/src/SMAPI.Web/Framework/RewriteSubdomainRule.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using System.Linq; -using System.Text.RegularExpressions; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Rewrite; - -namespace StardewModdingAPI.Web.Framework -{ - /// Rewrite requests to prepend the subdomain portion (if any) to the path. - /// Derived from . - internal class RewriteSubdomainRule : IRule - { - /********* - ** Accessors - *********/ - /// The paths (excluding the hostname portion) to not rewrite. - public Regex[] ExceptPaths { get; set; } - - - /********* - ** Public methods - *********/ - /// Applies the rule. Implementations of ApplyRule should set the value for (defaults to RuleResult.ContinueRules). - /// The rewrite context. - public void ApplyRule(RewriteContext context) - { - context.Result = RuleResult.ContinueRules; - HttpRequest request = context.HttpContext.Request; - - // check ignores - if (this.ExceptPaths?.Any(pattern => pattern.IsMatch(request.Path)) == true) - return; - - // get host parts - string host = request.Host.Host; - string[] parts = host.Split('.'); - - // validate - if (parts.Length < 2) - return; - if (parts.Length < 3 && !"localhost".Equals(parts[1], StringComparison.InvariantCultureIgnoreCase)) - return; - - // prepend to path - request.Path = $"/{parts[0]}{request.Path}"; - } - } -} -- cgit From d545281ef3d83d4db43d5ca56eb59800c8a1b8d2 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 28 Oct 2017 12:24:50 -0400 Subject: redirect web views to HTTPS (#358) --- .../RewriteRules/ConditionalRedirectToHttpsRule.cs | 62 ++++++++++++++++++++++ .../ConditionalRewriteSubdomainRule.cs | 4 +- 2 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 src/SMAPI.Web/Framework/RewriteRules/ConditionalRedirectToHttpsRule.cs (limited to 'src/SMAPI.Web/Framework') diff --git a/src/SMAPI.Web/Framework/RewriteRules/ConditionalRedirectToHttpsRule.cs b/src/SMAPI.Web/Framework/RewriteRules/ConditionalRedirectToHttpsRule.cs new file mode 100644 index 00000000..d6a56bb7 --- /dev/null +++ b/src/SMAPI.Web/Framework/RewriteRules/ConditionalRedirectToHttpsRule.cs @@ -0,0 +1,62 @@ +using System; +using System.Net; +using System.Text; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Rewrite; + +namespace StardewModdingAPI.Web.Framework.RewriteRules +{ + /// Redirect requests to HTTPS. + /// Derived from and . + internal class ConditionalRedirectToHttpsRule : IRule + { + /********* + ** Properties + *********/ + /// A predicate which indicates when the rule should be applied. + private readonly Func ShouldRewrite; + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// A predicate which indicates when the rule should be applied. + public ConditionalRedirectToHttpsRule(Func shouldRewrite = null) + { + this.ShouldRewrite = shouldRewrite ?? (req => true); + } + + /// Applies the rule. Implementations of ApplyRule should set the value for (defaults to RuleResult.ContinueRules). + /// The rewrite context. + public void ApplyRule(RewriteContext context) + { + HttpRequest request = context.HttpContext.Request; + + // check condition + if (this.IsSecure(request) || !this.ShouldRewrite(request)) + return; + + // redirect request + HttpResponse response = context.HttpContext.Response; + response.StatusCode = (int)HttpStatusCode.RedirectKeepVerb; + response.Headers["Location"] = new StringBuilder() + .Append("https://") + .Append(request.Host.Host) + .Append(request.PathBase) + .Append(request.Path) + .Append(request.QueryString) + .ToString(); + context.Result = RuleResult.EndResponse; + } + + /// Get whether the request was received over HTTPS. + /// The request to check. + public bool IsSecure(HttpRequest request) + { + return + request.IsHttps // HTTPS to server + || string.Equals(request.Headers["x-forwarded-proto"], "HTTPS", StringComparison.OrdinalIgnoreCase); // HTTPS to AWS load balancer + } + } +} diff --git a/src/SMAPI.Web/Framework/RewriteRules/ConditionalRewriteSubdomainRule.cs b/src/SMAPI.Web/Framework/RewriteRules/ConditionalRewriteSubdomainRule.cs index 83f23058..920632ab 100644 --- a/src/SMAPI.Web/Framework/RewriteRules/ConditionalRewriteSubdomainRule.cs +++ b/src/SMAPI.Web/Framework/RewriteRules/ConditionalRewriteSubdomainRule.cs @@ -22,7 +22,7 @@ namespace StardewModdingAPI.Web.Framework.RewriteRules /// A predicate which indicates when the rule should be applied. public ConditionalRewriteSubdomainRule(Func shouldRewrite = null) { - this.ShouldRewrite = shouldRewrite; + this.ShouldRewrite = shouldRewrite ?? (req => true); } /// Applies the rule. Implementations of ApplyRule should set the value for (defaults to RuleResult.ContinueRules). @@ -32,7 +32,7 @@ namespace StardewModdingAPI.Web.Framework.RewriteRules HttpRequest request = context.HttpContext.Request; // check condition - if (this.ShouldRewrite != null && !this.ShouldRewrite(request)) + if (!this.ShouldRewrite(request)) return; // get host parts -- cgit From f895fedc6aa12742842c97e536b5bf2e5ca3553c Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 28 Oct 2017 14:03:53 -0400 Subject: move credentials into git-ignored file (#358) --- src/SMAPI.Web/Framework/LogParser/PastebinClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/SMAPI.Web/Framework') diff --git a/src/SMAPI.Web/Framework/LogParser/PastebinClient.cs b/src/SMAPI.Web/Framework/LogParser/PastebinClient.cs index 8536f249..e45a9eed 100644 --- a/src/SMAPI.Web/Framework/LogParser/PastebinClient.cs +++ b/src/SMAPI.Web/Framework/LogParser/PastebinClient.cs @@ -75,7 +75,7 @@ namespace StardewModdingAPI.Web.Framework.LogParser .PostAsync("api/api_post.php") .WithBodyContent(new FormUrlEncodedContent(new Dictionary { - ["api_dev_key"] = "b8219d942109d1e60ebb14fbb45f06f9", + ["api_dev_key"] = this.DevKey, ["api_option"] = "paste", ["api_paste_private"] = "1", ["api_paste_code"] = content, -- cgit From 790a62920b15f1f948724f5b2a70a937829355dd Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 28 Oct 2017 14:05:29 -0400 Subject: link pastes to Pastebin account & tweak paste options (#358) --- src/SMAPI.Web/Framework/ConfigModels/LogParserConfig.cs | 3 +++ src/SMAPI.Web/Framework/LogParser/PastebinClient.cs | 17 ++++++++++++----- 2 files changed, 15 insertions(+), 5 deletions(-) (limited to 'src/SMAPI.Web/Framework') diff --git a/src/SMAPI.Web/Framework/ConfigModels/LogParserConfig.cs b/src/SMAPI.Web/Framework/ConfigModels/LogParserConfig.cs index 18d8ff05..df5d605d 100644 --- a/src/SMAPI.Web/Framework/ConfigModels/LogParserConfig.cs +++ b/src/SMAPI.Web/Framework/ConfigModels/LogParserConfig.cs @@ -15,6 +15,9 @@ namespace StardewModdingAPI.Web.Framework.ConfigModels /// The user agent for the Pastebin API client, where {0} is the SMAPI version. public string PastebinUserAgent { get; set; } + /// The user key used to authenticate with the Pastebin API. + public string PastebinUserKey { get; set; } + /// The developer key used to authenticate with the Pastebin API. public string PastebinDevKey { get; set; } } diff --git a/src/SMAPI.Web/Framework/LogParser/PastebinClient.cs b/src/SMAPI.Web/Framework/LogParser/PastebinClient.cs index e45a9eed..738330d3 100644 --- a/src/SMAPI.Web/Framework/LogParser/PastebinClient.cs +++ b/src/SMAPI.Web/Framework/LogParser/PastebinClient.cs @@ -17,6 +17,9 @@ namespace StardewModdingAPI.Web.Framework.LogParser /// The underlying HTTP client. private readonly IClient Client; + /// The user key used to authenticate with the Pastebin API. + private readonly string UserKey; + /// The developer key used to authenticate with the Pastebin API. private readonly string DevKey; @@ -27,10 +30,12 @@ namespace StardewModdingAPI.Web.Framework.LogParser /// Construct an instance. /// The base URL for the Pastebin API. /// The user agent for the API client. + /// The user key used to authenticate with the Pastebin API. /// The developer key used to authenticate with the Pastebin API. - public PastebinClient(string baseUrl, string userAgent, string devKey) + public PastebinClient(string baseUrl, string userAgent, string userKey, string devKey) { this.Client = new FluentClient(baseUrl).SetUserAgent(userAgent); + this.UserKey = userKey; this.DevKey = devKey; } @@ -75,11 +80,13 @@ namespace StardewModdingAPI.Web.Framework.LogParser .PostAsync("api/api_post.php") .WithBodyContent(new FormUrlEncodedContent(new Dictionary { - ["api_dev_key"] = this.DevKey, ["api_option"] = "paste", - ["api_paste_private"] = "1", - ["api_paste_code"] = content, - ["api_paste_expire_date"] = "1W" + ["api_user_key"] = this.UserKey, + ["api_dev_key"] = this.DevKey, + ["api_paste_private"] = "1", // unlisted + ["api_paste_name"] = $"SMAPI log {DateTime.UtcNow:s}", + ["api_paste_expire_date"] = "1W", // one week + ["api_paste_code"] = content })) .AsString(); -- cgit From 6638701d0221e82daa42581eddb3cf051d1f8de4 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 29 Oct 2017 13:15:05 -0400 Subject: fix config not being injected from Amazon Beanstalk env props --- .../Framework/BeanstalkEnvPropsConfigProvider.cs | 54 ++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 src/SMAPI.Web/Framework/BeanstalkEnvPropsConfigProvider.cs (limited to 'src/SMAPI.Web/Framework') diff --git a/src/SMAPI.Web/Framework/BeanstalkEnvPropsConfigProvider.cs b/src/SMAPI.Web/Framework/BeanstalkEnvPropsConfigProvider.cs new file mode 100644 index 00000000..b39a3b61 --- /dev/null +++ b/src/SMAPI.Web/Framework/BeanstalkEnvPropsConfigProvider.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Microsoft.Extensions.Configuration; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace StardewModdingAPI.Web.Framework +{ + /// Reads configuration values from the AWS Beanstalk environment properties file (if present). + /// This is a workaround for AWS Beanstalk injection not working with .NET Core apps. + internal class BeanstalkEnvPropsConfigProvider : ConfigurationProvider, IConfigurationSource + { + /********* + ** Properties + *********/ + /// The absolute path to the container configuration file on an Amazon EC2 instance. + private const string ContainerConfigPath = @"C:\Program Files\Amazon\ElasticBeanstalk\config\containerconfiguration"; + + + /********* + ** Public methods + *********/ + /// Build the configuration provider for this source. + /// The configuration builder. + public IConfigurationProvider Build(IConfigurationBuilder builder) + { + return new BeanstalkEnvPropsConfigProvider(); + } + + /// Load the environment properties. + public override void Load() + { + this.Data = new Dictionary(StringComparer.OrdinalIgnoreCase); + + // get Beanstalk config file + FileInfo file = new FileInfo(BeanstalkEnvPropsConfigProvider.ContainerConfigPath); + if (!file.Exists) + return; + + // parse JSON + JObject jsonRoot = (JObject)JsonConvert.DeserializeObject(File.ReadAllText(file.FullName)); + if (jsonRoot["iis"]?["env"] is JArray jsonProps) + { + foreach (string prop in jsonProps.Values()) + { + string[] parts = prop.Split('=', 2); // key=value + if (parts.Length == 2) + this.Data[parts[0]] = parts[1]; + } + } + } + } +} -- cgit From 13baaf8920f4a80ac3c0cd41a16b9afb1b993048 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sun, 29 Oct 2017 22:18:08 -0400 Subject: add smapi.io shortcut URLs (#375) --- .../Framework/RewriteRules/RedirectToUrlRule.cs | 61 ++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 src/SMAPI.Web/Framework/RewriteRules/RedirectToUrlRule.cs (limited to 'src/SMAPI.Web/Framework') diff --git a/src/SMAPI.Web/Framework/RewriteRules/RedirectToUrlRule.cs b/src/SMAPI.Web/Framework/RewriteRules/RedirectToUrlRule.cs new file mode 100644 index 00000000..0719e311 --- /dev/null +++ b/src/SMAPI.Web/Framework/RewriteRules/RedirectToUrlRule.cs @@ -0,0 +1,61 @@ +using System; +using System.Net; +using System.Text.RegularExpressions; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Rewrite; + +namespace StardewModdingAPI.Web.Framework.RewriteRules +{ + /// Redirect requests to an external URL if they match a condition. + internal class RedirectToUrlRule : IRule + { + /********* + ** Properties + *********/ + /// A predicate which indicates when the rule should be applied. + private readonly Func ShouldRewrite; + + /// The new URL to which to redirect. + private readonly string NewUrl; + + + /********* + ** Public methods + *********/ + /// Construct an instance. + /// A predicate which indicates when the rule should be applied. + /// The new URL to which to redirect. + public RedirectToUrlRule(Func shouldRewrite, string url) + { + this.ShouldRewrite = shouldRewrite ?? (req => true); + this.NewUrl = url; + } + + /// Construct an instance. + /// A case-insensitive regex to match against the path. + /// The external URL. + public RedirectToUrlRule(string pathRegex, string url) + { + Regex regex = new Regex(pathRegex, RegexOptions.IgnoreCase | RegexOptions.Compiled); + this.ShouldRewrite = req => req.Path.HasValue && regex.IsMatch(req.Path.Value); + this.NewUrl = url; + } + + /// Applies the rule. Implementations of ApplyRule should set the value for (defaults to RuleResult.ContinueRules). + /// The rewrite context. + public void ApplyRule(RewriteContext context) + { + HttpRequest request = context.HttpContext.Request; + + // check condition + if (!this.ShouldRewrite(request)) + return; + + // redirect request + HttpResponse response = context.HttpContext.Response; + response.StatusCode = (int)HttpStatusCode.Redirect; + response.Headers["Location"] = this.NewUrl; + context.Result = RuleResult.EndResponse; + } + } +} -- cgit