From 3ee29084c3ed89b22efb9b05bbaf14e76c33c735 Mon Sep 17 00:00:00 2001 From: LinQiaoPorco Date: Mon, 22 Nov 2021 19:00:20 +0800 Subject: [PATCH 01/37] Changes to OIDC --- .vscode/settings.json | 4 + src/Blogifier.Admin/.vscode/settings.json | 4 + .../Pages/Account/ProfileView.razor.cs | 2 +- .../Properties/launchSettings.json | 2 +- .../Providers/AuthorProvider.cs | 1 + src/Blogifier/Blog.db | Bin 106496 -> 106496 bytes src/Blogifier/Blogifier.csproj | 1 + src/Blogifier/Controllers/AuthorController.cs | 211 ++++++++++-------- src/Blogifier/Controllers/HomeController.cs | 181 +++++++-------- src/Blogifier/Properties/launchSettings.json | 2 +- src/Blogifier/Startup.cs | 34 ++- .../Themes/standard/components/nav.cshtml | 125 ++++++++--- 12 files changed, 343 insertions(+), 224 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 src/Blogifier.Admin/.vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..eae26b95a --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "restructuredtext.pythonRecommendation.disabled": true, + "restructuredtext.languageServer.disabled": true +} \ No newline at end of file diff --git a/src/Blogifier.Admin/.vscode/settings.json b/src/Blogifier.Admin/.vscode/settings.json new file mode 100644 index 000000000..87c915622 --- /dev/null +++ b/src/Blogifier.Admin/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "restructuredtext.pythonRecommendation.disabled": true, + "restructuredtext.languageServer.disabled": true +} diff --git a/src/Blogifier.Admin/Pages/Account/ProfileView.razor.cs b/src/Blogifier.Admin/Pages/Account/ProfileView.razor.cs index 6daaa9650..23c57f381 100644 --- a/src/Blogifier.Admin/Pages/Account/ProfileView.razor.cs +++ b/src/Blogifier.Admin/Pages/Account/ProfileView.razor.cs @@ -4,6 +4,6 @@ namespace Blogifier.Admin.Pages.Profile { public partial class Profile { - + } } \ No newline at end of file diff --git a/src/Blogifier.Admin/Properties/launchSettings.json b/src/Blogifier.Admin/Properties/launchSettings.json index cf90d71ae..bd54dec33 100644 --- a/src/Blogifier.Admin/Properties/launchSettings.json +++ b/src/Blogifier.Admin/Properties/launchSettings.json @@ -21,7 +21,7 @@ "dotnetRunMessages": "true", "launchBrowser": true, "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", - "applicationUrl": "http://localhost:5000", + "applicationUrl": "http://localhost:5002", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } diff --git a/src/Blogifier.Core/Providers/AuthorProvider.cs b/src/Blogifier.Core/Providers/AuthorProvider.cs index 180eb480f..70b7f0420 100644 --- a/src/Blogifier.Core/Providers/AuthorProvider.cs +++ b/src/Blogifier.Core/Providers/AuthorProvider.cs @@ -14,6 +14,7 @@ public interface IAuthorProvider { Task> GetAuthors(); Task FindByEmail(string email); + Task Verify(LoginModel model); Task Register(RegisterModel model); Task Add(Author author); diff --git a/src/Blogifier/Blog.db b/src/Blogifier/Blog.db index 4adeb088a957c263940b7340a9033005001b9e24..09039224325bccf5846f1b4a4d5de7405e4c8a1e 100644 GIT binary patch delta 38 rcmZoTz}5gnTNq7i7 + diff --git a/src/Blogifier/Controllers/AuthorController.cs b/src/Blogifier/Controllers/AuthorController.cs index 9bfd41fba..45527bfc7 100644 --- a/src/Blogifier/Controllers/AuthorController.cs +++ b/src/Blogifier/Controllers/AuthorController.cs @@ -1,4 +1,5 @@ -using Blogifier.Core.Providers; +using System.Linq; +using Blogifier.Core.Providers; using Blogifier.Shared; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; @@ -7,99 +8,123 @@ using System.Collections.Generic; using System.Security.Claims; using System.Threading.Tasks; +using System; namespace Blogifier.Controllers { - [Route("api/[controller]")] - [ApiController] - public class AuthorController : ControllerBase - { - private readonly IAuthorProvider _authorProvider; - - public AuthorController(IAuthorProvider authorProvider) - { - _authorProvider = authorProvider; - } - - [Authorize] - [HttpGet("all")] - public async Task> All() - { - return await _authorProvider.GetAuthors(); - } - - [Authorize] - [HttpGet("email/{email}")] - public async Task> FindByEmail(string email) - { - return await _authorProvider.FindByEmail(email); - } - - [HttpGet("getcurrent")] - public async Task> GetCurrentAuthor() - { - if (User.Identity.IsAuthenticated) - return await FindByEmail(User.FindFirstValue(ClaimTypes.Name)); - return new Author(); - } - - [Authorize] - [HttpDelete("{id:int}")] - public async Task> RemoveAuthor(int id) - { - return await _authorProvider.Remove(id); - } - - [Authorize] - [HttpPost("add")] - public async Task> Add(Author author) - { - var success = await _authorProvider.Add(author); - return success ? Ok() : BadRequest(); - } - - [Authorize] - [HttpPut("update")] - public async Task> Update(Author author) - { - var success = await _authorProvider.Update(author); - return success ? Ok() : BadRequest(); - } - - [HttpPost("register")] - public async Task> Register(RegisterModel model) - { - var success = await _authorProvider.Register(model); - return success ? Ok() : BadRequest(); - } - - [HttpPost("login")] - public async Task Login(LoginModel model) - { - if (await _authorProvider.Verify(model) == false) - return BadRequest(); - - var claim = new Claim(ClaimTypes.Name, model.Email); - var claimsIdentity = new ClaimsIdentity(new[] { claim }, "serverAuth"); - var claimsPrincipal = new ClaimsPrincipal(claimsIdentity); - - await HttpContext.SignInAsync(claimsPrincipal); - return Ok(); - } - - [HttpGet("logout")] - public async Task> LogOutUser() - { - await HttpContext.SignOutAsync(); - return await Task.FromResult(true); - } - - [Authorize] - [HttpPut("changepassword")] - public async Task> ChangePassword(RegisterModel model) - { - var success = await _authorProvider.ChangePassword(model); - return success ? Ok() : BadRequest(); - } - } + [Route("api/[controller]")] + [ApiController] + public class AuthorController : ControllerBase + { + private readonly IAuthorProvider _authorProvider; + + public AuthorController(IAuthorProvider authorProvider) + { + _authorProvider = authorProvider; + } + + [Authorize] + [HttpGet("all")] + public async Task> All() + { + return await _authorProvider.GetAuthors(); + } + + [Authorize] + [HttpGet("email/{email}")] + public async Task> FindByEmail(string email) + { + return await _authorProvider.FindByEmail(email); + } + + [HttpGet("getcurrent")] + public async Task> GetCurrentAuthor() + { + if (User.Identity.IsAuthenticated) + { + foreach (var claim in User.Claims) + { + Console.WriteLine("{0} ===> {1}", claim.Type, claim.Value); + } + // return await FindByEmail(User.FindFirstValue(ClaimTypes.Name)); + return await new Task(() => CreateFromOIDC()); + } + return new Author(); + } + + [Authorize] + [HttpDelete("{id:int}")] + public async Task> RemoveAuthor(int id) + { + return await _authorProvider.Remove(id); + } + + [Authorize] + [HttpPost("add")] + public async Task> Add(Author author) + { + var success = await _authorProvider.Add(author); + return success ? Ok() : BadRequest(); + } + + [Authorize] + [HttpPut("update")] + public async Task> Update(Author author) + { + var success = await _authorProvider.Update(author); + return success ? Ok() : BadRequest(); + } + + [HttpPost("register")] + public async Task> Register(RegisterModel model) + { + var success = await _authorProvider.Register(model); + return success ? Ok() : BadRequest(); + } + + [HttpPost("login")] + public async Task Login(LoginModel model) + { + if (await _authorProvider.Verify(model) == false) + return BadRequest(); + + var claim = new Claim(ClaimTypes.Name, model.Email); + var claimsIdentity = new ClaimsIdentity(new[] { claim }, "serverAuth"); + var claimsPrincipal = new ClaimsPrincipal(claimsIdentity); + + await HttpContext.SignInAsync(claimsPrincipal); + return Ok(); + } + + [HttpGet("logout")] + public async Task> LogOutUser() + { + await HttpContext.SignOutAsync(); + return await Task.FromResult(true); + } + + [Authorize] + [HttpPut("changepassword")] + public async Task> ChangePassword(RegisterModel model) + { + var success = await _authorProvider.ChangePassword(model); + return success ? Ok() : BadRequest(); + } + + [HttpGet("getcurrent")] + protected Author CreateFromOIDC() + { + var tempAuthor = new Author(); + tempAuthor.Avatar = + "https://auth.prime-minister.pub/images/user_avatars/" + + User.Claims.FirstOrDefault(claim => claim.Type == "avatar").Value + ".png"; + + tempAuthor.DisplayName = User.Claims.FirstOrDefault(claim => claim.Type == "name").Value; + tempAuthor.Email = User.Claims.FirstOrDefault(claim => claim.Type == "email").Value; + Console.WriteLine(tempAuthor.Avatar); + Console.WriteLine(tempAuthor.DisplayName); + Console.WriteLine(tempAuthor.Email); + return tempAuthor; + } + } } diff --git a/src/Blogifier/Controllers/HomeController.cs b/src/Blogifier/Controllers/HomeController.cs index 239863dcd..6dce53c5e 100644 --- a/src/Blogifier/Controllers/HomeController.cs +++ b/src/Blogifier/Controllers/HomeController.cs @@ -14,41 +14,42 @@ namespace Blogifier.Controllers { - public class HomeController : Controller - { - protected readonly IBlogProvider _blogProvider; - protected readonly IPostProvider _postProvider; - protected readonly IFeedProvider _feedProvider; - protected readonly IAuthorProvider _authorProvider; - protected readonly IThemeProvider _themeProvider; - protected readonly IStorageProvider _storageProvider; + public class HomeController : Controller + { + protected readonly IBlogProvider _blogProvider; + protected readonly IPostProvider _postProvider; + protected readonly IFeedProvider _feedProvider; + protected readonly IAuthorProvider _authorProvider; + protected readonly IThemeProvider _themeProvider; + protected readonly IStorageProvider _storageProvider; protected readonly ICompositeViewEngine _compositeViewEngine; public HomeController(IBlogProvider blogProvider, IPostProvider postProvider, IFeedProvider feedProvider, IAuthorProvider authorProvider, IThemeProvider themeProvider, IStorageProvider storageProvider, ICompositeViewEngine compositeViewEngine) - { - _blogProvider = blogProvider; - _postProvider = postProvider; - _feedProvider = feedProvider; - _authorProvider = authorProvider; - _themeProvider = themeProvider; - _storageProvider = storageProvider; + { + _blogProvider = blogProvider; + _postProvider = postProvider; + _feedProvider = feedProvider; + _authorProvider = authorProvider; + _themeProvider = themeProvider; + _storageProvider = storageProvider; _compositeViewEngine = compositeViewEngine; - } + } - public async Task Index(int page = 1) - { + public async Task Index(int page = 1) + { var model = await getBlogPosts(pager: page); //If no blogs are setup redirect to first time registration - if(model == null){ + if (model == null) + { return Redirect("~/admin/register"); } - return View($"~/Views/Themes/{model.Blog.Theme}/Index.cshtml", model); - } + return View($"~/Views/Themes/{model.Blog.Theme}/Index.cshtml", model); + } [HttpGet("/{slug}")] public async Task Index(string slug) @@ -61,15 +62,16 @@ public async Task Index(string slug) } [HttpGet("/admin")] - public async Task Admin() + // public async Task Admin() + public IActionResult Admin() { return File("~/index.html", "text/html"); } [HttpPost] - public async Task Search(string term, int page = 1) - { - + public async Task Search(string term, int page = 1) + { + if (!string.IsNullOrEmpty(term)) { var model = await getBlogPosts(term, page); @@ -79,14 +81,15 @@ public async Task Search(string term, int page = 1) else return Redirect("~/home"); } - else{ + else + { return Redirect("~/home"); } } [HttpGet("categories/{category}")] - public async Task Categories(string category, int page = 1) - { + public async Task Categories(string category, int page = 1) + { var model = await getBlogPosts("", page, category); string viewPath = $"~/Views/Themes/{model.Blog.Theme}/Category.cshtml"; @@ -96,7 +99,7 @@ public async Task Categories(string category, int page = 1) return View(viewPath, model); return View($"~/Views/Themes/{model.Blog.Theme}/Index.cshtml", model); - } + } [HttpGet("posts/{slug}")] public async Task Single(string slug) @@ -123,59 +126,59 @@ public async Task Error() } [ResponseCache(Duration = 1200)] - [HttpGet("feed/{type}")] - public async Task Rss(string type) - { - string host = Request.Scheme + "://" + Request.Host; - var blog = await _blogProvider.GetBlog(); - - var posts = await _feedProvider.GetEntries(type, host); - var items = new List(); - - var feed = new SyndicationFeed( - blog.Title, - blog.Description, - new Uri(host), - host, - posts.FirstOrDefault().Published - ); - - if (posts != null && posts.Count() > 0) - { - foreach (var post in posts) - { - var item = new SyndicationItem( - post.Title, - post.Description.MdToHtml(), - new Uri(post.Id), - post.Id, - post.Published - ); - item.PublishDate = post.Published; - items.Add(item); - } - } - feed.Items = items; - - var settings = new XmlWriterSettings - { - Encoding = Encoding.UTF8, - NewLineHandling = NewLineHandling.Entitize, - NewLineOnAttributes = true, - Indent = true - }; - - using (var stream = new MemoryStream()) - { - using (var xmlWriter = XmlWriter.Create(stream, settings)) - { - var rssFormatter = new Rss20FeedFormatter(feed, false); - rssFormatter.WriteTo(xmlWriter); - xmlWriter.Flush(); - } - return File(stream.ToArray(), "application/xml; charset=utf-8"); - } - } + [HttpGet("feed/{type}")] + public async Task Rss(string type) + { + string host = Request.Scheme + "://" + Request.Host; + var blog = await _blogProvider.GetBlog(); + + var posts = await _feedProvider.GetEntries(type, host); + var items = new List(); + + var feed = new SyndicationFeed( + blog.Title, + blog.Description, + new Uri(host), + host, + posts.FirstOrDefault().Published + ); + + if (posts != null && posts.Count() > 0) + { + foreach (var post in posts) + { + var item = new SyndicationItem( + post.Title, + post.Description.MdToHtml(), + new Uri(post.Id), + post.Id, + post.Published + ); + item.PublishDate = post.Published; + items.Add(item); + } + } + feed.Items = items; + + var settings = new XmlWriterSettings + { + Encoding = Encoding.UTF8, + NewLineHandling = NewLineHandling.Entitize, + NewLineOnAttributes = true, + Indent = true + }; + + using (var stream = new MemoryStream()) + { + using (var xmlWriter = XmlWriter.Create(stream, settings)) + { + var rssFormatter = new Rss20FeedFormatter(feed, false); + rssFormatter.WriteTo(xmlWriter); + xmlWriter.Flush(); + } + return File(stream.ToArray(), "application/xml; charset=utf-8"); + } + } private bool IsViewExists(string viewPath) { @@ -184,7 +187,8 @@ private bool IsViewExists(string viewPath) } - public async Task getSingleBlogPost(string slug){ + public async Task getSingleBlogPost(string slug) + { try { ViewBag.Slug = slug; @@ -217,9 +221,10 @@ public async Task getSingleBlogPost(string slug){ return Redirect("~/error"); } } - public async Task getBlogPosts(string term ="", int pager = 1, string category = "", string slug = ""){ + public async Task getBlogPosts(string term = "", int pager = 1, string category = "", string slug = "") + { - var model = new ListModel{}; + var model = new ListModel { }; try { @@ -231,8 +236,8 @@ public async Task getBlogPosts(string term ="", int pager = 1, string } model.Pager = new Pager(pager, model.Blog.ItemsPerPage); - - if(!string.IsNullOrEmpty(category)) + + if (!string.IsNullOrEmpty(category)) { model.PostListType = PostListType.Category; model.Posts = await _postProvider.GetList(model.Pager, 0, category, "PF"); @@ -258,5 +263,5 @@ public async Task getBlogPosts(string term ="", int pager = 1, string return model; } - } + } } diff --git a/src/Blogifier/Properties/launchSettings.json b/src/Blogifier/Properties/launchSettings.json index b7852472e..f6a02ef75 100644 --- a/src/Blogifier/Properties/launchSettings.json +++ b/src/Blogifier/Properties/launchSettings.json @@ -21,7 +21,7 @@ "dotnetRunMessages": "true", "launchBrowser": true, "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", - "applicationUrl": "http://localhost:5000", + "applicationUrl": "https://localhost:5002", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } diff --git a/src/Blogifier/Startup.cs b/src/Blogifier/Startup.cs index 9ed638358..b0ad9d743 100644 --- a/src/Blogifier/Startup.cs +++ b/src/Blogifier/Startup.cs @@ -2,9 +2,13 @@ using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Microsoft.AspNetCore.Authentication; +using System.IdentityModel.Tokens.Jwt; +using System.Net.Http; using Serilog; namespace Blogifier @@ -29,11 +33,34 @@ public void ConfigureServices(IServiceCollection services) Log.Warning("Start configure services"); services.AddLocalization(opts => { opts.ResourcesPath = "Resources"; }); - + JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); services.AddAuthentication(options => { - options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; - }).AddCookie(); + options.DefaultScheme = "cookie"; + options.DefaultChallengeScheme = "oidc"; + }) + .AddCookie("cookie", options => + { + options.Cookie.SameSite = SameSiteMode.None; + }) + .AddOpenIdConnect("oidc", options => + { + options.Authority = "https://auth.prime-minister.pub/"; + options.ClientId = "code_the_auto_blog"; + options.ClientSecret = "blog_secret"; + + options.ResponseType = "code id_token"; + options.UsePkce = true; + //options.ResponseMode = "query"; + + options.Scope.Add("profile"); + options.Scope.Add("avatar"); + options.Scope.Add("email"); + options.Scope.Add("comments.read"); + options.SaveTokens = true; + options.GetClaimsFromUserInfoEndpoint = true; + options.ClaimActions.MapJsonKey("avatar", "picture"); + }); services.AddCors(o => o.AddPolicy("BlogifierPolicy", builder => { @@ -44,7 +71,6 @@ public void ConfigureServices(IServiceCollection services) services.AddBlogProviders(); - services.AddControllersWithViews(); services.AddRazorPages(); diff --git a/src/Blogifier/Views/Themes/standard/components/nav.cshtml b/src/Blogifier/Views/Themes/standard/components/nav.cshtml index e94cc16a1..203e5cf95 100644 --- a/src/Blogifier/Views/Themes/standard/components/nav.cshtml +++ b/src/Blogifier/Views/Themes/standard/components/nav.cshtml @@ -1,6 +1,8 @@ @using Blogifier.Shared.Resources @using Blogifier.Shared @using Microsoft.Extensions.Localization +@using System.Net.Http; +@using System.Net.Http.Json; @inject IStringLocalizer _localizer @inject ICategoryProvider _categoryProvider @inject IAuthorProvider _authorProvider @@ -8,7 +10,9 @@ @{ var categories = await _categoryProvider.Categories(); var catUrl = Url.Content("~/categories"); - var currentUserAuthor = await _authorProvider.FindByEmail(User.Identity.Name); + @* var currentUserAuthor = await _authorProvider.FindByEmail(User.Identity.Name); *@ + var httpClient = new HttpClient(); + Author currentUserAuthor = await httpClient.GetFromJsonAsync("https://localhost:5002/api/author/getcurrent"); }