files tree made better.

This commit is contained in:
Paul Schneider 2019-01-01 16:28:47 +00:00
commit ccc91bbf19
1630 changed files with 18209 additions and 41860 deletions

View file

@ -0,0 +1,28 @@
using System.Web.Optimization;
namespace Yavsc
{
public class BundleConfig
{
public static void RegisterBundles(BundleCollection bundles)
{
bundles.Add(new ScriptBundle("~/bundles/bootjq").Include(
"~/bower_components/bootstrap/dist/js",
"~/bower_components/jquery/dist/js",
"~/bower_components/jquery.validation/dist/js",
"~/bower_components/jquery-validation-unobtrusive/dist/js",
"~/bower_components/bootstrap-datepicker/dist/js"));
bundles.Add(new StyleBundle("~/Content/themes/base/css").Include(
));
bundles.Add(new ScriptBundle("~/bundles/markdown").Include(
"~/bower_components/dropzone/dist/min/dropzone-amd-module.min.js",
"~/bower_components/dropzone/dist/min/dropzone.min.js"
));
bundles.Add(new StyleBundle("~/Content/markdown").Include(
"~/bower_components/dropzone/dist/min/basic.min.css",
"~/bower_components/dropzone/dist/min/dropzone.min.css"
));
}
}
}

View file

@ -0,0 +1,40 @@
using System;
using System.IO;
using System.Web;
using Microsoft.AspNet.DataProtection.Infrastructure;
using Microsoft.Extensions.DependencyInjection;
namespace Yavsc
{
public partial class Startup
{
public void ConfigureProtectionServices(IServiceCollection services)
{
services.AddDataProtection();
services.Add(ServiceDescriptor.Singleton(typeof(IApplicationDiscriminator),
typeof(SystemWebApplicationDiscriminator)));
services.ConfigureDataProtection(configure =>
{
configure.SetApplicationName(Configuration["Site:Title"]);
configure.SetDefaultKeyLifetime(TimeSpan.FromDays(45));
configure.PersistKeysToFileSystem(
new DirectoryInfo(Configuration["DataProtection:Keys:Dir"]));
});
}
private sealed class SystemWebApplicationDiscriminator : IApplicationDiscriminator
{
private readonly Lazy<string> _lazyDiscriminator = new Lazy<string>(GetAppDiscriminatorCore);
public string Discriminator => _lazyDiscriminator.Value;
private static string GetAppDiscriminatorCore()
{
return HttpRuntime.AppDomainAppId;
}
}
}
}

View file

@ -0,0 +1,89 @@
using System.IO;
using System.Security.Claims;
using Microsoft.AspNet.Authorization;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.FileProviders;
using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.StaticFiles;
using Microsoft.Extensions.Logging;
using Yavsc.Abstract.FileSystem;
using Yavsc.ViewModels.Auth;
namespace Yavsc
{
public partial class Startup
{
public static FileServerOptions UserFilesOptions { get; private set; }
public static FileServerOptions GitOptions { get; private set; }
public static FileServerOptions AvatarsOptions { get; set; }
public void ConfigureFileServerApp(IApplicationBuilder app,
SiteSettings siteSettings, IHostingEnvironment env, IAuthorizationService authorizationService)
{
var userFilesDirInfo = new DirectoryInfo( siteSettings.Blog );
AbstractFileSystemHelpers.UserFilesDirName = userFilesDirInfo.FullName;
if (!userFilesDirInfo.Exists) userFilesDirInfo.Create();
UserFilesOptions = new FileServerOptions()
{
FileProvider = new PhysicalFileProvider(AbstractFileSystemHelpers.UserFilesDirName),
RequestPath = new PathString(Constants.UserFilesPath),
EnableDirectoryBrowsing = env.IsDevelopment(),
};
UserFilesOptions.EnableDefaultFiles=true;
UserFilesOptions.StaticFileOptions.ServeUnknownFileTypes=true;
/* TODO needs a better design, at implementation time (don't use database, but in memory data)
UserFilesOptions.StaticFileOptions.OnPrepareResponse += async context =>
{
var uname = context.Context.User.GetUserName();
var path = context.Context.Request.Path;
var result = await authorizationService.AuthorizeAsync(context.Context.User, new ViewFileContext
{ UserName = uname, File = context.File, Path = path } , new ViewRequirement());
};
*/
var avatarsDirInfo = new DirectoryInfo(Startup.SiteSetup.Avatars);
if (!avatarsDirInfo.Exists) avatarsDirInfo.Create();
AvatarsDirName = avatarsDirInfo.FullName;
AvatarsOptions = new FileServerOptions()
{
FileProvider = new PhysicalFileProvider(AvatarsDirName),
RequestPath = new PathString(Constants.AvatarsPath),
EnableDirectoryBrowsing = env.IsDevelopment()
};
var gitdirinfo = new DirectoryInfo(Startup.SiteSetup.GitRepository);
GitDirName = gitdirinfo.FullName;
if (!gitdirinfo.Exists) gitdirinfo.Create();
GitOptions = new FileServerOptions()
{
FileProvider = new PhysicalFileProvider(GitDirName),
RequestPath = new PathString(Constants.GitPath),
EnableDirectoryBrowsing = env.IsDevelopment(),
};
GitOptions.DefaultFilesOptions.DefaultFileNames.Add("index.md");
GitOptions.StaticFileOptions.ServeUnknownFileTypes = true;
logger.LogInformation( $"{GitDirName}");
GitOptions.StaticFileOptions.OnPrepareResponse+= OnPrepareGitRepoResponse;
app.UseFileServer(UserFilesOptions);
app.UseFileServer(AvatarsOptions);
app.UseFileServer(GitOptions);
app.UseStaticFiles();
}
static void OnPrepareGitRepoResponse(StaticFileResponseContext context)
{
if (context.File.Name.EndsWith(".ansi.log"))
{
context.Context.Response.Redirect("/Git"+context.Context.Request.Path);
}
}
}
}

