4 Commits

Author SHA1 Message Date
50be1ea726 Bump braces and gulp
Bumps [braces](https://github.com/micromatch/braces) to 3.0.3 and updates ancestor dependency [gulp](https://github.com/gulpjs/gulp). These dependencies need to be updated together.


Updates `braces` from 2.3.2 to 3.0.3
- [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/braces/commits/3.0.3)

Updates `gulp` from 4.0.2 to 5.0.1
- [Release notes](https://github.com/gulpjs/gulp/releases)
- [Changelog](https://github.com/gulpjs/gulp/blob/master/CHANGELOG.md)
- [Commits](https://github.com/gulpjs/gulp/compare/v4.0.2...v5.0.1)

---
updated-dependencies:
- dependency-name: braces
  dependency-version: 3.0.3
  dependency-type: indirect
- dependency-name: gulp
  dependency-version: 5.0.1
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-13 14:46:45 +00:00
fe6f1581dd updated js (bis) 2025-06-13 15:45:25 +01:00
f322e3f485 updated js 2025-06-13 15:38:03 +01:00
c4dc16dde4 migration net9.0 2025-06-13 15:22:02 +01:00
103 changed files with 2357 additions and 32025 deletions

View File

@ -44,7 +44,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 9.0.x
dotnet-version: 8.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build

7
.github/workflows/main.yml vendored Normal file
View File

@ -0,0 +1,7 @@
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
View File

@ -4,32 +4,6 @@
// 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"
}
},
/* {
@ -109,11 +83,6 @@
"serverReadyAction": {
"action": "openExternally",
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
},
"presentation": {
"hidden": false,
"group": "run",
"order": 1
}
},
{

6
.vscode/tasks.json vendored
View File

@ -9,12 +9,8 @@
"args": [
"build",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary;ForceNoAlign",
"/consoleloggerparameters:NoSummary;ForceNoAlign"
],
"group": "build",
"isBuildCommand": true,
"isTestCommand": false,
"problemMatcher": "$msCompile"
},
{

View File

@ -15,13 +15,14 @@ WorkingDirectory=/srv/www/yavsc/
ExecStart=/srv/www/yavsc/Yavsc
Restart=always
Environment="HOME=/srv/www/yavsc"
Environment="ASPNETCORE_ENVIRONMENT=Production"
Environment="ASPNETCORE_ConnectionStrings__DefaultConnection=YOUR Postgresql CONNECTION STRING"
Environment="ASPNETCORE_ENVIRONMENT=lua"
Environment="ASPNETCORE_ConnectionStrings__DefaultConnection=Server=localhost;Port=5432;Database=lua;Username=lua;Password=f3s-*Vx$;"
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=yavsc
SyslogIdentifier=isnd
[Install]
WantedBy=multi-user.target

View File

@ -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);
}
}

View File

@ -1,16 +1,23 @@
using Yavsc.Attributes.Validation;
namespace Yavsc.Models.FileSystem
{
public class RenameFileQuery {
[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; }
}
public class MoveFileQuery {
[ValidRemoteUserFilePath]
[YaStringLength(1, 512)]
public string id { get; set; }
[YaStringLength(0, 512)]
[ValidRemoteUserFilePath]
public required string To { get; set; }
}
[YaStringLength(0, 512)]
[ValidRemoteUserFilePath]
public string to { get; set; }
}
}

View File

@ -1,15 +0,0 @@
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; }
}
}

View File

@ -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") ;

View File

@ -40,7 +40,7 @@ namespace Yavsc.ApiControllers
return Ok();
}
[HttpPost("query/accept")]
[HttpPost("query/reject")]
public IActionResult AcceptQuery(string billingCode, long queryId)
{
if (billingCode == null) return BadRequest("billingCode");
@ -51,6 +51,7 @@ namespace Yavsc.ApiControllers
billing.Decided = true;
dbContext.SaveChanges();
return Ok();
}
}
}

View File

@ -32,8 +32,7 @@ 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");

View File

@ -6,7 +6,6 @@ using Microsoft.EntityFrameworkCore;
using Yavsc.Models;
using Yavsc.Api.Helpers;
using Yavsc.Server.Helpers;
using System.Diagnostics;
namespace Yavsc.WebApi.Controllers
{
@ -31,9 +30,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);
@ -58,7 +57,7 @@ namespace Yavsc.WebApi.Controllers
[HttpGet("myhost")]
public IActionResult MyHost ()
{
return Ok(new { host = Request.ForwardedFor() });
return Ok(new { host = Request.ForHost() });
}

View File

@ -13,8 +13,8 @@ namespace Yavsc.Api.Helpers
public static class RequestHelpers
{
// Check for some apache proxy header, if any
public static string? ForwardedFor(this HttpRequest request) {
string? host = request.Headers["X-Forwarded-For"];
public static string ForHost(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

View File

@ -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");
}

View File

@ -11,6 +11,8 @@
*/
using IdentityModel;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Yavsc.Helpers;
@ -61,8 +63,7 @@ internal class Program
options.IncludeErrorDetails = true;
options.Authority = "https://localhost:5001";
options.TokenValidationParameters =
new() { ValidateAudience = false, RoleClaimType = JwtClaimTypes.Role };
options.MapInboundClaims = true;
new() { ValidateAudience = false };
});
services.AddDbContext<ApplicationDbContext>(options =>
@ -73,9 +74,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();
@ -100,10 +101,15 @@ 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();
}
;
}
}

