reorg
This commit is contained in:
parent
d000f77098
commit
40e8e08690
3487 changed files with 39 additions and 21 deletions
27
src/Yavsc.Org/Extensions/ControlerExtensions.cs
Normal file
27
src/Yavsc.Org/Extensions/ControlerExtensions.cs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
using IdentityServer8.Models;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Yavsc.Models.Access;
|
||||
|
||||
namespace Yavsc.Extensions ;
|
||||
|
||||
public static class Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if the redirect URI is for a native client.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static bool IsNativeClient(this AuthorizationRequest context)
|
||||
{
|
||||
return !context.RedirectUri.StartsWith("https", StringComparison.Ordinal)
|
||||
&& !context.RedirectUri.StartsWith("http", StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
public static IActionResult LoadingPage(this Controller controller, string viewName, string redirectUri)
|
||||
{
|
||||
controller.HttpContext.Response.StatusCode = 200;
|
||||
controller.HttpContext.Response.Headers["Location"] = "";
|
||||
|
||||
return controller.View(viewName, new RedirectViewModel { RedirectUrl = redirectUri });
|
||||
}
|
||||
}
|
||||
|
||||
88
src/Yavsc.Org/Extensions/EnumExtensions.cs
Normal file
88
src/Yavsc.Org/Extensions/EnumExtensions.cs
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
namespace Yavsc.Extensions
|
||||
{
|
||||
public static class EnumExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Builds select items from an enum type
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="SR"></param>
|
||||
/// <param name="valueSelected"></param>
|
||||
/// <returns></returns>
|
||||
public static List<SelectListItem> GetSelectList (Type type, IStringLocalizer SR, Enum valueSelected)
|
||||
{
|
||||
var typeInfo = type.GetTypeInfo();
|
||||
var values = Enum.GetValues(type).Cast<Enum>();
|
||||
var items = new List<SelectListItem>();
|
||||
|
||||
foreach (var value in values)
|
||||
{
|
||||
items.Add(new SelectListItem {
|
||||
Text = SR[GetDescription(value, typeInfo)],
|
||||
Value = value.ToString(),
|
||||
Selected = value == valueSelected
|
||||
});
|
||||
}
|
||||
return items;
|
||||
}
|
||||
public static List<SelectListItem> GetSelectList (Type type, IStringLocalizer SR, string selectedValue = null)
|
||||
{
|
||||
var typeInfo = type.GetTypeInfo();
|
||||
var values = Enum.GetValues(type).Cast<Enum>();
|
||||
var items = new List<SelectListItem>();
|
||||
|
||||
foreach (var value in values)
|
||||
{
|
||||
var strval = value.ToString();
|
||||
|
||||
items.Add(new SelectListItem {
|
||||
Text = SR[GetDescription(value, typeInfo)],
|
||||
Value = strval,
|
||||
Selected = strval == selectedValue
|
||||
});
|
||||
}
|
||||
return items;
|
||||
}
|
||||
public static string GetDescription(this Enum value, TypeInfo typeInfo )
|
||||
{
|
||||
var declaredMember = typeInfo.DeclaredMembers.FirstOrDefault(i => i.Name == value.ToString());
|
||||
var attribute = declaredMember?.GetCustomAttribute<DisplayAttribute>();
|
||||
return attribute == null ? value.ToString() : attribute.Description ?? attribute.Name;
|
||||
}
|
||||
public static string GetDescription(this Enum value)
|
||||
{
|
||||
var type = value.GetType();
|
||||
var typeInfo = type.GetTypeInfo();
|
||||
return GetDescription(value, typeInfo);
|
||||
}
|
||||
|
||||
public static IEnumerable<string> GetDescriptions(Type type)
|
||||
{
|
||||
var values = Enum.GetValues(type).Cast<Enum>();
|
||||
var descriptions = new List<string>();
|
||||
|
||||
foreach (var value in values)
|
||||
{
|
||||
descriptions.Add(value.GetDescription());
|
||||
}
|
||||
|
||||
return descriptions;
|
||||
}
|
||||
|
||||
public static Enum GetEnumFromDescription(string description, Type enumType)
|
||||
{
|
||||
var enumValues = Enum.GetValues(enumType).Cast<Enum>();
|
||||
var descriptionToEnum = enumValues.ToDictionary(k => k.GetDescription(), v => v);
|
||||
return descriptionToEnum[description];
|
||||
}
|
||||
}
|
||||
}
|
||||
492
src/Yavsc.Org/Extensions/HostingExtensions.cs
Normal file
492
src/Yavsc.Org/Extensions/HostingExtensions.cs
Normal file
|
|
@ -0,0 +1,492 @@
|
|||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using Google.Apis.Util.Store;
|
||||
using IdentityServer8;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using IdentityServer8.Stores;
|
||||
using IdentityServer8.EntityFramework;
|
||||
using IdentityServer8.Extensions;
|
||||
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Localization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Razor;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Newtonsoft.Json;
|
||||
using Yavsc.Helpers;
|
||||
using Yavsc.Interface;
|
||||
using Yavsc.Models;
|
||||
using Yavsc.Services;
|
||||
using Yavsc.Settings;
|
||||
using Yavsc.ViewModels.Auth;
|
||||
using Yavsc.Server.Helpers;
|
||||
using System.Security.Cryptography;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using Microsoft.IdentityModel.Protocols.Configuration;
|
||||
using IdentityModel;
|
||||
using Yavsc.Interfaces;
|
||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||
using Npgsql;
|
||||
using System.Reflection;
|
||||
using IdentityServer8.EntityFramework.DbContexts;
|
||||
using IdentityServer8.EntityFramework.Mappers;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using IdentityServer8.EntityFramework.Stores;
|
||||
using IdentityServer8.EntityFramework.Services;
|
||||
using IdentityServer8.EntityFramework.Interfaces;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
|
||||
namespace Yavsc.Extensions;
|
||||
|
||||
|
||||
public static class HostingExtensions
|
||||
{
|
||||
|
||||
public static WebApplication ConfigureWebAppServices(this WebApplicationBuilder builder)
|
||||
{
|
||||
builder.Services.AddSwaggerGen();
|
||||
|
||||
IServiceCollection services = LoadConfiguration(builder);
|
||||
|
||||
services.AddSession();
|
||||
|
||||
// TODO .AddServerSideSessionStore<YavscServerSideSessionStore>()
|
||||
|
||||
|
||||
// Add the system clock service
|
||||
_ = 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>();
|
||||
|
||||
|
||||
services.AddAuthentication("Bearer")
|
||||
.AddJwtBearer("Bearer", options =>
|
||||
{
|
||||
options.IncludeErrorDetails = true;
|
||||
options.Authority = builder.Configuration.GetSection("Site")["Authority"];
|
||||
options.Audience = builder.Configuration.GetSection("Site")["Audience"];
|
||||
options.TokenValidationParameters =
|
||||
new()
|
||||
{
|
||||
ValidateAudience = false,
|
||||
RoleClaimType = Constants.RoleClaimType
|
||||
};
|
||||
options.MapInboundClaims = true;
|
||||
});
|
||||
|
||||
services.AddTransient<RoleManager<IdentityRole>>();
|
||||
services.AddTransient<IRoleStore<IdentityRole>, RoleStore<IdentityRole, ApplicationDbContext>>();
|
||||
|
||||
return builder.Build();
|
||||
}
|
||||
|
||||
public static IdentityBuilder AddIdentityDBAndStores(this WebApplicationBuilder builder)
|
||||
{
|
||||
IServiceCollection services = builder.Services;
|
||||
services.AddDbContext<ApplicationDbContext>(options =>
|
||||
{
|
||||
options.UseNpgsql(builder.Configuration.GetConnectionString(Constants.YavscConnectionStringName),
|
||||
options => options.MigrationsAssembly(typeof(Program).Assembly));
|
||||
});
|
||||
|
||||
return services.AddIdentity<ApplicationUser, IdentityRole>(
|
||||
options =>
|
||||
{
|
||||
options.SignIn.RequireConfirmedAccount = builder.Environment.IsEnvironment(
|
||||
builder.Environment.EnvironmentName);
|
||||
options.ClaimsIdentity.UserNameClaimType = JwtClaimTypes.PreferredUserName;
|
||||
options.ClaimsIdentity.RoleClaimType = Constants.RoleClaimType;
|
||||
}
|
||||
)
|
||||
.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(Constants.RoleClaimType,
|
||||
new string[] { Constants.PerformerGroupName, Constants.AdminGroupName })
|
||||
;
|
||||
});
|
||||
options.AddPolicy("AdministratorOnly", policy =>
|
||||
{
|
||||
_ = policy
|
||||
.RequireAuthenticatedUser()
|
||||
.RequireClaim(Constants.RoleClaimType, 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)
|
||||
{
|
||||
builder.Services.Configure<IdentityOptions>(options =>
|
||||
{
|
||||
options.ClaimsIdentity.UserIdClaimType = JwtClaimTypes.Subject;
|
||||
options.ClaimsIdentity.UserNameClaimType = JwtClaimTypes.Name;
|
||||
options.ClaimsIdentity.RoleClaimType = Constants.RoleClaimType;
|
||||
});
|
||||
var migrationsAssembly = typeof(Program).GetTypeInfo().Assembly.GetName().Name;
|
||||
var connectionString = builder.Configuration.GetConnectionString(Constants.YavscConnectionStringName);
|
||||
|
||||
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;
|
||||
|
||||
})
|
||||
.AddAspNetIdentity<ApplicationUser>()
|
||||
.AddClientStore<ClientStore>()
|
||||
.AddCorsPolicyService<CorsPolicyService>()
|
||||
.AddResourceStore<ResourceStore>()
|
||||
.AddConfigurationStore(options =>
|
||||
{
|
||||
options.ConfigureDbContext = b => b.UseNpgsql(connectionString,
|
||||
sql => sql.MigrationsAssembly(migrationsAssembly));
|
||||
})
|
||||
.AddOperationalStore(options =>
|
||||
{
|
||||
options.ConfigureDbContext = b => b.UseNpgsql(connectionString,
|
||||
sql => sql.MigrationsAssembly(migrationsAssembly));
|
||||
});
|
||||
|
||||
if (builder.Environment.IsDevelopment())
|
||||
{
|
||||
identityServerBuilder.AddDeveloperSigningCredential();
|
||||
}
|
||||
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 }
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public async static Task<WebApplication> ConfigurePipeline(this WebApplication app)
|
||||
{
|
||||
ILoggerFactory loggerFactory = app.Services.GetRequiredService<ILoggerFactory>();
|
||||
var logger = loggerFactory.CreateLogger<Program>();
|
||||
|
||||
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
|
||||
|
||||
JwtSecurityTokenHandler.DefaultMapInboundClaims = true;
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
else
|
||||
{
|
||||
app.UseExceptionHandler("/Home/Error");
|
||||
app.InitializeDatabase();
|
||||
}
|
||||
|
||||
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.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
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;
|
||||
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<Startup>>();
|
||||
Startup.Configure(app, siteSettings, smtpSettings,
|
||||
payPalSettings, googleAuthSettings, localization, loggerFactory,
|
||||
app.Environment.EnvironmentName);
|
||||
app.ConfigureFileServerApp();
|
||||
app.UseSession();
|
||||
return app;
|
||||
}
|
||||
private static void InitializeDatabase(this IApplicationBuilder app)
|
||||
{
|
||||
using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
foreach (Type contextType in new Type[]
|
||||
{
|
||||
typeof(PersistedGrantDbContext),
|
||||
typeof(ConfigurationDbContext),
|
||||
typeof(ApplicationDbContext)
|
||||
})
|
||||
{
|
||||
((DbContext)serviceScope.ServiceProvider
|
||||
.GetRequiredService(contextType))
|
||||
.Database.Migrate();
|
||||
}
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
app.Properties["DegradedDBContext"] = ex.Message;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void LoadGoogleConfig(IConfigurationRoot configuration)
|
||||
{
|
||||
string? googleClientFile = configuration["Authentication:Google:GoogleWebClientJson"];
|
||||
string? googleServiceAccountJsonFile = configuration["Authentication:Google:GoogleServiceAccountJson"];
|
||||
if (googleClientFile != null)
|
||||
{
|
||||
Config.GoogleWebClientConfiguration = new ConfigurationBuilder().AddJsonFile(googleClientFile).Build();
|
||||
}
|
||||
|
||||
if (googleServiceAccountJsonFile != null)
|
||||
{
|
||||
FileInfo safile = new FileInfo(googleServiceAccountJsonFile);
|
||||
Config.GServiceAccount = JsonConvert.DeserializeObject<GoogleServiceAccount>(safile.OpenText().ReadToEnd());
|
||||
}
|
||||
}
|
||||
|
||||
public static IApplicationBuilder ConfigureFileServerApp(this IApplicationBuilder app,
|
||||
bool enableDirectoryBrowsing = false)
|
||||
{
|
||||
|
||||
var userFilesDirInfo = new DirectoryInfo(Config.SiteSetup.Blog);
|
||||
AbstractFileSystemHelpers.UserFilesDirName = userFilesDirInfo.FullName;
|
||||
|
||||
if (!userFilesDirInfo.Exists) userFilesDirInfo.Create();
|
||||
|
||||
Config.UserFilesOptions = new FileServerOptions()
|
||||
{
|
||||
FileProvider = new PhysicalFileProvider(AbstractFileSystemHelpers.UserFilesDirName),
|
||||
RequestPath = PathString.FromUriComponent(Constants.UserFilesPath),
|
||||
EnableDirectoryBrowsing = enableDirectoryBrowsing,
|
||||
};
|
||||
Config.UserFilesOptions.EnableDefaultFiles = true;
|
||||
Config.UserFilesOptions.StaticFileOptions.ServeUnknownFileTypes = true;
|
||||
|
||||
var avatarsDirInfo = new DirectoryInfo(Config.SiteSetup.Avatars);
|
||||
if (!avatarsDirInfo.Exists) avatarsDirInfo.Create();
|
||||
Config.AvatarsDirName = avatarsDirInfo.FullName;
|
||||
|
||||
Config.AvatarsOptions = new FileServerOptions()
|
||||
{
|
||||
FileProvider = new PhysicalFileProvider(Config.AvatarsDirName),
|
||||
RequestPath = PathString.FromUriComponent(Constants.AvatarsPath),
|
||||
EnableDirectoryBrowsing = enableDirectoryBrowsing
|
||||
};
|
||||
|
||||
|
||||
var gitdirinfo = new DirectoryInfo(Config.SiteSetup.GitRepository);
|
||||
Config.GitDirName = gitdirinfo.FullName;
|
||||
if (!gitdirinfo.Exists) gitdirinfo.Create();
|
||||
Config.GitOptions = new FileServerOptions()
|
||||
{
|
||||
FileProvider = new PhysicalFileProvider(Config.GitDirName),
|
||||
RequestPath = PathString.FromUriComponent(Constants.GitPath),
|
||||
EnableDirectoryBrowsing = enableDirectoryBrowsing,
|
||||
};
|
||||
Config.GitOptions.DefaultFilesOptions.DefaultFileNames.Add("index.md");
|
||||
Config.GitOptions.StaticFileOptions.ServeUnknownFileTypes = true;
|
||||
|
||||
app.UseFileServer(Config.UserFilesOptions);
|
||||
|
||||
app.UseFileServer(Config.AvatarsOptions);
|
||||
|
||||
app.UseFileServer(Config.GitOptions);
|
||||
app.UseStaticFiles();
|
||||
return app;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
54
src/Yavsc.Org/Extensions/HttpContextExtensions.cs
Normal file
54
src/Yavsc.Org/Extensions/HttpContextExtensions.cs
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
|
||||
using System.Security.Claims;
|
||||
using IdentityServer8;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Yavsc.Models;
|
||||
using Yavsc.Models.Access;
|
||||
|
||||
namespace Yavsc.Extensions;
|
||||
|
||||
internal static class HttpContextExtensions
|
||||
{
|
||||
public static async Task SignInAsync(this HttpContext context,
|
||||
ApplicationUser user, RoleManager<IdentityRole> roleManager,
|
||||
bool rememberMe,
|
||||
ApplicationDbContext applicationDbContext)
|
||||
{
|
||||
AuthenticationProperties props = null;
|
||||
if (AccountOptions.AllowRememberLogin && rememberMe)
|
||||
{
|
||||
props = new AuthenticationProperties
|
||||
{
|
||||
IsPersistent = true,
|
||||
ExpiresUtc = DateTimeOffset.UtcNow.Add(AccountOptions.RememberMeLoginDuration),
|
||||
// Parameters =
|
||||
};
|
||||
};
|
||||
|
||||
// roles
|
||||
var roles = applicationDbContext.UserRoles.Where(r => r.UserId == user.Id).ToArray();
|
||||
|
||||
// issue authentication cookie with subject ID and username
|
||||
|
||||
List<Claim> additionalClaims = new List<Claim>();
|
||||
|
||||
foreach (var role in roles)
|
||||
{
|
||||
var idRole = await roleManager.Roles.SingleOrDefaultAsync(i => i.Id == role.RoleId);
|
||||
if (idRole != null)
|
||||
{
|
||||
additionalClaims.Add(new Claim(ClaimTypes.Role, idRole.Name));
|
||||
}
|
||||
}
|
||||
additionalClaims.Add(new Claim(ClaimTypes.Name, user.UserName));
|
||||
var isUser = new IdentityServerUser(user.Id)
|
||||
{
|
||||
DisplayName = user.UserName,
|
||||
AdditionalClaims = additionalClaims.ToArray()
|
||||
};
|
||||
|
||||
await context.SignInAsync(isUser, props);
|
||||
}
|
||||
}
|
||||
99
src/Yavsc.Org/Extensions/PermissionHandler.cs
Normal file
99
src/Yavsc.Org/Extensions/PermissionHandler.cs
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
using System.Security.Claims;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Yavsc.Models;
|
||||
using Yavsc.Models.Blog;
|
||||
using Yavsc.Server.Helpers;
|
||||
using Yavsc.ViewModels.Auth;
|
||||
|
||||
namespace Yavsc.Extensions;
|
||||
|
||||
public class PermissionHandler : IAuthorizationHandler
|
||||
{
|
||||
ApplicationDbContext applicationDbContext;
|
||||
public PermissionHandler(ApplicationDbContext applicationDbContext)
|
||||
{
|
||||
this.applicationDbContext = applicationDbContext;
|
||||
}
|
||||
public Task HandleAsync(AuthorizationHandlerContext context)
|
||||
{
|
||||
var pendingRequirements = context.PendingRequirements.ToList();
|
||||
|
||||
foreach (var requirement in pendingRequirements)
|
||||
{
|
||||
if (requirement is ReadPermission)
|
||||
{
|
||||
if (IsPublic(context.Resource))
|
||||
{
|
||||
context.Succeed(requirement);
|
||||
}
|
||||
else if (IsOwner(context.User, context.Resource)
|
||||
|| IsSponsor(context.User, context.Resource))
|
||||
{
|
||||
context.Succeed(requirement);
|
||||
}
|
||||
else if (context.User.IsInMsRole("Administrator"))
|
||||
{
|
||||
context.Succeed(requirement);
|
||||
}
|
||||
}
|
||||
else if (requirement is EditPermission || requirement is DeletePermission)
|
||||
{
|
||||
if (IsOwner(context.User, context.Resource))
|
||||
{
|
||||
context.Succeed(requirement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private bool IsPublic(object? resource)
|
||||
{
|
||||
if (resource is BlogPost blogPost)
|
||||
{
|
||||
return
|
||||
applicationDbContext.blogSpotPublications
|
||||
.Any(p=>p.BlogpostId == blogPost.Id);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool IsOwner(ClaimsPrincipal user, object? resource)
|
||||
{
|
||||
if (resource is BlogPost blogPost)
|
||||
{
|
||||
return blogPost.AuthorId == user.GetUserId();
|
||||
}
|
||||
else
|
||||
if (resource is DefaultHttpContext httpContext)
|
||||
{
|
||||
if (httpContext.Request.Path.StartsWithSegments(
|
||||
"/Blogspot/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;
|
||||
}
|
||||
|
||||
private bool IsSponsor(ClaimsPrincipal user, object? resource)
|
||||
{
|
||||
if (resource is BlogPost blogPost)
|
||||
{
|
||||
return applicationDbContext.CircleMembers
|
||||
.Include(c => c.Circle)
|
||||
.Where(m=>m.MemberId==user.GetUserId() && m.Circle.OwnerId == blogPost.AuthorId)
|
||||
.Any();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue