diff --git a/.travis.yml b/.travis.yml index 533cb0e0..a009a47d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,8 @@ install: - cd ../OAuth.AspNet.AuthServer && dnu restore --ignore-failed-sources - cd ../Yavsc.Abstract && dnu restore --ignore-failed-sources - cd ../Yavsc.Server && dnu restore --ignore-failed-sources -- cd ../Yavsc && dnu restore --ignore-failed-sources +- cd ../yavsc && dnu restore --ignore-failed-sources +- cd ../test && dnu restore --ignore-failed-sources script: - "dnu build" diff --git a/src/Yavsc.Abstract/Resources/Yavsc.Models.IT.Fixing.Bug.Designer.cs b/src/Yavsc.Abstract/Resources/Yavsc.Abstract.Resources.Bug.Designer.cs similarity index 81% rename from src/Yavsc.Abstract/Resources/Yavsc.Models.IT.Fixing.Bug.Designer.cs rename to src/Yavsc.Abstract/Resources/Yavsc.Abstract.Resources.Bug.Designer.cs index 6022902b..4583bb86 100644 --- a/src/Yavsc.Abstract/Resources/Yavsc.Models.IT.Fixing.Bug.Designer.cs +++ b/src/Yavsc.Abstract/Resources/Yavsc.Abstract.Resources.Bug.Designer.cs @@ -1,12 +1,12 @@ -// ------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Mono Runtime Version: 4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -// ------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ namespace Yavsc.Models.IT.Fixing { using System; diff --git a/src/Yavsc.Abstract/Resources/Yavsc.Models.Messaging.Resources.Designer.cs b/src/Yavsc.Abstract/Resources/Yavsc.Abstract.Resources.Resources.Designer.cs similarity index 80% rename from src/Yavsc.Abstract/Resources/Yavsc.Models.Messaging.Resources.Designer.cs rename to src/Yavsc.Abstract/Resources/Yavsc.Abstract.Resources.Resources.Designer.cs index afe973c5..c4fbcc6e 100644 --- a/src/Yavsc.Abstract/Resources/Yavsc.Models.Messaging.Resources.Designer.cs +++ b/src/Yavsc.Abstract/Resources/Yavsc.Abstract.Resources.Resources.Designer.cs @@ -1,12 +1,12 @@ -// ------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Mono Runtime Version: 4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -// ------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ namespace Yavsc.Models.Messaging { using System; diff --git a/src/Yavsc.Server/Resources/Yavsc.Models.Relationship.HyperLink.Designer.cs b/src/Yavsc.Server/Resources/Yavsc.Server.Resources.HyperLink.Designer.cs similarity index 84% rename from src/Yavsc.Server/Resources/Yavsc.Models.Relationship.HyperLink.Designer.cs rename to src/Yavsc.Server/Resources/Yavsc.Server.Resources.HyperLink.Designer.cs index 5bae0726..ba871857 100644 --- a/src/Yavsc.Server/Resources/Yavsc.Models.Relationship.HyperLink.Designer.cs +++ b/src/Yavsc.Server/Resources/Yavsc.Server.Resources.HyperLink.Designer.cs @@ -1,12 +1,12 @@ -// ------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Mono Runtime Version: 4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -// ------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ namespace Yavsc.Models.Relationship { using System; diff --git a/src/Yavsc/Resources/Yavsc.ViewComponents.CommentViewComponent.Designer.cs b/src/Yavsc/Resources/Yavsc.Resources.CommentViewComponent.Designer.cs similarity index 87% rename from src/Yavsc/Resources/Yavsc.ViewComponents.CommentViewComponent.Designer.cs rename to src/Yavsc/Resources/Yavsc.Resources.CommentViewComponent.Designer.cs index 7c8d5810..f62f8775 100644 --- a/src/Yavsc/Resources/Yavsc.ViewComponents.CommentViewComponent.Designer.cs +++ b/src/Yavsc/Resources/Yavsc.Resources.CommentViewComponent.Designer.cs @@ -1,12 +1,12 @@ -// ------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Mono Runtime Version: 4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -// ------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ namespace Yavsc.ViewComponents { using System; diff --git a/src/Yavsc/Resources/Yavsc.ViewModels.EnrolerViewModel.Designer.cs b/src/Yavsc/Resources/Yavsc.Resources.EnrolerViewModel.Designer.cs similarity index 81% rename from src/Yavsc/Resources/Yavsc.ViewModels.EnrolerViewModel.Designer.cs rename to src/Yavsc/Resources/Yavsc.Resources.EnrolerViewModel.Designer.cs index a765b326..b5fd2404 100644 --- a/src/Yavsc/Resources/Yavsc.ViewModels.EnrolerViewModel.Designer.cs +++ b/src/Yavsc/Resources/Yavsc.Resources.EnrolerViewModel.Designer.cs @@ -1,12 +1,12 @@ -// ------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Mono Runtime Version: 4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -// ------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ namespace Yavsc.ViewModels { using System; diff --git a/src/Yavsc/Resources/Yavsc.ViewModels.FrontOffice.PerformerProfileViewModel.Designer.cs b/src/Yavsc/Resources/Yavsc.Resources.PerformerProfileViewModel.Designer.cs similarity index 80% rename from src/Yavsc/Resources/Yavsc.ViewModels.FrontOffice.PerformerProfileViewModel.Designer.cs rename to src/Yavsc/Resources/Yavsc.Resources.PerformerProfileViewModel.Designer.cs index b9560d06..511dbf5b 100644 --- a/src/Yavsc/Resources/Yavsc.ViewModels.FrontOffice.PerformerProfileViewModel.Designer.cs +++ b/src/Yavsc/Resources/Yavsc.Resources.PerformerProfileViewModel.Designer.cs @@ -1,12 +1,12 @@ -// ------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Mono Runtime Version: 4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -// ------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ namespace Yavsc.ViewModels.FrontOffice { using System; diff --git a/src/Yavsc/Resources/Yavsc.Models.IT.Fixing.Resources.Designer.cs b/src/Yavsc/Resources/Yavsc.Resources.Resources.Designer.cs similarity index 100% rename from src/Yavsc/Resources/Yavsc.Models.IT.Fixing.Resources.Designer.cs rename to src/Yavsc/Resources/Yavsc.Resources.Resources.Designer.cs diff --git a/src/Yavsc/Resources/YavscLocalisation.Designer.cs b/src/Yavsc/Resources/Yavsc.Resources.YavscLocalisation.Designer.cs similarity index 100% rename from src/Yavsc/Resources/YavscLocalisation.Designer.cs rename to src/Yavsc/Resources/Yavsc.Resources.YavscLocalisation.Designer.cs diff --git a/src/test/Mandatory/RegisterApi.cs b/src/test/Mandatory/RegisterApi.cs new file mode 100644 index 00000000..dec1e96c --- /dev/null +++ b/src/test/Mandatory/RegisterApi.cs @@ -0,0 +1,46 @@ +// // YavscWorkInProgress.cs +// /* +// paul 21/06/2018 10:11 20182018 6 21 +// */ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNet.Hosting; +using Microsoft.Extensions.PlatformAbstractions; +using Xunit; +using Xunit.Abstractions; +using Yavsc.Authentication; +using static OAuth.AspNet.AuthServer.Constants; +using Microsoft.AspNet.Mvc; +using Microsoft.AspNet.Mvc.Filters; +using Microsoft.AspNet.Mvc.Razor; + +namespace test +{ + [Collection("Yavsc Work In Progress")] + [Trait("regres", "yes")] + [Trait("module", "api")] + public class RegiserAPI : BaseTestContext, IClassFixture + { + public RegiserAPI(ServerSideFixture serverFixture, ITestOutputHelper output) + : base(output, serverFixture) + { + + } + + [Fact] + public void EnsureWeb() + { + var host = new WebHostBuilder(); + host.UseEnvironment("Development") + .UseServer("Microsoft.AspNet.Server.Kestrel") + .UseStartup() + .Build().Start(); + } + + } +} diff --git a/src/test/Mandatory/Remoting.cs b/src/test/Mandatory/Remoting.cs index 0f181c7c..65f753fe 100644 --- a/src/test/Mandatory/Remoting.cs +++ b/src/test/Mandatory/Remoting.cs @@ -22,10 +22,12 @@ namespace test [Trait("regres", "yes")] public class Remoting : BaseTestContext, IClassFixture { + RegiserAPI r; public Remoting(ServerSideFixture serverFixture, ITestOutputHelper output) : base(output, serverFixture) { + r = new RegiserAPI(serverFixture, output); } [Theory] @@ -44,6 +46,8 @@ namespace test { try { + r.EnsureWeb(); + var oauthor = new OAuthenticator(clientId, clientSecret, scope, new Uri(authorizeUrl), new Uri(redirectUrl), new Uri(accessTokenUrl)); var query = new Dictionary @@ -52,7 +56,6 @@ namespace test [Parameters.Password] = Startup.Testing.ValidCreds[0].Password, [Parameters.GrantType] = GrantTypes.Password }; - EnsureWeb(); var result = await oauthor.RequestAccessTokenAsync(query); Console.WriteLine(">> Got an output"); @@ -76,24 +79,7 @@ namespace test throw; } } - internal static void EnsureWeb() - { - throw new NotImplementedException(); - - DirectoryInfo di = new DirectoryInfo("../Yavsc"); - Environment.CurrentDirectory = di.FullName; - var host = new WebHostBuilder(); - - var hostengine = host - - .UseEnvironment("Development") - - .UseServer("Microsoft.AspNet.Server.Kestrel") - .UseStartup() - .Build(); -// hostengine.ApplicationServices - var startup = hostengine.Start(); - } + public static IEnumerable GetLoginIntentData(int numTests) { diff --git a/src/test/Startup.cs b/src/test/Startup.cs index 60c9d889..17cc8f66 100644 --- a/src/test/Startup.cs +++ b/src/test/Startup.cs @@ -13,6 +13,41 @@ using Yavsc.Services; using Microsoft.Data.Entity; using Microsoft.Extensions.WebEncoders; using test.Settings; +using Microsoft.AspNet.Diagnostics; +using System.Net; +using Yavsc.Extensions; +using Microsoft.AspNet.Authentication.Cookies; +using Microsoft.AspNet.Authentication.JwtBearer; +using OAuth.AspNet.Tokens; +using System.Threading.Tasks; +using Microsoft.AspNet.Http; +using Microsoft.AspNet.Authentication.OAuth; +using Yavsc.Helpers.Auth; +using Google.Apis.Util.Store; +using System.Security.Claims; +using Google.Apis.Auth.OAuth2.Responses; +using Constants = Yavsc.Constants; +using OAuth.AspNet.AuthServer; +using Yavsc.Models.Auth; +using Microsoft.AspNet.Identity; +using System.Collections.Concurrent; +using System.Security.Principal; +using System.Linq; +using System.Collections.Generic; +using Microsoft.AspNet.Mvc; +using Microsoft.AspNet.Mvc.Filters; +using Microsoft.AspNet.Mvc.Razor; +using Microsoft.AspNet.Authentication; +using Microsoft.AspNet.Authorization; +using Yavsc.AuthorizationHandlers; +using Yavsc.Formatters; +using Microsoft.Net.Http.Headers; +using static Yavsc.Startup; +using Microsoft.AspNet.DataProtection.Infrastructure; +using System.IO; +using Microsoft.AspNet.Identity.EntityFramework; +using Yavsc.Auth; + namespace test { public class Startup @@ -21,18 +56,29 @@ namespace test public static IConfiguration Configuration { get; set; } public static string HostingFullName { get; private set; } + + public ApplicationDbContext DbContext { get; private set; } + public static Testing Testing { get; private set; } - ILogger logger; + public static IConfigurationRoot GoogleWebClientConfiguration { get; set; } + + public static CookieAuthenticationOptions ExternalCookieAppOptions { get; private set; } + public MonoDataProtectionProvider ProtectionProvider { get; private set; } + public static OAuth.AspNet.AuthServer.OAuthAuthorizationServerOptions OAuthServerAppOptions { get; private set; } + public Yavsc.Auth.YavscGoogleOptions YavscGoogleAppOptions { 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":""; + 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) @@ -43,9 +89,15 @@ namespace test builder.AddUserSecrets(); } Configuration = builder.Build(); + + var googleClientFile = Configuration["Authentication:Google:GoogleWebClientJson"]; + var googleServiceAccountJsonFile = Configuration["Authentication:Google:GoogleServiceAccountJson"]; + if (googleClientFile != null) + GoogleWebClientConfiguration = new ConfigurationBuilder().AddJsonFile(googleClientFile).Build(); + } - public void ConfigureServices (IServiceCollection services) + public void ConfigureServices(IServiceCollection services) { services.AddOptions(); var siteSettingsconf = Configuration.GetSection("Site"); @@ -56,7 +108,7 @@ namespace test services.Configure(dbSettingsconf); var testingconf = Configuration.GetSection("Testing"); services.Configure(testingconf); - + services.AddInstance(typeof(ILoggerFactory), new LoggerFactory()); services.AddTransient(typeof(IEmailSender), typeof(MailSender)); services.AddEntityFramework().AddNpgsql().AddDbContext(); @@ -67,36 +119,524 @@ namespace test { options.ResourcesPath = "Resources"; }); - - services.AddEntityFramework() - .AddNpgsql() - .AddDbContext( - db => db.UseNpgsql(Testing.ConnectionStrings.Default) - ); + // Add memory cache services + services.AddCaching(); + + // Add session related services. + services.AddSession(); + + // Add the system clock service + services.AddSingleton(); + + 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.AddDataProtection(); + services.ConfigureDataProtection(configure => + { + configure.SetApplicationName(Configuration["Site:Title"]); + configure.SetDefaultKeyLifetime(TimeSpan.FromDays(45)); + configure.PersistKeysToFileSystem( + new DirectoryInfo(Configuration["DataProtection:Keys:Dir"])); + }); + + services.Add(ServiceDescriptor.Singleton(typeof(IApplicationDiscriminator), + typeof(SystemWebApplicationDiscriminator))); + + + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton>(); + services.AddIdentity( + option => + { + IdentityAppOptions = option; + option.User.AllowedUserNameCharacters += " "; + option.User.RequireUniqueEmail = true; + option.Cookies.ApplicationCookie.LoginPath = "/signin"; + // option.Cookies.TwoFactorRememberMeCookie.ExpireTimeSpan = TimeSpan.FromDays(30); + // option.Cookies.TwoFactorRememberMeCookie.DataProtectionProvider = ProtectionProvider; + // option.Cookies.ApplicationCookie.DataProtectionProvider = ProtectionProvider; + // option.Cookies.ExternalCookie.DataProtectionProvider = ProtectionProvider; + // option.Cookies.ApplicationCookie.AuthenticationScheme = Constants.ApplicationAuthenticationSheme; + /* + 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.ExternalCookieAuthenticationScheme = Constants.ExternalAuthenticationSheme; + option.Cookies.ExternalCookie.AutomaticAuthenticate = true; + option.Cookies.ExternalCookie.AuthenticationScheme = Constants.ExternalAuthenticationSheme; + */ + } + ).AddEntityFrameworkStores() + .AddTokenProvider>(Constants.DefaultFactor) + // .AddTokenProvider(Constants.DefaultFactor) + // .AddTokenProvider(Constants.SMSFactor) + .AddTokenProvider(Constants.EMailFactor) + // .AddTokenProvider(Constants.AppFactor) + // + ; + + 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(); + + // Inject ticket formatting + services.AddTransient(typeof(ISecureDataFormat<>), typeof(SecureDataFormat<>)); + services.AddTransient, Microsoft.AspNet.Authentication.SecureDataFormat>(); + services.AddTransient, TicketDataFormat>(); + + // Add application services. + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient((sp) => new FileDataStore("googledatastore", false)); + services.AddTransient(); services.AddTransient(); } - public void Configure (IApplicationBuilder app, IHostingEnvironment env, - IOptions testingSettings, - ILoggerFactory loggerFactory) + + #region OAuth Methods + + + private Client GetApplication(string clientId) + { + if (DbContext == null) + { + logger.LogError("no db!"); + return null; + } + Client app = DbContext.Applications.FirstOrDefault(x => x.Id == clientId); + if (app == null) logger.LogError($"no app for <{clientId}>"); + return app; + } + + 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(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(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(null); + } + } + } + + if (!client.Active) + { + context.SetError("invalid_clientId", "Client is inactive."); + logger.LogInformation($"invalid_clientId: Client is inactive."); + return Task.FromResult(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); + } + + + private async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) + { + logger.LogWarning($"GrantResourceOwnerCredentials task ... {context.UserName}"); + + ApplicationUser user = null; + user = DbContext.Users.Include(u => u.Membership).First(u => u.UserName == context.UserName); + +#if USERMANAGER + + if (await _usermanager.CheckPasswordAsync(user, context.Password)) + { + + var claims = new List( + context.Scope.Select(x => new Claim("urn:oauth:scope", x)) + ) + { + new Claim(ClaimTypes.NameIdentifier, user.Id), + new Claim(ClaimTypes.Email, user.Email) + }; + claims.AddRange((await _usermanager.GetRolesAsync(user)).Select( + r => new Claim(ClaimTypes.Role, r) + )); + claims.AddRange(user.Membership.Select( + m => new Claim(YavscClaimTypes.CircleMembership, m.CircleId.ToString()) + )); + ClaimsPrincipal principal = new ClaimsPrincipal( + new ClaimsIdentity( + new GenericIdentity(context.UserName, OAuthDefaults.AuthenticationType), + claims) + ); + context.HttpContext.User = principal; + context.Validated(principal); + } +#endif + return Task.FromResult(0); + } + private Task GrantClientCredetails(OAuthGrantClientCredentialsContext context) + { + var id = new GenericIdentity(context.ClientId, OAuthDefaults.AuthenticationType); + var claims = context.Scope.Select(x => new Claim("urn:oauth:scope", x)); + var cid = new ClaimsIdentity(id, claims); + ClaimsPrincipal principal = new ClaimsPrincipal(cid); + + context.Validated(principal); + + return Task.FromResult(0); + } + private readonly ConcurrentDictionary _authenticationCodes = new ConcurrentDictionary(StringComparer.Ordinal); + + 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); + } + + #endregion + +#if USERMANAGER + _usermanager = usermanager; +#endif + + public void Configure( + IApplicationBuilder app, + IHostingEnvironment env, + ApplicationDbContext dbContext, + IOptions testingSettings, + ILoggerFactory loggerFactory) { loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug(); logger = loggerFactory.CreateLogger(); logger.LogInformation(env.EnvironmentName); + this.DbContext = dbContext; Testing = testingSettings.Value; - if (Testing.ConnectionStrings==null) + if (Testing.ConnectionStrings == null) logger.LogInformation($" Testing.ConnectionStrings is null : "); - else { + else + { AppDomain.CurrentDomain.SetData("YAVSC_DB_CONNECTION", Testing.ConnectionStrings.Default); } - + var authConf = Configuration.GetSection("Authentication").GetSection("Yavsc"); var clientId = authConf.GetSection("ClientId").Value; var clientSecret = authConf.GetSection("ClientSecret").Value; + + + + app.UseDeveloperExceptionPage(); + app.UseRuntimeInfoPage(); + var epo = new ErrorPageOptions + { + SourceCodeLineCount = 20 + }; + app.UseDeveloperExceptionPage(epo); + app.UseDatabaseErrorPage( + x => + { + x.EnableAll(); + x.ShowExceptionDetails = true; + } + ); + + app.UseWelcomePage("/welcome"); + + // before fixing the security protocol, let beleive our lib it's done with it. + var cxmgr = PayPal.Manager.ConnectionManager.Instance; + // then, fix it. + ServicePointManager.SecurityProtocol = (SecurityProtocolType)0xC00; // Tls12, required by PayPal + + app.UseIISPlatformHandler(options => + { + options.AuthenticationDescriptions.Clear(); + options.AutomaticAuthentication = false; + }); + app.UseSession(); + + + app.UseIdentity(); + + ProtectionProvider = new MonoDataProtectionProvider(Configuration["Site:Title"]); ; + + app.UseWhen(context => context.Request.Path.StartsWithSegments("/api") + || context.Request.Path.StartsWithSegments("/live"), + branchLiveOrApi => + { + branchLiveOrApi.UseJwtBearerAuthentication( + + options => + { + options.AuthenticationScheme = JwtBearerDefaults.AuthenticationScheme; + options.AutomaticAuthenticate = true; + options.SecurityTokenValidators.Clear(); + var tickeDataProtector = new TicketDataFormatTokenValidator( + ProtectionProvider + ); + options.SecurityTokenValidators.Add(tickeDataProtector); + options.Events = new JwtBearerEvents + { + OnReceivingToken = context => + { + return Task.Run(() => + { + var signalRTokenHeader = context.Request.Query["signalRTokenHeader"]; + + if (!string.IsNullOrEmpty(signalRTokenHeader) && + (context.HttpContext.WebSockets.IsWebSocketRequest || context.Request.Headers["Accept"] == "text/event-stream")) + { + context.Token = context.Request.Query["signalRTokenHeader"]; + } + }); + } + }; + }); + }); + + app.UseWhen(context => !context.Request.Path.StartsWithSegments("/api") && !context.Request.Path.StartsWithSegments("/live"), + branch => + { + // External authentication shared cookie: + branch.UseCookieAuthentication(options => + { + ExternalCookieAppOptions = options; + options.AuthenticationScheme = Yavsc.Constants.ExternalAuthenticationSheme; + options.AutomaticAuthenticate = true; + options.ExpireTimeSpan = TimeSpan.FromMinutes(5); + options.LoginPath = new PathString(Yavsc.Constants.LoginPath.Substring(1)); + options.AccessDeniedPath = new PathString(Yavsc.Constants.LoginPath.Substring(1)); + }); + + YavscGoogleAppOptions = new Yavsc.Auth.YavscGoogleOptions + { + ClientId = GoogleWebClientConfiguration["web:client_id"], + ClientSecret = GoogleWebClientConfiguration["web:client_secret"], + AccessType = "offline", + Scope = { + "profile", + "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() + .CreateScope()) + { + var gcontext = context as Yavsc.Auth.GoogleOAuthCreatingTicketContext; + context.Identity.AddClaim(new Claim(YavscClaimTypes.GoogleUserId, gcontext.GoogleUserId)); + var DbContext = serviceScope.ServiceProvider.GetService(); + + var store = serviceScope.ServiceProvider.GetService(); + 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(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"); + + /* TODO + + ConfigureFileServerApp(app, SiteSetup, env, authorizationService); + + app.UseRequestLocalization(localizationOptions.Value, (RequestCulture)new RequestCulture((string)"en-US")); + + ConfigureWorkflow(); + */ + + app.UseMvc(routes => + { + routes.MapRoute( + name: "default", + template: "{controller=Home}/{action=Index}/{id?}"); + }); + } } diff --git a/src/test/YavscServerFactory.cs b/src/test/YavscServerFactory.cs index 8ee6e044..0a5ccc92 100644 --- a/src/test/YavscServerFactory.cs +++ b/src/test/YavscServerFactory.cs @@ -6,7 +6,7 @@ using Microsoft.Extensions.Configuration; namespace Yavsc.Server { - public class cliServerFactory : IServerFactory + public class CliServerFactory : IServerFactory { public IFeatureCollection Initialize(IConfiguration configuration) { diff --git a/src/test/project.json b/src/test/project.json index c85b7dac..471c8a0f 100644 --- a/src/test/project.json +++ b/src/test/project.json @@ -40,6 +40,10 @@ "EntityFramework7.Npgsql": "3.1.0-rc1-3", "EntityFramework7.Npgsql.Design": "3.1.0-rc1-5", "Newtonsoft.Json": "7.0.1", + "Microsoft.AspNet.Mvc": "6.0.0-rc1-*", + "Microsoft.AspNet.StaticFiles": "1.0.0-rc1-*", + "Microsoft.AspNet.Tooling.Razor": "1.0.0-rc1-*", + "Microsoft.AspNet.Identity.EntityFramework": "3.0.0-rc1-*", "xunit": "2.1.0", "xunit.analyzers": "0.9.0", "xunit.assert": "2.1.0", @@ -51,6 +55,10 @@ "target": "project", "type": "build" }, + "Yavsc.Abstract": { + "target": "project", + "type": "build" + }, "Yavsc": { "target": "project", "type": "build" @@ -60,6 +68,7 @@ "dnx451": {} }, "commands": { + "web": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.Kestrel --server.urls https://*:5001", "test": "xunit.runner.dnx" }, "userSecretsId": "aspnet5-YavscWeb-a0dadd21-2ced-43d3-96f9-7e504345102f",