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/LogParser/GetPasteResponse.cs | 15 +++ .../Framework/LogParser/PastebinClient.cs | 110 +++++++++++++++++++++ .../Framework/LogParser/SavePasteResponse.cs | 15 +++ 3 files changed, 140 insertions(+) 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/LogParser') 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 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) --- .gitignore | 3 +++ src/SMAPI.Web/Framework/LogParser/PastebinClient.cs | 2 +- src/SMAPI.Web/appsettings.Development.json | 16 +++++++++++++++- src/SMAPI.Web/appsettings.json | 16 ++++++++++++---- 4 files changed, 31 insertions(+), 6 deletions(-) (limited to 'src/SMAPI.Web/Framework/LogParser') diff --git a/.gitignore b/.gitignore index f2d50778..7e0c1e9d 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,6 @@ _ReSharper*/ **/packages/* *.nuget.props *.nuget.targets + +# sensitive files +appsettings.Development.json 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, diff --git a/src/SMAPI.Web/appsettings.Development.json b/src/SMAPI.Web/appsettings.Development.json index e49eabb7..1080ee00 100644 --- a/src/SMAPI.Web/appsettings.Development.json +++ b/src/SMAPI.Web/appsettings.Development.json @@ -1,3 +1,12 @@ +/* + + + This file is committed to source control with the default settings, but added to .gitignore to + avoid accidentally committing login details. + + + +*/ { "Logging": { "IncludeScopes": false, @@ -7,7 +16,12 @@ "Microsoft": "Information" } }, + "ModUpdateCheck": { + "GitHubUsername": null, + "GitHubPassword": null + }, "LogParser": { - "SectionUrl": "http://localhost:59482/log/" + "SectionUrl": "http://localhost:59482/log/", + "PastebinDevKey": null } } diff --git a/src/SMAPI.Web/appsettings.json b/src/SMAPI.Web/appsettings.json index f99748d6..397765eb 100644 --- a/src/SMAPI.Web/appsettings.json +++ b/src/SMAPI.Web/appsettings.json @@ -1,3 +1,11 @@ +/* + + + This contains the default settings for the web app. Login credentials and contextual settings are + configured via appsettings.Development.json locally, or environment properties in AWS. + + +*/ { "Logging": { "IncludeScopes": false, @@ -19,8 +27,8 @@ "GitHubBaseUrl": "https://api.github.com", "GitHubReleaseUrlFormat": "repos/{0}/releases/latest", "GitHubAcceptHeader": "application/vnd.github.v3+json", - "GitHubUsername": null, /* set via environment properties */ - "GitHubPassword": null, /* set via environment properties */ + "GitHubUsername": null, // see top note + "GitHubPassword": null, // see top note "NexusKey": "Nexus", "NexusUserAgent": "Nexus Client v0.63.15", @@ -28,9 +36,9 @@ "NexusModUrlFormat": "mods/{0}" }, "LogParser": { - "SectionUrl": null, /* set via environment properties */ + "SectionUrl": null, // see top note "PastebinBaseUrl": "https://pastebin.com/", "PastebinUserAgent": "SMAPI/{0} (+https://github.com/Pathoschild/SMAPI)", - "PastebinDevKey": "b8219d942109d1e60ebb14fbb45f06f9" + "PastebinDevKey": null // see top note } } -- 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/Controllers/LogParserController.cs | 2 +- src/SMAPI.Web/Framework/ConfigModels/LogParserConfig.cs | 3 +++ src/SMAPI.Web/Framework/LogParser/PastebinClient.cs | 17 ++++++++++++----- src/SMAPI.Web/appsettings.Development.json | 1 + src/SMAPI.Web/appsettings.json | 1 + 5 files changed, 18 insertions(+), 6 deletions(-) (limited to 'src/SMAPI.Web/Framework/LogParser') diff --git a/src/SMAPI.Web/Controllers/LogParserController.cs b/src/SMAPI.Web/Controllers/LogParserController.cs index f9943707..8e986196 100644 --- a/src/SMAPI.Web/Controllers/LogParserController.cs +++ b/src/SMAPI.Web/Controllers/LogParserController.cs @@ -35,7 +35,7 @@ namespace StardewModdingAPI.Web.Controllers this.Config = configProvider.Value; string version = this.GetType().Assembly.GetName().Version.ToString(3); string userAgent = string.Format(this.Config.PastebinUserAgent, version); - this.PastebinClient = new PastebinClient(this.Config.PastebinBaseUrl, userAgent, this.Config.PastebinDevKey); + this.PastebinClient = new PastebinClient(this.Config.PastebinBaseUrl, userAgent, this.Config.PastebinUserKey, this.Config.PastebinDevKey); } /*** 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(); diff --git a/src/SMAPI.Web/appsettings.Development.json b/src/SMAPI.Web/appsettings.Development.json index 1080ee00..87c35ca9 100644 --- a/src/SMAPI.Web/appsettings.Development.json +++ b/src/SMAPI.Web/appsettings.Development.json @@ -22,6 +22,7 @@ }, "LogParser": { "SectionUrl": "http://localhost:59482/log/", + "PastebinUserKey": null, "PastebinDevKey": null } } diff --git a/src/SMAPI.Web/appsettings.json b/src/SMAPI.Web/appsettings.json index 397765eb..eb6ecc9b 100644 --- a/src/SMAPI.Web/appsettings.json +++ b/src/SMAPI.Web/appsettings.json @@ -39,6 +39,7 @@ "SectionUrl": null, // see top note "PastebinBaseUrl": "https://pastebin.com/", "PastebinUserAgent": "SMAPI/{0} (+https://github.com/Pathoschild/SMAPI)", + "PastebinUserKey": null, // see top note "PastebinDevKey": null // see top note } } -- cgit