View File

@ -1,19 +1,16 @@

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; }
}
}

View File

@ -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,7 +52,9 @@ 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";
}
}

View File

@ -54,7 +54,7 @@ namespace Yavsc.Models.Google.Messaging
/// <summary>
/// The error.
/// </summary>
public string? error;
public string error;
}
/// <summary>

View File

@ -3,10 +3,10 @@
public interface IApplicationUser
{
string Id { get; set; }
string? UserName { get; set; }
string? Avatar { get ; set; }
IAccountBalance? AccountBalance { get; }
string? DedicatedGoogleCalendar { get; }
ILocation? PostalAddress { get; }
string UserName { get; set; }
string Avatar { get ; set; }
IAccountBalance AccountBalance { get; set; }
string DedicatedGoogleCalendar { get; set; }
ILocation PostalAddress { get; set; }
}
}

View File

@ -3,7 +3,7 @@ namespace Yavsc.Abstract.Identity.Security
public interface ICircleAuthorized
{
long Id { get; set; }
string AuthorId { get; }
string OwnerId { get; }
bool AuthorizeCircle(long circleId);
ICircleAuthorization [] GetACL();

View File

@ -1,13 +0,0 @@
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-ef": {
"version": "9.0.6",
"commands": [
"dotnet-ef"
],
"rollForward": false
}
}
}

View File

@ -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" }
},
};

View File

@ -1,17 +0,0 @@
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)
{
}
}

View File

@ -16,7 +16,7 @@ namespace Yavsc.Helpers
public static string GetUserName(this ClaimsPrincipal user)
{
return user.FindFirstValue("name");
return user.FindFirstValue(ClaimTypes.Name);
}
public static bool IsSignedIn(this ClaimsPrincipal user)
@ -24,14 +24,13 @@ 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.blogSpotPublications.Include(
b => b.BlogPost
).Where(x => x.BlogPost.AuthorId == posterId)
.Select(x=>x.BlogPost).ToArray();
var userPosts = dbContext.BlogSpot.Include(
b => b.Author
).Where(x => ((x.AuthorId == posterId))).ToArray();
return userPosts;
}
else

View File

@ -29,13 +29,11 @@ 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
{
@ -194,11 +192,10 @@ namespace Yavsc
NotifyUserInRoom(NotificationTypes.Error, room, "already registered.");
return;
}
Debug.Assert(Context.User != null);
string userName = Context.User.GetUserName();
string userName = Context.User.Identity.Name;
var user = _dbContext.Users.FirstOrDefault(u => u.UserName == userName);
var newroom = new ChatRoom { Name = room, OwnerId = Context.User.GetUserId() };
var newroom = new ChatRoom { Name = room, OwnerId = user.Id };
ChatRoomInfo chanInfo;
if (_cxManager.TryGetChanInfo(room, out chanInfo))
{
@ -322,7 +319,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);
}
@ -334,8 +331,6 @@ 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))
@ -349,21 +344,19 @@ namespace Yavsc
return ;
}
_logger.LogInformation($"Message form is validated.");
var identityUserName = Context.User.GetUserName();
if (userName[0] != '?' && Context.User!=null)
if (userName[0] != '?')
if (!Context.User.IsInRole(Constants.AdminGroupName))
{
var bl = _dbContext.BlackListed
.Include(r => r.User)
.Include(r => r.Owner)
.Where(r => r.User.UserName == identityUserName && r.Owner.UserName == userName)
.Where(r => r.User.UserName == Context.User.Identity.Name && r.Owner.UserName == userName)
.Select(r => r.OwnerId);
if (bl.Count() > 0)
{
_logger.LogError($"Black listed : {identityUserName}");
_logger.LogError($"Black listed : {Context.User.Identity.Name}");
await NotifyUser(NotificationTypes.PrivateMessageDenied, userName, "you are black listed.");
return;
}
@ -379,7 +372,7 @@ namespace Yavsc
_logger.LogInformation($"cx: {connectionId}");
var cli = Clients.Client(connectionId);
_logger.LogInformation($"cli: {cli.ToString()}");
await cli.SendAsync("addPV", identityUserName, message);
await cli.SendAsync("addPV", Context.User.Identity.Name, message);
_logger.LogInformation($"Sent pv to cx {connectionId}");
}
}
@ -387,9 +380,6 @@ 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);

