Compare commits
24 Commits
dependabot
...
52ba9ff3a8
Author | SHA1 | Date | |
---|---|---|---|
52ba9ff3a8 | |||
0aeff6118b | |||
84366812f9 | |||
984b76b170 | |||
19a3ba6f87 | |||
a39f39c692 | |||
e870271fe4 | |||
080578c101 | |||
d0d1c652fe | |||
0a1bef37fe | |||
2a825da32e | |||
e6f8947c08 | |||
15d35e5508 | |||
3f1bfc1c3c | |||
fdf75934e5 | |||
f603d87b33 | |||
bebca989d0 | |||
531797d348 | |||
2002b8b232 | |||
0e0a79c6cd | |||
0f52a875de | |||
70771e5ab5 | |||
447d926ca6 | |||
b4870a1814 |
@ -44,7 +44,7 @@ jobs:
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
dotnet-version: 9.0.x
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
- name: Build
|
7
.github/workflows/main.yml
vendored
7
.github/workflows/main.yml
vendored
@ -1,7 +0,0 @@
|
||||
on: workflow_call
|
||||
jobs:
|
||||
my_first_job:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Run my action
|
||||
uses: ./.github/worklflows/dotnet
|
31
.vscode/launch.json
vendored
31
.vscode/launch.json
vendored
@ -4,6 +4,32 @@
|
||||
// Pour plus d'informations, visitez : https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "C#: sampleWebAsWebApiClient Debug",
|
||||
"type": "dotnet",
|
||||
"request": "launch",
|
||||
"projectPath": "${workspaceFolder}/src/sampleWebAsWebApiClient/sampleWebAsWebApiClient.csproj"
|
||||
},
|
||||
{
|
||||
"name": ".NET Core Launch (web)",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
"program": "${workspaceFolder}/bin/Debug/<target-framework>/<project-name.dll>",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"stopAtEntry": false,
|
||||
"serverReadyAction": {
|
||||
"action": "openExternally",
|
||||
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
|
||||
},
|
||||
"env": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"sourceFileMap": {
|
||||
"/Views": "${workspaceFolder}/Views"
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/* {
|
||||
@ -83,6 +109,11 @@
|
||||
"serverReadyAction": {
|
||||
"action": "openExternally",
|
||||
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
|
||||
},
|
||||
"presentation": {
|
||||
"hidden": false,
|
||||
"group": "run",
|
||||
"order": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
|
6
.vscode/tasks.json
vendored
6
.vscode/tasks.json
vendored
@ -9,8 +9,12 @@
|
||||
"args": [
|
||||
"build",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary;ForceNoAlign"
|
||||
"/consoleloggerparameters:NoSummary;ForceNoAlign",
|
||||
|
||||
],
|
||||
"group": "build",
|
||||
"isBuildCommand": true,
|
||||
"isTestCommand": false,
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
|
@ -15,14 +15,13 @@ WorkingDirectory=/srv/www/yavsc/
|
||||
ExecStart=/srv/www/yavsc/Yavsc
|
||||
Restart=always
|
||||
Environment="HOME=/srv/www/yavsc"
|
||||
Environment="ASPNETCORE_ENVIRONMENT=lua"
|
||||
Environment="ASPNETCORE_ConnectionStrings__DefaultConnection=Server=localhost;Port=5432;Database=lua;Username=lua;Password=f3s-*Vx$;"
|
||||
Environment="ASPNETCORE_ENVIRONMENT=Production"
|
||||
Environment="ASPNETCORE_ConnectionStrings__DefaultConnection=YOUR Postgresql CONNECTION STRING"
|
||||
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
|
||||
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||
StandardOutput=syslog
|
||||
StandardError=syslog
|
||||
SyslogIdentifier=isnd
|
||||
SyslogIdentifier=yavsc
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
|
@ -111,9 +111,9 @@ namespace Yavsc.ApiControllers
|
||||
var user = dbContext.Users.Single(
|
||||
u => u.Id == uid
|
||||
);
|
||||
var info = user.MoveUserFileToDir(query.id, query.to);
|
||||
var info = user.MoveUserFileToDir(query.Id, query.To);
|
||||
if (!info.Done) return new BadRequestObjectResult(info);
|
||||
return Ok(new { moved = query.id });
|
||||
return Ok(new { moved = query.Id });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
@ -124,21 +124,21 @@ namespace Yavsc.ApiControllers
|
||||
if (!ModelState.IsValid) {
|
||||
var idvr = new ValidRemoteUserFilePathAttribute();
|
||||
|
||||
return this.BadRequest(new { id = idvr.IsValid(query.id), to = idvr.IsValid(query.to), errors = ModelState });
|
||||
return this.BadRequest(new { id = idvr.IsValid(query.Id), to = idvr.IsValid(query.To), errors = ModelState });
|
||||
}
|
||||
_logger.LogInformation($"Valid move query: {query.id} => {query.to}");
|
||||
_logger.LogInformation($"Valid move query: {query.Id} => {query.To}");
|
||||
var uid = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
var user = dbContext.Users.Single(
|
||||
u => u.Id == uid
|
||||
);
|
||||
try {
|
||||
if (Config.UserFilesOptions.FileProvider.GetFileInfo(Path.Combine(user.UserName, query.id)).Exists)
|
||||
if (Config.UserFilesOptions.FileProvider.GetFileInfo(Path.Combine(user.UserName, query.Id)).Exists)
|
||||
{
|
||||
var result = user.MoveUserFile(query.id, query.to);
|
||||
var result = user.MoveUserFile(query.Id, query.To);
|
||||
if (!result.Done) return new BadRequestObjectResult(result);
|
||||
}
|
||||
else {
|
||||
var result = user.MoveUserDir(query.id, query.to);
|
||||
var result = user.MoveUserDir(query.Id, query.To);
|
||||
if (!result.Done) return new BadRequestObjectResult(result);
|
||||
}
|
||||
}
|
||||
|
@ -1,23 +1,16 @@
|
||||
using Yavsc.Attributes.Validation;
|
||||
namespace Yavsc.Models.FileSystem
|
||||
{
|
||||
public class RenameFileQuery {
|
||||
[ValidRemoteUserFilePath]
|
||||
[YaStringLength(1, 512)]
|
||||
public string id { get; set; }
|
||||
|
||||
[YaStringLength(0, 512)]
|
||||
[ValidRemoteUserFilePath]
|
||||
public string to { get; set; }
|
||||
}
|
||||
public class MoveFileQuery {
|
||||
[ValidRemoteUserFilePath]
|
||||
[YaStringLength(1, 512)]
|
||||
public string id { get; set; }
|
||||
public class MoveFileQuery
|
||||
{
|
||||
[ValidRemoteUserFilePath]
|
||||
[YaStringLength(1, 512)]
|
||||
public required string Id { get; set; }
|
||||
|
||||
[YaStringLength(0, 512)]
|
||||
[ValidRemoteUserFilePath]
|
||||
public string to { get; set; }
|
||||
}
|
||||
[YaStringLength(0, 512)]
|
||||
[ValidRemoteUserFilePath]
|
||||
public required string To { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
|
15
src/Api/Controllers/Blogspot/RenameFileQuery.cs
Normal file
15
src/Api/Controllers/Blogspot/RenameFileQuery.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using Yavsc.Attributes.Validation;
|
||||
namespace Yavsc.Models.FileSystem
|
||||
{
|
||||
public class RenameFileQuery
|
||||
{
|
||||
[ValidRemoteUserFilePath]
|
||||
[YaStringLength(1, 512)]
|
||||
public required string Id { get; set; }
|
||||
|
||||
[YaStringLength(0, 512)]
|
||||
[ValidRemoteUserFilePath]
|
||||
public required string To { get; set; }
|
||||
}
|
||||
|
||||
}
|
@ -37,7 +37,7 @@ namespace Yavsc.Controllers
|
||||
}
|
||||
// GET: api/Estimate{?ownerId=User.GetUserId()}
|
||||
[HttpGet]
|
||||
public IActionResult GetEstimates(string ownerId = null)
|
||||
public IActionResult GetEstimates(string? ownerId = null)
|
||||
{
|
||||
if (ownerId == null) ownerId = User.GetUserId();
|
||||
else if (!UserIsAdminOrThis(ownerId)) // throw new Exception("Not authorized") ;
|
||||
|
@ -40,7 +40,7 @@ namespace Yavsc.ApiControllers
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpPost("query/reject")]
|
||||
[HttpPost("query/accept")]
|
||||
public IActionResult AcceptQuery(string billingCode, long queryId)
|
||||
{
|
||||
if (billingCode == null) return BadRequest("billingCode");
|
||||
@ -51,7 +51,6 @@ namespace Yavsc.ApiControllers
|
||||
billing.Decided = true;
|
||||
dbContext.SaveChanges();
|
||||
return Ok();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,8 @@ public class NativeConfidentialController : Controller
|
||||
[FromBody] DeviceDeclaration declaration)
|
||||
{
|
||||
var uid = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
|
||||
if (uid == null)
|
||||
throw new InvalidOperationException("no name identifier from claims");
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
_logger.LogError("Invalid model for GCMD");
|
||||
|
@ -6,6 +6,7 @@ using Microsoft.EntityFrameworkCore;
|
||||
using Yavsc.Models;
|
||||
using Yavsc.Api.Helpers;
|
||||
using Yavsc.Server.Helpers;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Yavsc.WebApi.Controllers
|
||||
{
|
||||
@ -30,9 +31,9 @@ namespace Yavsc.WebApi.Controllers
|
||||
return new BadRequestObjectResult(
|
||||
new { error = "user not found" });
|
||||
var uid = User.GetUserId();
|
||||
|
||||
Debug.Assert(uid != null, "uid is null");
|
||||
var userData = await GetUserData(uid);
|
||||
|
||||
Debug.Assert(userData != null, "userData is null");
|
||||
var user = new Yavsc.Models.Auth.Me(userData.Id, userData.UserName, userData.Email,
|
||||
userData.Avatar,
|
||||
userData.PostalAddress, userData.DedicatedGoogleCalendar);
|
||||
@ -57,7 +58,7 @@ namespace Yavsc.WebApi.Controllers
|
||||
[HttpGet("myhost")]
|
||||
public IActionResult MyHost ()
|
||||
{
|
||||
return Ok(new { host = Request.ForHost() });
|
||||
return Ok(new { host = Request.ForwardedFor() });
|
||||
}
|
||||
|
||||
|
||||
|
@ -13,8 +13,8 @@ namespace Yavsc.Api.Helpers
|
||||
public static class RequestHelpers
|
||||
{
|
||||
// Check for some apache proxy header, if any
|
||||
public static string ForHost(this HttpRequest request) {
|
||||
string host = request.Headers["X-Forwarded-For"];
|
||||
public static string? ForwardedFor(this HttpRequest request) {
|
||||
string? host = request.Headers["X-Forwarded-For"];
|
||||
if (string.IsNullOrEmpty(host)) {
|
||||
host = request.Host.Value;
|
||||
} else { // Using X-Forwarded-For last address
|
||||
|
@ -9,7 +9,7 @@ namespace Yavsc.Api.Helpers
|
||||
{
|
||||
public static class UserHelpers
|
||||
{
|
||||
public static string GetUserId(this ClaimsPrincipal user)
|
||||
public static string? GetUserId(this ClaimsPrincipal user)
|
||||
{
|
||||
return user.FindFirstValue("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier");
|
||||
}
|
||||
|
@ -11,8 +11,6 @@
|
||||
*/
|
||||
|
||||
using IdentityModel;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Yavsc.Helpers;
|
||||
@ -63,7 +61,8 @@ internal class Program
|
||||
options.IncludeErrorDetails = true;
|
||||
options.Authority = "https://localhost:5001";
|
||||
options.TokenValidationParameters =
|
||||
new() { ValidateAudience = false };
|
||||
new() { ValidateAudience = false, RoleClaimType = JwtClaimTypes.Role };
|
||||
options.MapInboundClaims = true;
|
||||
});
|
||||
|
||||
services.AddDbContext<ApplicationDbContext>(options =>
|
||||
@ -74,9 +73,9 @@ internal class Program
|
||||
.AddTransient<ICalendarManager, CalendarManager>();
|
||||
services.AddTransient<IFileSystemAuthManager, FileSystemAuthManager>();
|
||||
/*
|
||||
services.AddIdentityApiEndpoints<ApplicationUser>();
|
||||
services.AddSingleton<IConnexionManager, HubConnectionManager>();
|
||||
services.AddSingleton<ILiveProcessor, LiveProcessor>();
|
||||
services.AddIdentityApiEndpoints<ApplicationUser>();
|
||||
services.AddSession();
|
||||
*/
|
||||
WorkflowHelpers.ConfigureBillingService();
|
||||
@ -101,15 +100,10 @@ internal class Program
|
||||
app.MapDefaultControllerRoute();
|
||||
app.MapGet("/identity", (HttpContext context) =>
|
||||
new JsonResult(context?.User?.Claims.Select(c => new { c.Type, c.Value }))
|
||||
);
|
||||
);
|
||||
|
||||
// app.UseSession();
|
||||
await app.RunAsync();
|
||||
}
|
||||
;
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,19 @@
|
||||
|
||||
|
||||
|
||||
using Yavsc.Abstract.Identity;
|
||||
|
||||
namespace Yavsc
|
||||
{
|
||||
public interface IBlogPostPayLoad
|
||||
{
|
||||
string Content { get; set; }
|
||||
string Photo { get; set; }
|
||||
string? Content { get; set; }
|
||||
string? Photo { get; set; }
|
||||
|
||||
}
|
||||
public interface IBlogPost :IBlogPostPayLoad, ITrackedEntity, IIdentified<long>, ITitle
|
||||
public interface IBlogPost : IBlogPostPayLoad, ITrackedEntity, IIdentified<long>, ITitle
|
||||
{
|
||||
string AuthorId { get; set; }
|
||||
IApplicationUser Author { get; }
|
||||
}
|
||||
}
|
||||
|
@ -5,21 +5,21 @@ namespace Yavsc
|
||||
|
||||
public static class Constants
|
||||
{
|
||||
public static readonly Scope[] SiteScopes = {
|
||||
new Scope { Id = "profile", Description = "Your profile informations" },
|
||||
new Scope { Id = "book" , Description ="Your booking interface"},
|
||||
new Scope { Id = "blog" , Description ="Your blogging interface"},
|
||||
new Scope { Id = "estimate" , Description ="Your estimation interface"},
|
||||
new Scope { Id = "contract" , Description ="Your contract signature access"},
|
||||
new Scope { Id = "admin" , Description ="Your administration rights on this site"},
|
||||
new Scope { Id = "moderation" , Description ="Your moderator interface"},
|
||||
public static readonly Scope[] SiteScopes = {
|
||||
new Scope { Id = "profile", Description = "Your profile informations" },
|
||||
new Scope { Id = "book" , Description ="Your booking interface"},
|
||||
new Scope { Id = "blog" , Description ="Your blogging interface"},
|
||||
new Scope { Id = "estimate" , Description ="Your estimation interface"},
|
||||
new Scope { Id = "contract" , Description ="Your contract signature access"},
|
||||
new Scope { Id = "admin" , Description ="Your administration rights on this site"},
|
||||
new Scope { Id = "moderation" , Description ="Your moderator interface"},
|
||||
new Scope { Id = "frontoffice" , Description ="Your front office interface" }
|
||||
};
|
||||
|
||||
public const string CompanyClaimType = "https://schemas.pschneider.fr/identity/claims/Company";
|
||||
public const string UserNameRegExp = @"^[a-zA-Z][a-zA-Z0-9._-]*$";
|
||||
public const string UserFileNamePatternRegExp = @"^([a-zA-Z0-9._-]*/)*[a-zA-Z0-9._-]+$";
|
||||
|
||||
|
||||
public const string LoginPath = "/signin";
|
||||
public const string LogoutPath = "/signout";
|
||||
|
||||
@ -52,9 +52,7 @@ namespace Yavsc
|
||||
public const int MaxUserNameLength = 26;
|
||||
|
||||
public const string LivePath = "/live/cast";
|
||||
|
||||
public const string StreamingPath = "/api/stream/put";
|
||||
|
||||
public const string RoleClaimName = "http://schemas.microsoft.com/ws/2008/06/identity/claims/role";
|
||||
public const string StreamingPath = "/api/stream/put";
|
||||
}
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ namespace Yavsc.Models.Google.Messaging
|
||||
/// <summary>
|
||||
/// The error.
|
||||
/// </summary>
|
||||
public string error;
|
||||
public string? error;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -3,10 +3,10 @@
|
||||
public interface IApplicationUser
|
||||
{
|
||||
string Id { get; set; }
|
||||
string UserName { get; set; }
|
||||
string Avatar { get ; set; }
|
||||
IAccountBalance AccountBalance { get; set; }
|
||||
string DedicatedGoogleCalendar { get; set; }
|
||||
ILocation PostalAddress { get; set; }
|
||||
string? UserName { get; set; }
|
||||
string? Avatar { get ; set; }
|
||||
IAccountBalance? AccountBalance { get; }
|
||||
string? DedicatedGoogleCalendar { get; }
|
||||
ILocation? PostalAddress { get; }
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ namespace Yavsc.Abstract.Identity.Security
|
||||
public interface ICircleAuthorized
|
||||
{
|
||||
long Id { get; set; }
|
||||
string OwnerId { get; }
|
||||
string AuthorId { get; }
|
||||
bool AuthorizeCircle(long circleId);
|
||||
ICircleAuthorization [] GetACL();
|
||||
|
||||
|
13
src/Yavsc.Server/.config/dotnet-tools.json
Normal file
13
src/Yavsc.Server/.config/dotnet-tools.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"version": 1,
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"dotnet-ef": {
|
||||
"version": "9.0.6",
|
||||
"commands": [
|
||||
"dotnet-ef"
|
||||
],
|
||||
"rollForward": false
|
||||
}
|
||||
}
|
||||
}
|
@ -80,12 +80,12 @@ public static class Config
|
||||
PostLogoutRedirectUris = { "https://localhost:5003/signout-callback-oidc",
|
||||
"http://localhost:5002/signout-callback-oidc" },
|
||||
|
||||
AllowedScopes = {
|
||||
AllowedScopes = {
|
||||
IdentityServerConstants.StandardScopes.OpenId,
|
||||
IdentityServerConstants.StandardScopes.Profile,
|
||||
IdentityServerConstants.StandardScopes.Email,
|
||||
IdentityServerConstants.StandardScopes.OfflineAccess,
|
||||
"scope2" }
|
||||
"scope2" },
|
||||
},
|
||||
};
|
||||
|
||||
|
17
src/Yavsc.Server/Exceptions/AuthorizationFailureException.cs
Normal file
17
src/Yavsc.Server/Exceptions/AuthorizationFailureException.cs
Normal file
@ -0,0 +1,17 @@
|
||||
namespace Yavsc.Server.Exceptions;
|
||||
|
||||
[Serializable]
|
||||
public class AuthorizationFailureException : Exception
|
||||
{
|
||||
public AuthorizationFailureException(Microsoft.AspNetCore.Authorization.AuthorizationResult auth) : base(auth?.Failure?.ToString()??auth?.ToString()??"AuthorizationResult failure")
|
||||
{
|
||||
}
|
||||
|
||||
public AuthorizationFailureException(string? message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public AuthorizationFailureException(string? message, Exception? innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
@ -16,7 +16,7 @@ namespace Yavsc.Helpers
|
||||
|
||||
public static string GetUserName(this ClaimsPrincipal user)
|
||||
{
|
||||
return user.FindFirstValue(ClaimTypes.Name);
|
||||
return user.FindFirstValue("name");
|
||||
}
|
||||
|
||||
public static bool IsSignedIn(this ClaimsPrincipal user)
|
||||
@ -24,13 +24,14 @@ namespace Yavsc.Helpers
|
||||
return user.Identity.IsAuthenticated;
|
||||
}
|
||||
|
||||
public static IEnumerable<BlogPost> UserPosts(this ApplicationDbContext dbContext, string posterId, string readerId)
|
||||
public static IEnumerable<BlogPost> UserPosts(this ApplicationDbContext dbContext, string posterId, string? readerId)
|
||||
{
|
||||
if (readerId == null)
|
||||
{
|
||||
var userPosts = dbContext.BlogSpot.Include(
|
||||
b => b.Author
|
||||
).Where(x => ((x.AuthorId == posterId))).ToArray();
|
||||
var userPosts = dbContext.blogSpotPublications.Include(
|
||||
b => b.BlogPost
|
||||
).Where(x => x.BlogPost.AuthorId == posterId)
|
||||
.Select(x=>x.BlogPost).ToArray();
|
||||
return userPosts;
|
||||
}
|
||||
else
|
||||
|
@ -29,11 +29,13 @@ using Microsoft.Extensions.Localization;
|
||||
|
||||
namespace Yavsc
|
||||
{
|
||||
using System.Diagnostics;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Models;
|
||||
using Models.Chat;
|
||||
using Yavsc.Abstract.Chat;
|
||||
using Yavsc.Helpers;
|
||||
using Yavsc.Services;
|
||||
public partial class ChatHub : Hub, IDisposable
|
||||
{
|
||||
@ -192,10 +194,11 @@ namespace Yavsc
|
||||
NotifyUserInRoom(NotificationTypes.Error, room, "already registered.");
|
||||
return;
|
||||
}
|
||||
string userName = Context.User.Identity.Name;
|
||||
Debug.Assert(Context.User != null);
|
||||
string userName = Context.User.GetUserName();
|
||||
var user = _dbContext.Users.FirstOrDefault(u => u.UserName == userName);
|
||||
|
||||
var newroom = new ChatRoom { Name = room, OwnerId = user.Id };
|
||||
var newroom = new ChatRoom { Name = room, OwnerId = Context.User.GetUserId() };
|
||||
ChatRoomInfo chanInfo;
|
||||
if (_cxManager.TryGetChanInfo(room, out chanInfo))
|
||||
{
|
||||
@ -319,7 +322,7 @@ namespace Yavsc
|
||||
|
||||
async Task NotifyUser(string type, string targetId, string message)
|
||||
{
|
||||
_logger.LogInformation("notifying user {type} {targetId} : {message}");
|
||||
_logger.LogInformation($"notifying user {type} {targetId} : {message}");
|
||||
await Clients.Caller.SendAsync("notifyUser", type, targetId, message);
|
||||
}
|
||||
|
||||
@ -331,6 +334,8 @@ namespace Yavsc
|
||||
[Authorize]
|
||||
public async Task SendPV(string userName, string message)
|
||||
{
|
||||
// Authorized code
|
||||
Debug.Assert(Context.User != null);
|
||||
_logger.LogInformation($"Sending pv to {userName}");
|
||||
|
||||
if (!InputValidator.ValidateUserName(userName))
|
||||
@ -344,19 +349,21 @@ namespace Yavsc
|
||||
return ;
|
||||
}
|
||||
_logger.LogInformation($"Message form is validated.");
|
||||
var identityUserName = Context.User.GetUserName();
|
||||
|
||||
if (userName[0] != '?')
|
||||
if (userName[0] != '?' && Context.User!=null)
|
||||
if (!Context.User.IsInRole(Constants.AdminGroupName))
|
||||
{
|
||||
|
||||
var bl = _dbContext.BlackListed
|
||||
.Include(r => r.User)
|
||||
.Include(r => r.Owner)
|
||||
.Where(r => r.User.UserName == Context.User.Identity.Name && r.Owner.UserName == userName)
|
||||
.Where(r => r.User.UserName == identityUserName && r.Owner.UserName == userName)
|
||||
.Select(r => r.OwnerId);
|
||||
|
||||
if (bl.Count() > 0)
|
||||
{
|
||||
_logger.LogError($"Black listed : {Context.User.Identity.Name}");
|
||||
_logger.LogError($"Black listed : {identityUserName}");
|
||||
await NotifyUser(NotificationTypes.PrivateMessageDenied, userName, "you are black listed.");
|
||||
return;
|
||||
}
|
||||
@ -372,7 +379,7 @@ namespace Yavsc
|
||||
_logger.LogInformation($"cx: {connectionId}");
|
||||
var cli = Clients.Client(connectionId);
|
||||
_logger.LogInformation($"cli: {cli.ToString()}");
|
||||
await cli.SendAsync("addPV", Context.User.Identity.Name, message);
|
||||
await cli.SendAsync("addPV", identityUserName, message);
|
||||
_logger.LogInformation($"Sent pv to cx {connectionId}");
|
||||
}
|
||||
}
|
||||
@ -380,6 +387,9 @@ namespace Yavsc
|
||||
[Authorize]
|
||||
public async Task SendStream(string connectionId, long streamId, string message)
|
||||
{
|
||||
// Authorized code
|
||||
Debug.Assert(Context.User != null);
|
||||
Debug.Assert(Context.User.Identity != null);
|
||||
if (!InputValidator.ValidateMessage(message)) return;
|
||||
var sender = Context.User.Identity.Name;
|
||||
var cli = Clients.Client(connectionId);
|
||||
|
3497
src/Yavsc.Server/Migrations/20250613181510_ActivityNulls.Designer.cs
generated
Normal file
3497
src/Yavsc.Server/Migrations/20250613181510_ActivityNulls.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
108
src/Yavsc.Server/Migrations/20250613181510_ActivityNulls.cs
Normal file
108
src/Yavsc.Server/Migrations/20250613181510_ActivityNulls.cs
Normal file
@ -0,0 +1,108 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Yavsc.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class ActivityNulls : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "UserModified",
|
||||
table: "Activities",
|
||||
type: "text",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "UserCreated",
|
||||
table: "Activities",
|
||||
type: "text",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "SettingsClassName",
|
||||
table: "Activities",
|
||||
type: "text",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Photo",
|
||||
table: "Activities",
|
||||
type: "text",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "ModeratorGroupName",
|
||||
table: "Activities",
|
||||
type: "text",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "UserModified",
|
||||
table: "Activities",
|
||||
type: "text",
|
||||
nullable: false,
|
||||
defaultValue: "",
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "UserCreated",
|
||||
table: "Activities",
|
||||
type: "text",
|
||||
nullable: false,
|
||||
defaultValue: "",
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "SettingsClassName",
|
||||
table: "Activities",
|
||||
type: "text",
|
||||
nullable: false,
|
||||
defaultValue: "",
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Photo",
|
||||
table: "Activities",
|
||||
type: "text",
|
||||
nullable: false,
|
||||
defaultValue: "",
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "ModeratorGroupName",
|
||||
table: "Activities",
|
||||
type: "text",
|
||||
nullable: false,
|
||||
defaultValue: "",
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text",
|
||||
oldNullable: true);
|
||||
}
|
||||
}
|
||||
}
|
3494
src/Yavsc.Server/Migrations/20250620185047_BrusherProfileSchedulerId.Designer.cs
generated
Normal file
3494
src/Yavsc.Server/Migrations/20250620185047_BrusherProfileSchedulerId.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,59 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Yavsc.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class BrusherProfileSchedulerId : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_BrusherProfile_Schedule_ScheduleOwnerId",
|
||||
table: "BrusherProfile");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "ScheduleOwnerId",
|
||||
table: "BrusherProfile",
|
||||
type: "text",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_BrusherProfile_Schedule_ScheduleOwnerId",
|
||||
table: "BrusherProfile",
|
||||
column: "ScheduleOwnerId",
|
||||
principalTable: "Schedule",
|
||||
principalColumn: "OwnerId");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_BrusherProfile_Schedule_ScheduleOwnerId",
|
||||
table: "BrusherProfile");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "ScheduleOwnerId",
|
||||
table: "BrusherProfile",
|
||||
type: "text",
|
||||
nullable: false,
|
||||
defaultValue: "",
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_BrusherProfile_Schedule_ScheduleOwnerId",
|
||||
table: "BrusherProfile",
|
||||
column: "ScheduleOwnerId",
|
||||
principalTable: "Schedule",
|
||||
principalColumn: "OwnerId",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
}
|
||||
}
|
3515
src/Yavsc.Server/Migrations/20250628150549_blogPostPub.Designer.cs
generated
Normal file
3515
src/Yavsc.Server/Migrations/20250628150549_blogPostPub.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
38
src/Yavsc.Server/Migrations/20250628150549_blogPostPub.cs
Normal file
38
src/Yavsc.Server/Migrations/20250628150549_blogPostPub.cs
Normal file
@ -0,0 +1,38 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Yavsc.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class blogPostPub : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "blogspotPublications",
|
||||
columns: table => new
|
||||
{
|
||||
BlogpostId = table.Column<long>(type: "bigint", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_blogspotPublications", x => x.BlogpostId);
|
||||
table.ForeignKey(
|
||||
name: "FK_blogspotPublications_BlogSpot_BlogpostId",
|
||||
column: x => x.BlogpostId,
|
||||
principalTable: "BlogSpot",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "blogspotPublications");
|
||||
}
|
||||
}
|
||||
}
|
3518
src/Yavsc.Server/Migrations/20250629174955_blogPusb2.Designer.cs
generated
Normal file
3518
src/Yavsc.Server/Migrations/20250629174955_blogPusb2.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
79
src/Yavsc.Server/Migrations/20250629174955_blogPusb2.cs
Normal file
79
src/Yavsc.Server/Migrations/20250629174955_blogPusb2.cs
Normal file
@ -0,0 +1,79 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Yavsc.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class blogPusb2 : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_blogspotPublications_BlogSpot_BlogpostId",
|
||||
table: "blogspotPublications");
|
||||
|
||||
migrationBuilder.DropPrimaryKey(
|
||||
name: "PK_blogspotPublications",
|
||||
table: "blogspotPublications");
|
||||
|
||||
migrationBuilder.RenameTable(
|
||||
name: "blogspotPublications",
|
||||
newName: "blogSpotPublications");
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "Publish",
|
||||
table: "BlogSpot",
|
||||
type: "boolean",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddPrimaryKey(
|
||||
name: "PK_blogSpotPublications",
|
||||
table: "blogSpotPublications",
|
||||
column: "BlogpostId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_blogSpotPublications_BlogSpot_BlogpostId",
|
||||
table: "blogSpotPublications",
|
||||
column: "BlogpostId",
|
||||
principalTable: "BlogSpot",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_blogSpotPublications_BlogSpot_BlogpostId",
|
||||
table: "blogSpotPublications");
|
||||
|
||||
migrationBuilder.DropPrimaryKey(
|
||||
name: "PK_blogSpotPublications",
|
||||
table: "blogSpotPublications");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Publish",
|
||||
table: "BlogSpot");
|
||||
|
||||
migrationBuilder.RenameTable(
|
||||
name: "blogSpotPublications",
|
||||
newName: "blogspotPublications");
|
||||
|
||||
migrationBuilder.AddPrimaryKey(
|
||||
name: "PK_blogspotPublications",
|
||||
table: "blogspotPublications",
|
||||
column: "BlogpostId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_blogspotPublications_BlogSpot_BlogpostId",
|
||||
table: "blogspotPublications",
|
||||
column: "BlogpostId",
|
||||
principalTable: "BlogSpot",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
}
|
||||
}
|
3518
src/Yavsc.Server/Migrations/20250629175034_blogPub2.Designer.cs
generated
Normal file
3518
src/Yavsc.Server/Migrations/20250629175034_blogPub2.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
22
src/Yavsc.Server/Migrations/20250629175034_blogPub2.cs
Normal file
22
src/Yavsc.Server/Migrations/20250629175034_blogPub2.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Yavsc.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class blogPub2 : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
3519
src/Yavsc.Server/Migrations/20250705112955_commentAllowed.Designer.cs
generated
Normal file
3519
src/Yavsc.Server/Migrations/20250705112955_commentAllowed.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
54
src/Yavsc.Server/Migrations/20250705112955_commentAllowed.cs
Normal file
54
src/Yavsc.Server/Migrations/20250705112955_commentAllowed.cs
Normal file
@ -0,0 +1,54 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Yavsc.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class commentAllowed : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "Comment",
|
||||
table: "CircleAuthorizationToBlogPost",
|
||||
type: "boolean",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Avatar",
|
||||
table: "AspNetUsers",
|
||||
type: "character varying(512)",
|
||||
maxLength: 512,
|
||||
nullable: true,
|
||||
defaultValue: "/images/Users/icon_user.png",
|
||||
oldClrType: typeof(string),
|
||||
oldType: "character varying(512)",
|
||||
oldMaxLength: 512,
|
||||
oldDefaultValue: "/images/Users/icon_user.png");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Comment",
|
||||
table: "CircleAuthorizationToBlogPost");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Avatar",
|
||||
table: "AspNetUsers",
|
||||
type: "character varying(512)",
|
||||
maxLength: 512,
|
||||
nullable: false,
|
||||
defaultValue: "/images/Users/icon_user.png",
|
||||
oldClrType: typeof(string),
|
||||
oldType: "character varying(512)",
|
||||
oldMaxLength: 512,
|
||||
oldNullable: true,
|
||||
oldDefaultValue: "/images/Users/icon_user.png");
|
||||
}
|
||||
}
|
||||
}
|
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);
|
||||
}
|
||||
}
|
||||
}
|
@ -303,6 +303,9 @@ namespace Yavsc.Migrations
|
||||
b.Property<long>("BlogPostId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<bool>("Comment")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.HasKey("CircleId", "BlogPostId");
|
||||
|
||||
b.HasIndex("BlogPostId");
|
||||
@ -338,7 +341,6 @@ namespace Yavsc.Migrations
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("Avatar")
|
||||
.IsRequired()
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("character varying(512)")
|
||||
@ -857,6 +859,16 @@ namespace Yavsc.Migrations
|
||||
b.ToTable("Comment");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Yavsc.Models.BlogSpotPublication", b =>
|
||||
{
|
||||
b.Property<long>("BlogpostId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.HasKey("BlogpostId");
|
||||
|
||||
b.ToTable("blogSpotPublications");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Yavsc.Models.Calendar.Schedule", b =>
|
||||
{
|
||||
b.Property<string>("OwnerId")
|
||||
@ -1123,7 +1135,6 @@ namespace Yavsc.Migrations
|
||||
.HasColumnType("numeric");
|
||||
|
||||
b.Property<string>("ScheduleOwnerId")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<decimal>("ShampooPrice")
|
||||
@ -2081,7 +2092,6 @@ namespace Yavsc.Migrations
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("ModeratorGroupName")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Name")
|
||||
@ -2092,22 +2102,18 @@ namespace Yavsc.Migrations
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Photo")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("Rate")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("SettingsClassName")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("UserCreated")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("UserModified")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Code");
|
||||
@ -2769,6 +2775,17 @@ namespace Yavsc.Migrations
|
||||
b.Navigation("Post");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Yavsc.Models.BlogSpotPublication", b =>
|
||||
{
|
||||
b.HasOne("Yavsc.Models.Blog.BlogPost", "BlogPost")
|
||||
.WithMany()
|
||||
.HasForeignKey("BlogpostId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("BlogPost");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Yavsc.Models.Calendar.Schedule", b =>
|
||||
{
|
||||
b.HasOne("Yavsc.Models.ApplicationUser", "Owner")
|
||||
@ -2840,9 +2857,7 @@ namespace Yavsc.Migrations
|
||||
{
|
||||
b.HasOne("Yavsc.Models.Calendar.Schedule", "Schedule")
|
||||
.WithMany()
|
||||
.HasForeignKey("ScheduleOwnerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
.HasForeignKey("ScheduleOwnerId");
|
||||
|
||||
b.HasOne("Yavsc.Models.Workflow.PerformerProfile", "BaseProfile")
|
||||
.WithMany()
|
||||
@ -3377,8 +3392,7 @@ namespace Yavsc.Migrations
|
||||
|
||||
modelBuilder.Entity("Yavsc.Models.ApplicationUser", b =>
|
||||
{
|
||||
b.Navigation("AccountBalance")
|
||||
.IsRequired();
|
||||
b.Navigation("AccountBalance");
|
||||
|
||||
b.Navigation("BankInfo");
|
||||
|
||||
|
@ -8,8 +8,8 @@ namespace Yavsc.Models.Access
|
||||
|
||||
public class CircleAuthorizationToBlogPost : ICircleAuthorization
|
||||
{
|
||||
public long CircleId { get; set; }
|
||||
public long BlogPostId { get; set; }
|
||||
public long CircleId { get; set; }
|
||||
public long BlogPostId { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
[ForeignKey("BlogPostId")]
|
||||
@ -17,7 +17,10 @@ namespace Yavsc.Models.Access
|
||||
|
||||
[JsonIgnore]
|
||||
[ForeignKey("CircleId")]
|
||||
public virtual Circle Allowed { get; set; }
|
||||
public virtual Circle Allowed { get; set; }
|
||||
|
||||
public bool Comment { get; set; }
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,4 @@
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using Yavsc.Models.Haircut;
|
||||
using Yavsc.Models.IT.Evolution;
|
||||
using Yavsc.Models.IT.Fixing;
|
||||
@ -10,7 +6,6 @@ using Yavsc.Server.Models.EMailing;
|
||||
using Yavsc.Server.Models.IT.SourceCode;
|
||||
using Yavsc.Server.Models.IT;
|
||||
using Yavsc.Models.Streaming;
|
||||
using Yavsc.Models.Musical;
|
||||
|
||||
namespace Yavsc.Models
|
||||
{
|
||||
@ -32,7 +27,6 @@ namespace Yavsc.Models
|
||||
using Attributes;
|
||||
using Bank;
|
||||
using Payment;
|
||||
using Calendar;
|
||||
using Blog;
|
||||
using Yavsc.Abstract.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@ -48,10 +42,12 @@ namespace Yavsc.Models
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
base.OnModelCreating(builder);
|
||||
@ -61,7 +57,7 @@ namespace Yavsc.Models
|
||||
builder.Entity<Contact>().HasKey(x => new { x.OwnerId, x.UserId });
|
||||
builder.Entity<DeviceDeclaration>().Property(x => x.DeclarationDate).HasDefaultValueSql("LOCALTIMESTAMP");
|
||||
builder.Entity<BlogTag>().HasKey(x => new { x.PostId, x.TagId });
|
||||
|
||||
|
||||
builder.Entity<ApplicationUser>().Property(u => u.FullName).IsRequired(false);
|
||||
builder.Entity<ApplicationUser>().Property(u => u.DedicatedGoogleCalendar).IsRequired(false);
|
||||
builder.Entity<ApplicationUser>().HasMany<ChatConnection>(c => c.Connections);
|
||||
@ -81,7 +77,7 @@ namespace Yavsc.Models
|
||||
builder.Entity<Models.Cratie.Option>().HasKey(o => new { o.Code, o.CodeScrutin });
|
||||
builder.Entity<Notification>().Property(n => n.icon).HasDefaultValue("exclam");
|
||||
builder.Entity<ChatRoomAccess>().HasKey(p => new { room = p.ChannelName, user = p.UserId });
|
||||
builder.Entity<InstrumentRating>().HasAlternateKey(i => new { Instrument= i.InstrumentId, owner = i.OwnerId });
|
||||
builder.Entity<InstrumentRating>().HasAlternateKey(i => new { Instrument = i.InstrumentId, owner = i.OwnerId });
|
||||
|
||||
foreach (var et in builder.Model.GetEntityTypes())
|
||||
{
|
||||
@ -89,23 +85,22 @@ namespace Yavsc.Models
|
||||
et.FindProperty("DateCreated").SetAfterSaveBehavior(Microsoft.EntityFrameworkCore.Metadata.PropertySaveBehavior.Ignore);
|
||||
}
|
||||
|
||||
builder.Entity<Activity>().Property(a=>a.ParentCode).IsRequired(false);
|
||||
//builder.Entity<BlogPost>().HasOne(p => p.Author).WithMany(a => a.Posts);
|
||||
|
||||
builder.Entity<Activity>().Property(a => a.ParentCode).IsRequired(false);
|
||||
// builder.Entity<IdentityUserLogin<String>>().HasKey(i=> new { i.LoginProvider, i.UserId, i.ProviderKey });
|
||||
}
|
||||
|
||||
// this is not a failback procedure.
|
||||
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
var appSetup = (string) AppDomain.CurrentDomain.GetData(Constants.YavscConnectionStringEnvName);
|
||||
if (!string.IsNullOrWhiteSpace(appSetup))
|
||||
var appSetup = (string)AppDomain.CurrentDomain.GetData(Constants.YavscConnectionStringEnvName);
|
||||
if (!string.IsNullOrWhiteSpace(appSetup))
|
||||
{
|
||||
optionsBuilder.UseNpgsql(appSetup);
|
||||
return;
|
||||
}
|
||||
var envSetup = Environment.GetEnvironmentVariable(Constants.YavscConnectionStringEnvName);
|
||||
if (envSetup!=null)
|
||||
if (envSetup != null)
|
||||
optionsBuilder.UseNpgsql(envSetup);
|
||||
else optionsBuilder.UseNpgsql();
|
||||
}
|
||||
@ -125,7 +120,7 @@ namespace Yavsc.Models
|
||||
/// Users posts
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public DbSet<Blog.BlogPost> BlogSpot { get; set; }
|
||||
public DbSet<BlogPost> BlogSpot { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Skills powered by this site
|
||||
@ -226,14 +221,14 @@ namespace Yavsc.Models
|
||||
((ITrackedEntity)entity.Entity).UserModified = userId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int SaveChanges(string userId)
|
||||
{
|
||||
AddTimestamps(userId);
|
||||
return base.SaveChanges();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public async Task<int> SaveChangesAsync(string userId, CancellationToken ctoken = default(CancellationToken))
|
||||
{
|
||||
@ -282,7 +277,7 @@ namespace Yavsc.Models
|
||||
public DbSet<Comment> Comment { get; set; }
|
||||
|
||||
public DbSet<Announce> Announce { get; set; }
|
||||
|
||||
|
||||
// TODO remove and opt for for memory only storing,
|
||||
// as long as it must be set empty each time the service is restarted,
|
||||
// and that chatting should be kept as must as possible independent from db context
|
||||
@ -295,7 +290,7 @@ namespace Yavsc.Models
|
||||
public DbSet<GitRepositoryReference> GitRepositoryReference { get; set; }
|
||||
|
||||
public DbSet<Project> Project { get; set; }
|
||||
|
||||
|
||||
[Obsolete("use signaled flows")]
|
||||
public DbSet<LiveFlow> LiveFlow { get; set; }
|
||||
|
||||
@ -304,6 +299,8 @@ namespace Yavsc.Models
|
||||
public DbSet<InstrumentRating> InstrumentRating { get; set; }
|
||||
|
||||
public DbSet<Scope> Scopes { get; set; }
|
||||
|
||||
|
||||
public DbSet<BlogSpotPublication> blogSpotPublications{ get; set; }
|
||||
// public DbSet<IdentityUserLogin<String>> AspNetUserLogins { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -7,10 +7,12 @@ using Yavsc.Models.Identity;
|
||||
using Yavsc.Models.Chat;
|
||||
using Yavsc.Models.Bank;
|
||||
using Yavsc.Models.Access;
|
||||
using Yavsc.Abstract.Identity;
|
||||
|
||||
namespace Yavsc.Models
|
||||
{
|
||||
[Table("AspNetUsers")]
|
||||
public class ApplicationUser : IdentityUser
|
||||
public class ApplicationUser : IdentityUser, IApplicationUser
|
||||
{
|
||||
/// <summary>
|
||||
/// Another me, as a byte array.TG7@Eu%80rufzkhbb
|
||||
@ -22,10 +24,10 @@ namespace Yavsc.Models
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[MaxLength(512)]
|
||||
public string Avatar { get; set; }
|
||||
public string? Avatar { get; set; }
|
||||
|
||||
[MaxLength(512)]
|
||||
public string FullName { get; set; }
|
||||
public string? FullName { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
@ -33,32 +35,31 @@ namespace Yavsc.Models
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[Display(Name = "Account balance")]
|
||||
public virtual AccountBalance AccountBalance { get; set; }
|
||||
public virtual AccountBalance? AccountBalance { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// User's posts
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[InverseProperty("Author"), JsonIgnore]
|
||||
public virtual List<Blog.BlogPost> Posts { get; set; }
|
||||
public virtual List<Blog.BlogPost>? Posts { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// User's contact list
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[InverseProperty("Owner"), JsonIgnore]
|
||||
public virtual List<Contact> Book { get; set; }
|
||||
public virtual List<Contact>? Book { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// External devices using the API
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[InverseProperty("DeviceOwner"), JsonIgnore]
|
||||
public virtual List<DeviceDeclaration> DeviceDeclaration { get; set; }
|
||||
public virtual List<DeviceDeclaration>? DeviceDeclaration { get; set; }
|
||||
|
||||
[InverseProperty("Owner"), JsonIgnore]
|
||||
public virtual List<ChatConnection> Connections { get; set; }
|
||||
|
||||
public virtual List<ChatConnection>? Connections { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// User's circles
|
||||
@ -66,7 +67,7 @@ namespace Yavsc.Models
|
||||
/// <returns></returns>
|
||||
[InverseProperty("Owner"), JsonIgnore]
|
||||
|
||||
public virtual List<Circle> Circles { get; set; }
|
||||
public virtual List<Circle>? Circles { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Billing postal address
|
||||
@ -81,14 +82,14 @@ namespace Yavsc.Models
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[MaxLength(512)]
|
||||
public string DedicatedGoogleCalendar { get; set; }
|
||||
public string? DedicatedGoogleCalendar { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return this.Id + " " + this.AccountBalance?.Credits.ToString() + this.Email + " " + this.UserName + " $" + this.AccountBalance?.Credits.ToString();
|
||||
}
|
||||
|
||||
public virtual List<BankIdentity> BankInfo { get; set; }
|
||||
public virtual List<BankIdentity>? BankInfo { get; set; }
|
||||
|
||||
public long DiskQuota { get; set; } = 512 * 1024 * 1024;
|
||||
public long DiskUsage { get; set; } = 0;
|
||||
@ -97,21 +98,24 @@ namespace Yavsc.Models
|
||||
|
||||
[JsonIgnore]
|
||||
[InverseProperty("Owner")]
|
||||
public virtual List<BlackListed> BlackList { get; set; }
|
||||
public virtual List<BlackListed>? BlackList { get; set; }
|
||||
|
||||
public bool AllowMonthlyEmail { get; set; } = false;
|
||||
|
||||
[JsonIgnore]
|
||||
[InverseProperty("Owner")]
|
||||
public virtual List<ChatRoom> Rooms { get; set; }
|
||||
public virtual List<ChatRoom>? Rooms { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
[InverseProperty("User")]
|
||||
public virtual List<ChatRoomAccess> RoomAccess { get; set; }
|
||||
public virtual List<ChatRoomAccess>? RoomAccess { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
[InverseProperty("Member")]
|
||||
public virtual List<CircleMember> Membership { get; set; }
|
||||
public virtual List<CircleMember>? Membership { get; set; }
|
||||
|
||||
IAccountBalance? IApplicationUser.AccountBalance => AccountBalance;
|
||||
|
||||
ILocation? IApplicationUser.PostalAddress { get => PostalAddress; }
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,8 @@ using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Newtonsoft.Json;
|
||||
using Yavsc;
|
||||
using Yavsc.Abstract.Identity;
|
||||
using Yavsc.Abstract.Identity.Security;
|
||||
using Yavsc.Attributes.Validation;
|
||||
using Yavsc.Interfaces;
|
||||
@ -14,7 +16,8 @@ using Yavsc.ViewModels.Blog;
|
||||
|
||||
namespace Yavsc.Models.Blog
|
||||
{
|
||||
public class BlogPost : BlogPostInputViewModel, IBlogPost, ICircleAuthorized, ITaggable<long>, IIdentified<long>
|
||||
public class BlogPost : BlogPostBase,
|
||||
IBlogPost, ICircleAuthorized, ITaggable<long>
|
||||
{
|
||||
[Key(), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
[Display(Name="Identifiant du post")]
|
||||
@ -85,7 +88,6 @@ namespace Yavsc.Models.Blog
|
||||
[InverseProperty("Post")]
|
||||
public virtual List<Comment> Comments { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
public string OwnerId => AuthorId;
|
||||
IApplicationUser IBlogPost.Author { get => this.Author; }
|
||||
}
|
||||
}
|
||||
|
15
src/Yavsc.Server/Models/Blog/BlogspotPublication.cs
Normal file
15
src/Yavsc.Server/Models/Blog/BlogspotPublication.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Yavsc.Models.Blog;
|
||||
|
||||
namespace Yavsc.Models
|
||||
{
|
||||
public class BlogSpotPublication
|
||||
{
|
||||
[Key]
|
||||
public long BlogpostId { get; set; }
|
||||
|
||||
[ForeignKey("BlogpostId")]
|
||||
public virtual BlogPost BlogPost{ get; set; }
|
||||
}
|
||||
}
|
@ -60,7 +60,7 @@ namespace Yavsc.Models.Haircut
|
||||
|
||||
[DisplayFormat(ConvertEmptyStringToNull = true, NullDisplayText = "[Pas d'emploi du temps spécifié]")]
|
||||
[Display(Name="Emploi du temps")]
|
||||
public virtual Schedule Schedule { get; set; }
|
||||
public virtual Schedule? Schedule { get; set; }
|
||||
|
||||
[Display(Name="Coupe femme cheveux longs"),DisplayFormat(DataFormatString="{0:C}")]
|
||||
|
||||
|
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; }
|
||||
|
||||
}
|
||||
}
|
@ -28,7 +28,7 @@ namespace Yavsc.Models.Workflow
|
||||
[YaStringLength(512)]
|
||||
[Display(Name = "Code du parent")]
|
||||
[ForeignKey("Parent")]
|
||||
public string ParentCode { get; set; }
|
||||
public string? ParentCode { get; set; }
|
||||
|
||||
[Display(Name = "Activité parent"), JsonIgnore]
|
||||
public virtual Activity Parent { get; set; }
|
||||
@ -41,7 +41,7 @@ namespace Yavsc.Models.Workflow
|
||||
public string Description { get; set; }
|
||||
|
||||
[Display(Name = "Photo")]
|
||||
public string Photo { get; set; }
|
||||
public string? Photo { get; set; }
|
||||
|
||||
[InverseProperty("Context")]
|
||||
[DisplayAttribute(Name = "Services liés")]
|
||||
@ -52,7 +52,7 @@ namespace Yavsc.Models.Workflow
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[DisplayAttribute(Name = "Groupe de modération")]
|
||||
public string ModeratorGroupName { get; set; }
|
||||
public string? ModeratorGroupName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// indice de recherche de cette activité
|
||||
@ -64,7 +64,7 @@ namespace Yavsc.Models.Workflow
|
||||
[DisplayFormatAttribute(DataFormatString="{0}%")]
|
||||
public int Rate { get; set; }
|
||||
[DisplayAttribute(Name = "Classe de paramétrage")]
|
||||
public string SettingsClassName { get; set; }
|
||||
public string? SettingsClassName { get; set; }
|
||||
|
||||
[InverseProperty("Context")]
|
||||
[Display(Name="Formulaires de commande")]
|
||||
@ -77,7 +77,7 @@ namespace Yavsc.Models.Workflow
|
||||
}
|
||||
|
||||
[Display(Name="Createur")]
|
||||
public string UserCreated
|
||||
public string? UserCreated
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
@ -89,7 +89,7 @@ namespace Yavsc.Models.Workflow
|
||||
}
|
||||
|
||||
[Display(Name="Utilisateur ayant modifié le dernier")]
|
||||
public string UserModified
|
||||
public string? UserModified
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
@ -8,10 +8,10 @@ namespace Yavsc.Models.societe.com
|
||||
public class CompanyInfoMessage
|
||||
{
|
||||
public bool success { get; set; }
|
||||
public string errorType { get; set; }
|
||||
public string errorCode { get; set; }
|
||||
public string errorMessage { get; set; }
|
||||
public CompanyInfo result { get; set; }
|
||||
public string? errorType { get; set; }
|
||||
public string? errorCode { get; set; }
|
||||
public string? errorMessage { get; set; }
|
||||
public CompanyInfo? result { get; set; }
|
||||
|
||||
}
|
||||
|
||||
|
13
src/Yavsc.Server/Services/BlogPostEdition.cs
Normal file
13
src/Yavsc.Server/Services/BlogPostEdition.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using Yavsc.Models.Blog;
|
||||
|
||||
public class BlogPostEdition
|
||||
{
|
||||
public string Content { get; internal set; }
|
||||
public string Title { get; internal set; }
|
||||
public string Photo { get; internal set; }
|
||||
|
||||
internal static BlogPostEdition From(BlogPost blog)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
193
src/Yavsc.Server/Services/BlogSpotService.cs
Normal file
193
src/Yavsc.Server/Services/BlogSpotService.cs
Normal file
@ -0,0 +1,193 @@
|
||||
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Yavsc;
|
||||
using Yavsc.Helpers;
|
||||
using Yavsc.Models;
|
||||
using Yavsc.Models.Blog;
|
||||
using Yavsc.Server.Exceptions;
|
||||
using Yavsc.ViewModels.Auth;
|
||||
using Yavsc.ViewModels.Blog;
|
||||
|
||||
public class BlogSpotService
|
||||
{
|
||||
private readonly ApplicationDbContext _context;
|
||||
private readonly IAuthorizationService _authorizationService;
|
||||
|
||||
|
||||
public BlogSpotService(ApplicationDbContext context,
|
||||
IAuthorizationService authorizationService)
|
||||
{
|
||||
_authorizationService = authorizationService;
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public BlogPost Create(string userId, BlogPostEditViewModel blogInput)
|
||||
{
|
||||
BlogPost post = new BlogPost
|
||||
{
|
||||
Title = blogInput.Title,
|
||||
Content = blogInput.Content,
|
||||
Photo = blogInput.Photo,
|
||||
AuthorId = userId
|
||||
};
|
||||
_context.BlogSpot.Add(post);
|
||||
_context.SaveChanges(userId);
|
||||
return post;
|
||||
}
|
||||
public async Task<BlogPostEditViewModel> GetPostForEdition(ClaimsPrincipal user, long blogPostId)
|
||||
{
|
||||
var blog = await _context.BlogSpot.Include(x => x.Author).Include(x => x.ACL).SingleAsync(m => m.Id == blogPostId);
|
||||
var auth = await _authorizationService.AuthorizeAsync(user, blog, new EditPermission());
|
||||
if (!auth.Succeeded)
|
||||
{
|
||||
throw new AuthorizationFailureException(auth);
|
||||
}
|
||||
return BlogPostEditViewModel.From(blog);
|
||||
}
|
||||
|
||||
public async Task<BlogPost> Details(ClaimsPrincipal user, long blogPostId)
|
||||
{
|
||||
|
||||
BlogPost blog = await _context.BlogSpot
|
||||
.Include(p => p.Author)
|
||||
.Include(p => p.Tags)
|
||||
.Include(p => p.Comments)
|
||||
.Include(p => p.ACL)
|
||||
.SingleAsync(m => m.Id == blogPostId);
|
||||
if (blog == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var auth = await _authorizationService.AuthorizeAsync(user, blog, new ReadPermission());
|
||||
if (!auth.Succeeded)
|
||||
{
|
||||
throw new AuthorizationFailureException(auth);
|
||||
}
|
||||
foreach (var c in blog.Comments)
|
||||
{
|
||||
c.Author = _context.Users.First(u => u.Id == c.AuthorId);
|
||||
}
|
||||
return blog;
|
||||
}
|
||||
|
||||
public async Task Modify(ClaimsPrincipal user, BlogPostEditViewModel blogEdit)
|
||||
{
|
||||
var blog = _context.BlogSpot.SingleOrDefault(b => b.Id == blogEdit.Id);
|
||||
Debug.Assert(blog != null);
|
||||
var auth = await _authorizationService.AuthorizeAsync(user, blog, new EditPermission());
|
||||
if (!auth.Succeeded)
|
||||
{
|
||||
throw new AuthorizationFailureException(auth);
|
||||
}
|
||||
blog.Content = blogEdit.Content;
|
||||
blog.Title = blogEdit.Title;
|
||||
blog.Photo = blogEdit.Photo;
|
||||
blog.ACL = blogEdit.ACL;
|
||||
// saves the change
|
||||
_context.Update(blog);
|
||||
var publication = await _context.blogSpotPublications.SingleOrDefaultAsync
|
||||
(p=>p.BlogpostId==blogEdit.Id);
|
||||
if (publication != null)
|
||||
{
|
||||
if (!blogEdit.Publish)
|
||||
{
|
||||
_context.blogSpotPublications.Remove(publication);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (blogEdit.Publish)
|
||||
{
|
||||
_context.blogSpotPublications.Add(
|
||||
new BlogSpotPublication
|
||||
{
|
||||
BlogpostId = blogEdit.Id
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
_context.SaveChanges(user.GetUserId());
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<IBlogPost>> Index(ClaimsPrincipal user, string id, int skip = 0, int take = 25)
|
||||
{
|
||||
IEnumerable<IBlogPost> posts;
|
||||
|
||||
if (user.Identity.IsAuthenticated)
|
||||
{
|
||||
string viewerId = user.GetUserId();
|
||||
long[] userCircles = await _context.Circle.Include(c => c.Members).
|
||||
Where(c => c.Members.Any(m => m.MemberId == viewerId))
|
||||
.Select(c => c.Id).ToArrayAsync();
|
||||
|
||||
posts = _context.BlogSpot
|
||||
.Include(b => b.Author)
|
||||
.Include(p => p.ACL)
|
||||
.Include(p => p.Tags)
|
||||
.Include(p => p.Comments)
|
||||
.Where(p => p.ACL == null
|
||||
|| p.ACL.Count == 0
|
||||
|| (p.AuthorId == viewerId)
|
||||
|| (userCircles != null &&
|
||||
p.ACL.Any(a => userCircles.Contains(a.CircleId)))
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
posts = _context.blogSpotPublications
|
||||
.Include(p => p.BlogPost)
|
||||
.Include(b => b.BlogPost.Author)
|
||||
.Include(p => p.BlogPost.ACL)
|
||||
.Include(p => p.BlogPost.Tags)
|
||||
.Include(p => p.BlogPost.Comments)
|
||||
.Where(p => p.BlogPost.ACL == null
|
||||
|| p.BlogPost.ACL.Count == 0)
|
||||
.Select(p => p.BlogPost).ToArray();
|
||||
}
|
||||
|
||||
var data = posts.OrderByDescending(p => p.DateModified);
|
||||
return data;
|
||||
}
|
||||
|
||||
public async Task Delete(ClaimsPrincipal user, long id)
|
||||
{
|
||||
var uid = user.GetUserId();
|
||||
BlogPost blog = _context.BlogSpot.Single(m => m.Id == id);
|
||||
|
||||
_context.BlogSpot.Remove(blog);
|
||||
_context.SaveChanges(user.GetUserId());
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<BlogPost>> UserPosts(
|
||||
string posterName,
|
||||
string? readerId,
|
||||
int pageLen = 10,
|
||||
int pageNum = 0)
|
||||
{
|
||||
string? posterId = (await _context.Users.SingleOrDefaultAsync(u => u.UserName == posterName))?.Id ?? null;
|
||||
if (posterId == null) return Array.Empty<BlogPost>();
|
||||
return _context.UserPosts(posterId, readerId);
|
||||
}
|
||||
|
||||
public object? GetTitle(string title)
|
||||
{
|
||||
return _context.BlogSpot.Include(
|
||||
b => b.Author
|
||||
).Where(x => x.Title == title).OrderByDescending(
|
||||
x => x.DateCreated
|
||||
).ToList();
|
||||
}
|
||||
|
||||
public async Task<BlogPost?> GetBlogPostAsync(long value)
|
||||
{
|
||||
return await _context.BlogSpot
|
||||
.Include(b => b.Author)
|
||||
.Include(b => b.ACL)
|
||||
.SingleOrDefaultAsync(x => x.Id == value);
|
||||
}
|
||||
|
||||
}
|
@ -130,7 +130,6 @@ namespace Yavsc.Services
|
||||
public bool Part(string cxId, string roomName, string reason)
|
||||
{
|
||||
ChatRoomInfo chanInfo;
|
||||
var userName = ChatUserNames[cxId];
|
||||
if (Channels.TryGetValue(roomName, out chanInfo))
|
||||
{
|
||||
if (!chanInfo.Users.Contains(cxId))
|
||||
|
@ -1,13 +1,9 @@
|
||||
using System.Text;
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using MailKit.Net.Smtp;
|
||||
using MailKit.Security;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using MimeKit;
|
||||
using Yavsc.Abstract.Manage;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Yavsc.Interface;
|
||||
using Yavsc.Settings;
|
||||
@ -27,8 +23,7 @@ namespace Yavsc.Services
|
||||
public MailSender(
|
||||
IOptions<SiteSettings> sitesOptions,
|
||||
IOptions<SmtpSettings> smtpOptions,
|
||||
ILoggerFactory loggerFactory,
|
||||
IStringLocalizer<Yavsc.YavscLocalization> localizer
|
||||
ILoggerFactory loggerFactory
|
||||
)
|
||||
{
|
||||
this.localizer = localizer;
|
||||
|
@ -57,18 +57,18 @@ namespace Yavsc.Services
|
||||
var roles = await this._userManager.GetRolesAsync(user);
|
||||
if (roles.Count()>0)
|
||||
{
|
||||
claims.AddRange(roles.Select(r => new Claim(Constants.RoleClaimName, r)));
|
||||
claims.AddRange(roles.Select(r => new Claim(JwtClaimTypes.Role, r)));
|
||||
}
|
||||
}
|
||||
return claims;
|
||||
}
|
||||
|
||||
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
|
||||
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
|
||||
{
|
||||
var subjectId = GetSubjectId(context.Subject);
|
||||
if (subjectId==null) return;
|
||||
if (subjectId == null) return;
|
||||
var user = await _userManager.FindByIdAsync(subjectId);
|
||||
if (user==null) return ;
|
||||
if (user == null) return;
|
||||
context.IssuedClaims = await GetClaimsFromUserAsync(context, user);
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,9 @@ namespace Yavsc
|
||||
public class SiteSettings
|
||||
{
|
||||
public string Title { get; set; } = "Yavsc";
|
||||
|
||||
public string Slogan { get; set; } = "";
|
||||
public string Banner { get; set; } = "";
|
||||
|
||||
public string StyleSheet { get; set; } = "site.css";
|
||||
public string FavIcon { get; set; } = "favicon.ico";
|
||||
|
@ -5,8 +5,8 @@ using Yavsc.Models.Access;
|
||||
|
||||
namespace Yavsc.ViewModels.Blog
|
||||
{
|
||||
public class BlogPostInputViewModel
|
||||
{
|
||||
public class BlogPostBase
|
||||
{
|
||||
[StringLength(1024)]
|
||||
public string? Photo { get; set; }
|
||||
|
||||
@ -18,8 +18,8 @@ namespace Yavsc.ViewModels.Blog
|
||||
public string? Content { get; set; }
|
||||
|
||||
[InverseProperty("Target")]
|
||||
[Display(Name="Liste de contrôle d'accès")]
|
||||
public virtual List<CircleAuthorizationToBlogPost>? ACL { get; set; }
|
||||
[Display(Name = "Liste de contrôle d'accès")]
|
||||
public virtual List<CircleAuthorizationToBlogPost>? ACL { get; set; }
|
||||
|
||||
|
||||
}
|
@ -1,11 +1,50 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Yavsc.Models.Blog;
|
||||
|
||||
namespace Yavsc.ViewModels.Blog;
|
||||
|
||||
public class BlogPostEditViewModel : BlogPostInputViewModel
|
||||
|
||||
public class BlogPostCreateViewModel : BlogPostBase
|
||||
{
|
||||
public bool Publish { get; set; }
|
||||
}
|
||||
|
||||
public class BlogPostEditViewModel : BlogPostCreateViewModel
|
||||
{
|
||||
|
||||
[Required]
|
||||
|
||||
public required long Id { get; set; }
|
||||
public required long Id { get; set; }
|
||||
|
||||
|
||||
public BlogPostEditViewModel()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static BlogPostEditViewModel From(BlogPost blogInput)
|
||||
{
|
||||
return new BlogPostEditViewModel
|
||||
{
|
||||
Id = blogInput.Id,
|
||||
Title = blogInput.Title,
|
||||
Publish = false,
|
||||
Photo = blogInput.Photo,
|
||||
Content = blogInput.Content,
|
||||
ACL = blogInput.ACL
|
||||
};
|
||||
}
|
||||
public static BlogPostEditViewModel FromViewModel(BlogPostEditViewModel blogInput)
|
||||
{
|
||||
return new BlogPostEditViewModel
|
||||
{
|
||||
Id = blogInput.Id,
|
||||
Title = blogInput.Title,
|
||||
Publish = false,
|
||||
Photo = blogInput.Photo,
|
||||
Content = blogInput.Content,
|
||||
ACL = blogInput.ACL
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
|
||||
<PackageReference Include="HigginsSoft.IdentityServer8" Version="8.0.4" />
|
||||
<PackageReference Include="HigginsSoft.IdentityServer8" Version="8.0.5-preview-net9" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.6" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="9.0.6" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Http.Features" Version="5.0.17" />
|
||||
@ -19,20 +19,14 @@
|
||||
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.3.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.6" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="9.0.0" />
|
||||
<PackageReference Include="Google.Apis.Calendar.v3" Version="1.60.0.2993" />
|
||||
<PackageReference Include="Google.Apis.Calendar.v3" Version="1.69.0.3746" />
|
||||
<PackageReference Include="PayPalMerchantSDK" Version="2.16.250" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.9" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.10" />
|
||||
<PackageReference Include="RazorEngine.NetCore" Version="3.1.0" />
|
||||
<PackageReference Include="MailKit" Version="4.12.1" />
|
||||
<PackageReference Include="MimeKit" Version="4.12.0" />
|
||||
<PackageReference Include="MailKit" Version="4.13.0" />
|
||||
<PackageReference Include="MimeKit" Version="4.13.0" />
|
||||
<PackageReference Include="pazof.rules" Version="1.1.3" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System.ComponentModel.DataAnnotations" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Net" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../Yavsc.Abstract/Yavsc.Abstract.csproj" />
|
||||
</ItemGroup>
|
||||
|
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);
|
||||
}
|
@ -1,22 +1,15 @@
|
||||
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Yavsc.Models;
|
||||
using Yavsc.ViewModels.Auth;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Yavsc.Models.Blog;
|
||||
using Yavsc.Helpers;
|
||||
using Microsoft.AspNetCore.Localization;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Diagnostics;
|
||||
using Yavsc.ViewModels.Blog;
|
||||
using System.Collections;
|
||||
using Yavsc.Server.Exceptions;
|
||||
|
||||
// For more information on enabling Web API for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860
|
||||
|
||||
@ -29,111 +22,70 @@ namespace Yavsc.Controllers
|
||||
private readonly IAuthorizationService _authorizationService;
|
||||
readonly RequestLocalizationOptions _localisationOptions;
|
||||
|
||||
readonly BlogSpotService blogSpotService;
|
||||
public BlogspotController(
|
||||
ApplicationDbContext context,
|
||||
ILoggerFactory loggerFactory,
|
||||
IAuthorizationService authorizationService,
|
||||
IOptions<RequestLocalizationOptions> localisationOptions)
|
||||
IOptions<RequestLocalizationOptions> localisationOptions,
|
||||
BlogSpotService blogSpotService)
|
||||
{
|
||||
_context = context;
|
||||
_logger = loggerFactory.CreateLogger<AccountController>();
|
||||
_authorizationService = authorizationService;
|
||||
_localisationOptions = localisationOptions.Value;
|
||||
this.blogSpotService = blogSpotService;
|
||||
}
|
||||
|
||||
// GET: Blog
|
||||
[AllowAnonymous]
|
||||
public async Task<IActionResult> Index(string id, int skip=0, int take=25)
|
||||
public async Task<IActionResult> Index(string id, int skip = 0, int take = 25)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(id)) {
|
||||
return View("UserPosts", await UserPosts(id));
|
||||
}
|
||||
IEnumerable<BlogPost> posts;
|
||||
|
||||
if (User.Identity.IsAuthenticated)
|
||||
if (!string.IsNullOrEmpty(id))
|
||||
{
|
||||
string viewerId = User.GetUserId();
|
||||
long[] usercircles = await _context.Circle.Include(c=>c.Members).
|
||||
Where(c=>c.Members.Any(m=>m.MemberId == viewerId))
|
||||
.Select(c=>c.Id).ToArrayAsync();
|
||||
|
||||
posts = _context.BlogSpot
|
||||
.Include(b => b.Author)
|
||||
.Include(p=>p.ACL)
|
||||
.Include(p=>p.Tags)
|
||||
.Include(p=>p.Comments)
|
||||
.Where(p =>(p.ACL.Count == 0)
|
||||
|| (p.AuthorId == viewerId)
|
||||
|| (usercircles != null && p.ACL.Any(a => usercircles.Contains(a.CircleId)))
|
||||
);
|
||||
return View("UserPosts",
|
||||
await blogSpotService.UserPosts(id, User.GetUserId(),
|
||||
skip, take));
|
||||
}
|
||||
else
|
||||
{
|
||||
posts = _context.BlogSpot
|
||||
.Include(b => b.Author)
|
||||
.Include(p=>p.ACL)
|
||||
.Include(p=>p.Tags)
|
||||
.Include(p=>p.Comments)
|
||||
.Where(p => p.ACL.Count == 0 ).ToArray();
|
||||
}
|
||||
|
||||
var data = posts.OrderByDescending( p=> p.DateCreated);
|
||||
var grouped = data.GroupBy(p=> p.Title).Skip(skip).Take(take);
|
||||
|
||||
return View(grouped);
|
||||
IEnumerable<IBlogPost> index = await this.blogSpotService.Index(User, id, skip, take);
|
||||
return View(index);
|
||||
}
|
||||
|
||||
[Route("~/Title/{id?}")]
|
||||
[AllowAnonymous]
|
||||
public IActionResult Title(string id)
|
||||
{
|
||||
var uid = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
ViewData["Title"] = id;
|
||||
return View("Title", _context.BlogSpot.Include(
|
||||
b => b.Author
|
||||
).Where(x => x.Title == id && (x.AuthorId == uid )).OrderByDescending(
|
||||
x => x.DateCreated
|
||||
).ToList());
|
||||
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)
|
||||
{
|
||||
string posterId = (await _context.Users.SingleOrDefaultAsync(u=>u.UserName == userName))?.Id ?? null ;
|
||||
return _context.UserPosts(posterId, User.Identity.Name);
|
||||
return await blogSpotService.UserPosts(userName, User.GetUserId(), pageLen, pageNum);
|
||||
|
||||
}
|
||||
// GET: Blog/Details/5
|
||||
[AllowAnonymous]
|
||||
public async Task<IActionResult> Details(long? id)
|
||||
{
|
||||
if (id == null)
|
||||
if (id == null) return this.NotFound();
|
||||
try
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
var blog = await blogSpotService.Details(User, id.Value);
|
||||
ViewData["apicmtctlr"] = "/api/blogcomments";
|
||||
ViewData["moderatoFlag"] = User.IsInRole(Constants.BlogModeratorGroupName);
|
||||
|
||||
BlogPost blog = _context.BlogSpot
|
||||
.Include(p => p.Author)
|
||||
.Include(p => p.Tags)
|
||||
.Include(p => p.Comments)
|
||||
.Include(p => p.ACL)
|
||||
.Single(m => m.Id == id);
|
||||
if (blog == null)
|
||||
return View(blog);
|
||||
|
||||
}
|
||||
catch (AuthorizationFailureException ex)
|
||||
{
|
||||
return NotFound();
|
||||
return Challenge();
|
||||
}
|
||||
if ( _authorizationService.AuthorizeAsync(User, blog, new ReadPermission()).IsFaulted)
|
||||
{
|
||||
return new ChallengeResult();
|
||||
}
|
||||
foreach (var c in blog.Comments) {
|
||||
c.Author = _context.Users.First(u=>u.Id==c.AuthorId);
|
||||
}
|
||||
ViewData["apicmtctlr"] = "/api/blogcomments";
|
||||
ViewData["moderatoFlag"] = User.IsInRole(Constants.BlogModeratorGroupName);
|
||||
return View(blog);
|
||||
}
|
||||
void SetLangItems()
|
||||
{
|
||||
ViewBag.LangItems = _localisationOptions.SupportedUICultures.Select
|
||||
ViewBag.LangItems = _localisationOptions.SupportedUICultures?.Select
|
||||
(
|
||||
sc => new SelectListItem { Value = sc.IetfLanguageTag, Text = sc.NativeName, Selected = System.Globalization.CultureInfo.CurrentUICulture == sc }
|
||||
);
|
||||
@ -143,33 +95,25 @@ namespace Yavsc.Controllers
|
||||
[Authorize()]
|
||||
public IActionResult Create(string title)
|
||||
{
|
||||
var result = new BlogPostInputViewModel{Title=title
|
||||
var result = new BlogPostCreateViewModel
|
||||
{
|
||||
Title = title
|
||||
};
|
||||
ViewData["PostTarget"]="Create";
|
||||
SetLangItems();
|
||||
return View(result);
|
||||
}
|
||||
|
||||
// POST: Blog/Create
|
||||
[HttpPost, Authorize, ValidateAntiForgeryToken]
|
||||
public IActionResult Create(BlogPostInputViewModel blogInput)
|
||||
public IActionResult Create(BlogPostEditViewModel blogInput)
|
||||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
BlogPost post = new BlogPost
|
||||
{
|
||||
Title = blogInput.Title,
|
||||
Content = blogInput.Content,
|
||||
Photo = blogInput.Photo,
|
||||
AuthorId = User.GetUserId()
|
||||
};
|
||||
_context.BlogSpot.Add(post);
|
||||
_context.SaveChanges(User.GetUserId());
|
||||
BlogPost post = blogSpotService.Create(User.GetUserId(),
|
||||
BlogPostEditViewModel.FromViewModel(blogInput));
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
ModelState.AddModelError("Unknown","Invalid Blog posted ...");
|
||||
ViewData["PostTarget"]="Create";
|
||||
return View("Edit",blogInput);
|
||||
return View("Edit", blogInput);
|
||||
}
|
||||
|
||||
[Authorize()]
|
||||
@ -180,37 +124,18 @@ namespace Yavsc.Controllers
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
ViewData["PostTarget"]="Edit";
|
||||
BlogPost blog = _context.BlogSpot.Include(x => x.Author).Include(x => x.ACL).Single(m => m.Id == id);
|
||||
|
||||
if (blog == null)
|
||||
try
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
if (!_authorizationService.AuthorizeAsync(User, blog, new EditPermission()).IsFaulted)
|
||||
{
|
||||
ViewBag.ACL = _context.Circle.Where(
|
||||
c=>c.OwnerId == blog.AuthorId)
|
||||
.Select(
|
||||
c => new SelectListItem
|
||||
{
|
||||
Text = c.Name,
|
||||
Value = c.Id.ToString(),
|
||||
Selected = blog.AuthorizeCircle(c.Id)
|
||||
}
|
||||
);
|
||||
SetLangItems();
|
||||
return View(new BlogPostEditViewModel
|
||||
var blog = await blogSpotService.GetPostForEdition(User, id.Value);
|
||||
if (blog == null)
|
||||
{
|
||||
Id = blog.Id,
|
||||
Title = blog.Title,
|
||||
Content = blog.Content,
|
||||
ACL = blog.ACL,
|
||||
Photo = blog.Photo
|
||||
});
|
||||
return NotFound();
|
||||
}
|
||||
SetLangItems();
|
||||
return View(blog);
|
||||
|
||||
}
|
||||
else
|
||||
catch (AuthorizationFailureException)
|
||||
{
|
||||
return new ChallengeResult();
|
||||
}
|
||||
@ -218,65 +143,41 @@ namespace Yavsc.Controllers
|
||||
|
||||
// POST: Blog/Edit/5
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken,Authorize()]
|
||||
[ValidateAntiForgeryToken, Authorize()]
|
||||
public async Task<IActionResult> Edit(BlogPostEditViewModel blogEdit)
|
||||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
var blog = _context.BlogSpot.SingleOrDefault(b=>b.Id == blogEdit.Id);
|
||||
if (blog == null) {
|
||||
ModelState.AddModelError("Id", "not found");
|
||||
return View();
|
||||
}
|
||||
if (!(await _authorizationService.AuthorizeAsync(User, blog, new EditPermission())).Succeeded) {
|
||||
ViewData["StatusMessage"] = "Accès restreint";
|
||||
return new ChallengeResult();
|
||||
}
|
||||
blog.Content=blogEdit.Content;
|
||||
blog.Title = blogEdit.Title;
|
||||
blog.Photo = blogEdit.Photo;
|
||||
blog.ACL = blogEdit.ACL;
|
||||
// saves the change
|
||||
_context.Update(blog);
|
||||
_context.SaveChanges(User.GetUserId());
|
||||
await blogSpotService.Modify(User, blogEdit);
|
||||
ViewData["StatusMessage"] = "Post modified";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
ViewData["PostTarget"]="Edit";
|
||||
return View(blogEdit);
|
||||
}
|
||||
|
||||
// GET: Blog/Delete/5
|
||||
[ActionName("Delete"),Authorize()]
|
||||
public IActionResult Delete(long? id)
|
||||
[ActionName("Delete"), Authorize()]
|
||||
public async Task<IActionResult> Delete(long? id)
|
||||
{
|
||||
if (id == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
BlogPost blog = _context.BlogSpot.Include(
|
||||
b => b.Author
|
||||
).Single(m => m.Id == id);
|
||||
var blog = await blogSpotService.GetBlogPostAsync(id.Value);
|
||||
if (blog == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return View(blog);
|
||||
}
|
||||
|
||||
// POST: Blog/Delete/5
|
||||
[HttpPost, ActionName("Delete"), Authorize("IsTheAuthor")]
|
||||
[HttpPost, ActionName("Delete"), Authorize("TheAuthor")]
|
||||
[ValidateAntiForgeryToken]
|
||||
public IActionResult DeleteConfirmed(long id)
|
||||
public async Task<IActionResult> DeleteConfirmed(long id)
|
||||
{
|
||||
var uid = User.GetUserId();
|
||||
BlogPost blog = _context.BlogSpot.Single(m => m.Id == id);
|
||||
|
||||
_context.BlogSpot.Remove(blog);
|
||||
_context.SaveChanges(User.GetUserId());
|
||||
|
||||
await blogSpotService.Delete(User, id);
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
}
|
||||
|
@ -33,13 +33,337 @@ using Yavsc.Server.Helpers;
|
||||
using System.Security.Cryptography;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using Microsoft.IdentityModel.Protocols.Configuration;
|
||||
using IdentityModel;
|
||||
using System.Security.Claims;
|
||||
using IdentityServer8.Security;
|
||||
using Yavsc.Interfaces;
|
||||
|
||||
namespace Yavsc.Extensions;
|
||||
|
||||
|
||||
public static class HostingExtensions
|
||||
{
|
||||
#region files config
|
||||
|
||||
internal static WebApplication ConfigureWebAppServices(this WebApplicationBuilder builder)
|
||||
{
|
||||
IServiceCollection services = LoadConfiguration(builder);
|
||||
|
||||
services.AddSession();
|
||||
|
||||
// TODO .AddServerSideSessionStore<YavscServerSideSessionStore>()
|
||||
|
||||
|
||||
// Add the system clock service
|
||||
_ = services.AddSingleton<ISystemClock, SystemClock>();
|
||||
_ = services.AddSingleton<IConnexionManager, HubConnectionManager>();
|
||||
_ = services.AddSingleton<ILiveProcessor, LiveProcessor>();
|
||||
_ = services.AddTransient<IFileSystemAuthManager, FileSystemAuthManager>();
|
||||
|
||||
AddIdentityDBAndStores(builder).AddDefaultTokenProviders();
|
||||
AddIdentityServer(builder);
|
||||
|
||||
services.AddSignalR(o =>
|
||||
{
|
||||
o.EnableDetailedErrors = true;
|
||||
});
|
||||
|
||||
services.AddMvc(config =>
|
||||
{
|
||||
/* var policy = new AuthorizationPolicyBuilder()
|
||||
.RequireAuthenticatedUser()
|
||||
.Build();
|
||||
config.Filters.Add(new AuthorizeFilter(policy)); */
|
||||
config.Filters.Add(new ProducesAttribute("application/json"));
|
||||
// config.ModelBinders.Insert(0,new MyDateTimeModelBinder());
|
||||
// config.ModelBinders.Insert(0,new MyDecimalModelBinder());
|
||||
config.EnableEndpointRouting = true;
|
||||
}).AddFormatterMappings(
|
||||
config => config.SetMediaTypeMappingForFormat("text/pdf",
|
||||
new MediaTypeHeaderValue("text/pdf"))
|
||||
).AddFormatterMappings(
|
||||
config => config.SetMediaTypeMappingForFormat("text/x-tex",
|
||||
new MediaTypeHeaderValue("text/x-tex"))
|
||||
)
|
||||
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix,
|
||||
options =>
|
||||
{
|
||||
options.ResourcesPath = "Resources";
|
||||
}).AddDataAnnotationsLocalization();
|
||||
|
||||
services.AddTransient<ITrueEmailSender, MailSender>()
|
||||
.AddTransient<Microsoft.AspNetCore.Identity.UI.Services.IEmailSender, MailSender>()
|
||||
.AddTransient<IYavscMessageSender, YavscMessageSender>()
|
||||
.AddTransient<IBillingService, BillingService>()
|
||||
.AddTransient<IDataStore, FileDataStore>((sp) => new FileDataStore("googledatastore", false))
|
||||
.AddTransient<ICalendarManager, CalendarManager>()
|
||||
.AddTransient<BlogSpotService>();
|
||||
|
||||
// TODO for SMS: services.AddTransient<ISmsSender, AuthMessageSender>();
|
||||
|
||||
_ = services.AddLocalization(options =>
|
||||
{
|
||||
options.ResourcesPath = "Resources";
|
||||
});
|
||||
var dataDirConfig = builder.Configuration["Site:DataDir"] ?? "DataDir";
|
||||
|
||||
var dataDir = new DirectoryInfo(dataDirConfig);
|
||||
// Add session related services.
|
||||
|
||||
services.AddDataProtection().PersistKeysToFileSystem(dataDir);
|
||||
AddYavscPolicies(services);
|
||||
|
||||
services.AddScoped<IAuthorizationHandler, PermissionHandler>();
|
||||
services.AddTransient<IExternalIdentityManager, ExternalIdentityManager>();
|
||||
|
||||
|
||||
AddAuthentication(builder);
|
||||
// accepts any access token issued by identity server
|
||||
|
||||
return builder.Build();
|
||||
}
|
||||
|
||||
public static IdentityBuilder AddIdentityDBAndStores(this WebApplicationBuilder builder)
|
||||
{
|
||||
IServiceCollection services = builder.Services;
|
||||
services.AddDbContext<ApplicationDbContext>(options =>
|
||||
options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));
|
||||
|
||||
return services.AddIdentity<ApplicationUser, IdentityRole>()
|
||||
.AddEntityFrameworkStores<ApplicationDbContext>();
|
||||
|
||||
}
|
||||
|
||||
private static void AddYavscPolicies(IServiceCollection services)
|
||||
{
|
||||
services.AddAuthorization(options =>
|
||||
{
|
||||
options.AddPolicy("ApiScope", policy =>
|
||||
{
|
||||
policy.RequireAuthenticatedUser()
|
||||
.RequireClaim("scope", "scope2");
|
||||
});
|
||||
options.AddPolicy("Performer", policy =>
|
||||
{
|
||||
policy
|
||||
.RequireAuthenticatedUser()
|
||||
.RequireClaim(JwtClaimTypes.Role, "Performer");
|
||||
});
|
||||
options.AddPolicy("AdministratorOnly", policy =>
|
||||
{
|
||||
_ = policy
|
||||
.RequireAuthenticatedUser()
|
||||
.RequireClaim(JwtClaimTypes.Role, Constants.AdminGroupName);
|
||||
});
|
||||
|
||||
options.AddPolicy("FrontOffice", policy => policy.RequireRole(Constants.FrontOfficeGroupName));
|
||||
|
||||
// options.AddPolicy("EmployeeId", policy => policy.RequireClaim("EmployeeId", "123", "456"));
|
||||
// options.AddPolicy("BuildingEntry", policy => policy.Requirements.Add(new OfficeEntryRequirement()));
|
||||
options.AddPolicy("Authenticated", policy => policy.RequireAuthenticatedUser());
|
||||
options.AddPolicy("TheAuthor", policy => policy.Requirements.Add(new EditPermission()));
|
||||
})
|
||||
.AddCors(options =>
|
||||
{
|
||||
options.AddPolicy("default", builder =>
|
||||
{
|
||||
_ = builder.WithOrigins("*")
|
||||
.AllowAnyHeader()
|
||||
.AllowAnyMethod();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public static IServiceCollection LoadConfiguration(this WebApplicationBuilder builder)
|
||||
{
|
||||
var siteSection = builder.Configuration.GetSection("Site");
|
||||
var smtpSection = builder.Configuration.GetSection("Smtp");
|
||||
var paypalSection = builder.Configuration.GetSection("Authentication:PayPal");
|
||||
// OAuth2AppSettings
|
||||
var googleAuthSettings = builder.Configuration.GetSection("Authentication:Google");
|
||||
|
||||
//LoadGoogleConfig(builder.Configuration);
|
||||
|
||||
|
||||
var services = builder.Services;
|
||||
_ = services.AddControllersWithViews()
|
||||
.AddNewtonsoftJson();
|
||||
|
||||
services.Configure<SiteSettings>(siteSection);
|
||||
services.Configure<SmtpSettings>(smtpSection);
|
||||
services.Configure<PayPalSettings>(paypalSection);
|
||||
services.Configure<GoogleAuthSettings>(googleAuthSettings);
|
||||
ConfigureRequestLocalization(services);
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
private static void AddAuthentication(WebApplicationBuilder builder)
|
||||
{
|
||||
IServiceCollection services=builder.Services;
|
||||
IConfigurationRoot configurationRoot=builder.Configuration;
|
||||
string? googleClientId = configurationRoot["Authentication:Google:ClientId"];
|
||||
string? googleClientSecret = configurationRoot["Authentication:Google:ClientSecret"];
|
||||
|
||||
var authenticationBuilder = services.AddAuthentication();
|
||||
|
||||
if (googleClientId!=null && googleClientSecret!=null)
|
||||
authenticationBuilder.AddGoogle(options =>
|
||||
{
|
||||
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
|
||||
|
||||
// register your IdentityServer with Google at https://console.developers.google.com
|
||||
// enable the Google+ API
|
||||
// set the redirect URI to https://localhost:5001/signin-google
|
||||
options.ClientId = googleClientId;
|
||||
options.ClientSecret = googleClientSecret;
|
||||
|
||||
});
|
||||
}
|
||||
private static IIdentityServerBuilder AddIdentityServer(WebApplicationBuilder builder)
|
||||
{
|
||||
var identityServerBuilder = builder.Services.AddIdentityServer(options =>
|
||||
{
|
||||
options.Events.RaiseErrorEvents = true;
|
||||
options.Events.RaiseInformationEvents = true;
|
||||
options.Events.RaiseFailureEvents = true;
|
||||
options.Events.RaiseSuccessEvents = true;
|
||||
|
||||
// see https://IdentityServer8.readthedocs.io/en/latest/topics/resources.html
|
||||
options.EmitStaticAudienceClaim = true;
|
||||
})
|
||||
.AddInMemoryIdentityResources(Config.IdentityResources)
|
||||
.AddInMemoryClients(Config.Clients)
|
||||
.AddInMemoryApiScopes(Config.ApiScopes)
|
||||
.AddAspNetIdentity<ApplicationUser>()
|
||||
;
|
||||
if (builder.Environment.IsDevelopment())
|
||||
{
|
||||
identityServerBuilder.AddDeveloperSigningCredential();
|
||||
}
|
||||
else
|
||||
{
|
||||
var path = builder.Configuration["SigningCert:Path"];
|
||||
var pass = builder.Configuration["SigningCert:Password"];
|
||||
if (path == null)
|
||||
throw new InvalidConfigurationException("No signing cert path");
|
||||
FileInfo certFileInfo = new FileInfo(path);
|
||||
Debug.Assert(certFileInfo.Exists);
|
||||
RSA rsa = RSA.Create();
|
||||
rsa.ImportFromPem(File.ReadAllText(certFileInfo.FullName));
|
||||
var signingCredentials = new SigningCredentials(new RsaSecurityKey(rsa), SecurityAlgorithms.RsaSha256)
|
||||
{
|
||||
CryptoProviderFactory = new CryptoProviderFactory { CacheSignatureProviders = false }
|
||||
};
|
||||
identityServerBuilder.AddSigningCredential(signingCredentials);
|
||||
}
|
||||
return identityServerBuilder;
|
||||
}
|
||||
|
||||
private static void ConfigureRequestLocalization(IServiceCollection services)
|
||||
{
|
||||
services.Configure<RequestLocalizationOptions>(options =>
|
||||
{
|
||||
CultureInfo[] supportedCultures = new[]
|
||||
{
|
||||
new CultureInfo("en"),
|
||||
new CultureInfo("fr"),
|
||||
new CultureInfo("pt")
|
||||
};
|
||||
|
||||
CultureInfo[] supportedUICultures = new[]
|
||||
{
|
||||
new CultureInfo("fr"),
|
||||
new CultureInfo("en"),
|
||||
new CultureInfo("pt")
|
||||
};
|
||||
|
||||
// You must explicitly state which cultures your application supports.
|
||||
// These are the cultures the app supports for formatting numbers, dates, etc.
|
||||
options.SupportedCultures = supportedCultures;
|
||||
|
||||
// These are the cultures the app supports for UI strings, i.e. we have localized resources for.
|
||||
options.SupportedUICultures = supportedUICultures;
|
||||
|
||||
options.RequestCultureProviders = new List<IRequestCultureProvider>
|
||||
{
|
||||
new QueryStringRequestCultureProvider { Options = options },
|
||||
new CookieRequestCultureProvider { Options = options, CookieName="ASPNET_CULTURE" },
|
||||
new AcceptLanguageHeaderRequestCultureProvider { Options = options }
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
internal async static Task<WebApplication> ConfigurePipeline(this WebApplication app)
|
||||
{
|
||||
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
else
|
||||
{
|
||||
app.UseExceptionHandler("/Home/Error");
|
||||
using (var scope = app.Services.CreateScope())
|
||||
{
|
||||
var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
||||
await db.Database.MigrateAsync();
|
||||
}
|
||||
}
|
||||
app.Use(async (context, next) => {
|
||||
if (context.Request.Path.StartsWithSegments("/robots.txt")) {
|
||||
var robotsTxtPath = System.IO.Path.Combine(app.Environment.WebRootPath, $"robots.txt");
|
||||
string output = "User-agent: * \nDisallow: /";
|
||||
if (File.Exists(robotsTxtPath)) {
|
||||
output = await File.ReadAllTextAsync(robotsTxtPath);
|
||||
}
|
||||
context.Response.ContentType = "text/plain";
|
||||
await context.Response.WriteAsync(output);
|
||||
} else await next();
|
||||
});
|
||||
app.UseStaticFiles();
|
||||
app.UseRouting();
|
||||
app.UseIdentityServer();
|
||||
app.UseAuthorization();
|
||||
app.UseCors("default");
|
||||
app.MapDefaultControllerRoute();
|
||||
//app.MapRazorPages();
|
||||
app.MapHub<ChatHub>("/chatHub");
|
||||
|
||||
WorkflowHelpers.ConfigureBillingService();
|
||||
|
||||
var services = app.Services;
|
||||
ILoggerFactory loggerFactory = services.GetRequiredService<ILoggerFactory>();
|
||||
var siteSettings = services.GetRequiredService<IOptions<SiteSettings>>();
|
||||
var smtpSettings = services.GetRequiredService<IOptions<SmtpSettings>>();
|
||||
var payPalSettings = services.GetRequiredService<IOptions<PayPalSettings>>();
|
||||
var googleAuthSettings = services.GetRequiredService<IOptions<GoogleAuthSettings>>();
|
||||
var localization = services.GetRequiredService<IStringLocalizer<YavscLocalization>>();
|
||||
Startup.Configure(app, siteSettings, smtpSettings,
|
||||
payPalSettings, googleAuthSettings, localization, loggerFactory,
|
||||
app.Environment.EnvironmentName);
|
||||
app.ConfigureFileServerApp();
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
static void LoadGoogleConfig(IConfigurationRoot configuration)
|
||||
{
|
||||
string? googleClientFile = configuration["Authentication:Google:GoogleWebClientJson"];
|
||||
string? googleServiceAccountJsonFile = 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());
|
||||
}
|
||||
}
|
||||
|
||||
public static IApplicationBuilder ConfigureFileServerApp(this IApplicationBuilder app,
|
||||
bool enableDirectoryBrowsing = false)
|
||||
{
|
||||
@ -91,321 +415,5 @@ public static class HostingExtensions
|
||||
return app;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
internal static WebApplication ConfigureWebAppServices(this WebApplicationBuilder builder)
|
||||
{
|
||||
IServiceCollection services = LoadConfiguration(builder);
|
||||
|
||||
//services.AddRazorPages();
|
||||
|
||||
|
||||
services.AddSession();
|
||||
|
||||
// TODO .AddServerSideSessionStore<YavscServerSideSessionStore>()
|
||||
|
||||
|
||||
// Add the system clock service
|
||||
_ = services.AddSingleton<ISystemClock, SystemClock>();
|
||||
_ = services.AddSingleton<IConnexionManager, HubConnectionManager>();
|
||||
_ = services.AddSingleton<ILiveProcessor, LiveProcessor>();
|
||||
_ = services.AddTransient<IFileSystemAuthManager, FileSystemAuthManager>();
|
||||
|
||||
AddIdentityDBAndStores(builder).AddDefaultTokenProviders();
|
||||
AddIdentityServer(builder);
|
||||
|
||||
services.AddSignalR(o =>
|
||||
{
|
||||
o.EnableDetailedErrors = true;
|
||||
});
|
||||
|
||||
services.AddMvc(config =>
|
||||
{
|
||||
/* var policy = new AuthorizationPolicyBuilder()
|
||||
.RequireAuthenticatedUser()
|
||||
.Build();
|
||||
config.Filters.Add(new AuthorizeFilter(policy)); */
|
||||
config.Filters.Add(new ProducesAttribute("application/json"));
|
||||
// config.ModelBinders.Insert(0,new MyDateTimeModelBinder());
|
||||
// config.ModelBinders.Insert(0,new MyDecimalModelBinder());
|
||||
config.EnableEndpointRouting = true;
|
||||
}).AddFormatterMappings(
|
||||
config => config.SetMediaTypeMappingForFormat("text/pdf",
|
||||
new MediaTypeHeaderValue("text/pdf"))
|
||||
).AddFormatterMappings(
|
||||
config => config.SetMediaTypeMappingForFormat("text/x-tex",
|
||||
new MediaTypeHeaderValue("text/x-tex"))
|
||||
)
|
||||
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix,
|
||||
options =>
|
||||
{
|
||||
options.ResourcesPath = "Resources";
|
||||
}).AddDataAnnotationsLocalization();
|
||||
|
||||
services.AddTransient<ITrueEmailSender, MailSender>()
|
||||
.AddTransient<Microsoft.AspNetCore.Identity.UI.Services.IEmailSender, MailSender>()
|
||||
.AddTransient<IYavscMessageSender, YavscMessageSender>()
|
||||
.AddTransient<IBillingService, BillingService>()
|
||||
.AddTransient<IDataStore, FileDataStore>((sp) => new FileDataStore("googledatastore", false))
|
||||
.AddTransient<ICalendarManager, CalendarManager>();
|
||||
|
||||
// TODO for SMS: services.AddTransient<ISmsSender, AuthMessageSender>();
|
||||
|
||||
_ = services.AddLocalization(options =>
|
||||
{
|
||||
options.ResourcesPath = "Resources";
|
||||
});
|
||||
var dataDirConfig = builder.Configuration["Site:DataDir"] ?? "DataDir";
|
||||
|
||||
var dataDir = new DirectoryInfo(dataDirConfig);
|
||||
// Add session related services.
|
||||
|
||||
services.AddDataProtection().PersistKeysToFileSystem(dataDir);
|
||||
AddYavscPolicies(services);
|
||||
|
||||
services.AddScoped<IAuthorizationHandler, PermissionHandler>();
|
||||
|
||||
|
||||
AddAuthentication(builder);
|
||||
// accepts any access token issued by identity server
|
||||
|
||||
return builder.Build();
|
||||
}
|
||||
|
||||
public static IdentityBuilder AddIdentityDBAndStores(this WebApplicationBuilder builder)
|
||||
{
|
||||
IServiceCollection services = builder.Services;
|
||||
services.AddDbContext<ApplicationDbContext>(options =>
|
||||
options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));
|
||||
|
||||
return services.AddIdentity<ApplicationUser, IdentityRole>()
|
||||
.AddEntityFrameworkStores<ApplicationDbContext>();
|
||||
|
||||
}
|
||||
|
||||
private static void AddYavscPolicies(IServiceCollection services)
|
||||
{
|
||||
services.AddAuthorization(options =>
|
||||
{
|
||||
options.AddPolicy("ApiScope", policy =>
|
||||
{
|
||||
policy.RequireAuthenticatedUser()
|
||||
.RequireClaim("scope", "scope2");
|
||||
});
|
||||
options.AddPolicy("Performer", policy =>
|
||||
{
|
||||
policy
|
||||
.RequireAuthenticatedUser()
|
||||
.RequireClaim("http://schemas.microsoft.com/ws/2008/06/identity/claims/role", "Performer");
|
||||
});
|
||||
options.AddPolicy("AdministratorOnly", policy =>
|
||||
{
|
||||
_ = policy.RequireClaim("http://schemas.microsoft.com/ws/2008/06/identity/claims/role", Constants.AdminGroupName);
|
||||
});
|
||||
|
||||
options.AddPolicy("FrontOffice", policy => policy.RequireRole(Constants.FrontOfficeGroupName));
|
||||
|
||||
// options.AddPolicy("EmployeeId", policy => policy.RequireClaim("EmployeeId", "123", "456"));
|
||||
// options.AddPolicy("BuildingEntry", policy => policy.Requirements.Add(new OfficeEntryRequirement()));
|
||||
options.AddPolicy("Authenticated", policy => policy.RequireAuthenticatedUser());
|
||||
options.AddPolicy("IsTheAuthor", policy => policy.Requirements.Add(new EditPermission()));
|
||||
})
|
||||
.AddCors(options =>
|
||||
{
|
||||
options.AddPolicy("default", builder =>
|
||||
{
|
||||
_ = builder.WithOrigins("*")
|
||||
.AllowAnyHeader()
|
||||
.AllowAnyMethod();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public static IServiceCollection LoadConfiguration(this WebApplicationBuilder builder)
|
||||
{
|
||||
var siteSection = builder.Configuration.GetSection("Site");
|
||||
var smtpSection = builder.Configuration.GetSection("Smtp");
|
||||
var paypalSection = builder.Configuration.GetSection("Authentication:PayPal");
|
||||
// OAuth2AppSettings
|
||||
var googleAuthSettings = builder.Configuration.GetSection("Authentication:Google");
|
||||
|
||||
string? googleClientFile = builder.Configuration["Authentication:Google:GoogleWebClientJson"];
|
||||
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;
|
||||
_ = services.AddControllersWithViews()
|
||||
.AddNewtonsoftJson();
|
||||
LoadGoogleConfig(builder.Configuration);
|
||||
|
||||
services.Configure<SiteSettings>(siteSection);
|
||||
services.Configure<SmtpSettings>(smtpSection);
|
||||
services.Configure<PayPalSettings>(paypalSection);
|
||||
services.Configure<GoogleAuthSettings>(googleAuthSettings);
|
||||
ConfigureRequestLocalization(services);
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
private static void AddAuthentication(WebApplicationBuilder builder)
|
||||
{
|
||||
IServiceCollection services=builder.Services;
|
||||
IConfigurationRoot configurationRoot=builder.Configuration;
|
||||
string? googleClientId = configurationRoot["Authentication:Google:ClientId"];
|
||||
string? googleClientSecret = configurationRoot["Authentication:Google:ClientSecret"];
|
||||
|
||||
var authenticationBuilder = services.AddAuthentication();
|
||||
|
||||
if (googleClientId!=null && googleClientSecret!=null)
|
||||
authenticationBuilder.AddGoogle(options =>
|
||||
{
|
||||
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
|
||||
|
||||
// register your IdentityServer with Google at https://console.developers.google.com
|
||||
// enable the Google+ API
|
||||
// set the redirect URI to https://localhost:5001/signin-google
|
||||
options.ClientId = googleClientId;
|
||||
options.ClientSecret = googleClientSecret;
|
||||
});
|
||||
}
|
||||
private static IIdentityServerBuilder AddIdentityServer(WebApplicationBuilder builder)
|
||||
{
|
||||
builder.Services.AddTransient<IProfileService,ProfileService>();
|
||||
var identityServerBuilder = builder.Services.AddIdentityServer(options =>
|
||||
{
|
||||
options.Events.RaiseErrorEvents = true;
|
||||
options.Events.RaiseInformationEvents = true;
|
||||
options.Events.RaiseFailureEvents = true;
|
||||
options.Events.RaiseSuccessEvents = true;
|
||||
|
||||
// see https://IdentityServer8.readthedocs.io/en/latest/topics/resources.html
|
||||
options.EmitStaticAudienceClaim = true;
|
||||
})
|
||||
.AddInMemoryIdentityResources(Config.IdentityResources)
|
||||
.AddInMemoryClients(Config.Clients)
|
||||
.AddInMemoryApiScopes(Config.ApiScopes)
|
||||
.AddAspNetIdentity<ApplicationUser>()
|
||||
.AddProfileService<ProfileService>()
|
||||
;
|
||||
if (builder.Environment.IsDevelopment())
|
||||
{
|
||||
identityServerBuilder.AddDeveloperSigningCredential();
|
||||
}
|
||||
else
|
||||
{
|
||||
var path = builder.Configuration["SigningCert:Path"];
|
||||
var pass = builder.Configuration["SigningCert:Password"];
|
||||
if (path == null)
|
||||
throw new InvalidConfigurationException("No signing cert path");
|
||||
FileInfo certFileInfo = new FileInfo(path);
|
||||
Debug.Assert(certFileInfo.Exists);
|
||||
RSA rsa = RSA.Create();
|
||||
rsa.ImportFromPem(File.ReadAllText(certFileInfo.FullName));
|
||||
var signingCredentials = new SigningCredentials(new RsaSecurityKey(rsa), SecurityAlgorithms.RsaSha256)
|
||||
{
|
||||
CryptoProviderFactory = new CryptoProviderFactory { CacheSignatureProviders = false }
|
||||
};
|
||||
identityServerBuilder.AddSigningCredential(signingCredentials);
|
||||
}
|
||||
return identityServerBuilder;
|
||||
}
|
||||
|
||||
private static void ConfigureRequestLocalization(IServiceCollection services)
|
||||
{
|
||||
services.Configure<RequestLocalizationOptions>(options =>
|
||||
{
|
||||
CultureInfo[] supportedCultures = new[]
|
||||
{
|
||||
new CultureInfo("en"),
|
||||
new CultureInfo("fr"),
|
||||
new CultureInfo("pt")
|
||||
};
|
||||
|
||||
CultureInfo[] supportedUICultures = new[]
|
||||
{
|
||||
new CultureInfo("fr"),
|
||||
new CultureInfo("en"),
|
||||
new CultureInfo("pt")
|
||||
};
|
||||
|
||||
// You must explicitly state which cultures your application supports.
|
||||
// These are the cultures the app supports for formatting numbers, dates, etc.
|
||||
options.SupportedCultures = supportedCultures;
|
||||
|
||||
// These are the cultures the app supports for UI strings, i.e. we have localized resources for.
|
||||
options.SupportedUICultures = supportedUICultures;
|
||||
|
||||
options.RequestCultureProviders = new List<IRequestCultureProvider>
|
||||
{
|
||||
new QueryStringRequestCultureProvider { Options = options },
|
||||
new CookieRequestCultureProvider { Options = options, CookieName="ASPNET_CULTURE" },
|
||||
new AcceptLanguageHeaderRequestCultureProvider { Options = options }
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
internal static WebApplication ConfigurePipeline(this WebApplication app)
|
||||
{
|
||||
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
else
|
||||
{
|
||||
app.UseExceptionHandler("/Home/Error");
|
||||
}
|
||||
|
||||
app.UseStaticFiles();
|
||||
app.UseRouting();
|
||||
app.UseIdentityServer();
|
||||
app.UseAuthorization();
|
||||
app.UseCors("default");
|
||||
app.MapDefaultControllerRoute();
|
||||
//pp.MapRazorPages();
|
||||
app.MapHub<ChatHub>("/chatHub");
|
||||
|
||||
WorkflowHelpers.ConfigureBillingService();
|
||||
|
||||
var services = app.Services;
|
||||
ILoggerFactory loggerFactory = services.GetRequiredService<ILoggerFactory>();
|
||||
var siteSettings = services.GetRequiredService<IOptions<SiteSettings>>();
|
||||
var smtpSettings = services.GetRequiredService<IOptions<SmtpSettings>>();
|
||||
var payPalSettings = services.GetRequiredService<IOptions<PayPalSettings>>();
|
||||
var googleAuthSettings = services.GetRequiredService<IOptions<GoogleAuthSettings>>();
|
||||
var localization = services.GetRequiredService<IStringLocalizer<YavscLocalization>>();
|
||||
Startup.Configure(app, siteSettings, smtpSettings,
|
||||
payPalSettings, googleAuthSettings, localization, loggerFactory,
|
||||
app.Environment.EnvironmentName);
|
||||
app.ConfigureFileServerApp();
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
static void LoadGoogleConfig(IConfigurationRoot configuration)
|
||||
{
|
||||
string? googleClientFile = configuration["Authentication:Google:GoogleWebClientJson"];
|
||||
string? googleServiceAccountJsonFile = 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using RazorEngine.Compilation.ImpromptuInterface.Optimization;
|
||||
using Yavsc.Helpers;
|
||||
using Yavsc.Migrations;
|
||||
using Yavsc.Models;
|
||||
using Yavsc.Models.Blog;
|
||||
using Yavsc.ViewModels.Auth;
|
||||
@ -32,6 +34,10 @@ public class PermissionHandler : IAuthorizationHandler
|
||||
{
|
||||
context.Succeed(requirement);
|
||||
}
|
||||
else if (context.User.IsInRole("Administrator"))
|
||||
{
|
||||
context.Succeed(requirement);
|
||||
}
|
||||
}
|
||||
else if (requirement is EditPermission || requirement is DeletePermission)
|
||||
{
|
||||
@ -49,18 +55,33 @@ public class PermissionHandler : IAuthorizationHandler
|
||||
{
|
||||
if (resource is BlogPost blogPost)
|
||||
{
|
||||
if (blogPost.ACL.Count==0)
|
||||
return true;
|
||||
return
|
||||
applicationDbContext.blogSpotPublications
|
||||
.Any(p=>p.BlogpostId == blogPost.Id);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsOwner(ClaimsPrincipal user, object? resource)
|
||||
private bool IsOwner(ClaimsPrincipal user, object? resource)
|
||||
{
|
||||
if (resource is BlogPost blogPost)
|
||||
{
|
||||
return blogPost.AuthorId == user.GetUserId();
|
||||
}
|
||||
else
|
||||
if (resource is DefaultHttpContext httpContext)
|
||||
{
|
||||
if (httpContext.Request.Path.StartsWithSegments("/blogpost/Delete", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
string postId = (string)httpContext.GetRouteValue("id");
|
||||
if (long.TryParse(postId, out long id))
|
||||
{
|
||||
BlogPost b = applicationDbContext.BlogSpot.FirstOrDefault(b => b.Id == id && b.AuthorId == user.GetUserId());
|
||||
return b != null;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -70,7 +91,7 @@ public class PermissionHandler : IAuthorizationHandler
|
||||
{
|
||||
return applicationDbContext.CircleMembers
|
||||
.Include(c => c.Circle)
|
||||
.Where(m=>m.MemberId==user.GetUserId() && m.Circle.OwnerId == blogPost.OwnerId)
|
||||
.Where(m=>m.MemberId==user.GetUserId() && m.Circle.OwnerId == blogPost.AuthorId)
|
||||
.Any();
|
||||
}
|
||||
return true;
|
||||
|
@ -1,27 +1,50 @@
|
||||
using System.Text.Encodings.Web;
|
||||
using AsciiDocNet;
|
||||
using Microsoft.AspNetCore.Html;
|
||||
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||
|
||||
namespace Yavsc.Helpers
|
||||
{
|
||||
public class AsciidocTagHelper : TagHelper
|
||||
{
|
||||
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);
|
||||
var content = await output.GetChildContentAsync();
|
||||
string text = content.GetContent();
|
||||
if (string.IsNullOrWhiteSpace(text)) return;
|
||||
Document document = Document.Parse(text);
|
||||
var html = document.ToHtml(2);
|
||||
using var stringWriter = new StringWriter();
|
||||
html.WriteTo(stringWriter, HtmlEncoder.Default);
|
||||
var processedHere = stringWriter.ToString();
|
||||
output.Content.AppendHtml(processedHere);
|
||||
|
||||
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);
|
||||
var html = document.ToHtml(2);
|
||||
using var stringWriter = new StringWriter();
|
||||
html.WriteTo(stringWriter, HtmlEncoder.Default);
|
||||
var processedHere = stringWriter.ToString();
|
||||
output.Content.AppendHtml(processedHere);
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
// silently render the text
|
||||
output.Content.AppendHtml("<pre>" + text + "</pre>\n");
|
||||
// and an error
|
||||
output.Content.AppendHtml("<pre class=\"parsingError\">" + ex.Message + "</pre>\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ namespace Yavsc.Helpers
|
||||
);
|
||||
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))
|
||||
throw new InvalidOperationException("No Google login");
|
||||
@ -82,7 +82,7 @@ namespace Yavsc.Helpers
|
||||
var token = await store.GetAsync<TokenResponse>(googleUserLoginKey);
|
||||
// token != null
|
||||
var c = SystemClock.Default;
|
||||
if (token.IsExpired(c)) {
|
||||
if (token.IsStale) {
|
||||
token = await RefreshToken(googleAuthSettings, token);
|
||||
}
|
||||
return new UserCredential(flow, googleUserLoginKey, token);
|
||||
|
@ -1,6 +1,6 @@
|
||||
DESTDIR=/srv/www/yavsc
|
||||
CONFIGURATION=Release
|
||||
USER=www-data
|
||||
USER_AND_GROUP=www-data:www-data
|
||||
SERVICE_PROD=yavsc
|
||||
DOTNET_FRAMEWORK=net9.0
|
||||
|
||||
@ -22,12 +22,17 @@ install_service:
|
||||
pushInProd: publish
|
||||
sudo systemctl stop $(SERVICE_PROD)
|
||||
sudo cp -a bin/$(CONFIGURATION)/$(DOTNET_FRAMEWORK)/publish/* $(DESTDIR)
|
||||
sudo chown -R $(USER) $(DESTDIR)
|
||||
sudo chown -R $(USER_AND_GROUP) $(DESTDIR)
|
||||
sudo sync
|
||||
sudo systemctl start $(SERVICE_PROD)
|
||||
|
||||
%.css: %.scss
|
||||
scss $^ > $@
|
||||
|
||||
%.min.js: %.js
|
||||
jsmin < $^ > $@
|
||||
|
||||
%.min.css: %.css
|
||||
jsmin < $^ > $@
|
||||
|
||||
css: wwwroot/css/site.css
|
||||
|
@ -6,7 +6,7 @@ namespace Yavsc
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
public static async Task Main(string[] args)
|
||||
{
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
@ -15,7 +15,7 @@ namespace Yavsc
|
||||
.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true)
|
||||
.AddEnvironmentVariables()
|
||||
.Build();
|
||||
var app = builder.ConfigureWebAppServices().ConfigurePipeline();
|
||||
var app = await builder.ConfigureWebAppServices().ConfigurePipeline();
|
||||
app.UseSession();
|
||||
app.Run();
|
||||
}
|
||||
|
32
src/Yavsc/Services/ExternalIdentityManager.cs
Normal file
32
src/Yavsc/Services/ExternalIdentityManager.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Yavsc.Interfaces;
|
||||
using Yavsc.Models;
|
||||
|
||||
public class ExternalIdentityManager : IExternalIdentityManager
|
||||
{
|
||||
private ApplicationDbContext _applicationDbContext;
|
||||
private SignInManager<ApplicationUser> _signInManager;
|
||||
|
||||
public ExternalIdentityManager(ApplicationDbContext applicationDbContext, SignInManager<ApplicationUser> signInManager)
|
||||
{
|
||||
_applicationDbContext = applicationDbContext;
|
||||
_signInManager = signInManager;
|
||||
}
|
||||
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.UserLogins
|
||||
.FirstOrDefaultAsync(
|
||||
i => (i.LoginProvider == provider) && (i.ProviderKey == providerUserId)
|
||||
);
|
||||
if (user == null) return null;
|
||||
return await _applicationDbContext.Users.FirstOrDefaultAsync(u=>u.Id == user.UserId);
|
||||
}
|
||||
}
|
@ -1,7 +1,5 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Yavsc.Helpers;
|
||||
using Yavsc.Server.Helpers;
|
||||
using Yavsc.Settings;
|
||||
|
||||
|
@ -22,7 +22,7 @@ namespace Yavsc.ViewComponents
|
||||
{
|
||||
if (target!=null)
|
||||
{
|
||||
var oid = target.OwnerId;
|
||||
var oid = target.AuthorId;
|
||||
ViewBag.ACL = dbContext.Circle.Where(
|
||||
c=>c.OwnerId == oid)
|
||||
.Select(
|
||||
|
@ -1,4 +1,4 @@
|
||||
@model Yavsc.ViewModels.Blog.BlogPostInputViewModel
|
||||
@model BlogPostEditViewModel
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Blog post edition";
|
||||
@ -47,6 +47,7 @@
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group mdcoding">
|
||||
<label asp-for="Content" class="col-md-2 control-label" ></label>
|
||||
<div class="col-md-10">
|
||||
@ -56,6 +57,7 @@
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label asp-for="ACL" class="col-md-2 control-label"></label>
|
||||
<div class="col-md-10">
|
||||
@ -63,6 +65,13 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label asp-for="Publish" class="col-md-2 control-label"></label>
|
||||
<div class="col-md-10">
|
||||
<input asp-for="Publish" class="form-control" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<button class="btn btn-primary" >Save</button>
|
||||
|
@ -60,10 +60,14 @@ $('#commentValidation').html(
|
||||
}
|
||||
</style>
|
||||
}
|
||||
<div class="container">
|
||||
<div class="blogpost">
|
||||
<h1 class="blogtitle" ismarkdown>@Model.Title</h1>
|
||||
<img class="blogphoto" alt="" src="@Model.Photo" >
|
||||
|
||||
<div class="post">
|
||||
<div class="float-left">
|
||||
<img alt="" src="@Model.Photo" >
|
||||
</div>
|
||||
|
||||
<h1 ismarkdown>@Model.Title</h1>
|
||||
|
||||
@Html.DisplayFor(m=>m.Author)
|
||||
<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="Index" class="btn btn-link">Back to List</a>
|
||||
</div>
|
||||
|
||||
|
@ -39,7 +39,7 @@
|
||||
|
||||
}
|
||||
|
||||
<h2 > Blog post edition </h2>
|
||||
<h2 >Blog post</h2>
|
||||
<label><input type="checkbox" id="vcbtn" />Editer le code source Markdown</label>
|
||||
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
@ -97,6 +97,13 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label asp-for="Publish" class="col-md-2 control-label"></label>
|
||||
<div class="col-md-10">
|
||||
<input type="checkbox" asp-for="Publish" class="form-control" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<input type="submit" value="Save" class="btn btn-default" />
|
||||
@ -111,5 +118,3 @@
|
||||
<div>
|
||||
<a asp-action="Index">Back to List</a>
|
||||
</div>
|
||||
|
||||
using Yavsc.Migrations;
|
||||
|
@ -1,4 +1,4 @@
|
||||
@model IEnumerable<IGrouping<string,BlogPost>>
|
||||
@model IEnumerable<IBlogPost>
|
||||
@{
|
||||
ViewData["Title"] = "Blogs, l'index";
|
||||
}
|
||||
@ -43,58 +43,51 @@
|
||||
<a asp-action="Create">Create a new article</a>
|
||||
</p>
|
||||
}
|
||||
|
||||
<div class="container">
|
||||
|
||||
<table class="table">
|
||||
@foreach (var group in Model) {
|
||||
var title = group.Key ?? "@";
|
||||
string secondclass="";
|
||||
var first = group.First();
|
||||
int maxTextLen = 256;
|
||||
<div class="blog">
|
||||
@{
|
||||
int maxTextLen = 75;
|
||||
foreach (var post in Model) {
|
||||
<div class="post">
|
||||
|
||||
|
||||
<tr><td colspan="3">
|
||||
<a asp-action="Title" asp-route-id="@group.Key" >@title</a></td></tr>
|
||||
@foreach (var item in group) {
|
||||
var trunked = item.Content?.Length > maxTextLen;
|
||||
<tr>
|
||||
<td><a asp-action="Details" asp-route-id="@item.Id" class="bloglink">
|
||||
<img src="@item.Photo" class="blogphoto"></a>
|
||||
</td>
|
||||
<td>
|
||||
<asciidoc summary="@maxTextLen">@item.Content</asciidoc>
|
||||
@if (trunked) { <a asp-action="Details" asp-route-id="@item.Id" class="bloglink">...</a> }
|
||||
<span style="font-size:x-small;">@Html.DisplayFor(m => item.Author)</span>
|
||||
<span style="font-size:xx-small;">
|
||||
posté le @item.DateCreated.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>
|
||||
</td>
|
||||
<td>
|
||||
<ul class="actiongroup">
|
||||
@if ((await AuthorizationService.AuthorizeAsync(User, item, new ReadPermission())).Succeeded) {
|
||||
<li>
|
||||
<a asp-action="Details" asp-route-id="@item.Id" class="btn btn-lg">Details</a>
|
||||
</li>
|
||||
}
|
||||
else {
|
||||
<a asp-action="Details" asp-route-id="@item.Id" class="btn btn-lg">Details DEBUG</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>
|
||||
</li>
|
||||
<li><a asp-action="Delete" asp-route-id="@item.Id" class="btn btn-danger">Delete</a>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<div class="float-left" >
|
||||
<a asp-action="Details" asp-route-id="@post.Id" class="bloglink" style="display: float-left;">
|
||||
<img src="@post.Photo" >
|
||||
</a>
|
||||
</div>
|
||||
<h3>@post.Title</h3>
|
||||
<div>
|
||||
<a asp-action="Details" asp-route-id="@post.Id">
|
||||
<asciidoc summary="@maxTextLen">@post.Content</asciidoc></a>
|
||||
<span style="font-size:x-small;">@Html.DisplayFor(m => post.Author)</span>
|
||||
<span style="font-size:xx-small;">
|
||||
posté le @post.DateCreated.ToString("dddd d MMM yyyy à H:mm")
|
||||
@if ((post.DateModified - post.DateCreated).Minutes > 0){
|
||||
@:- Modifié le @post.DateModified.ToString("dddd d MMM yyyy à H:mm")
|
||||
})
|
||||
</span>
|
||||
</div>
|
||||
<div class="actiongroup">
|
||||
@if ((await AuthorizationService.AuthorizeAsync(User, post, new ReadPermission())).Succeeded)
|
||||
{
|
||||
<a asp-action="Details" asp-route-id="@post.Id" class="btn btn-light">Details</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a asp-action="Details" asp-route-id="@post.Id" class="btn btn-light">Details</a>
|
||||
}
|
||||
@if ((await AuthorizationService.AuthorizeAsync(User, post, new EditPermission())).Succeeded)
|
||||
{
|
||||
<a asp-action="Edit" asp-route-id="@post.Id" class="btn btn-default">Edit</a>
|
||||
|
||||
<a asp-action="Delete" asp-route-id="@post.Id" class="btn btn-danger">Delete</a>
|
||||
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
@if(Model.Count()==0){<p>Néant</p>}
|
||||
|
@ -28,7 +28,7 @@
|
||||
<ul class="actiongroup">
|
||||
@if ((await AuthorizationService.AuthorizeAsync(User, item, new ReadPermission())).Succeeded) {
|
||||
<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>
|
||||
}
|
||||
@if ((await AuthorizationService.AuthorizeAsync(User, item, new EditPermission())).Succeeded) {
|
||||
|
@ -51,7 +51,7 @@
|
||||
@act.Description </p>
|
||||
|
||||
@if (act.Children.Count>0) {
|
||||
<a asp-route-id="@act.Code" class="btn btn-default">
|
||||
<a asp-route-id="@act.Code" class="btn btn-light">
|
||||
@foreach (Activity c in act.Children) {
|
||||
@Html.DisplayFor(subact=>c)
|
||||
}
|
||||
|
@ -19,7 +19,7 @@
|
||||
<environment names="Development">
|
||||
<script>
|
||||
var CREATE_PAYMENT_URL = 'http://pschneider.fr:85/api/payment/create';
|
||||
var EXECUTE_PAYMENT_URL = 'http://dev.pschneider.fr:85/api/payment/execute';
|
||||
var EXECUTE_PAYMENT_URL = 'http://pschneider.fr:85/api/payment/execute';
|
||||
var PAYPAL_ENV = 'sandbox';
|
||||
</script>
|
||||
</environment>
|
||||
|
@ -20,9 +20,9 @@
|
||||
}
|
||||
</ul>
|
||||
<p>
|
||||
<input type="submit" class="btn btn-lg 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-lg btn-success" name="submit.Login" value="Sign in as different user" />
|
||||
<input type="submit" class="btn btn-light btn-success" name="submit.Grant" value="Grant" />
|
||||
<input type="submit" class="btn btn-light btn-danger" name="submit.Deny" value="Deny" />
|
||||
<input type="submit" class="btn btn-light btn-success" name="submit.Login" value="Sign in as different user" />
|
||||
</p>
|
||||
|
||||
</form>
|
||||
|
@ -1,14 +1,10 @@
|
||||
@using Yavsc.Abstract.Identity
|
||||
@model ApplicationUser
|
||||
@{
|
||||
var avuri = "/Avatars/"+Model.UserName+".s.png";
|
||||
var userPosted = Model.Posts!=null && Model.Posts.Count()>=1;
|
||||
var avuri = "/Avatars/" + Model.UserName + ".s.png";
|
||||
}
|
||||
<div class="userinfo">
|
||||
@if (userPosted) { <a asp-controller="Blogspot" asp-action="Index"
|
||||
asp-route-id="@Model.UserName" class="btn btn-primary">
|
||||
<img src="@avuri" asp-append-version="true" class="smalltofhol" alt="@Model.UserName" title="@Model.UserName"/>
|
||||
</a>
|
||||
} else {
|
||||
Html.DisplayFor(m=>m.UserName);
|
||||
}
|
||||
<a title="Posts" asp-controller="Blogspot" asp-action="Index" asp-route-id="@Model.UserName" class="btn btn-primary">
|
||||
<img src="@avuri" asp-append-version="true" class="smalltofhol" alt="@Model.UserName" title="@Model.UserName" />
|
||||
</a>
|
||||
</div>
|
||||
|
@ -0,0 +1,10 @@
|
||||
@using Yavsc.Abstract.Identity
|
||||
@model IApplicationUser
|
||||
@{
|
||||
var avuri = "/Avatars/" + Model.UserName + ".s.png";
|
||||
}
|
||||
<div class="userinfo">
|
||||
<a title="Posts" asp-controller="Blogspot" asp-action="Index" asp-route-id="@Model.UserName" class="btn btn-primary">
|
||||
<img src="@avuri" asp-append-version="true" class="smalltofhol" alt="@Model.UserName" title="@Model.UserName" />
|
||||
</a>
|
||||
</div>
|
@ -14,6 +14,6 @@
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -37,7 +37,7 @@
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<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>
|
||||
<p>
|
||||
|
@ -16,7 +16,10 @@
|
||||
<script src="~/js/site.js" asp-append-version="true"></script>
|
||||
@await RenderSectionAsync("header", false)
|
||||
</head>
|
||||
<body>
|
||||
<body style="background-image: url('@Config.SiteSetup.Banner');
|
||||
background-position: 0%;
|
||||
background-attachment: fixed;
|
||||
">
|
||||
<partial name="_Nav" />
|
||||
@RenderSection("ctxmenu", required: false)
|
||||
@if (ViewData["Notify"] != null)
|
||||
|
@ -2,23 +2,15 @@
|
||||
@inject SignInManager<ApplicationUser> SignInManager
|
||||
@inject UserManager<ApplicationUser> UserManager
|
||||
|
||||
@{
|
||||
#nullable enable
|
||||
string? name = null;
|
||||
if (Context.User!=null)
|
||||
{
|
||||
name = Context.User.GetUserName();
|
||||
}
|
||||
}
|
||||
|
||||
@if (name!=null)
|
||||
@if (Context.User?.Identity?.IsAuthenticated ?? false)
|
||||
{
|
||||
string userName = User.GetUserName();
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<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">
|
||||
<li><a class="dropdown-item" asp-controller="Bug" asp-action="Index">Bugs</a></li>
|
||||
<li><a class="dropdown-item" asp-controller="HyperLink" asp-action="Index">HyperLink</a></li>
|
||||
<li><a class="dropdown-item" asp-controller="LiveFlow" asp-action="Index">LiveFlow</a></li>
|
||||
<li><a class="dropdown-item" asp-controller="Feature" asp-action="Index">Features</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
@ -38,7 +30,7 @@
|
||||
</ul>
|
||||
</li>}
|
||||
<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">
|
||||
<li>
|
||||
<a class="dropdown-item" asp-controller="Manage" asp-action="Index" title="Manage">
|
||||
@ -62,3 +54,4 @@ else
|
||||
<a class="nav-link" asp-controller="Account" asp-action="Login" asp-route-ReturnUrl="~/" >Login</a>
|
||||
</li>
|
||||
}
|
||||
|
||||
|
@ -1,24 +1,18 @@
|
||||
|
||||
<nav class="navbar navbar-expand-sm navbar-dark bg-dark" aria-label="Yavsc">
|
||||
|
||||
<a href="~/" class="navbar-brand"><img src="~/images/it/free-sofware.svg" class="icon-banner" alt="Lua"></a>
|
||||
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
|
||||
<a class="navbar-brand" href="#">@Config.SiteSetup.Title</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
|
||||
data-bs-target="#navbar" aria-controls="navbar"
|
||||
aria-expanded="true" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbar">
|
||||
<div class="collapse navbar-collapse" id="navbar" style="background-image: @Config.SiteSetup.Banner;">
|
||||
<ul class="navbar-nav me-auto" >
|
||||
<li class="nav-item"><a class="nav-link active" aria-current="page" href="/">Home</a></li>
|
||||
<li class="nav-item"><a asp-controller="Blogspot" asp-action="Index" class="nav-link">@SR["Blogs"]</a></li>
|
||||
<li class="nav-item"><a asp-controller="Home" asp-action="Chat" class="nav-link">@SR["Chat"]</a></li>
|
||||
<li class="nav-item"><a asp-controller="Home" asp-action="Contact" class="nav-link">@SR["Contact"]</a></li>
|
||||
<li class="nav-item"><a asp-controller="Home" asp-action="About" class="nav-link">@SR["About"]</a></li>
|
||||
|
||||
<partial name="_LoginPartial" />
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
@ -10,36 +10,38 @@
|
||||
<PackageReference Include="popper.js" Version="1.16.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="bootstrap" Version="5.3.3">
|
||||
<PackageReference Include="bootstrap" Version="5.3.7">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="HigginsSoft.IdentityServer8.AspNetIdentity" Version="8.0.4" />
|
||||
<PackageReference Include="HigginsSoft.IdentityServer8" 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="Microsoft.AspNetCore.Authentication.Google" Version="9.0.6" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="8.0.13" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.6" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="9.0.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.6">
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="9.0.7" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="9.0.7" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.7" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="9.0.7" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.7">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</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>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.6" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="9.0.6" />
|
||||
<PackageReference Include="Google.Apis.Compute.v1" Version="1.54.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.7" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="9.0.7" />
|
||||
<PackageReference Include="Google.Apis.Compute.v1" Version="1.70.0.3829" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.2.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Razor" Version="2.3.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="Swashbuckle.AspNetCore" Version="8.1.4" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.3" />
|
||||
<PackageReference Include="AsciiDocNet" Version="1.0.0-alpha6" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.9" />
|
||||
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.10" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="9.0.7" />
|
||||
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../Yavsc.Server/Yavsc.Server.csproj" />
|
||||
|
@ -13,7 +13,6 @@
|
||||
.navbar-dark .navbar-toggler-icon {
|
||||
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); }
|
||||
|
||||
/* bootstrap.css | http://localhost:5000/lib/bootstrap/css/bootstrap.css */
|
||||
div.carousel-inner > div.item > div.carousel-caption-s {
|
||||
margin: .5em;
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
@ -25,13 +24,28 @@ img.blogphoto {
|
||||
max-width: 100%;
|
||||
max-height: 100%; }
|
||||
|
||||
.cmtdatetime {
|
||||
font-style: italic;
|
||||
font-size: x-small;
|
||||
}
|
||||
|
||||
.blogcomment {
|
||||
display: inline-block;
|
||||
padding: .3em;
|
||||
border: solid black 1px;
|
||||
}
|
||||
input[type='checkbox'] {
|
||||
appearance: auto;
|
||||
min-width: 1em;
|
||||
min-height: 1em; }
|
||||
|
||||
.container {
|
||||
background-color: #00000093;
|
||||
color: #fff;
|
||||
padding: .5em;
|
||||
margin: .5em; }
|
||||
|
||||
.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; }
|
||||
|
@ -1,3 +1,5 @@
|
||||
|
||||
|
||||
.grants-page {
|
||||
.card {
|
||||
margin-top: 20px;
|
||||
@ -23,10 +25,6 @@
|
||||
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
|
||||
}
|
||||
|
||||
|
||||
/* bootstrap.css | http://localhost:5000/lib/bootstrap/css/bootstrap.css */
|
||||
|
||||
|
||||
div.carousel-inner > div.item > div.carousel-caption-s {
|
||||
margin: .5em;
|
||||
background-color: rgba(0,0,0,.6);
|
||||
@ -39,3 +37,36 @@ img.blogphoto {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
input[type='checkbox'] {
|
||||
appearance: auto;
|
||||
min-width: 1em;
|
||||
min-height: 1em;
|
||||
}
|
||||
|
||||
.container {
|
||||
background-color: #00000093;
|
||||
color: #fff;
|
||||
padding: .5em;
|
||||
margin: .5em;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
@ -10,7 +10,6 @@
|
||||
@include rfs($nav-link-font-size, --#{$prefix}nav-link-font-size);
|
||||
--#{$prefix}nav-link-font-weight: #{$nav-link-font-weight};
|
||||
--#{$prefix}nav-link-color: #{$nav-link-color};
|
||||
--#{$prefix}nav-link-background-color: #{$dropdown-dark-bg};
|
||||
--#{$prefix}nav-link-hover-color: #{$nav-link-hover-color};
|
||||
--#{$prefix}nav-link-disabled-color: #{$nav-link-disabled-color};
|
||||
// scss-docs-end nav-css-vars
|
||||
@ -28,9 +27,8 @@
|
||||
@include font-size(var(--#{$prefix}nav-link-font-size));
|
||||
font-weight: var(--#{$prefix}nav-link-font-weight);
|
||||
color: var(--#{$prefix}nav-link-color);
|
||||
background-color: var(--#{$prefix}nav-link-background-color);
|
||||
text-decoration: if($link-decoration == none, null, none);
|
||||
background-color: var(--#{$prefix}nav-tabs-link-active-bg);
|
||||
background: none;
|
||||
border: 0;
|
||||
@include transition($nav-link-transition);
|
||||
|
||||
|
@ -1607,6 +1607,7 @@ $list-group-action-active-color: var(--#{$prefix}body-color) !default;
|
||||
$list-group-action-active-bg: var(--#{$prefix}secondary-bg) !default;
|
||||
// scss-docs-end list-group-variables
|
||||
|
||||
|
||||
// Image thumbnails
|
||||
|
||||
// scss-docs-start thumbnail-variables
|
||||
@ -1618,6 +1619,7 @@ $thumbnail-border-radius: var(--#{$prefix}border-radius) !default;
|
||||
$thumbnail-box-shadow: var(--#{$prefix}box-shadow-sm) !default;
|
||||
// scss-docs-end thumbnail-variables
|
||||
|
||||
|
||||
// Figures
|
||||
|
||||
// scss-docs-start figure-variables
|
||||
@ -1745,3 +1747,5 @@ $kbd-bg: var(--#{$prefix}body-color) !default;
|
||||
$nested-kbd-font-weight: null !default; // Deprecated in v5.2.0, removing in v6
|
||||
|
||||
$pre-color: null !default;
|
||||
|
||||
@import "variables-dark"; // TODO: can be removed safely in v6, only here to avoid breaking changes in v5.3
|
||||
|
@ -11,7 +11,6 @@
|
||||
font-weight: $input-font-weight;
|
||||
line-height: $input-line-height;
|
||||
color: $input-color;
|
||||
appearance: none; // Fix appearance for date inputs in Safari
|
||||
background-color: $input-bg;
|
||||
background-clip: padding-box;
|
||||
border: $input-border-width solid $input-border-color;
|
||||
|
@ -1,5 +1,6 @@
|
||||
User-agent: *
|
||||
Disallow: /signin
|
||||
Disallow: /signin-google
|
||||
Disallow: /Account/Register
|
||||
Disallow: /External
|
||||
Disallow: /Command/Create
|
||||
|
||||
|
@ -13,8 +13,7 @@
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
|
||||
JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
|
||||
|
||||
JwtSecurityTokenHandler.DefaultMapInboundClaims = true;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
@ -28,8 +27,7 @@ builder.Services
|
||||
.AddCookie("Cookies")
|
||||
.AddOpenIdConnect("oidc", options =>
|
||||
{
|
||||
options.Authority = "https://localhost:5001";
|
||||
|
||||
options.Authority = builder.Configuration.GetValue<String>("AuthIssuer");
|
||||
options.ClientId = "mvc";
|
||||
options.ClientSecret = "49C1A7E1-0C79-4A89-A3D6-A37998FB86B0";
|
||||
options.ResponseType = "code";
|
||||
@ -38,15 +36,14 @@ builder.Services
|
||||
options.Scope.Add("profile");
|
||||
options.Scope.Add("scope2");
|
||||
options.MapInboundClaims = true;
|
||||
options.ClaimActions.MapUniqueJsonKey("preferred_username","preferred_username");
|
||||
options.ClaimActions.MapUniqueJsonKey("preferred_username", "preferred_username");
|
||||
options.ClaimActions.MapUniqueJsonKey("gender", "gender");
|
||||
|
||||
options.SaveTokens = true;
|
||||
});
|
||||
|
||||
|
||||
using (var app = builder.Build())
|
||||
{
|
||||
|
||||
if (app.Environment.IsDevelopment())
|
||||
app.UseDeveloperExceptionPage();
|
||||
else
|
||||
|
@ -19,26 +19,26 @@
|
||||
}
|
||||
|
||||
<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 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 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 action="~/Home/PostFiles/?subdir=test" method="post" enctype="multipart/form-data">
|
||||
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>
|
||||
|
||||
<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 {
|
||||
<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>
|
||||
|
@ -6,6 +6,7 @@
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"AuthIssuer": "https://localhost:5001",
|
||||
"Kestrel": {
|
||||
"Endpoints":
|
||||
{
|
||||
|
@ -8,7 +8,7 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Yavsc.Abstract\Yavsc.Abstract.csproj" />
|
||||
<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>
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 8.1 KiB |
@ -2120,9 +2120,6 @@ progress {
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
color: var(--bs-body-color);
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
background-color: var(--bs-body-bg);
|
||||
background-clip: padding-box;
|
||||
border: var(--bs-border-width) solid var(--bs-border-color);
|
||||
@ -12040,4 +12037,4 @@ textarea.form-control-lg {
|
||||
}
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=bootstrap.css.map */
|
||||
/*# sourceMappingURL=bootstrap.css.map */
|
||||
|
@ -11,7 +11,6 @@
|
||||
font-weight: $input-font-weight;
|
||||
line-height: $input-line-height;
|
||||
color: $input-color;
|
||||
appearance: none; // Fix appearance for date inputs in Safari
|
||||
background-color: $input-bg;
|
||||
background-clip: padding-box;
|
||||
border: $input-border-width solid $input-border-color;
|
||||
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user