View file

@ -0,0 +1,229 @@
using System;
using System.Security.Claims;
using Microsoft.AspNet.Authentication;
using Microsoft.AspNet.Authentication.Cookies;
using Microsoft.AspNet.Authentication.Facebook;
using Microsoft.AspNet.Authentication.Twitter;
using Microsoft.AspNet.Authentication.JwtBearer;
using Microsoft.AspNet.Authentication.OAuth;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.OptionsModel;
using Microsoft.Extensions.WebEncoders;
using OAuth.AspNet.AuthServer;
using OAuth.AspNet.Tokens;
using Google.Apis.Util.Store;
using Microsoft.Extensions.Logging;
using Google.Apis.Auth.OAuth2.Responses;
namespace Yavsc
{
using Auth;
using Extensions;
using Models;
using Helpers.Google;
public partial class Startup
{
public static CookieAuthenticationOptions ExternalCookieAppOptions { get; private set; }
public static IdentityOptions IdentityAppOptions { get; set; }
public static FacebookOptions FacebookAppOptions { get; private set; }
public static TwitterOptions TwitterAppOptions { get; private set; }
public static OAuthAuthorizationServerOptions OAuthServerAppOptions { get; private set; }
public static YavscGoogleOptions YavscGoogleAppOptions { get; private set; }
public static MonoDataProtectionProvider ProtectionProvider { get; private set; }
// public static CookieAuthenticationOptions BearerCookieOptions { get; private set; }
private void ConfigureOAuthServices(IServiceCollection services)
{
services.Configure<SharedAuthenticationOptions>(options => options.SignInScheme = Constants.ApplicationAuthenticationSheme);
services.Add(ServiceDescriptor.Singleton(typeof(IOptions<OAuth2AppSettings>), typeof(OptionsManager<OAuth2AppSettings>)));
// used by the YavscGoogleOAuth middelware (TODO drop it)
services.AddTransient<Microsoft.Extensions.WebEncoders.UrlEncoder, UrlEncoder>();
services.AddAuthentication(options =>
{
options.SignInScheme = Constants.ExternalAuthenticationSheme;
});
ProtectionProvider = new MonoDataProtectionProvider(Configuration["Site:Title"]); ;
services.AddInstance<MonoDataProtectionProvider>
(ProtectionProvider);
services.AddIdentity<ApplicationUser, IdentityRole>(
option =>
{
IdentityAppOptions = option;
option.User.AllowedUserNameCharacters += " ";
option.User.RequireUniqueEmail = true;
// option.Cookies.ApplicationCookieAuthenticationScheme = Constants.ApplicationAuthenticationSheme;
option.Cookies.ApplicationCookie.LoginPath = "/signin";
// option.Cookies.ApplicationCookie.AuthenticationScheme = Constants.ApplicationAuthenticationSheme;
/*
option.Cookies.ApplicationCookie.DataProtectionProvider = protector;
option.Cookies.ApplicationCookie.LoginPath = new PathString(Constants.LoginPath.Substring(1));
option.Cookies.ApplicationCookie.AccessDeniedPath = new PathString(Constants.AccessDeniedPath.Substring(1));
option.Cookies.ApplicationCookie.AutomaticAuthenticate = true;
option.Cookies.ApplicationCookie.AuthenticationScheme = Constants.ApplicationAuthenticationSheme;
option.Cookies.ApplicationCookieAuthenticationScheme = Constants.ApplicationAuthenticationSheme;
option.Cookies.TwoFactorRememberMeCookie.ExpireTimeSpan = TimeSpan.FromDays(30);
option.Cookies.TwoFactorRememberMeCookie.DataProtectionProvider = protector;
option.Cookies.ExternalCookieAuthenticationScheme = Constants.ExternalAuthenticationSheme;
option.Cookies.ExternalCookie.AutomaticAuthenticate = true;
option.Cookies.ExternalCookie.AuthenticationScheme = Constants.ExternalAuthenticationSheme;
option.Cookies.ExternalCookie.DataProtectionProvider = protector;
*/
}
).AddEntityFrameworkStores<ApplicationDbContext>()
.AddTokenProvider<EmailTokenProvider<ApplicationUser>>(Constants.DefaultFactor)
// .AddTokenProvider<UserTokenProvider>(Constants.DefaultFactor)
// .AddTokenProvider<UserTokenProvider>(Constants.SMSFactor)
// .AddTokenProvider<UserTokenProvider>(Constants.EMailFactor)
// .AddTokenProvider<UserTokenProvider>(Constants.AppFactor)
// .AddDefaultTokenProviders()
;
}
private void ConfigureOAuthApp(IApplicationBuilder app,
SiteSettings settingsOptions, ILogger logger)
{
app.UseIdentity();
app.UseWhen(context => context.Request.Path.StartsWithSegments("/api"),
branch =>
{
branch.UseJwtBearerAuthentication(
options =>
{
options.AuthenticationScheme = JwtBearerDefaults.AuthenticationScheme;
options.AutomaticAuthenticate = true;
options.SecurityTokenValidators.Clear();
options.SecurityTokenValidators.Add(new TicketDataFormatTokenValidator(
ProtectionProvider
));
}
);
});
app.UseWhen(context => !context.Request.Path.StartsWithSegments("/api"),
branch =>
{
// External authentication shared cookie:
branch.UseCookieAuthentication(options =>
{
ExternalCookieAppOptions = options;
options.AuthenticationScheme = Constants.ExternalAuthenticationSheme;
options.AutomaticAuthenticate = true;
options.ExpireTimeSpan = TimeSpan.FromMinutes(5);
options.LoginPath = new PathString(Constants.LoginPath.Substring(1));
// TODO implement an access denied page
options.AccessDeniedPath = new PathString(Constants.LoginPath.Substring(1));
});
YavscGoogleAppOptions = new YavscGoogleOptions
{
ClientId = GoogleWebClientConfiguration ["web:client_id"],
ClientSecret = GoogleWebClientConfiguration ["web:client_secret"],
AccessType = "offline",
Scope = { "profile", "https://www.googleapis.com/auth/plus.login",
"https://www.googleapis.com/auth/admin.directory.resource.calendar",
"https://www.googleapis.com/auth/calendar",
"https://www.googleapis.com/auth/calendar.events"},
SaveTokensAsClaims = true,
UserInformationEndpoint = "https://www.googleapis.com/plus/v1/people/me",
Events = new OAuthEvents
{
OnCreatingTicket = async context =>
{
using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>()
.CreateScope())
{
var gcontext = context as GoogleOAuthCreatingTicketContext;
context.Identity.AddClaim(new Claim(YavscClaimTypes.GoogleUserId, gcontext.GoogleUserId));
var dbContext = serviceScope.ServiceProvider.GetService<ApplicationDbContext>();
var store = serviceScope.ServiceProvider.GetService<IDataStore>();
await store.StoreAsync(gcontext.GoogleUserId, new TokenResponse {
AccessToken = gcontext.TokenResponse.AccessToken,
RefreshToken = gcontext.TokenResponse.RefreshToken,
TokenType = gcontext.TokenResponse.TokenType,
ExpiresInSeconds = int.Parse(gcontext.TokenResponse.ExpiresIn),
IssuedUtc = DateTime.Now
});
await dbContext.StoreTokenAsync (gcontext.GoogleUserId,
gcontext.TokenResponse.Response,
gcontext.TokenResponse.AccessToken,
gcontext.TokenResponse.TokenType,
gcontext.TokenResponse.RefreshToken,
gcontext.TokenResponse.ExpiresIn);
}
}
}
};
branch.UseMiddleware<Yavsc.Auth.GoogleMiddleware>(YavscGoogleAppOptions);
/* FIXME 403
branch.UseTwitterAuthentication(options=>
{
TwitterAppOptions = options;
options.ConsumerKey = Configuration["Authentication:Twitter:ClientId"];
options.ConsumerSecret = Configuration["Authentication:Twitter:ClientSecret"];
}); */
branch.UseOAuthAuthorizationServer(
options =>
{
OAuthServerAppOptions = options;
options.AuthorizeEndpointPath = new PathString(Constants.AuthorizePath.Substring(1));
options.TokenEndpointPath = new PathString(Constants.TokenPath.Substring(1));
options.ApplicationCanDisplayErrors = true;
options.AllowInsecureHttp = true;
options.AuthenticationScheme = OAuthDefaults.AuthenticationType;
options.TokenDataProtector = ProtectionProvider.CreateProtector("Bearer protection");
options.Provider = new OAuthAuthorizationServerProvider
{
OnValidateClientRedirectUri = ValidateClientRedirectUri,
OnValidateClientAuthentication = ValidateClientAuthentication,
OnGrantResourceOwnerCredentials = GrantResourceOwnerCredentials,
OnGrantClientCredentials = GrantClientCredetails
};
options.AuthorizationCodeProvider = new AuthenticationTokenProvider
{
OnCreate = CreateAuthenticationCode,
OnReceive = ReceiveAuthenticationCode,
};
options.RefreshTokenProvider = new AuthenticationTokenProvider
{
OnCreate = CreateRefreshToken,
OnReceive = ReceiveRefreshToken,
};
options.AutomaticAuthenticate = true;
options.AutomaticChallenge = true;
}
);
});
Environment.SetEnvironmentVariable ("GOOGLE_APPLICATION_CREDENTIALS", "google-secret.json");
}
}
}