File diff suppressed because it is too large Load Diff

View File

@ -1,108 +0,0 @@
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);
}
}
}

View File

@ -1,59 +0,0 @@
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);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,38 +0,0 @@
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");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,79 +0,0 @@
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);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,22 +0,0 @@
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)
{
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,54 +0,0 @@
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");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,29 +0,0 @@
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);
}
}
}

View File

@ -303,9 +303,6 @@ namespace Yavsc.Migrations
b.Property<long>("BlogPostId")
.HasColumnType("bigint");
b.Property<bool>("Comment")
.HasColumnType("boolean");
b.HasKey("CircleId", "BlogPostId");
b.HasIndex("BlogPostId");
@ -341,6 +338,7 @@ namespace Yavsc.Migrations
.HasColumnType("boolean");
b.Property<string>("Avatar")
.IsRequired()
.ValueGeneratedOnAdd()
.HasMaxLength(512)
.HasColumnType("character varying(512)")
@ -859,16 +857,6 @@ 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")
@ -1135,6 +1123,7 @@ namespace Yavsc.Migrations
.HasColumnType("numeric");
b.Property<string>("ScheduleOwnerId")
.IsRequired()
.HasColumnType("text");
b.Property<decimal>("ShampooPrice")
@ -2092,6 +2081,7 @@ namespace Yavsc.Migrations
.HasColumnType("boolean");
b.Property<string>("ModeratorGroupName")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Name")
@ -2102,18 +2092,22 @@ 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");
@ -2775,17 +2769,6 @@ 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")
@ -2857,7 +2840,9 @@ namespace Yavsc.Migrations
{
b.HasOne("Yavsc.Models.Calendar.Schedule", "Schedule")
.WithMany()
.HasForeignKey("ScheduleOwnerId");
.HasForeignKey("ScheduleOwnerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Yavsc.Models.Workflow.PerformerProfile", "BaseProfile")
.WithMany()
@ -3392,7 +3377,8 @@ namespace Yavsc.Migrations
modelBuilder.Entity("Yavsc.Models.ApplicationUser", b =>
{
b.Navigation("AccountBalance");
b.Navigation("AccountBalance")
.IsRequired();
b.Navigation("BankInfo");

View File

@ -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,10 +17,7 @@ namespace Yavsc.Models.Access
[JsonIgnore]
[ForeignKey("CircleId")]
public virtual Circle Allowed { get; set; }
public bool Comment { get; set; }
public virtual Circle Allowed { get; set; }
}
}

View File

@ -1,4 +1,8 @@

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;
@ -6,6 +10,7 @@ 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
{
@ -27,6 +32,7 @@ namespace Yavsc.Models
using Attributes;
using Bank;
using Payment;
using Calendar;
using Blog;
using Yavsc.Abstract.Identity;
using Microsoft.EntityFrameworkCore;
@ -42,12 +48,10 @@ namespace Yavsc.Models
{
}
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
{
}
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
@ -57,7 +61,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);
@ -77,7 +81,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())
{
@ -85,22 +89,23 @@ namespace Yavsc.Models
et.FindProperty("DateCreated").SetAfterSaveBehavior(Microsoft.EntityFrameworkCore.Metadata.PropertySaveBehavior.Ignore);
}
builder.Entity<Activity>().Property(a => a.ParentCode).IsRequired(false);
// builder.Entity<IdentityUserLogin<String>>().HasKey(i=> new { i.LoginProvider, i.UserId, i.ProviderKey });
builder.Entity<Activity>().Property(a=>a.ParentCode).IsRequired(false);
//builder.Entity<BlogPost>().HasOne(p => p.Author).WithMany(a => a.Posts);
}
// 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();
}
@ -120,7 +125,7 @@ namespace Yavsc.Models
/// Users posts
/// </summary>
/// <returns></returns>
public DbSet<BlogPost> BlogSpot { get; set; }
public DbSet<Blog.BlogPost> BlogSpot { get; set; }
/// <summary>
/// Skills powered by this site
@ -221,14 +226,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))
{
@ -277,7 +282,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
@ -290,7 +295,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; }
@ -299,8 +304,6 @@ 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; }
}
}

View File

@ -7,12 +7,10 @@ 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, IApplicationUser
public class ApplicationUser : IdentityUser
{
/// <summary>
/// Another me, as a byte array.TG7@Eu%80rufzkhbb
@ -24,10 +22,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>
@ -35,31 +33,32 @@ 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
@ -67,7 +66,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
@ -82,14 +81,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;
@ -98,24 +97,21 @@ 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; }
}
}

View File

