From 07ca5edbcbca627efa0c6d1535d75e94207357cc Mon Sep 17 00:00:00 2001 From: Madpeter Date: Tue, 12 May 2020 20:00:10 +0100 Subject: [PATCH] WebUI -> Login/Logout and session vaildation (More work than you would expect) --- CLI/HttpServer/SecondBotHttpServer.cs | 45 ++-- CLI/HttpWebUi/theme/js/site.js | 8 +- CLI/HttpWebUi/theme/layout/cdn.css.layout | 2 +- CLI/HttpWebUi/theme/layout/cdn.js.layout | 3 +- CLI/HttpWebUi/theme/layout/login.block | 2 +- CLI/HttpWebUi/theme/layout/sidemenu.layout | 2 +- CLI/HttpWebUi/webui.cs | 242 ++++++++++++++++----- 7 files changed, 231 insertions(+), 73 deletions(-) diff --git a/CLI/HttpServer/SecondBotHttpServer.cs b/CLI/HttpServer/SecondBotHttpServer.cs index 5ca777a..30aaac2 100644 --- a/CLI/HttpServer/SecondBotHttpServer.cs +++ b/CLI/HttpServer/SecondBotHttpServer.cs @@ -5,17 +5,25 @@ using BetterSecondBotShared.logs; using BetterSecondBotShared.Static; using BSB; using BSB.bottypes; +using Newtonsoft.Json; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; +using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Threading.Tasks; namespace BetterSecondBot.HttpServer { + public class json_http_reply + { + public bool status; + public string message; + public string redirect; + } public class SecondBotHttpServer { protected HttpListener listener; @@ -95,18 +103,20 @@ namespace BetterSecondBot.HttpServer HttpListenerContext ctx = await listener.GetContextAsync().ConfigureAwait(true); HttpListenerRequest req = ctx.Request; HttpListenerResponse resp = ctx.Response; + resp.Cookies = req.Cookies; string test = req.Url.AbsolutePath.Substring(1); if (helpers.notempty(Config.Http_PublicUrl) == true) { test = test.Replace(Config.Http_PublicUrl, ""); } + json_http_reply reply = new json_http_reply(); List http_args = test.Split('/', StringSplitOptions.RemoveEmptyEntries).ToList(); byte[] data; resp.StatusCode = 200; resp.ContentType = "text/html"; if (req.HttpMethod == "GET") { - KeyValuePair replydata = myUI.Get_Process(http_args, req.Cookies); + KeyValuePair replydata = myUI.Get_Process(req, resp, http_args); if (replydata.Value != null) { resp.ContentType = replydata.Key; @@ -142,16 +152,8 @@ namespace BetterSecondBot.HttpServer post_args.Add(bits[0].Trim(), bits[1].Trim()); } } - resp.StatusCode = 200; - KeyValuePair UI_process = myUI.Post_process(http_args,post_args); - if (UI_process.Key == false) - { - data = Encoding.UTF8.GetBytes(UI_process.Value); - resp.ContentLength64 = data.LongLength; - await resp.OutputStream.WriteAsync(data, 0, data.Length).ConfigureAwait(true); - } - else + if (myUI.Post_process(reply, req, resp, http_args, post_args) == true) { bool accept_request = false; string why_request_failed = "Bad signing request"; @@ -186,19 +188,24 @@ namespace BetterSecondBot.HttpServer { why_request_failed = "Post missing command value"; } + reply.message = why_request_failed; + reply.status = accept_request; if (accept_request == true) { - data = Encoding.UTF8.GetBytes(String.Join("###", post_controler.Call(command, arg))); + reply.message = String.Join("###", post_controler.Call(command, arg)); } - else - { - resp.StatusCode = 417; - data = Encoding.UTF8.GetBytes(why_request_failed); - } - resp.ContentLength64 = data.LongLength; - await resp.OutputStream.WriteAsync(data, 0, data.Length); } - + if(reply.redirect != "") + { + if(reply.redirect == "/") + { + reply.redirect = ""; + } + reply.redirect = "" + Config.Http_PublicUrl + "" + reply.redirect + ""; + } + data = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(reply)); + resp.ContentLength64 = data.LongLength; + await resp.OutputStream.WriteAsync(data, 0, data.Length).ConfigureAwait(true); resp.Close(); } } diff --git a/CLI/HttpWebUi/theme/js/site.js b/CLI/HttpWebUi/theme/js/site.js index 9dec0ef..d47f807 100644 --- a/CLI/HttpWebUi/theme/js/site.js +++ b/CLI/HttpWebUi/theme/js/site.js @@ -30,7 +30,13 @@ $( document ).ready(function() { } if(jsondata.hasOwnProperty('redirect')) { - setTimeout(function(){ $(location).attr('href', jsondata.redirect) }, redirectdelay); + if(jsondata.redirect != "") + { + if(jsondata.redirect != null) + { + setTimeout(function(){ $(location).attr('href', jsondata.redirect) }, redirectdelay); + } + } } } else diff --git a/CLI/HttpWebUi/theme/layout/cdn.css.layout b/CLI/HttpWebUi/theme/layout/cdn.css.layout index 57c2197..ab0ae43 100644 --- a/CLI/HttpWebUi/theme/layout/cdn.css.layout +++ b/CLI/HttpWebUi/theme/layout/cdn.css.layout @@ -1,2 +1,2 @@ - + diff --git a/CLI/HttpWebUi/theme/layout/cdn.js.layout b/CLI/HttpWebUi/theme/layout/cdn.js.layout index 07f9a83..b2ca079 100644 --- a/CLI/HttpWebUi/theme/layout/cdn.js.layout +++ b/CLI/HttpWebUi/theme/layout/cdn.js.layout @@ -1,3 +1,4 @@  - \ No newline at end of file + + \ No newline at end of file diff --git a/CLI/HttpWebUi/theme/layout/login.block b/CLI/HttpWebUi/theme/layout/login.block index c336d96..8a26ea4 100644 --- a/CLI/HttpWebUi/theme/layout/login.block +++ b/CLI/HttpWebUi/theme/layout/login.block @@ -4,7 +4,7 @@
-
+
diff --git a/CLI/HttpWebUi/theme/layout/sidemenu.layout b/CLI/HttpWebUi/theme/layout/sidemenu.layout index cd4b3b1..387b9f6 100644 --- a/CLI/HttpWebUi/theme/layout/sidemenu.layout +++ b/CLI/HttpWebUi/theme/layout/sidemenu.layout @@ -27,7 +27,7 @@
diff --git a/CLI/HttpWebUi/webui.cs b/CLI/HttpWebUi/webui.cs index 2cb7e9a..48a9b03 100644 --- a/CLI/HttpWebUi/webui.cs +++ b/CLI/HttpWebUi/webui.cs @@ -1,4 +1,5 @@ -using BetterSecondBotShared.Json; +using BetterSecondBot.HttpServer; +using BetterSecondBotShared.Json; using BetterSecondBotShared.Static; using BSB.bottypes; using System; @@ -14,79 +15,222 @@ namespace BetterSecondBot.HttpWebUi { JsonConfig siteconfig; SecondBot bot; + Dictionary vaildlogincookies = new Dictionary(); public webui(JsonConfig setconfig,SecondBot setbot) { bot = setbot; siteconfig = setconfig; } - public KeyValuePair Post_process(List args, Dictionary post_args) + protected KeyValuePair logincookievaild(HttpListenerRequest reqs, HttpListenerResponse resp) { - if(args != null) + return logincookievaild(reqs, resp, false); + } + protected KeyValuePair logincookievaild(HttpListenerRequest reqs, HttpListenerResponse resp,bool force_logout) + { + foreach (Cookie C in resp.Cookies) { - if(args.Count == 1) + if (C.Name == "logincookie") + { + Cookie old = C; + if (force_logout == true) + { + if (vaildlogincookies.ContainsKey(C.Value) == true) + { + vaildlogincookies.Remove(C.Value); + } + Console.WriteLine("Cookie wiped"); + } + else + { + if (vaildlogincookies.ContainsKey(C.Value) == true) + { + if (vaildlogincookies[C.Value] > helpers.UnixTimeNow()) + { + Console.WriteLine("Cookie ok"); + return new KeyValuePair(true, true); + } + else + { + Console.WriteLine("Cookie expired"); + } + } + else + { + Console.WriteLine("Cookie not vaild"); + } + } + resp.Cookies.Remove(C); + C.Expired = true; + resp.Cookies.Add(C); + return new KeyValuePair(true, false); + } + } + Console.WriteLine("No cookie"); + return new KeyValuePair(false, false); + } + public bool Post_process(json_http_reply reply,HttpListenerRequest reqs, HttpListenerResponse resp,List args, Dictionary post_args) + { + bool processed = false; + if (args != null) + { + if(args.Count >= 1) { if(args[0] == "ajax") { + processed = true; // webui only cares about ajax post requests - - } - } - } - return new KeyValuePair(false, ""); - } - public KeyValuePair Get_Process(List args,CookieCollection cookies) - { - Cookie logincookie = null; - foreach(Cookie C in cookies) - { - if(C.Name == "logincookie") - { - logincookie = C; - break; - } - } - if (logincookie == null) - { - if (args != null) - { - if (args.Count >= 1) - { - if (args[0] == "theme") + string mod = "none"; + string area = "none"; + if (args.Count >= 2) { - if (args.Count >= 2) + mod = args[1]; + } + if (args.Count >= 3) + { + area = args[2]; + } + KeyValuePair logincheck = logincookievaild(reqs, resp); + + if (logincheck.Key == false) + { + if (mod == "login") { - if (args[1] == "images") + if (post_args.ContainsKey("logincode") == true) { - return new KeyValuePair("image/png", helpers.ReadResourceFileBinary(Assembly.GetExecutingAssembly(), args[2])); + if (post_args["logincode"] == siteconfig.Security_WebUIKey) + { + reply.status = true; + string newcookiecode = ""; + while ((newcookiecode == null) || (newcookiecode == "") || (vaildlogincookies.ContainsKey(newcookiecode) == true)) + { + newcookiecode = helpers.GetSHA1(siteconfig.Security_WebUIKey + helpers.UnixTimeNow().ToString() + new Random().Next(12345).ToString()); + } + vaildlogincookies.Add(newcookiecode, helpers.UnixTimeNow() + (5 * 60)); // auto logout after 5 mins + Cookie logincookie = new Cookie("logincookie", newcookiecode); + logincookie.Expires = DateTime.Now.AddDays(1); + logincookie.Path = "/"; + resp.Cookies.Add(logincookie); + Console.WriteLine("Cookie created: "+ logincookie.Value+" <=> "+ newcookiecode+""); + reply.message = "ok"; + reply.redirect = "/"; + } + else + { + reply.message = "fail"; + } } - else if (args[1] == "css") + else { - return new KeyValuePair("text/css", Encoding.UTF8.GetBytes(helpers.ReadResourceFile(Assembly.GetExecutingAssembly(), args[2]))); - } - else if (args[1] == "js") - { - return new KeyValuePair("text/javascript", Encoding.UTF8.GetBytes(helpers.ReadResourceFile(Assembly.GetExecutingAssembly(), args[2]))); + reply.message = "Missing code"; } } + else + { + reply.message = "No access"; + } + } + else + { + if (logincheck.Value == true) + { + reply.status = true; + // logged in + if (mod == "logout") + { + logincookievaild(reqs, resp, true); + reply.message = "logged-out"; + } + reply.message = "No action"; + } + else + { + // login expired + reply.message = "login-expired"; + reply.redirect = "/"; + logincookievaild(reqs, resp,true); + } } - return new KeyValuePair("text/html", null); } } - string layout = helpers.ReadResourceFile(Assembly.GetExecutingAssembly(), "full.layout"); - layout = layout.Replace("[[page_content]]", helpers.ReadResourceFile(Assembly.GetExecutingAssembly(), "login.block")); - layout = layout.Replace("[[url_base]]", siteconfig.Http_PublicUrl); - layout = layout.Replace("[[html_title_after]]", "Secondbot web UI"); - layout = layout.Replace("[[html_title]]", siteconfig.Basic_BotUserName); - layout = layout.Replace("[[html_cs_top]]", helpers.ReadResourceFile(Assembly.GetExecutingAssembly(), "cdn.css.layout")); - layout = layout.Replace("[[html_js_bottom]]", helpers.ReadResourceFile(Assembly.GetExecutingAssembly(), "cdn.js.layout")); - - layout = layout.Replace("[[html_js_onready]]", ""); - return new KeyValuePair("text/html", Encoding.UTF8.GetBytes(layout)); + } + return !processed; + } + public KeyValuePair html_logged_out() + { + string layout = helpers.ReadResourceFile(Assembly.GetExecutingAssembly(), "full.layout"); + layout = layout.Replace("[[page_content]]", helpers.ReadResourceFile(Assembly.GetExecutingAssembly(), "login.block")); + layout = layout.Replace("[[url_base]]", siteconfig.Http_PublicUrl); + layout = layout.Replace("[[html_title_after]]", "Secondbot web UI"); + layout = layout.Replace("[[html_title]]", siteconfig.Basic_BotUserName); + layout = layout.Replace("[[html_cs_top]]", helpers.ReadResourceFile(Assembly.GetExecutingAssembly(), "cdn.css.layout")); + layout = layout.Replace("[[html_js_bottom]]", helpers.ReadResourceFile(Assembly.GetExecutingAssembly(), "cdn.js.layout")); + + layout = layout.Replace("[[html_js_onready]]", ""); + return new KeyValuePair("text/html", Encoding.UTF8.GetBytes(layout)); + } + public KeyValuePair Get_Process(HttpListenerRequest reqs, HttpListenerResponse resp, List args) + { + // get content + if (args != null) + { + if (args.Count >= 1) + { + if (args[0] == "theme") + { + if (args.Count >= 2) + { + if (args[1] == "images") + { + return new KeyValuePair("image/png", helpers.ReadResourceFileBinary(Assembly.GetExecutingAssembly(), args[2])); + } + else if (args[1] == "css") + { + return new KeyValuePair("text/css", Encoding.UTF8.GetBytes(helpers.ReadResourceFile(Assembly.GetExecutingAssembly(), args[2]))); + } + else if (args[1] == "js") + { + return new KeyValuePair("text/javascript", Encoding.UTF8.GetBytes(helpers.ReadResourceFile(Assembly.GetExecutingAssembly(), args[2]))); + } + } + } + } + } + // get display + KeyValuePair logincheck = logincookievaild(reqs, resp); + if(logincheck.Key == true) + { + if(logincheck.Value == false) + { + logincookievaild(reqs, resp, true); + } + } + if (logincheck.Value == false) + { + return html_logged_out(); } else { - return new KeyValuePair("text/html", null); + string mod = "none"; + string area = "none"; + if (args.Count >= 1) + { + mod = args[0]; + } + if (args.Count >= 2) + { + area = args[1]; + } + if (mod == "logout") + { + logincookievaild(reqs, resp, true); + return html_logged_out(); + } + else + { + // logged in + return new KeyValuePair("text/html", Encoding.UTF8.GetBytes("logged in")); + } } } }