View file

@ -0,0 +1,169 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Security.Principal;
using System.Threading.Tasks;
using Microsoft.AspNet.Identity;
using Microsoft.Extensions.Logging;
using OAuth.AspNet.AuthServer;
using Yavsc.Models;
using Yavsc.Models.Auth;
namespace Yavsc
{
public partial class Startup
{
private Client GetApplication(string clientId)
{
if (_dbContext==null)
logger.LogError("no db!");
Client app = _dbContext.Applications.FirstOrDefault(x => x.Id == clientId);
if (app==null)
logger.LogError("no app!");
return app;
}
private readonly ConcurrentDictionary<string, string> _authenticationCodes = new ConcurrentDictionary<string, string>(StringComparer.Ordinal);
private Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
{
if (context == null) throw new InvalidOperationException("context == null");
var app = GetApplication(context.ClientId);
if (app == null) return Task.FromResult(0);
Startup.logger.LogInformation($"ValidateClientRedirectUri: Validated ({app.RedirectUri})");
context.Validated(app.RedirectUri);
return Task.FromResult(0);
}
private Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
string clientId, clientSecret;
if (context.TryGetBasicCredentials(out clientId, out clientSecret) ||
context.TryGetFormCredentials(out clientId, out clientSecret))
{
logger.LogInformation($"ValidateClientAuthentication: Got id: ({clientId} secret: {clientSecret})");
var client = GetApplication(clientId);
if (client==null) {
context.SetError("invalid_clientId", "Client secret is invalid.");
return Task.FromResult<object>(null);
} else
if (client.Type == ApplicationTypes.NativeConfidential)
{
logger.LogInformation($"NativeConfidential key");
if (string.IsNullOrWhiteSpace(clientSecret))
{
logger.LogInformation($"invalid_clientId: Client secret should be sent.");
context.SetError("invalid_clientId", "Client secret should be sent.");
return Task.FromResult<object>(null);
}
else
{
// if (client.Secret != Helper.GetHash(clientSecret))
// TODO store a hash in db, not the pass
if (client.Secret != clientSecret)
{
context.SetError("invalid_clientId", "Client secret is invalid.");
logger.LogInformation($"invalid_clientId: Client secret is invalid.");
return Task.FromResult<object>(null);
}
}
}
if (!client.Active)
{
context.SetError("invalid_clientId", "Client is inactive.");
logger.LogInformation($"invalid_clientId: Client is inactive.");
return Task.FromResult<object>(null);
}
if (client != null && client.Secret == clientSecret)
{
logger.LogInformation($"\\o/ ValidateClientAuthentication: Validated ({clientId})");
context.Validated();
}
else logger.LogInformation($":'( ValidateClientAuthentication: KO ({clientId})");
}
else logger.LogWarning($"ValidateClientAuthentication: neither Basic nor Form credential were found");
return Task.FromResult(0);
}
UserManager<ApplicationUser> _usermanager;
private async Task<Task> GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
logger.LogWarning($"GrantResourceOwnerCredentials task ... {context.UserName}");
ApplicationUser user = null;
user = await _usermanager.FindByNameAsync(context.UserName);
if (await _usermanager.CheckPasswordAsync(user, context.Password))
{
var claims = new List<Claim>(
context.Scope.Select(x => new Claim("urn:oauth:scope", x))
);
claims.Add(new Claim(ClaimTypes.NameIdentifier, user.Id));
claims.Add(new Claim(ClaimTypes.Email, user.Email));
claims.AddRange((await _usermanager.GetRolesAsync(user)).Select(
r => new Claim(ClaimTypes.Role, r)
));
ClaimsPrincipal principal = new ClaimsPrincipal(
new ClaimsIdentity(
new GenericIdentity(context.UserName, OAuthDefaults.AuthenticationType),
claims)
);
// TODO set a NameIdentifier, roles and scopes claims
context.HttpContext.User = principal;
context.Validated(principal);
}
return Task.FromResult(0);
}
private Task GrantClientCredetails(OAuthGrantClientCredentialsContext context)
{
ClaimsPrincipal principal = new ClaimsPrincipal(new ClaimsIdentity(new GenericIdentity(context.ClientId, OAuthDefaults.AuthenticationType), context.Scope.Select(x => new Claim("urn:oauth:scope", x))));
context.Validated(principal);
return Task.FromResult(0);
}
private void CreateAuthenticationCode(AuthenticationTokenCreateContext context)
{
logger.LogInformation("CreateAuthenticationCode");
context.SetToken(Guid.NewGuid().ToString("n") + Guid.NewGuid().ToString("n"));
_authenticationCodes[context.Token] = context.SerializeTicket();
}
private void ReceiveAuthenticationCode(AuthenticationTokenReceiveContext context)
{
string value;
if (_authenticationCodes.TryRemove(context.Token, out value))
{
context.DeserializeTicket(value);
logger.LogInformation("ReceiveAuthenticationCode: Success");
}
}
private void CreateRefreshToken(AuthenticationTokenCreateContext context)
{
var uid = context.Ticket.Principal.GetUserId();
logger.LogInformation($"CreateRefreshToken for {uid}");
foreach (var c in context.Ticket.Principal.Claims)
logger.LogInformation($"| User claim: {c.Type} {c.Value}");
context.SetToken(context.SerializeTicket());
}
private void ReceiveRefreshToken(AuthenticationTokenReceiveContext context)
{
var uid = context.Ticket.Principal.GetUserId();
logger.LogInformation($"ReceiveRefreshToken for {uid}");
foreach (var c in context.Ticket.Principal.Claims)
logger.LogInformation($"| User claim: {c.Type} {c.Value}");
context.DeserializeTicket(context.Token);
}
}
}

View file

@ -0,0 +1,53 @@
using System;
using System.IO;
using Google.Apis.Auth.OAuth2;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
namespace Yavsc
{
// ensures we may count on :
// * Google credentials
// * an AppData folder
public partial class Startup
{
public void CheckServices(IServiceCollection services)
{
}
public void CheckApp(IApplicationBuilder app,
SiteSettings siteSettings, IHostingEnvironment env,
ILoggerFactory loggerFactory
)
{
var logger = loggerFactory.CreateLogger<Startup>();
var appData = Environment.GetEnvironmentVariable("APPDATA");
if (appData == null)
{
logger.LogWarning("AppData was not found in environment variables");
if (SiteSetup.DataDir == null) {
SiteSetup.DataDir = "AppData"+env.EnvironmentName;
logger.LogInformation("Using: "+SiteSetup.DataDir);
} else logger.LogInformation("Using value from settings: "+SiteSetup.DataDir);
DirectoryInfo di = new DirectoryInfo(SiteSetup.DataDir);
if (!di.Exists)
{
di.Create();
logger.LogWarning("Created dir : "+di.FullName);
}
else logger.LogInformation("Using existing directory: "+di.Name);
SiteSetup.DataDir = Path.Combine(Directory.GetCurrentDirectory(),di.Name);
Environment.SetEnvironmentVariable("APPDATA", SiteSetup.DataDir);
logger.LogWarning("It has been set to : "+Environment.GetEnvironmentVariable("APPDATA"));
}
}
}
}

View file

@ -0,0 +1,15 @@
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Hosting;
namespace Yavsc
{
public partial class Startup
{
public void ConfigureWebSocketsApp(IApplicationBuilder app,
SiteSettings siteSettings, IHostingEnvironment env)
{
app.UseWebSockets();
app.UseSignalR("/api/signalr");
}
}
}

View file

@ -0,0 +1,109 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNet.Builder;
using Microsoft.Extensions.Logging;
namespace Yavsc
{
using Microsoft.Data.Entity;
using Models;
using Yavsc.Abstract.Workflow;
using Yavsc.Billing;
using Yavsc.Models.Billing;
using Yavsc.Models.Haircut;
using Yavsc.Models.Workflow;
using Yavsc.Services;
public partial class Startup
{
/// <summary>
/// Lists Available user profile classes,
/// populated at startup, using reflexion.
/// </summary>
public static List<Type> ProfileTypes = new List<Type>();
/// <summary>
/// Lists available command forms.
/// This is hard coded.
/// </summary>
public static readonly string[] Forms = new string[] { "Profiles", "HairCut" };
private void ConfigureWorkflow(IApplicationBuilder app, SiteSettings settings, ILogger logger)
{
// System.AppDomain.CurrentDomain.ResourceResolve += OnYavscResourceResolve;
try {
foreach (var a in System.AppDomain.CurrentDomain.GetAssemblies())
{
foreach (var c in a.GetTypes())
{
if (c.IsClass && !c.IsAbstract &&
c.GetInterface("ISpecializationSettings") != null)
{
ProfileTypes.Add(c);
}
}
}
}
catch (Exception ex)
{
logger.LogError(ex.TargetSite.Name);
}
foreach (var propinfo in typeof(ApplicationDbContext).GetProperties())
{
foreach (var attr in propinfo.CustomAttributes)
{
// something like a DbSet?
if (attr.AttributeType == typeof(Yavsc.Attributes.ActivitySettingsAttribute))
{
// TODO swith () case {}
if (typeof(IQueryable<ISpecializationSettings>).IsAssignableFrom(propinfo.PropertyType))
{// double-bingo
logger.LogVerbose($"Pro: {propinfo.Name}");
BillingService.UserSettings.Add(propinfo);
}
else
// Design time error
{
var msg =
$@"La propriété {propinfo.Name} du contexte de la
base de donnée porte l'attribut [ActivitySetting],
mais n'implemente pas l'interface IQueryable<ISpecializationSettings>
({propinfo.MemberType.GetType()})";
logger.LogCritical(msg);
}
}
}
}
RegisterBilling<HairCutQuery>(BillingCodes.Brush, new Func<ApplicationDbContext,long,IDecidableQuery>
( ( db, id) =>
{
var query = db.HairCutQueries.Include(q=>q.Prestation).Include(q=>q.Regularisation).Single(q=>q.Id == id) ;
query.SelectedProfile = db.BrusherProfile.Single(b=>b.UserId == query.PerformerId);
return query;
})) ;
RegisterBilling<HairMultiCutQuery>(BillingCodes.MBrush,new Func<ApplicationDbContext,long,IDecidableQuery>
( (db, id) => db.HairMultiCutQueries.Include(q=>q.Regularisation).Single(q=>q.Id == id)));
RegisterBilling<RdvQuery>(BillingCodes.Rdv, new Func<ApplicationDbContext,long,IDecidableQuery>
( (db, id) => db.RdvQueries.Include(q=>q.Regularisation).Single(q=>q.Id == id)));
}
public static System.Reflection.Assembly OnYavscResourceResolve(object sender, ResolveEventArgs ev)
{
return AppDomain.CurrentDomain.GetAssemblies()[0];
}
public static void RegisterBilling<T>(string code, Func<ApplicationDbContext,long,IDecidableQuery> getter) where T : IBillable
{
BillingService.Billing.Add(code,getter) ;
BillingService.GlobalBillingMap.Add(typeof(T).Name,code);
}
}
}