@ -5,8 +5,6 @@ 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;
@ -16,8 +14,7 @@ using Yavsc.ViewModels.Blog;
namespace Yavsc.Models.Blog
{
public class BlogPost : BlogPostBase,
IBlogPost, ICircleAuthorized, ITaggable<long>
public class BlogPost : BlogPostInputViewModel, IBlogPost, ICircleAuthorized, ITaggable<long>, IIdentified<long>
{
[Key(), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Display(Name="Identifiant du post")]
@ -88,6 +85,7 @@ namespace Yavsc.Models.Blog
[InverseProperty("Post")]
public virtual List<Comment> Comments { get; set; }
IApplicationUser IBlogPost.Author { get => this.Author; }
[NotMapped]
public string OwnerId => AuthorId;
}
}

View File

@ -1,15 +0,0 @@
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; }
}
}

View File

@ -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}")]

View File

@ -1,33 +0,0 @@
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; }
}
}

View File

@ -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;
}

View File

@ -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; }
}

View File

@ -1,13 +0,0 @@
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();
}
}

View File

@ -1,193 +0,0 @@
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);
}
}

View File

@ -130,6 +130,7 @@ 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))

View File

@ -1,9 +1,13 @@
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;
@ -23,7 +27,8 @@ namespace Yavsc.Services
public MailSender(
IOptions<SiteSettings> sitesOptions,
IOptions<SmtpSettings> smtpOptions,
ILoggerFactory loggerFactory
ILoggerFactory loggerFactory,
IStringLocalizer<Yavsc.YavscLocalization> localizer
)
{
this.localizer = localizer;

View File

@ -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(JwtClaimTypes.Role, r)));
claims.AddRange(roles.Select(r => new Claim(Constants.RoleClaimName, 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);
}

View File

@ -6,9 +6,7 @@ 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";

View File

@ -1,50 +1,11 @@
using System.ComponentModel.DataAnnotations;
using Yavsc.Models.Blog;
namespace Yavsc.ViewModels.Blog;
public class BlogPostCreateViewModel : BlogPostBase
{
public bool Publish { get; set; }
}
public class BlogPostEditViewModel : BlogPostCreateViewModel
public class BlogPostEditViewModel : BlogPostInputViewModel
{
[Required]
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
};
}
public required long Id { get; set; }
}

View File

