Compare commits
5 Commits
d0d1c652fe
...
984b76b170
Author | SHA1 | Date | |
---|---|---|---|
984b76b170 | |||
19a3ba6f87 | |||
a39f39c692 | |||
e870271fe4 | |||
080578c101 |
5
.vscode/launch.json
vendored
5
.vscode/launch.json
vendored
@ -109,6 +109,11 @@
|
|||||||
"serverReadyAction": {
|
"serverReadyAction": {
|
||||||
"action": "openExternally",
|
"action": "openExternally",
|
||||||
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
|
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
|
||||||
|
},
|
||||||
|
"presentation": {
|
||||||
|
"hidden": false,
|
||||||
|
"group": "run",
|
||||||
|
"order": 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -16,7 +16,7 @@ namespace Yavsc.Helpers
|
|||||||
|
|
||||||
public static string GetUserName(this ClaimsPrincipal user)
|
public static string GetUserName(this ClaimsPrincipal user)
|
||||||
{
|
{
|
||||||
return user.FindFirstValue(ClaimTypes.Name);
|
return user.FindFirstValue("name");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsSignedIn(this ClaimsPrincipal user)
|
public static bool IsSignedIn(this ClaimsPrincipal user)
|
||||||
|
3516
src/Yavsc.Server/Migrations/20250709123810_noPublishColumn.Designer.cs
generated
Normal file
3516
src/Yavsc.Server/Migrations/20250709123810_noPublishColumn.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,29 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Yavsc.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class noPublishColumn : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "Publish",
|
||||||
|
table: "BlogSpot");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<bool>(
|
||||||
|
name: "Publish",
|
||||||
|
table: "BlogSpot",
|
||||||
|
type: "boolean",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -776,9 +776,6 @@ namespace Yavsc.Migrations
|
|||||||
.HasMaxLength(1024)
|
.HasMaxLength(1024)
|
||||||
.HasColumnType("character varying(1024)");
|
.HasColumnType("character varying(1024)");
|
||||||
|
|
||||||
b.Property<bool>("Publish")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<string>("Title")
|
b.Property<string>("Title")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasMaxLength(1024)
|
.HasMaxLength(1024)
|
||||||
|
@ -36,6 +36,7 @@ namespace Yavsc.Models
|
|||||||
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
||||||
using Yavsc.Abstract.Models.Messaging;
|
using Yavsc.Abstract.Models.Messaging;
|
||||||
using Org.BouncyCastle.Asn1.Crmf;
|
using Org.BouncyCastle.Asn1.Crmf;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
|
||||||
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
|
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
|
||||||
{
|
{
|
||||||
@ -85,8 +86,7 @@ namespace Yavsc.Models
|
|||||||
}
|
}
|
||||||
|
|
||||||
builder.Entity<Activity>().Property(a => a.ParentCode).IsRequired(false);
|
builder.Entity<Activity>().Property(a => a.ParentCode).IsRequired(false);
|
||||||
//builder.Entity<BlogPost>().HasOne(p => p.Author).WithMany(a => a.Posts);
|
builder.Entity<IdentityUserLogin<String>>().HasKey(i=> new { i.LoginProvider, i.UserId, i.ProviderKey });
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is not a failback procedure.
|
// this is not a failback procedure.
|
||||||
@ -301,6 +301,6 @@ namespace Yavsc.Models
|
|||||||
public DbSet<Scope> Scopes { get; set; }
|
public DbSet<Scope> Scopes { get; set; }
|
||||||
|
|
||||||
public DbSet<BlogSpotPublication> blogSpotPublications{ get; set; }
|
public DbSet<BlogSpotPublication> blogSpotPublications{ get; set; }
|
||||||
|
public DbSet<IdentityUserLogin<String>> AspNetUserLogins { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
33
src/Yavsc.Server/Models/IdentityUserLogin.cs
Normal file
33
src/Yavsc.Server/Models/IdentityUserLogin.cs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
namespace Yavsc.Models.Auth
|
||||||
|
{
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
public class YaIdentityUserLogin
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the login provider for the login (e.g. facebook, google)
|
||||||
|
/// </summary>
|
||||||
|
public virtual string LoginProvider { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the unique provider identifier for this login.
|
||||||
|
/// </summary>
|
||||||
|
public virtual string ProviderKey { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the friendly name used in a UI for this login.
|
||||||
|
/// </summary>
|
||||||
|
public virtual string? ProviderDisplayName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the primary key of the user associated with this login.
|
||||||
|
/// </summary>
|
||||||
|
public String UserId { get; set; } = default!;
|
||||||
|
|
||||||
|
[ForeignKey("UserId")]
|
||||||
|
public virtual ApplicationUser User { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -113,7 +113,7 @@ public class BlogSpotService
|
|||||||
_context.SaveChanges(user.GetUserId());
|
_context.SaveChanges(user.GetUserId());
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<IGrouping<string, IBlogPost>>> IndexByTitle(ClaimsPrincipal user, string id, int skip = 0, int take = 25)
|
public async Task<IEnumerable<IBlogPost>> Index(ClaimsPrincipal user, string id, int skip = 0, int take = 25)
|
||||||
{
|
{
|
||||||
IEnumerable<IBlogPost> posts;
|
IEnumerable<IBlogPost> posts;
|
||||||
|
|
||||||
@ -149,9 +149,8 @@ public class BlogSpotService
|
|||||||
.Select(p => p.BlogPost).ToArray();
|
.Select(p => p.BlogPost).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
var data = posts.OrderByDescending(p => p.DateCreated);
|
var data = posts.OrderByDescending(p => p.DateModified);
|
||||||
var grouped = data.GroupBy(p => p.Title).Skip(skip).Take(take);
|
return data;
|
||||||
return grouped;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Delete(ClaimsPrincipal user, long id)
|
public async Task Delete(ClaimsPrincipal user, long id)
|
||||||
@ -174,7 +173,7 @@ public class BlogSpotService
|
|||||||
return _context.UserPosts(posterId, readerId);
|
return _context.UserPosts(posterId, readerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public object? ByTitle(string title)
|
public object? GetTitle(string title)
|
||||||
{
|
{
|
||||||
return _context.BlogSpot.Include(
|
return _context.BlogSpot.Include(
|
||||||
b => b.Author
|
b => b.Author
|
||||||
@ -190,4 +189,5 @@ public class BlogSpotService
|
|||||||
.Include(b => b.ACL)
|
.Include(b => b.ACL)
|
||||||
.SingleOrDefaultAsync(x => x.Id == value);
|
.SingleOrDefaultAsync(x => x.Id == value);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
228
src/Yavsc/Controllers/Accounting/ExternalController.cs
Normal file
228
src/Yavsc/Controllers/Accounting/ExternalController.cs
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/
|
||||||
|
|
||||||
|
Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
|
||||||
|
Source code and license this software can be found
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System.Security.Claims;
|
||||||
|
using IdentityModel;
|
||||||
|
using IdentityServer8;
|
||||||
|
using IdentityServer8.Events;
|
||||||
|
using IdentityServer8.Services;
|
||||||
|
using IdentityServer8.Stores;
|
||||||
|
using Microsoft.AspNetCore.Authentication;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Yavsc;
|
||||||
|
using Yavsc.Extensions;
|
||||||
|
using Yavsc.Interfaces;
|
||||||
|
using Yavsc.Models;
|
||||||
|
|
||||||
|
namespace IdentityServerHost.Quickstart.UI;
|
||||||
|
|
||||||
|
[SecurityHeaders]
|
||||||
|
[AllowAnonymous]
|
||||||
|
public class ExternalController : Controller
|
||||||
|
{
|
||||||
|
private readonly IIdentityServerInteractionService _interaction;
|
||||||
|
private readonly IClientStore _clientStore;
|
||||||
|
private readonly ILogger<ExternalController> _logger;
|
||||||
|
private readonly IEventService _events;
|
||||||
|
private IExternalIdentityManager _users;
|
||||||
|
|
||||||
|
private readonly SignInManager<ApplicationUser> _signInManager;
|
||||||
|
private readonly RoleManager<IdentityRole> _roleManager;
|
||||||
|
private readonly ApplicationDbContext _dbContext;
|
||||||
|
|
||||||
|
public ExternalController(
|
||||||
|
IIdentityServerInteractionService interaction,
|
||||||
|
IClientStore clientStore,
|
||||||
|
IEventService events,
|
||||||
|
ILogger<ExternalController> logger,
|
||||||
|
IExternalIdentityManager externalIdentityProviderManager,
|
||||||
|
SignInManager<ApplicationUser> signInManager,
|
||||||
|
ApplicationDbContext dbContext,
|
||||||
|
RoleManager<IdentityRole> roleManager
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// if the TestUserStore is not in DI, then we'll just use the global users collection
|
||||||
|
// this is where you would plug in your own custom identity management library (e.g. ASP.NET Identity)
|
||||||
|
_users = externalIdentityProviderManager;
|
||||||
|
_interaction = interaction;
|
||||||
|
_clientStore = clientStore;
|
||||||
|
_logger = logger;
|
||||||
|
_events = events;
|
||||||
|
_signInManager = signInManager;
|
||||||
|
_roleManager = roleManager;
|
||||||
|
_dbContext = dbContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// initiate roundtrip to external authentication provider
|
||||||
|
/// </summary>
|
||||||
|
[HttpGet]
|
||||||
|
public IActionResult Challenge(string scheme, string returnUrl)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(returnUrl)) returnUrl = "~/";
|
||||||
|
|
||||||
|
// validate returnUrl - either it is a valid OIDC URL or back to a local page
|
||||||
|
if (Url.IsLocalUrl(returnUrl) == false && _interaction.IsValidReturnUrl(returnUrl) == false)
|
||||||
|
{
|
||||||
|
// user might have clicked on a malicious link - should be logged
|
||||||
|
throw new Exception("invalid return URL");
|
||||||
|
}
|
||||||
|
|
||||||
|
// start challenge and roundtrip the return URL and scheme
|
||||||
|
var props = new AuthenticationProperties
|
||||||
|
{
|
||||||
|
RedirectUri = Url.Action(nameof(Callback)),
|
||||||
|
Items =
|
||||||
|
{
|
||||||
|
{ "returnUrl", returnUrl },
|
||||||
|
{ "scheme", scheme },
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return Challenge(props, scheme);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Post processing of external authentication
|
||||||
|
/// </summary>
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<IActionResult> Callback()
|
||||||
|
{
|
||||||
|
// read external identity from the temporary cookie
|
||||||
|
var result = await HttpContext.AuthenticateAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme);
|
||||||
|
if (result?.Succeeded != true)
|
||||||
|
{
|
||||||
|
throw new Exception("External authentication error");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_logger.IsEnabled(LogLevel.Debug))
|
||||||
|
{
|
||||||
|
var externalClaims = result.Principal.Claims.Select(c => $"{c.Type}: {c.Value}");
|
||||||
|
_logger.LogDebug("External claims: {@claims}", externalClaims);
|
||||||
|
}
|
||||||
|
|
||||||
|
// lookup our user and external provider info
|
||||||
|
var (user, provider, providerUserId, claims) = await FindUserFromExternalProvider(result);
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
// this might be where you might initiate a custom workflow for user registration
|
||||||
|
// in this sample we don't show how that would be done, as our sample implementation
|
||||||
|
// simply auto-provisions new external user
|
||||||
|
user = AutoProvisionUser(provider, providerUserId, claims);
|
||||||
|
}
|
||||||
|
|
||||||
|
// this allows us to collect any additional claims or properties
|
||||||
|
// for the specific protocols used and store them in the local auth cookie.
|
||||||
|
// this is typically used to store data needed for signout from those protocols.
|
||||||
|
var additionalLocalClaims = new List<Claim>();
|
||||||
|
var localSignInProps = new AuthenticationProperties();
|
||||||
|
ProcessLoginCallback(result, additionalLocalClaims, localSignInProps);
|
||||||
|
|
||||||
|
// issue authentication cookie for user
|
||||||
|
var isuser = new IdentityServerUser(user.Id)
|
||||||
|
{
|
||||||
|
DisplayName = user.UserName,
|
||||||
|
IdentityProvider = provider,
|
||||||
|
AdditionalClaims = additionalLocalClaims
|
||||||
|
};
|
||||||
|
await HttpContext.SignInAsync(isuser, localSignInProps);
|
||||||
|
//await HttpContext.SignInAsync(user, _roleManager, false, _dbContext);
|
||||||
|
|
||||||
|
// delete temporary cookie used during external authentication
|
||||||
|
await HttpContext.SignOutAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme);
|
||||||
|
// retrieve return URL
|
||||||
|
var returnUrl = result.Properties.Items["returnUrl"] ?? "~/";
|
||||||
|
|
||||||
|
// check if external login is in the context of an OIDC request
|
||||||
|
var context = await _interaction.GetAuthorizationContextAsync(returnUrl);
|
||||||
|
await _events.RaiseAsync(new UserLoginSuccessEvent(provider, providerUserId, user.Id, user.UserName, true, context?.Client.ClientId));
|
||||||
|
|
||||||
|
if (context != null)
|
||||||
|
{
|
||||||
|
if (context.IsNativeClient())
|
||||||
|
{
|
||||||
|
// The client is native, so this change in how to
|
||||||
|
// return the response is for better UX for the end user.
|
||||||
|
return this.LoadingPage("Redirect", returnUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return Redirect(returnUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<(ApplicationUser user,
|
||||||
|
string provider,
|
||||||
|
string providerUserId,
|
||||||
|
IEnumerable<Claim> claims)>
|
||||||
|
FindUserFromExternalProvider(AuthenticateResult result)
|
||||||
|
{
|
||||||
|
var externalUser = result.Principal;
|
||||||
|
|
||||||
|
// try to determine the unique id of the external user (issued by the provider)
|
||||||
|
// the most common claim type for that are the sub claim and the NameIdentifier
|
||||||
|
// depending on the external provider, some other claim type might be used
|
||||||
|
var userIdClaim = externalUser.FindFirst(JwtClaimTypes.Subject) ??
|
||||||
|
externalUser.FindFirst(ClaimTypes.NameIdentifier) ??
|
||||||
|
throw new Exception("Unknown userid");
|
||||||
|
|
||||||
|
// remove the user id claim so we don't include it as an extra claim if/when we provision the user
|
||||||
|
var claims = externalUser.Claims.ToList();
|
||||||
|
claims.Remove(userIdClaim);
|
||||||
|
|
||||||
|
var provider = result.Properties.Items["scheme"];
|
||||||
|
var providerUserId = userIdClaim.Value;
|
||||||
|
|
||||||
|
// find external user
|
||||||
|
|
||||||
|
ApplicationUser? user = await _users.FindByExternaleProviderAsync (provider, providerUserId);
|
||||||
|
|
||||||
|
return (user, provider, providerUserId, claims);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Register a new user by external id
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="provider"></param>
|
||||||
|
/// <param name="providerUserId"></param>
|
||||||
|
/// <param name="claims"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private ApplicationUser AutoProvisionUser(string provider, string providerUserId, IEnumerable<Claim> claims)
|
||||||
|
{
|
||||||
|
var user = _users.AutoProvisionUser(provider, providerUserId, claims.ToList());
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the external login is OIDC-based, there are certain things we need to preserve to make logout work
|
||||||
|
// this will be different for WS-Fed, SAML2p or other protocols
|
||||||
|
private void ProcessLoginCallback(AuthenticateResult externalResult, List<Claim> localClaims, AuthenticationProperties localSignInProps)
|
||||||
|
{
|
||||||
|
// if the external system sent a session id claim, copy it over
|
||||||
|
// so we can use it for single sign-out
|
||||||
|
var sid = externalResult.Principal.Claims.FirstOrDefault(x => x.Type == JwtClaimTypes.SessionId);
|
||||||
|
if (sid != null)
|
||||||
|
{
|
||||||
|
localClaims.Add(new Claim(JwtClaimTypes.SessionId, sid.Value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the external provider issued an id_token, we'll keep it for signout
|
||||||
|
var idToken = externalResult.Properties.GetTokenValue("id_token");
|
||||||
|
if (idToken != null)
|
||||||
|
{
|
||||||
|
localSignInProps.StoreTokens(new[] { new AuthenticationToken { Name = "id_token", Value = idToken } });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/
|
||||||
|
|
||||||
|
Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
|
||||||
|
Source code and license this software can be found
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System.Security.Claims;
|
||||||
|
using Yavsc.Models;
|
||||||
|
|
||||||
|
namespace Yavsc.Interfaces;
|
||||||
|
|
||||||
|
public interface IExternalIdentityManager
|
||||||
|
{
|
||||||
|
ApplicationUser AutoProvisionUser(string provider, string providerUserId, List<Claim> claims);
|
||||||
|
Task<ApplicationUser?> FindByExternaleProviderAsync(string provider, string providerUserId);
|
||||||
|
}
|
@ -47,8 +47,8 @@ namespace Yavsc.Controllers
|
|||||||
await blogSpotService.UserPosts(id, User.GetUserId(),
|
await blogSpotService.UserPosts(id, User.GetUserId(),
|
||||||
skip, take));
|
skip, take));
|
||||||
}
|
}
|
||||||
IEnumerable<IGrouping<string,IBlogPost>> byTitle = await this.blogSpotService.IndexByTitle(User, id, skip, take);
|
IEnumerable<IBlogPost> index = await this.blogSpotService.Index(User, id, skip, take);
|
||||||
return View(byTitle);
|
return View(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Route("~/Title/{id?}")]
|
[Route("~/Title/{id?}")]
|
||||||
@ -56,7 +56,7 @@ namespace Yavsc.Controllers
|
|||||||
public IActionResult Title(string id)
|
public IActionResult Title(string id)
|
||||||
{
|
{
|
||||||
ViewData["Title"] = id;
|
ViewData["Title"] = id;
|
||||||
return View("Title", blogSpotService.ByTitle(id));
|
return View("Title", blogSpotService.GetTitle(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<IEnumerable<BlogPost>> UserPosts(string userName, int pageLen = 10, int pageNum = 0)
|
private async Task<IEnumerable<BlogPost>> UserPosts(string userName, int pageLen = 10, int pageNum = 0)
|
||||||
|
@ -36,6 +36,7 @@ using Microsoft.IdentityModel.Protocols.Configuration;
|
|||||||
using IdentityModel;
|
using IdentityModel;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using IdentityServer8.Security;
|
using IdentityServer8.Security;
|
||||||
|
using Yavsc.Interfaces;
|
||||||
|
|
||||||
namespace Yavsc.Extensions;
|
namespace Yavsc.Extensions;
|
||||||
|
|
||||||
@ -43,57 +44,6 @@ namespace Yavsc.Extensions;
|
|||||||
public static class HostingExtensions
|
public static class HostingExtensions
|
||||||
{
|
{
|
||||||
|
|
||||||
public static IApplicationBuilder ConfigureFileServerApp(this IApplicationBuilder app,
|
|
||||||
bool enableDirectoryBrowsing = false)
|
|
||||||
{
|
|
||||||
|
|
||||||
var userFilesDirInfo = new DirectoryInfo(Config.SiteSetup.Blog);
|
|
||||||
AbstractFileSystemHelpers.UserFilesDirName = userFilesDirInfo.FullName;
|
|
||||||
|
|
||||||
if (!userFilesDirInfo.Exists) userFilesDirInfo.Create();
|
|
||||||
|
|
||||||
Config.UserFilesOptions = new FileServerOptions()
|
|
||||||
{
|
|
||||||
FileProvider = new PhysicalFileProvider(AbstractFileSystemHelpers.UserFilesDirName),
|
|
||||||
RequestPath = PathString.FromUriComponent(Constants.UserFilesPath),
|
|
||||||
EnableDirectoryBrowsing = enableDirectoryBrowsing,
|
|
||||||
};
|
|
||||||
Config.UserFilesOptions.EnableDefaultFiles = true;
|
|
||||||
Config.UserFilesOptions.StaticFileOptions.ServeUnknownFileTypes = true;
|
|
||||||
|
|
||||||
var avatarsDirInfo = new DirectoryInfo(Config.SiteSetup.Avatars);
|
|
||||||
if (!avatarsDirInfo.Exists) avatarsDirInfo.Create();
|
|
||||||
Config.AvatarsDirName = avatarsDirInfo.FullName;
|
|
||||||
|
|
||||||
Config.AvatarsOptions = new FileServerOptions()
|
|
||||||
{
|
|
||||||
FileProvider = new PhysicalFileProvider(Config.AvatarsDirName),
|
|
||||||
RequestPath = PathString.FromUriComponent(Constants.AvatarsPath),
|
|
||||||
EnableDirectoryBrowsing = enableDirectoryBrowsing
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
var gitdirinfo = new DirectoryInfo(Config.SiteSetup.GitRepository);
|
|
||||||
Config.GitDirName = gitdirinfo.FullName;
|
|
||||||
if (!gitdirinfo.Exists) gitdirinfo.Create();
|
|
||||||
Config.GitOptions = new FileServerOptions()
|
|
||||||
{
|
|
||||||
FileProvider = new PhysicalFileProvider(Config.GitDirName),
|
|
||||||
RequestPath = PathString.FromUriComponent(Constants.GitPath),
|
|
||||||
EnableDirectoryBrowsing = enableDirectoryBrowsing,
|
|
||||||
};
|
|
||||||
Config.GitOptions.DefaultFilesOptions.DefaultFileNames.Add("index.md");
|
|
||||||
Config.GitOptions.StaticFileOptions.ServeUnknownFileTypes = true;
|
|
||||||
|
|
||||||
app.UseFileServer(Config.UserFilesOptions);
|
|
||||||
|
|
||||||
app.UseFileServer(Config.AvatarsOptions);
|
|
||||||
|
|
||||||
app.UseFileServer(Config.GitOptions);
|
|
||||||
app.UseStaticFiles();
|
|
||||||
return app;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static WebApplication ConfigureWebAppServices(this WebApplicationBuilder builder)
|
internal static WebApplication ConfigureWebAppServices(this WebApplicationBuilder builder)
|
||||||
{
|
{
|
||||||
IServiceCollection services = LoadConfiguration(builder);
|
IServiceCollection services = LoadConfiguration(builder);
|
||||||
@ -163,6 +113,7 @@ public static class HostingExtensions
|
|||||||
AddYavscPolicies(services);
|
AddYavscPolicies(services);
|
||||||
|
|
||||||
services.AddScoped<IAuthorizationHandler, PermissionHandler>();
|
services.AddScoped<IAuthorizationHandler, PermissionHandler>();
|
||||||
|
services.AddTransient<IExternalIdentityManager, ExternalIdentityManager>();
|
||||||
|
|
||||||
|
|
||||||
AddAuthentication(builder);
|
AddAuthentication(builder);
|
||||||
@ -231,23 +182,13 @@ public static class HostingExtensions
|
|||||||
// OAuth2AppSettings
|
// OAuth2AppSettings
|
||||||
var googleAuthSettings = builder.Configuration.GetSection("Authentication:Google");
|
var googleAuthSettings = builder.Configuration.GetSection("Authentication:Google");
|
||||||
|
|
||||||
string? googleClientFile = builder.Configuration["Authentication:Google:GoogleWebClientJson"];
|
//LoadGoogleConfig(builder.Configuration);
|
||||||
string? googleServiceAccountJsonFile = builder.Configuration["Authentication:Google:GoogleServiceAccountJson"];
|
|
||||||
if (googleClientFile != null)
|
|
||||||
{
|
|
||||||
Config.GoogleWebClientConfiguration = new ConfigurationBuilder().AddJsonFile(googleClientFile).Build();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (googleServiceAccountJsonFile != null)
|
|
||||||
{
|
|
||||||
FileInfo safile = new FileInfo(googleServiceAccountJsonFile);
|
|
||||||
Config.GServiceAccount = JsonConvert.DeserializeObject<GoogleServiceAccount>(safile.OpenText().ReadToEnd());
|
|
||||||
}
|
|
||||||
var services = builder.Services;
|
var services = builder.Services;
|
||||||
_ = services.AddControllersWithViews()
|
_ = services.AddControllersWithViews()
|
||||||
.AddNewtonsoftJson();
|
.AddNewtonsoftJson();
|
||||||
LoadGoogleConfig(builder.Configuration);
|
|
||||||
|
|
||||||
services.Configure<SiteSettings>(siteSection);
|
services.Configure<SiteSettings>(siteSection);
|
||||||
services.Configure<SmtpSettings>(smtpSection);
|
services.Configure<SmtpSettings>(smtpSection);
|
||||||
services.Configure<PayPalSettings>(paypalSection);
|
services.Configure<PayPalSettings>(paypalSection);
|
||||||
@ -276,11 +217,12 @@ public static class HostingExtensions
|
|||||||
// set the redirect URI to https://localhost:5001/signin-google
|
// set the redirect URI to https://localhost:5001/signin-google
|
||||||
options.ClientId = googleClientId;
|
options.ClientId = googleClientId;
|
||||||
options.ClientSecret = googleClientSecret;
|
options.ClientSecret = googleClientSecret;
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
private static IIdentityServerBuilder AddIdentityServer(WebApplicationBuilder builder)
|
private static IIdentityServerBuilder AddIdentityServer(WebApplicationBuilder builder)
|
||||||
{
|
{
|
||||||
builder.Services.AddTransient<IProfileService,ProfileService>();
|
//builder.Services.AddTransient<IProfileService,ProfileService>();
|
||||||
var identityServerBuilder = builder.Services.AddIdentityServer(options =>
|
var identityServerBuilder = builder.Services.AddIdentityServer(options =>
|
||||||
{
|
{
|
||||||
options.Events.RaiseErrorEvents = true;
|
options.Events.RaiseErrorEvents = true;
|
||||||
@ -295,7 +237,7 @@ public static class HostingExtensions
|
|||||||
.AddInMemoryIdentityResources(Config.IdentityResources)
|
.AddInMemoryIdentityResources(Config.IdentityResources)
|
||||||
.AddInMemoryClients(Config.Clients)
|
.AddInMemoryClients(Config.Clients)
|
||||||
.AddInMemoryApiScopes(Config.ApiScopes)
|
.AddInMemoryApiScopes(Config.ApiScopes)
|
||||||
.AddProfileService<ProfileService>()
|
// .AddProfileService<ProfileService>()
|
||||||
.AddAspNetIdentity<ApplicationUser>()
|
.AddAspNetIdentity<ApplicationUser>()
|
||||||
;
|
;
|
||||||
if (builder.Environment.IsDevelopment())
|
if (builder.Environment.IsDevelopment())
|
||||||
@ -414,87 +356,57 @@ public static class HostingExtensions
|
|||||||
Config.GServiceAccount = JsonConvert.DeserializeObject<GoogleServiceAccount>(safile.OpenText().ReadToEnd());
|
Config.GServiceAccount = JsonConvert.DeserializeObject<GoogleServiceAccount>(safile.OpenText().ReadToEnd());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public static IApplicationBuilder ConfigureFileServerApp(this IApplicationBuilder app,
|
||||||
public class MyIdentityStore : IUserClaimStore<IdentityUser>
|
bool enableDirectoryBrowsing = false)
|
||||||
{
|
{
|
||||||
public Task AddClaimsAsync(IdentityUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken)
|
|
||||||
{
|
var userFilesDirInfo = new DirectoryInfo(Config.SiteSetup.Blog);
|
||||||
throw new NotImplementedException();
|
AbstractFileSystemHelpers.UserFilesDirName = userFilesDirInfo.FullName;
|
||||||
}
|
|
||||||
|
if (!userFilesDirInfo.Exists) userFilesDirInfo.Create();
|
||||||
public Task<IdentityResult> CreateAsync(IdentityUser user, CancellationToken cancellationToken)
|
|
||||||
{
|
Config.UserFilesOptions = new FileServerOptions()
|
||||||
throw new NotImplementedException();
|
{
|
||||||
}
|
FileProvider = new PhysicalFileProvider(AbstractFileSystemHelpers.UserFilesDirName),
|
||||||
|
RequestPath = PathString.FromUriComponent(Constants.UserFilesPath),
|
||||||
public Task<IdentityResult> DeleteAsync(IdentityUser user, CancellationToken cancellationToken)
|
EnableDirectoryBrowsing = enableDirectoryBrowsing,
|
||||||
{
|
};
|
||||||
throw new NotImplementedException();
|
Config.UserFilesOptions.EnableDefaultFiles = true;
|
||||||
}
|
Config.UserFilesOptions.StaticFileOptions.ServeUnknownFileTypes = true;
|
||||||
|
|
||||||
public void Dispose()
|
var avatarsDirInfo = new DirectoryInfo(Config.SiteSetup.Avatars);
|
||||||
{
|
if (!avatarsDirInfo.Exists) avatarsDirInfo.Create();
|
||||||
throw new NotImplementedException();
|
Config.AvatarsDirName = avatarsDirInfo.FullName;
|
||||||
}
|
|
||||||
|
Config.AvatarsOptions = new FileServerOptions()
|
||||||
public Task<IdentityUser?> FindByIdAsync(string userId, CancellationToken cancellationToken)
|
{
|
||||||
{
|
FileProvider = new PhysicalFileProvider(Config.AvatarsDirName),
|
||||||
throw new NotImplementedException();
|
RequestPath = PathString.FromUriComponent(Constants.AvatarsPath),
|
||||||
}
|
EnableDirectoryBrowsing = enableDirectoryBrowsing
|
||||||
|
};
|
||||||
public Task<IdentityUser?> FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
var gitdirinfo = new DirectoryInfo(Config.SiteSetup.GitRepository);
|
||||||
}
|
Config.GitDirName = gitdirinfo.FullName;
|
||||||
|
if (!gitdirinfo.Exists) gitdirinfo.Create();
|
||||||
public Task<IList<Claim>> GetClaimsAsync(IdentityUser user, CancellationToken cancellationToken)
|
Config.GitOptions = new FileServerOptions()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
FileProvider = new PhysicalFileProvider(Config.GitDirName),
|
||||||
}
|
RequestPath = PathString.FromUriComponent(Constants.GitPath),
|
||||||
|
EnableDirectoryBrowsing = enableDirectoryBrowsing,
|
||||||
public Task<string?> GetNormalizedUserNameAsync(IdentityUser user, CancellationToken cancellationToken)
|
};
|
||||||
{
|
Config.GitOptions.DefaultFilesOptions.DefaultFileNames.Add("index.md");
|
||||||
throw new NotImplementedException();
|
Config.GitOptions.StaticFileOptions.ServeUnknownFileTypes = true;
|
||||||
}
|
|
||||||
|
app.UseFileServer(Config.UserFilesOptions);
|
||||||
public Task<string> GetUserIdAsync(IdentityUser user, CancellationToken cancellationToken)
|
|
||||||
{
|
app.UseFileServer(Config.AvatarsOptions);
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
app.UseFileServer(Config.GitOptions);
|
||||||
|
app.UseStaticFiles();
|
||||||
public Task<string?> GetUserNameAsync(IdentityUser user, CancellationToken cancellationToken)
|
return app;
|
||||||
{
|
}
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<IList<IdentityUser>> GetUsersForClaimAsync(Claim claim, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task RemoveClaimsAsync(IdentityUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task ReplaceClaimAsync(IdentityUser user, Claim claim, Claim newClaim, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task SetNormalizedUserNameAsync(IdentityUser user, string? normalizedName, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task SetUserNameAsync(IdentityUser user, string? userName, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<IdentityResult> UpdateAsync(IdentityUser user, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System.Text.Encodings.Web;
|
using System.Text.Encodings.Web;
|
||||||
using AsciiDocNet;
|
using AsciiDocNet;
|
||||||
|
using Microsoft.AspNetCore.Html;
|
||||||
using Microsoft.AspNetCore.Razor.TagHelpers;
|
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||||
|
|
||||||
namespace Yavsc.Helpers
|
namespace Yavsc.Helpers
|
||||||
@ -8,17 +9,28 @@ namespace Yavsc.Helpers
|
|||||||
{
|
{
|
||||||
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
|
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
|
||||||
{
|
{
|
||||||
if (context.AllAttributes.ContainsName("summary"))
|
|
||||||
{
|
|
||||||
var summaryLength = context.AllAttributes["summary"].Value;
|
|
||||||
}
|
|
||||||
await base.ProcessAsync(context, output);
|
await base.ProcessAsync(context, output);
|
||||||
var content = await output.GetChildContentAsync();
|
var content = await output.GetChildContentAsync();
|
||||||
string text = content.GetContent();
|
string text = content.GetContent();
|
||||||
if (string.IsNullOrWhiteSpace(text)) return;
|
if (string.IsNullOrWhiteSpace(text)) return;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
if (context.AllAttributes.ContainsName("summary"))
|
||||||
|
{
|
||||||
|
var summaryLength = context.AllAttributes["summary"].Value;
|
||||||
|
if (summaryLength is HtmlString sumLenStr)
|
||||||
|
{
|
||||||
|
if (int.TryParse(sumLenStr.Value, out var sumLen))
|
||||||
|
{
|
||||||
|
if (text.Length > sumLen)
|
||||||
|
{
|
||||||
|
text = text.Substring(0, sumLen) + "(...)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Document document = Document.Parse(text);
|
Document document = Document.Parse(text);
|
||||||
var html = document.ToHtml(2);
|
var html = document.ToHtml(2);
|
||||||
using var stringWriter = new StringWriter();
|
using var stringWriter = new StringWriter();
|
||||||
|
@ -74,7 +74,7 @@ namespace Yavsc.Helpers
|
|||||||
);
|
);
|
||||||
return googleLogin;
|
return googleLogin;
|
||||||
}
|
}
|
||||||
public static async Task<UserCredential> GetGoogleCredential(GoogleAuthSettings googleAuthSettings, IDataStore store, string googleUserLoginKey)
|
public static async Task<UserCredential> GetGoogleCredential(GoogleAuthSettings googleAuthSettings, IDataStore store, string googleUserLoginKey)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(googleUserLoginKey))
|
if (string.IsNullOrEmpty(googleUserLoginKey))
|
||||||
throw new InvalidOperationException("No Google login");
|
throw new InvalidOperationException("No Google login");
|
||||||
@ -82,7 +82,7 @@ namespace Yavsc.Helpers
|
|||||||
var token = await store.GetAsync<TokenResponse>(googleUserLoginKey);
|
var token = await store.GetAsync<TokenResponse>(googleUserLoginKey);
|
||||||
// token != null
|
// token != null
|
||||||
var c = SystemClock.Default;
|
var c = SystemClock.Default;
|
||||||
if (token.IsExpired(c)) {
|
if (token.IsStale) {
|
||||||
token = await RefreshToken(googleAuthSettings, token);
|
token = await RefreshToken(googleAuthSettings, token);
|
||||||
}
|
}
|
||||||
return new UserCredential(flow, googleUserLoginKey, token);
|
return new UserCredential(flow, googleUserLoginKey, token);
|
||||||
|
@ -20,11 +20,11 @@ install_service:
|
|||||||
sudo systemctl enable yavsc
|
sudo systemctl enable yavsc
|
||||||
|
|
||||||
pushInProd: publish
|
pushInProd: publish
|
||||||
@sudo systemctl stop $(SERVICE_PROD)
|
sudo systemctl stop $(SERVICE_PROD)
|
||||||
@sudo cp -a bin/$(CONFIGURATION)/$(DOTNET_FRAMEWORK)/publish/* $(DESTDIR)
|
sudo cp -a bin/$(CONFIGURATION)/$(DOTNET_FRAMEWORK)/publish/* $(DESTDIR)
|
||||||
@sudo chown -R $(USER_AND_GROUP) $(DESTDIR)
|
sudo chown -R $(USER_AND_GROUP) $(DESTDIR)
|
||||||
@sudo sync
|
sudo sync
|
||||||
@sudo systemctl start $(SERVICE_PROD)
|
sudo systemctl start $(SERVICE_PROD)
|
||||||
|
|
||||||
%.css: %.scss
|
%.css: %.scss
|
||||||
scss $^ > $@
|
scss $^ > $@
|
||||||
|
28
src/Yavsc/Services/ExternalIdentityManager.cs
Normal file
28
src/Yavsc/Services/ExternalIdentityManager.cs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
using System.Security.Claims;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Yavsc.Interfaces;
|
||||||
|
using Yavsc.Models;
|
||||||
|
|
||||||
|
public class ExternalIdentityManager : IExternalIdentityManager
|
||||||
|
{
|
||||||
|
private ApplicationDbContext _applicationDbContext;
|
||||||
|
|
||||||
|
public ExternalIdentityManager(ApplicationDbContext applicationDbContext)
|
||||||
|
{
|
||||||
|
_applicationDbContext = applicationDbContext;
|
||||||
|
}
|
||||||
|
public ApplicationUser AutoProvisionUser(string provider, string providerUserId, List<Claim> claims)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ApplicationUser?> FindByExternaleProviderAsync(string provider, string providerUserId)
|
||||||
|
{
|
||||||
|
var user = await _applicationDbContext.AspNetUserLogins
|
||||||
|
.FirstOrDefaultAsync(
|
||||||
|
i => (i.LoginProvider == provider) && (i.ProviderKey == providerUserId)
|
||||||
|
);
|
||||||
|
if (user == null) return null;
|
||||||
|
return await _applicationDbContext.Users.FirstOrDefaultAsync(u=>u.Id == user.UserId);
|
||||||
|
}
|
||||||
|
}
|
@ -60,10 +60,14 @@ $('#commentValidation').html(
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
}
|
}
|
||||||
<div class="container">
|
|
||||||
<div class="blogpost">
|
<div class="post">
|
||||||
<h1 class="blogtitle" ismarkdown>@Model.Title</h1>
|
<div class="float-left">
|
||||||
<img class="blogphoto" alt="" src="@Model.Photo" >
|
<img alt="" src="@Model.Photo" >
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1 ismarkdown>@Model.Title</h1>
|
||||||
|
|
||||||
@Html.DisplayFor(m=>m.Author)
|
@Html.DisplayFor(m=>m.Author)
|
||||||
<asciidoc>@Html.DisplayFor(m=>m.Content)</asciidoc>
|
<asciidoc>@Html.DisplayFor(m=>m.Content)</asciidoc>
|
||||||
|
|
||||||
@ -98,4 +102,4 @@ $('#commentValidation').html(
|
|||||||
<a asp-action="Edit" asp-route-id="@Model.Id" class="btn btn-link">Edit</a>
|
<a asp-action="Edit" asp-route-id="@Model.Id" class="btn btn-link">Edit</a>
|
||||||
}
|
}
|
||||||
<a asp-action="Index" class="btn btn-link">Back to List</a>
|
<a asp-action="Index" class="btn btn-link">Back to List</a>
|
||||||
</div>
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@model IEnumerable<IGrouping<string,IBlogPost>>
|
@model IEnumerable<IBlogPost>
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "Blogs, l'index";
|
ViewData["Title"] = "Blogs, l'index";
|
||||||
}
|
}
|
||||||
@ -43,59 +43,51 @@
|
|||||||
<a asp-action="Create">Create a new article</a>
|
<a asp-action="Create">Create a new article</a>
|
||||||
</p>
|
</p>
|
||||||
}
|
}
|
||||||
|
|
||||||
<div class="container">
|
|
||||||
|
|
||||||
<table class="table">
|
<div class="blog">
|
||||||
|
@{
|
||||||
|
int maxTextLen = 75;
|
||||||
|
foreach (var post in Model) {
|
||||||
|
<div class="post">
|
||||||
|
|
||||||
@foreach (var group in Model) {
|
|
||||||
var title = group.Key ?? "@";
|
|
||||||
string secondclass="";
|
|
||||||
var first = group.First();
|
|
||||||
int maxTextLen = 256;
|
|
||||||
|
|
||||||
<tr><td colspan="3">
|
<div class="float-left" >
|
||||||
<a asp-action="Title" asp-route-id="@group.Key" >@title</a></td></tr>
|
<a asp-action="Details" asp-route-id="@post.Id" class="bloglink" style="display: float-left;">
|
||||||
@foreach (var item in group) {
|
<img src="@post.Photo" >
|
||||||
var trunked = item.Content?.Length > maxTextLen;
|
</a>
|
||||||
<tr>
|
</div>
|
||||||
<td><a asp-action="Details" asp-route-id="@item.Id" class="bloglink">
|
<h3>@post.Title</h3>
|
||||||
<img src="@item.Photo" class="blogphoto"></a>
|
<div>
|
||||||
</td>
|
<a asp-action="Details" asp-route-id="@post.Id">
|
||||||
<td>
|
<asciidoc summary="@maxTextLen">@post.Content</asciidoc></a>
|
||||||
<asciidoc summary="@maxTextLen">@item.Content</asciidoc>
|
<span style="font-size:x-small;">@Html.DisplayFor(m => post.Author)</span>
|
||||||
@if (trunked) { <a asp-action="Details" asp-route-id="@item.Id" class="bloglink">...</a> }
|
<span style="font-size:xx-small;">
|
||||||
<span style="font-size:x-small;">@Html.DisplayFor(m => item.Author)</span>
|
posté le @post.DateCreated.ToString("dddd d MMM yyyy à H:mm")
|
||||||
<span style="font-size:xx-small;">
|
@if ((post.DateModified - post.DateCreated).Minutes > 0){
|
||||||
posté le @item.DateCreated.ToString("dddd d MMM yyyy à H:mm")
|
@:- Modifié le @post.DateModified.ToString("dddd d MMM yyyy à H:mm")
|
||||||
@if ((item.DateModified - item.DateCreated).Minutes > 0){
|
})
|
||||||
@:- Modifié le @item.DateModified.ToString("dddd d MMM yyyy à H:mm")
|
</span>
|
||||||
})
|
</div>
|
||||||
</span>
|
<div class="actiongroup">
|
||||||
</td>
|
@if ((await AuthorizationService.AuthorizeAsync(User, post, new ReadPermission())).Succeeded)
|
||||||
<td>
|
{
|
||||||
<ul class="actiongroup">
|
<a asp-action="Details" asp-route-id="@post.Id" class="btn btn-light">Details</a>
|
||||||
@if ((await AuthorizationService.AuthorizeAsync(User, item, new ReadPermission())).Succeeded) {
|
}
|
||||||
<li>
|
else
|
||||||
<a asp-action="Details" asp-route-id="@item.Id" class="btn btn-lg">Details</a>
|
{
|
||||||
</li>
|
<a asp-action="Details" asp-route-id="@post.Id" class="btn btn-light">Details</a>
|
||||||
}
|
}
|
||||||
else {
|
@if ((await AuthorizationService.AuthorizeAsync(User, post, new EditPermission())).Succeeded)
|
||||||
<a asp-action="Details" asp-route-id="@item.Id" class="btn btn-lg">Details DEBUG</a>
|
{
|
||||||
}
|
<a asp-action="Edit" asp-route-id="@post.Id" class="btn btn-default">Edit</a>
|
||||||
@if ((await AuthorizationService.AuthorizeAsync(User, item, new EditPermission())).Succeeded) {
|
|
||||||
<li><a asp-action="Edit" asp-route-id="@item.Id" class="btn btn-default">Edit</a>
|
<a asp-action="Delete" asp-route-id="@post.Id" class="btn btn-danger">Delete</a>
|
||||||
</li>
|
|
||||||
<li><a asp-action="Delete" asp-route-id="@item.Id" class="btn btn-danger">Delete</a>
|
}
|
||||||
</li>
|
</div>
|
||||||
}
|
</div>
|
||||||
</ul>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</table>
|
</div>
|
||||||
|
|
||||||
@if(Model.Count()==0){<p>Néant</p>}
|
@if(Model.Count()==0){<p>Néant</p>}
|
||||||
|
|
||||||
</div>
|
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
<ul class="actiongroup">
|
<ul class="actiongroup">
|
||||||
@if ((await AuthorizationService.AuthorizeAsync(User, item, new ReadPermission())).Succeeded) {
|
@if ((await AuthorizationService.AuthorizeAsync(User, item, new ReadPermission())).Succeeded) {
|
||||||
<li>
|
<li>
|
||||||
<a asp-action="Details" asp-route-id="@item.Id" class="btn btn-lg">Details</a>
|
<a asp-action="Details" asp-route-id="@item.Id" class="btn btn-light">Details</a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
@if ((await AuthorizationService.AuthorizeAsync(User, item, new EditPermission())).Succeeded) {
|
@if ((await AuthorizationService.AuthorizeAsync(User, item, new EditPermission())).Succeeded) {
|
||||||
|
@ -20,9 +20,9 @@
|
|||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
<p>
|
<p>
|
||||||
<input type="submit" class="btn btn-lg btn-success" name="submit.Grant" value="Grant" />
|
<input type="submit" class="btn btn-light btn-success" name="submit.Grant" value="Grant" />
|
||||||
<input type="submit" class="btn btn-lg btn-danger" name="submit.Deny" value="Deny" />
|
<input type="submit" class="btn btn-light btn-danger" name="submit.Deny" value="Deny" />
|
||||||
<input type="submit" class="btn btn-lg btn-success" name="submit.Login" value="Sign in as different user" />
|
<input type="submit" class="btn btn-light btn-success" name="submit.Login" value="Sign in as different user" />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
@ -14,6 +14,6 @@
|
|||||||
<input type="hidden" name="@parameter.Key" value="@parameter.Value" />
|
<input type="hidden" name="@parameter.Key" value="@parameter.Value" />
|
||||||
}
|
}
|
||||||
|
|
||||||
<input class="btn btn-lg btn-success" name="Authorize" type="submit" value="Yeah, sure" />
|
<input class="btn btn-light btn-success" name="Authorize" type="submit" value="Yeah, sure" />
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-md-offset-2 col-md-10">
|
<div class="col-md-offset-2 col-md-10">
|
||||||
<button type="submit" class="btn btn-lg btn-success" name="submit.Signin">Login</button>
|
<button type="submit" class="btn btn-light btn-success" name="submit.Signin">Login</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<p>
|
||||||
|
@ -2,17 +2,10 @@
|
|||||||
@inject SignInManager<ApplicationUser> SignInManager
|
@inject SignInManager<ApplicationUser> SignInManager
|
||||||
@inject UserManager<ApplicationUser> UserManager
|
@inject UserManager<ApplicationUser> UserManager
|
||||||
|
|
||||||
@{
|
@if (Context.User?.Identity?.IsAuthenticated ?? false)
|
||||||
#nullable enable
|
|
||||||
string? name = null;
|
|
||||||
if (Context.User!=null)
|
|
||||||
{
|
|
||||||
name = Context.User.GetUserName();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@if (name!=null)
|
|
||||||
{
|
{
|
||||||
|
string userName = User.GetUserName();
|
||||||
|
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
<a class="nav-link dropdown-toggle" href="#" id="dropdown04" data-bs-toggle="dropdown" aria-expanded="false">Plateforme</a>
|
<a class="nav-link dropdown-toggle" href="#" id="dropdown04" data-bs-toggle="dropdown" aria-expanded="false">Plateforme</a>
|
||||||
<ul class="dropdown-menu" aria-labelledby="dropdown04">
|
<ul class="dropdown-menu" aria-labelledby="dropdown04">
|
||||||
@ -37,7 +30,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</li>}
|
</li>}
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
<a class="nav-link dropdown-toggle" href="#" id="dropdown04" data-bs-toggle="dropdown" aria-expanded="false">Hello @UserManager.GetUserName(User)!</a>
|
<a class="nav-link dropdown-toggle" href="#" id="dropdown04" data-bs-toggle="dropdown" aria-expanded="false">Hello @userName!</a>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li>
|
<li>
|
||||||
<a class="dropdown-item" asp-controller="Manage" asp-action="Index" title="Manage">
|
<a class="dropdown-item" asp-controller="Manage" asp-action="Index" title="Manage">
|
||||||
@ -61,3 +54,5 @@ else
|
|||||||
<a class="nav-link" asp-controller="Account" asp-action="Login" asp-route-ReturnUrl="~/" >Login</a>
|
<a class="nav-link" asp-controller="Account" asp-action="Login" asp-route-ReturnUrl="~/" >Login</a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
|
using IdentityServer8.Extensions;
|
||||||
|
#line default
|
||||||
|
@ -17,30 +17,31 @@
|
|||||||
<PackageReference Include="HigginsSoft.IdentityServer8.AspNetIdentity" Version="8.0.5-preview-net9" />
|
<PackageReference Include="HigginsSoft.IdentityServer8.AspNetIdentity" Version="8.0.5-preview-net9" />
|
||||||
|
|
||||||
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
|
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="9.0.6" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="9.0.7" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="9.0.6" />
|
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="9.0.7" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.6" />
|
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.7" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="9.0.6" />
|
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="9.0.7" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.6">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.7">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.6">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.7">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.6" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.7" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="9.0.6" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="9.0.7" />
|
||||||
<PackageReference Include="Google.Apis.Compute.v1" Version="1.70.0.3829" />
|
<PackageReference Include="Google.Apis.Compute.v1" Version="1.70.0.3829" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.2.0" />
|
<PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.2.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Razor" Version="2.3.0" />
|
<PackageReference Include="Microsoft.AspNetCore.Razor" Version="2.3.0" />
|
||||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="9.0.0" />
|
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="9.0.0" />
|
||||||
<PackageReference Include="System.Security.Cryptography.Pkcs" Version="9.0.6" />
|
<PackageReference Include="System.Security.Cryptography.Pkcs" Version="9.0.7" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Antiforgery" Version="2.3.0" />
|
<PackageReference Include="Microsoft.AspNetCore.Antiforgery" Version="2.3.0" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.1" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.3" />
|
||||||
<PackageReference Include="AsciiDocNet" Version="1.0.0-alpha6" />
|
<PackageReference Include="AsciiDocNet" Version="1.0.0-alpha6" />
|
||||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.10" />
|
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.10" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="9.0.7" />
|
||||||
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="../Yavsc.Server/Yavsc.Server.csproj" />
|
<ProjectReference Include="../Yavsc.Server/Yavsc.Server.csproj" />
|
||||||
|
@ -30,5 +30,21 @@ input[type='checkbox'] {
|
|||||||
min-height: 1em; }
|
min-height: 1em; }
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
background-color: #000000cf;
|
background-color: #00000040;
|
||||||
color: #ffffff; }
|
color: #fff;
|
||||||
|
border-radius: 2em; }
|
||||||
|
|
||||||
|
.post {
|
||||||
|
background-color: #000000dd;
|
||||||
|
color: #d1d1d1;
|
||||||
|
padding: 2.3em;
|
||||||
|
border-radius: 2em;
|
||||||
|
border: solid #441515a4 2pt; }
|
||||||
|
|
||||||
|
div.actiongroup {
|
||||||
|
float: right;
|
||||||
|
margin: .5em; }
|
||||||
|
|
||||||
|
div.float-left {
|
||||||
|
float: left;
|
||||||
|
margin: .5em; }
|
||||||
|
@ -45,6 +45,27 @@ input[type='checkbox'] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
background-color: #000000cf;
|
background-color: #00000040;
|
||||||
color:#ffffff;
|
color: #fff;
|
||||||
|
border-radius: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post {
|
||||||
|
background-color: #000000dd;
|
||||||
|
color:#d1d1d1;
|
||||||
|
padding: 2.3em;
|
||||||
|
border-radius: 2em;
|
||||||
|
border: solid #441515a4 2pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actiongroup
|
||||||
|
{
|
||||||
|
float: right;
|
||||||
|
margin:.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.float-left
|
||||||
|
{
|
||||||
|
float: left;
|
||||||
|
margin:.5em;
|
||||||
}
|
}
|
||||||
|
@ -13,8 +13,7 @@
|
|||||||
using System.IdentityModel.Tokens.Jwt;
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
|
|
||||||
JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
|
JwtSecurityTokenHandler.DefaultMapInboundClaims = true;
|
||||||
|
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
@ -28,8 +27,7 @@ builder.Services
|
|||||||
.AddCookie("Cookies")
|
.AddCookie("Cookies")
|
||||||
.AddOpenIdConnect("oidc", options =>
|
.AddOpenIdConnect("oidc", options =>
|
||||||
{
|
{
|
||||||
options.Authority = "https://localhost:5001";
|
options.Authority = builder.Configuration.GetValue<String>("AuthIssuer");
|
||||||
|
|
||||||
options.ClientId = "mvc";
|
options.ClientId = "mvc";
|
||||||
options.ClientSecret = "49C1A7E1-0C79-4A89-A3D6-A37998FB86B0";
|
options.ClientSecret = "49C1A7E1-0C79-4A89-A3D6-A37998FB86B0";
|
||||||
options.ResponseType = "code";
|
options.ResponseType = "code";
|
||||||
@ -38,15 +36,14 @@ builder.Services
|
|||||||
options.Scope.Add("profile");
|
options.Scope.Add("profile");
|
||||||
options.Scope.Add("scope2");
|
options.Scope.Add("scope2");
|
||||||
options.MapInboundClaims = true;
|
options.MapInboundClaims = true;
|
||||||
options.ClaimActions.MapUniqueJsonKey("preferred_username","preferred_username");
|
options.ClaimActions.MapUniqueJsonKey("preferred_username", "preferred_username");
|
||||||
options.ClaimActions.MapUniqueJsonKey("gender", "gender");
|
options.ClaimActions.MapUniqueJsonKey("gender", "gender");
|
||||||
|
|
||||||
options.SaveTokens = true;
|
options.SaveTokens = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
using (var app = builder.Build())
|
using (var app = builder.Build())
|
||||||
{
|
{
|
||||||
|
|
||||||
if (app.Environment.IsDevelopment())
|
if (app.Environment.IsDevelopment())
|
||||||
app.UseDeveloperExceptionPage();
|
app.UseDeveloperExceptionPage();
|
||||||
else
|
else
|
||||||
|
@ -19,26 +19,26 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
<form action="~/Home/GetUserInfo" method="post">
|
<form action="~/Home/GetUserInfo" method="post">
|
||||||
<button class="btn btn-lg btn-warning" type="submit">Get user info</button>
|
<button class="btn btn-light btn-warning" type="submit">Get user info</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<form action="~/Home/GetApiCall" method="post">
|
<form action="~/Home/GetApiCall" method="post">
|
||||||
<button class="btn btn-lg btn-warning" type="submit">Api Call</button>
|
<button class="btn btn-light btn-warning" type="submit">Api Call</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<form action="~/Home/PostDeviceInfo" method="post">
|
<form action="~/Home/PostDeviceInfo" method="post">
|
||||||
<button class="btn btn-lg btn-warning" type="submit">Post device info</button>
|
<button class="btn btn-light btn-warning" type="submit">Post device info</button>
|
||||||
</form>
|
</form>
|
||||||
<form action="~/Home/PostFiles/?subdir=test" method="post" enctype="multipart/form-data">
|
<form action="~/Home/PostFiles/?subdir=test" method="post" enctype="multipart/form-data">
|
||||||
Envoyer vers le dossier "test" <input type="file" name="file" multiple/>
|
Envoyer vers le dossier "test" <input type="file" name="file" multiple/>
|
||||||
<button class="btn btn-lg btn-warning" type="submit">Post files</button>
|
<button class="btn btn-light btn-warning" type="submit">Post files</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<a class="btn btn-lg btn-danger" href="/signout">Sign out</a>
|
<a class="btn btn-light btn-danger" href="/signout">Sign out</a>
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
<h1>Welcome, anonymous</h1>
|
<h1>Welcome, anonymous</h1>
|
||||||
<a class="btn btn-lg btn-success" href="/signin">Sign in</a>
|
<a class="btn btn-light btn-success" href="/signin">Sign in</a>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"AllowedHosts": "*",
|
"AllowedHosts": "*",
|
||||||
|
"AuthIssuer": "https://localhost:5001",
|
||||||
"Kestrel": {
|
"Kestrel": {
|
||||||
"Endpoints":
|
"Endpoints":
|
||||||
{
|
{
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Yavsc.Abstract\Yavsc.Abstract.csproj" />
|
<ProjectReference Include="..\Yavsc.Abstract\Yavsc.Abstract.csproj" />
|
||||||
<PackageReference Include="IdentityModel.AspNetCore" Version="4.3.0" />
|
<PackageReference Include="IdentityModel.AspNetCore" Version="4.3.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.6" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.7" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
Reference in New Issue
Block a user