412
src/Yavsc/Startup/Startup.cs Executable file
View file

@ -0,0 +1,412 @@
using System;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Web.Optimization;
using Microsoft.AspNet.Authentication;
using Microsoft.AspNet.Authorization;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Diagnostics;
using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.Localization;
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Mvc.Filters;
using Microsoft.AspNet.Mvc.Razor;
using Microsoft.Data.Entity;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.OptionsModel;
using Microsoft.Extensions.PlatformAbstractions;
using Microsoft.Net.Http.Headers;
using Newtonsoft.Json;
namespace Yavsc
{
using System.Collections.Generic;
using System.Net;
using Formatters;
using Google.Apis.Util.Store;
using Microsoft.AspNet.Identity;
using Microsoft.Extensions.Localization;
using Models;
using PayPal.Manager;
using Services;
using ViewModels.Auth.Handlers;
using Yavsc.Abstract.FileSystem;
using static System.Environment;
public partial class Startup
{
public static string AvatarsDirName { private set; get; }
public static string GitDirName { private set; get; }
public static string Authority { get; private set; }
public static string Temp { get; set; }
public static SiteSettings SiteSetup { get; private set; }
public static GoogleServiceAccount GServiceAccount { get; private set; }
public static string HostingFullName { get; set; }
public static PayPalSettings PayPalSettings { get; private set; }
private static ILogger logger;
public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv)
{
var devtag = env.IsDevelopment()?"D":"";
var prodtag = env.IsProduction()?"P":"";
var stagetag = env.IsStaging()?"S":"";
HostingFullName = $"{appEnv.RuntimeFramework.FullName} [{env.EnvironmentName}:{prodtag}{devtag}{stagetag}]";
// Set up configuration sources.
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
if (env.IsDevelopment())
{
// For more details on using the user secret store see http://go.microsoft.com/fwlink/?LinkID=532709
builder.AddUserSecrets();
BundleTable.EnableOptimizations = false;
}
BundleConfig.RegisterBundles(BundleTable.Bundles);
builder.AddEnvironmentVariables();
Configuration = builder.Build();
var auth = Configuration["Site:Authority"];
var cxstr = Configuration["Data:DefaultConnection:ConnectionString"];
ConnectionString = cxstr;
AppDomain.CurrentDomain.SetData(Constants.YavscConnectionStringEnvName, ConnectionString);
var googleClientFile = Configuration["Authentication:Google:GoogleWebClientJson"];
var googleServiceAccountJsonFile = Configuration["Authentication:Google:GoogleServiceAccountJson"];
GoogleWebClientConfiguration = new ConfigurationBuilder().AddJsonFile(googleClientFile).Build();
var safile = new FileInfo(googleServiceAccountJsonFile);
GServiceAccount = JsonConvert.DeserializeObject<GoogleServiceAccount>(safile.OpenText().ReadToEnd());
}
public static string ConnectionString { get; set; }
public static GoogleAuthSettings GoogleSettings { get; set; }
public IConfigurationRoot Configuration { get; set; }
public static IConfigurationRoot GoogleWebClientConfiguration { get; set; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Database connection
services.AddOptions();
var siteSettings = Configuration.GetSection("Site");
services.Configure<SiteSettings>(siteSettings);
var smtpSettings = Configuration.GetSection("Smtp");
services.Configure<SmtpSettings>(smtpSettings);
var googleSettings = Configuration.GetSection("Authentication").GetSection("Google");
services.Configure<GoogleAuthSettings>(googleSettings);
var cinfoSettings = Configuration.GetSection("Authentication").GetSection("Societeinfo");
services.Configure<CompanyInfoSettings>(cinfoSettings);
var oauthFacebookSettings = Configuration.GetSection("Authentication").GetSection("Facebook");
services.Configure<FacebookOAuth2AppSettings>(oauthFacebookSettings);
var paypalSettings = Configuration.GetSection("Authentication").GetSection("PayPal");
services.Configure<PayPalSettings>(paypalSettings);
services.Add(ServiceDescriptor.Singleton(typeof(IOptions<SiteSettings>), typeof(OptionsManager<SiteSettings>)));
services.Add(ServiceDescriptor.Singleton(typeof(IOptions<SmtpSettings>), typeof(OptionsManager<SmtpSettings>)));
services.Add(ServiceDescriptor.Singleton(typeof(IOptions<GoogleAuthSettings>), typeof(OptionsManager<GoogleAuthSettings>)));
services.Add(ServiceDescriptor.Singleton(typeof(IOptions<CompanyInfoSettings>), typeof(OptionsManager<CompanyInfoSettings>)));
services.Add(ServiceDescriptor.Singleton(typeof(IOptions<RequestLocalizationOptions>), typeof(OptionsManager<RequestLocalizationOptions>)));
services.Configure<RequestLocalizationOptions>(options =>
{
var supportedCultures = new[]
{
new CultureInfo("en"),
new CultureInfo("fr"),
new CultureInfo("pt")
};
var 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;
// You can change which providers are configured to determine the culture for requests, or even add a custom
// provider with your own logic. The providers will be asked in order to provide a culture for each request,
// and the first to provide a non-null result that is in the configured supported cultures list will be used.
// By default, the following built-in providers are configured:
// - QueryStringRequestCultureProvider, sets culture via "culture" and "ui-culture" query string values, useful for testing
// - CookieRequestCultureProvider, sets culture via "ASPNET_CULTURE" cookie
// - AcceptLanguageHeaderRequestCultureProvider, sets culture via the "Accept-Language" request header
//options.RequestCultureProviders.Insert(0, new CustomRequestCultureProvider(async context =>
//{
// // My custom request culture logic
// return new ProviderCultureResult("fr");
//}));
options.RequestCultureProviders = new List<IRequestCultureProvider>
{
new QueryStringRequestCultureProvider { Options = options },
new CookieRequestCultureProvider { Options = options, CookieName="ASPNET_CULTURE" },
new AcceptLanguageHeaderRequestCultureProvider { Options = options }
};
});
// DataProtection
ConfigureProtectionServices(services);
// Add framework services.
services.AddEntityFramework()
.AddNpgsql()
.AddDbContext<ApplicationDbContext>(
db => db.UseNpgsql(ConnectionString)
);
ConfigureOAuthServices(services);
services.AddCors(
options =>
{
options.AddPolicy("CorsPolicy", builder =>
{
builder.WithOrigins("*");
});
}
);
// Add memory cache services
services.AddCaching();
// Add session related services.
services.AddSession();
// Add the system clock service
services.AddSingleton<ISystemClock, SystemClock>();
services.AddAuthorization(options =>
{
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("Bearer", new AuthorizationPolicyBuilder()
.AddAuthenticationSchemes("yavsc")
.RequireAuthenticatedUser().Build());
// options.AddPolicy("EmployeeId", policy => policy.RequireClaim("EmployeeId", "123", "456"));
// options.AddPolicy("BuildingEntry", policy => policy.Requirements.Add(new OfficeEntryRequirement()));
options.AddPolicy("Authenticated", policy => policy.RequireAuthenticatedUser());
});
services.AddSingleton<IAuthorizationHandler, HasBadgeHandler>();
services.AddSingleton<IAuthorizationHandler, HasTemporaryPassHandler>();
services.AddSingleton<IAuthorizationHandler, BlogEditHandler>();
services.AddSingleton<IAuthorizationHandler, BlogViewHandler>();
services.AddSingleton<IAuthorizationHandler, BillEditHandler>();
services.AddSingleton<IAuthorizationHandler, BillViewHandler>();
services.AddSingleton<IAuthorizationHandler, PostUserFileHandler>();
services.AddSingleton<IAuthorizationHandler, ViewFileHandler>();
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.OutputFormatters.Add(new PdfFormatter());
}).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.AddScoped<LanguageActionFilter>();
// Inject ticket formatting
services.AddTransient(typeof(ISecureDataFormat<>), typeof(SecureDataFormat<>));
services.AddTransient<Microsoft.AspNet.Authentication.ISecureDataFormat<AuthenticationTicket>, Microsoft.AspNet.Authentication.SecureDataFormat<AuthenticationTicket>>();
services.AddTransient<ISecureDataFormat<AuthenticationTicket>, TicketDataFormat>();
// Add application services.
services.AddTransient<IEmailSender, MailSender>();
services.AddTransient<IGoogleCloudMessageSender, GCMSender>();
services.AddTransient<IBillingService, BillingService>();
services.AddTransient<IDataStore, FileDataStore>( (sp) => new FileDataStore("googledatastore",false) );
services.AddTransient<ICalendarManager, CalendarManager>();
// TODO for SMS: services.AddTransient<ISmsSender, AuthMessageSender>();
services.AddLocalization(options =>
{
options.ResourcesPath = "Resources";
});
CheckServices(services);
}
static ApplicationDbContext _dbContext;
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(
IApplicationBuilder app, IHostingEnvironment env,
ApplicationDbContext dbContext, IOptions<SiteSettings> siteSettings,
IOptions<RequestLocalizationOptions> localizationOptions,
IOptions<OAuth2AppSettings> oauth2SettingsContainer,
IAuthorizationService authorizationService,
IOptions<PayPalSettings> payPalSettings,
IOptions<GoogleAuthSettings> googleSettings,
IStringLocalizer<Yavsc.Resources.YavscLocalisation> localizer,
UserManager<ApplicationUser> usermanager,
ILoggerFactory loggerFactory)
{
_dbContext = dbContext;
_usermanager = usermanager;
GoogleSettings = googleSettings.Value;
ResourcesHelpers.GlobalLocalizer = localizer;
SiteSetup = siteSettings.Value;
Authority = siteSettings.Value.Authority;
var blogsDir = siteSettings.Value.Blog;
if (blogsDir==null) throw new Exception ("blogsDir is not set.");
var billsDir = siteSettings.Value.Bills;
if (billsDir==null) throw new Exception ("billsDir is not set.");
AbstractFileSystemHelpers.UserFilesDirName = new DirectoryInfo(blogsDir).FullName;
AbstractFileSystemHelpers.UserBillsDirName = new DirectoryInfo(billsDir).FullName;
Temp = siteSettings.Value.TempDir;
PayPalSettings = payPalSettings.Value;
// TODO implement an installation & upgrade procedure
// Create required directories
foreach (string dir in new string[] { AbstractFileSystemHelpers.UserFilesDirName, AbstractFileSystemHelpers.UserBillsDirName, SiteSetup.TempDir })
{
if (dir==null) throw new Exception (nameof(dir));
DirectoryInfo di = new DirectoryInfo(dir);
if (!di.Exists) di.Create();
}
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
logger = loggerFactory.CreateLogger<Startup>();
app.UseStatusCodePagesWithReExecute("/Home/Status/{0}");
if (env.IsDevelopment())
{
var logenvvar = Environment.GetEnvironmentVariable("ASPNET_LOG_LEVEL");
if (logenvvar!=null)
switch (logenvvar) {
case "info":
loggerFactory.MinimumLevel = LogLevel.Information;
break;
case "warn":
loggerFactory.MinimumLevel = LogLevel.Warning;
break;
case "err":
loggerFactory.MinimumLevel = LogLevel.Error;
break;
case "debug":
default:
loggerFactory.MinimumLevel = LogLevel.Debug;
break;
}
app.UseDeveloperExceptionPage();
app.UseRuntimeInfoPage();
var epo = new ErrorPageOptions();
epo.SourceCodeLineCount = 20;
app.UseDeveloperExceptionPage(epo);
app.UseDatabaseErrorPage(
x =>
{
x.EnableAll();
x.ShowExceptionDetails = true;
}
);
app.UseWelcomePage("/welcome");
}
else
{
// For more details on creating database during deployment see http://go.microsoft.com/fwlink/?LinkID=615859
app.UseExceptionHandler("/Home/Error");
try
{
using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>()
.CreateScope())
{
serviceScope.ServiceProvider.GetService<ApplicationDbContext>()
.Database.Migrate();
}
}
catch (TargetInvocationException ex)
{
if (ex.InnerException is InvalidOperationException)
// nothing to do ?
{
// TODO (or not) Hit the developper
}
else throw ex;
}
}
// before fixing the security protocol, let beleive our lib it's done with it.
var cxmgr = ConnectionManager.Instance;
// then, fix it.
ServicePointManager.SecurityProtocol = (SecurityProtocolType) 0xC00; // Tls12, required by PayPal
app.UseIISPlatformHandler(options =>
{
options.AuthenticationDescriptions.Clear();
options.AutomaticAuthentication = false;
});
ConfigureOAuthApp(app, SiteSetup, logger);
ConfigureFileServerApp(app, SiteSetup, env, authorizationService);
ConfigureWebSocketsApp(app, SiteSetup, env);
ConfigureWorkflow(app, SiteSetup, logger);
app.UseRequestLocalization(localizationOptions.Value, (RequestCulture) new RequestCulture((string)"en-US"));
app.UseSession();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
logger.LogInformation("LocalApplicationData: "+Environment.GetFolderPath(SpecialFolder.LocalApplicationData, SpecialFolderOption.DoNotVerify));
CheckApp(app, SiteSetup, env, loggerFactory);
}
// Entry point for the application.
public static void Main(string[] args) => Microsoft.AspNet.Hosting.WebApplication.Run<Startup>(args);
}
}
//