@ -5,8 +5,8 @@ using Yavsc.Models.Access;
namespace Yavsc.ViewModels.Blog
{
public class BlogPostBase
{
public class BlogPostInputViewModel
{
[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; }
}

View File

@ -8,7 +8,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
<PackageReference Include="HigginsSoft.IdentityServer8" Version="8.0.5-preview-net9" />
<PackageReference Include="HigginsSoft.IdentityServer8" Version="8.0.4" />
<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,14 +19,20 @@
<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.69.0.3746" />
<PackageReference Include="Google.Apis.Calendar.v3" Version="1.60.0.2993" />
<PackageReference Include="PayPalMerchantSDK" Version="2.16.250" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.10" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.9" />
<PackageReference Include="RazorEngine.NetCore" Version="3.1.0" />
<PackageReference Include="MailKit" Version="4.13.0" />
<PackageReference Include="MimeKit" Version="4.13.0" />
<PackageReference Include="MailKit" Version="4.12.1" />
<PackageReference Include="MimeKit" Version="4.12.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>

View File

@ -1,228 +0,0 @@
/*
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 } });
}
}
}

View File

@ -1,22 +0,0 @@
/*
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);
}

View File

@ -1,15 +1,22 @@
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 Yavsc.Server.Exceptions;
using System.Collections;
// For more information on enabling Web API for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860
@ -22,70 +29,111 @@ namespace Yavsc.Controllers
private readonly IAuthorizationService _authorizationService;
readonly RequestLocalizationOptions _localisationOptions;
readonly BlogSpotService blogSpotService;
public BlogspotController(
ApplicationDbContext context,
ILoggerFactory loggerFactory,
IAuthorizationService authorizationService,
IOptions<RequestLocalizationOptions> localisationOptions,
BlogSpotService blogSpotService)
IOptions<RequestLocalizationOptions> localisationOptions)
{
_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 blogSpotService.UserPosts(id, User.GetUserId(),
skip, take));
if (!string.IsNullOrEmpty(id)) {
return View("UserPosts", await UserPosts(id));
}
IEnumerable<IBlogPost> index = await this.blogSpotService.Index(User, id, skip, take);
return View(index);
IEnumerable<BlogPost> 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.Count == 0)
|| (p.AuthorId == viewerId)
|| (usercircles != null && p.ACL.Any(a => usercircles.Contains(a.CircleId)))
);
}
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);
}
[Route("~/Title/{id?}")]
[AllowAnonymous]
public IActionResult Title(string id)
{
var uid = User.FindFirstValue(ClaimTypes.NameIdentifier);
ViewData["Title"] = id;
return View("Title", blogSpotService.GetTitle(id));
return View("Title", _context.BlogSpot.Include(
b => b.Author
).Where(x => x.Title == id && (x.AuthorId == uid )).OrderByDescending(
x => x.DateCreated
).ToList());
}
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)
{
return await blogSpotService.UserPosts(userName, User.GetUserId(), pageLen, pageNum);
string posterId = (await _context.Users.SingleOrDefaultAsync(u=>u.UserName == userName))?.Id ?? null ;
return _context.UserPosts(posterId, User.Identity.Name);
}
// GET: Blog/Details/5
[AllowAnonymous]
public async Task<IActionResult> Details(long? id)
{
if (id == null) return this.NotFound();
try
if (id == null)
{
var blog = await blogSpotService.Details(User, id.Value);
ViewData["apicmtctlr"] = "/api/blogcomments";
ViewData["moderatoFlag"] = User.IsInRole(Constants.BlogModeratorGroupName);
return View(blog);
return NotFound();
}
catch (AuthorizationFailureException ex)
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 Challenge();
return NotFound();
}
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 }
);
@ -95,25 +143,33 @@ namespace Yavsc.Controllers
[Authorize()]
public IActionResult Create(string title)
{
var result = new BlogPostCreateViewModel
{
Title = title
var result = new BlogPostInputViewModel{Title=title
};
ViewData["PostTarget"]="Create";
SetLangItems();
return View(result);
}
// POST: Blog/Create
[HttpPost, Authorize, ValidateAntiForgeryToken]
public IActionResult Create(BlogPostEditViewModel blogInput)
public IActionResult Create(BlogPostInputViewModel blogInput)
{
if (ModelState.IsValid)
{
BlogPost post = blogSpotService.Create(User.GetUserId(),
BlogPostEditViewModel.FromViewModel(blogInput));
BlogPost post = new BlogPost
{
Title = blogInput.Title,
Content = blogInput.Content,
Photo = blogInput.Photo,
AuthorId = User.GetUserId()
};
_context.BlogSpot.Add(post);
_context.SaveChanges(User.GetUserId());
return RedirectToAction("Index");
}
return View("Edit", blogInput);
ModelState.AddModelError("Unknown","Invalid Blog posted ...");
ViewData["PostTarget"]="Create";
return View("Edit",blogInput);
}
[Authorize()]
@ -124,18 +180,37 @@ namespace Yavsc.Controllers
{
return NotFound();
}
try
{
var blog = await blogSpotService.GetPostForEdition(User, id.Value);
if (blog == null)
{
return NotFound();
}
SetLangItems();
return View(blog);
ViewData["PostTarget"]="Edit";
BlogPost blog = _context.BlogSpot.Include(x => x.Author).Include(x => x.ACL).Single(m => m.Id == id);
if (blog == null)
{
return NotFound();
}
catch (AuthorizationFailureException)
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
{
Id = blog.Id,
Title = blog.Title,
Content = blog.Content,
ACL = blog.ACL,
Photo = blog.Photo
});
}
else
{
return new ChallengeResult();
}
@ -143,41 +218,65 @@ namespace Yavsc.Controllers
// POST: Blog/Edit/5
[HttpPost]
[ValidateAntiForgeryToken, Authorize()]
[ValidateAntiForgeryToken,Authorize()]
public async Task<IActionResult> Edit(BlogPostEditViewModel blogEdit)
{
if (ModelState.IsValid)
{
await blogSpotService.Modify(User, blogEdit);
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());
ViewData["StatusMessage"] = "Post modified";
return RedirectToAction("Index");
}
ViewData["PostTarget"]="Edit";
return View(blogEdit);
}
// GET: Blog/Delete/5
[ActionName("Delete"), Authorize()]
public async Task<IActionResult> Delete(long? id)
[ActionName("Delete"),Authorize()]
public IActionResult Delete(long? id)
{
if (id == null)
{
return NotFound();
}
var blog = await blogSpotService.GetBlogPostAsync(id.Value);
BlogPost blog = _context.BlogSpot.Include(
b => b.Author
).Single(m => m.Id == id);
if (blog == null)
{
return NotFound();
}
return View(blog);
}
// POST: Blog/Delete/5
[HttpPost, ActionName("Delete"), Authorize("TheAuthor")]
[HttpPost, ActionName("Delete"), Authorize("IsTheAuthor")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(long id)
public IActionResult DeleteConfirmed(long id)
{
await blogSpotService.Delete(User, id);
var uid = User.GetUserId();
BlogPost blog = _context.BlogSpot.Single(m => m.Id == id);
_context.BlogSpot.Remove(blog);
_context.SaveChanges(User.GetUserId());
return RedirectToAction("Index");
}
}

View File

@ -33,337 +33,13 @@ 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
{
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());
}
}
#region files config
public static IApplicationBuilder ConfigureFileServerApp(this IApplicationBuilder app,
bool enableDirectoryBrowsing = false)
{
@ -415,5 +91,321 @@ 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());
}
}
}

View File

@ -1,9 +1,7 @@
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;
@ -34,10 +32,6 @@ public class PermissionHandler : IAuthorizationHandler
{
context.Succeed(requirement);
}
else if (context.User.IsInRole("Administrator"))
{
context.Succeed(requirement);
}
}
else if (requirement is EditPermission || requirement is DeletePermission)
{
@ -55,33 +49,18 @@ public class PermissionHandler : IAuthorizationHandler
{
if (resource is BlogPost blogPost)
{
return
applicationDbContext.blogSpotPublications
.Any(p=>p.BlogpostId == blogPost.Id);
if (blogPost.ACL.Count==0)
return true;
}
return false;
}
private bool IsOwner(ClaimsPrincipal user, object? resource)
private static 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;
}
@ -91,7 +70,7 @@ public class PermissionHandler : IAuthorizationHandler
{
return applicationDbContext.CircleMembers
.Include(c => c.Circle)
.Where(m=>m.MemberId==user.GetUserId() && m.Circle.OwnerId == blogPost.AuthorId)
.Where(m=>m.MemberId==user.GetUserId() && m.Circle.OwnerId == blogPost.OwnerId)
.Any();
}
return true;

View File

@ -1,50 +1,27 @@
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;
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");
}
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);
}
}
}

View File

@ -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.IsStale) {
if (token.IsExpired(c)) {
token = await RefreshToken(googleAuthSettings, token);
}
return new UserCredential(flow, googleUserLoginKey, token);

View File

@ -1,6 +1,6 @@
DESTDIR=/srv/www/yavsc
CONFIGURATION=Release
USER_AND_GROUP=www-data:www-data
USER=www-data
SERVICE_PROD=yavsc
DOTNET_FRAMEWORK=net9.0
@ -22,17 +22,12 @@ install_service:
pushInProd: publish
sudo systemctl stop $(SERVICE_PROD)
sudo cp -a bin/$(CONFIGURATION)/$(DOTNET_FRAMEWORK)/publish/* $(DESTDIR)
sudo chown -R $(USER_AND_GROUP) $(DESTDIR)
sudo chown -R $(USER) $(DESTDIR)
sudo sync
sudo systemctl start $(SERVICE_PROD)
%.css: %.scss
scss $^ > $@
%.min.js: %.js
jsmin < $^ > $@
%.min.css: %.css
jsmin < $^ > $@
css: wwwroot/css/site.css

View File

@ -6,7 +6,7 @@ namespace Yavsc
{
public class Program
{
public static async Task Main(string[] args)
public static void 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 = await builder.ConfigureWebAppServices().ConfigurePipeline();
var app = builder.ConfigureWebAppServices().ConfigurePipeline();
app.UseSession();
app.Run();
}

View File

@ -1,32 +0,0 @@
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);
}
}

View File

@ -1,5 +1,7 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Options;
using Yavsc.Helpers;
using Yavsc.Server.Helpers;
using Yavsc.Settings;

View File

@ -22,7 +22,7 @@ namespace Yavsc.ViewComponents
{
if (target!=null)
{
var oid = target.AuthorId;
var oid = target.OwnerId;
ViewBag.ACL = dbContext.Circle.Where(
c=>c.OwnerId == oid)
.Select(

View File

@ -1,4 +1,4 @@
@model BlogPostEditViewModel
@model Yavsc.ViewModels.Blog.BlogPostInputViewModel
@{
ViewData["Title"] = "Blog post edition";
@ -47,7 +47,6 @@
</span>
</div>
</div>
<div class="form-group mdcoding">
<label asp-for="Content" class="col-md-2 control-label" ></label>
<div class="col-md-10">
@ -57,7 +56,6 @@
</span>
</div>
</div>
<div class="form-group">
<label asp-for="ACL" class="col-md-2 control-label"></label>
<div class="col-md-10">
@ -65,13 +63,6 @@
</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>

View File

@ -60,14 +60,10 @@ $('#commentValidation').html(
}
</style>
}
<div class="post">
<div class="float-left">
<img alt="" src="@Model.Photo" >
</div>
<h1 ismarkdown>@Model.Title</h1>
<div class="container">
<div class="blogpost">
<h1 class="blogtitle" ismarkdown>@Model.Title</h1>
<img class="blogphoto" alt="" src="@Model.Photo" >
@Html.DisplayFor(m=>m.Author)
<asciidoc>@Html.DisplayFor(m=>m.Content)</asciidoc>
@ -102,4 +98,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>

View File

@ -39,7 +39,7 @@
}
<h2 >Blog post</h2>
<h2 > Blog post edition </h2>
<label><input type="checkbox" id="vcbtn" />Editer le code source Markdown</label>
<div asp-validation-summary="All" class="text-danger"></div>
@ -97,13 +97,6 @@
</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" />
@ -118,3 +111,5 @@
<div>
<a asp-action="Index">Back to List</a>
</div>
using Yavsc.Migrations;

View File

@ -1,4 +1,4 @@
@model IEnumerable<IBlogPost>
@model IEnumerable<IGrouping<string,BlogPost>>
@{
ViewData["Title"] = "Blogs, l'index";
}
@ -43,51 +43,58 @@
<a asp-action="Create">Create a new article</a>
</p>
}
<div class="blog">
@{
int maxTextLen = 75;
foreach (var post in Model) {
<div class="post">
<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>
<div class="container">
<table class="table">
@foreach (var group in Model) {
var title = group.Key ?? "@";
string secondclass="";
var first = group.First();
int maxTextLen = 256;
<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>
</table>
@if(Model.Count()==0){<p>Néant</p>}
</div>

View File

@ -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-light">Details</a>
<a asp-action="Details" asp-route-id="@item.Id" class="btn btn-lg">Details</a>
</li>
}
@if ((await AuthorizationService.AuthorizeAsync(User, item, new EditPermission())).Succeeded) {

View File

@ -51,7 +51,7 @@
@act.Description </p>
@if (act.Children.Count>0) {
<a asp-route-id="@act.Code" class="btn btn-light">
<a asp-route-id="@act.Code" class="btn btn-default">
@foreach (Activity c in act.Children) {
 @Html.DisplayFor(subact=>c)
}

View File

@ -19,7 +19,7 @@
<environment names="Development">
<script>
var CREATE_PAYMENT_URL = 'http://pschneider.fr:85/api/payment/create';
var EXECUTE_PAYMENT_URL = 'http://pschneider.fr:85/api/payment/execute';
var EXECUTE_PAYMENT_URL = 'http://dev.pschneider.fr:85/api/payment/execute';
var PAYPAL_ENV = 'sandbox';
</script>
</environment>

View File

@ -20,9 +20,9 @@
}
</ul>
<p>
<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" />
<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" />
</p>
</form>

View File

@ -1,10 +1,14 @@
@using Yavsc.Abstract.Identity
@model ApplicationUser
@{
var avuri = "/Avatars/" + Model.UserName + ".s.png";
var avuri = "/Avatars/"+Model.UserName+".s.png";
var userPosted = Model.Posts!=null && Model.Posts.Count()>=1;
}
<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>
@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);
}
</div>

View File

@ -1,10 +0,0 @@
@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>

View File

@ -14,6 +14,6 @@
<input type="hidden" name="@parameter.Key" value="@parameter.Value" />
}
<input class="btn btn-light btn-success" name="Authorize" type="submit" value="Yeah, sure" />
<input class="btn btn-lg btn-success" name="Authorize" type="submit" value="Yeah, sure" />
</form>
</div>
</div>

View File

@ -37,7 +37,7 @@
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<button type="submit" class="btn btn-light btn-success" name="submit.Signin">Login</button>
<button type="submit" class="btn btn-lg btn-success" name="submit.Signin">Login</button>
</div>
</div>
<p>

View File

@ -16,10 +16,7 @@
<script src="~/js/site.js" asp-append-version="true"></script>
@await RenderSectionAsync("header", false)
</head>
<body style="background-image: url('@Config.SiteSetup.Banner');
background-position: 0%;
background-attachment: fixed;
">
<body>
<partial name="_Nav" />
@RenderSection("ctxmenu", required: false)
@if (ViewData["Notify"] != null)

View File

@ -2,15 +2,23 @@
@inject SignInManager<ApplicationUser> SignInManager
@inject UserManager<ApplicationUser> UserManager
@if (Context.User?.Identity?.IsAuthenticated ?? false)
{
string userName = User.GetUserName();
@{
#nullable enable
string? name = null;
if (Context.User!=null)
{
name = Context.User.GetUserName();
}
}
@if (name!=null)
{
<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>
@ -30,7 +38,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 @userName!</a>
<a class="nav-link dropdown-toggle" href="#" id="dropdown04" data-bs-toggle="dropdown" aria-expanded="false">Hello @UserManager.GetUserName(User)!</a>
<ul class="dropdown-menu">
<li>
<a class="dropdown-item" asp-controller="Manage" asp-action="Index" title="Manage">
@ -54,4 +62,3 @@ else
<a class="nav-link" asp-controller="Account" asp-action="Login" asp-route-ReturnUrl="~/" >Login</a>
</li>
}

View File

@ -1,18 +1,24 @@
<nav class="navbar navbar-expand-sm navbar-dark bg-dark" aria-label="Yavsc">
<a class="navbar-brand" href="#">@Config.SiteSetup.Title</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
<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"
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" style="background-image: @Config.SiteSetup.Banner;">
<div class="collapse navbar-collapse" id="navbar">
<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>

View File

@ -10,38 +10,36 @@
<PackageReference Include="popper.js" Version="1.16.1">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="bootstrap" Version="5.3.7">
<PackageReference Include="bootstrap" Version="5.3.3">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="HigginsSoft.IdentityServer8" Version="8.0.5-preview-net9" />
<PackageReference Include="HigginsSoft.IdentityServer8.AspNetIdentity" Version="8.0.5-preview-net9" />
<PackageReference Include="HigginsSoft.IdentityServer8.AspNetIdentity" Version="8.0.4" />
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
<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">
<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">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.7">
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.6">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<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.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.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.7" />
<PackageReference Include="System.Security.Cryptography.Pkcs" Version="9.0.6" />
<PackageReference Include="Microsoft.AspNetCore.Antiforgery" Version="2.3.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.3" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.4" />
<PackageReference Include="AsciiDocNet" Version="1.0.0-alpha6" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.10" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="9.0.7" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.9" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../Yavsc.Server/Yavsc.Server.csproj" />

View File

@ -13,6 +13,7 @@
.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);
@ -24,28 +25,13 @@ 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; }
.cmtdatetime {
font-style: italic;
font-size: x-small;
}
.blogcomment {
display: inline-block;
padding: .3em;
border: solid black 1px;
}

View File

@ -1,5 +1,3 @@
.grants-page {
.card {
margin-top: 20px;
@ -25,6 +23,10 @@
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);
@ -37,36 +39,3 @@ 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;
}

View File

@ -10,6 +10,7 @@
@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
@ -27,8 +28,9 @@
@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: none;
background-color: var(--#{$prefix}nav-tabs-link-active-bg);
border: 0;
@include transition($nav-link-transition);

View File

@ -1607,7 +1607,6 @@ $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
@ -1619,7 +1618,6 @@ $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
@ -1747,5 +1745,3 @@ $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

View File

@ -11,6 +11,7 @@
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;

View File

@ -1,6 +1,5 @@
User-agent: *
Disallow: /signin
Disallow: /signin-google
Disallow: /Account/Register
Disallow: /External
Disallow: /Command/Create

View File

@ -13,7 +13,8 @@
using System.IdentityModel.Tokens.Jwt;
using Microsoft.AspNetCore.Authentication;
JwtSecurityTokenHandler.DefaultMapInboundClaims = true;
JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
var builder = WebApplication.CreateBuilder(args);
@ -27,7 +28,8 @@ builder.Services
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
options.Authority = builder.Configuration.GetValue<String>("AuthIssuer");
options.Authority = "https://localhost:5001";
options.ClientId = "mvc";
options.ClientSecret = "49C1A7E1-0C79-4A89-A3D6-A37998FB86B0";
options.ResponseType = "code";
@ -36,14 +38,15 @@ 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

View File

@ -19,26 +19,26 @@
}
<form action="~/Home/GetUserInfo" method="post">
<button class="btn btn-light btn-warning" type="submit">Get user info</button>
<button class="btn btn-lg btn-warning" type="submit">Get user info</button>
</form>
<form action="~/Home/GetApiCall" method="post">
<button class="btn btn-light btn-warning" type="submit">Api Call</button>
<button class="btn btn-lg btn-warning" type="submit">Api Call</button>
</form>
<form action="~/Home/PostDeviceInfo" method="post">
<button class="btn btn-light btn-warning" type="submit">Post device info</button>
<button class="btn btn-lg 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 &quot;test&quot; <input type="file" name="file" multiple/>
<button class="btn btn-light btn-warning" type="submit">Post files</button>
<button class="btn btn-lg btn-warning" type="submit">Post files</button>
</form>
<a class="btn btn-light btn-danger" href="/signout">Sign out</a>
<a class="btn btn-lg btn-danger" href="/signout">Sign out</a>
}
else {
<h1>Welcome, anonymous</h1>
<a class="btn btn-light btn-success" href="/signin">Sign in</a>
<a class="btn btn-lg btn-success" href="/signin">Sign in</a>
}
</div>

View File

@ -6,7 +6,6 @@
}
},
"AllowedHosts": "*",
"AuthIssuer": "https://localhost:5001",
"Kestrel": {
"Endpoints":
{

View File

@ -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.7" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.6" />
</ItemGroup>
<ItemGroup>

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

@ -2120,6 +2120,9 @@ 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);
@ -12037,4 +12040,4 @@ textarea.form-control-lg {
}
}
/*# sourceMappingURL=bootstrap.css.map */
/*# sourceMappingURL=bootstrap.css.map */

View File

@ -11,6 +11,7 @@
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;

Some files were not shown because too many files have changed in this diff Show More