Initial Commit
commit
15ffe475c7
|
|
@ -0,0 +1,275 @@
|
||||||
|
|
||||||
|
# Created by https://www.gitignore.io/api/aspnetcore
|
||||||
|
# Edit at https://www.gitignore.io/?templates=aspnetcore
|
||||||
|
|
||||||
|
### ASPNETCore ###
|
||||||
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
|
*.userprefs
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Dd]ebug/
|
||||||
|
[Dd]ebugPublic/
|
||||||
|
[Rr]elease/
|
||||||
|
[Rr]eleases/
|
||||||
|
x64/
|
||||||
|
x86/
|
||||||
|
bld/
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
[Ll]og/
|
||||||
|
|
||||||
|
# Visual Studio 2015 cache/options directory
|
||||||
|
.vs/
|
||||||
|
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||||
|
#wwwroot/
|
||||||
|
|
||||||
|
# MSTest test Results
|
||||||
|
[Tt]est[Rr]esult*/
|
||||||
|
[Bb]uild[Ll]og.*
|
||||||
|
|
||||||
|
# NUNIT
|
||||||
|
*.VisualState.xml
|
||||||
|
TestResult.xml
|
||||||
|
|
||||||
|
# Build Results of an ATL Project
|
||||||
|
[Dd]ebugPS/
|
||||||
|
[Rr]eleasePS/
|
||||||
|
dlldata.c
|
||||||
|
|
||||||
|
# DNX
|
||||||
|
project.lock.json
|
||||||
|
project.fragment.lock.json
|
||||||
|
artifacts/
|
||||||
|
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*_i.h
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.tmp_proj
|
||||||
|
*.log
|
||||||
|
*.vspscc
|
||||||
|
*.vssscc
|
||||||
|
.builds
|
||||||
|
*.pidb
|
||||||
|
*.svclog
|
||||||
|
*.scc
|
||||||
|
|
||||||
|
# Chutzpah Test files
|
||||||
|
_Chutzpah*
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opendb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
*.cachefile
|
||||||
|
*.VC.db
|
||||||
|
*.VC.VC.opendb
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
*.vspx
|
||||||
|
*.sap
|
||||||
|
|
||||||
|
# TFS 2012 Local Workspace
|
||||||
|
$tf/
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
|
_ReSharper*/
|
||||||
|
*.[Rr]e[Ss]harper
|
||||||
|
*.DotSettings.user
|
||||||
|
|
||||||
|
# JustCode is a .NET coding add-in
|
||||||
|
.JustCode
|
||||||
|
|
||||||
|
# TeamCity is a build add-in
|
||||||
|
_TeamCity*
|
||||||
|
|
||||||
|
# DotCover is a Code Coverage Tool
|
||||||
|
*.dotCover
|
||||||
|
|
||||||
|
# Visual Studio code coverage results
|
||||||
|
*.coverage
|
||||||
|
*.coveragexml
|
||||||
|
|
||||||
|
# NCrunch
|
||||||
|
_NCrunch_*
|
||||||
|
.*crunch*.local.xml
|
||||||
|
nCrunchTemp_*
|
||||||
|
|
||||||
|
# MightyMoose
|
||||||
|
*.mm.*
|
||||||
|
AutoTest.Net/
|
||||||
|
|
||||||
|
# Web workbench (sass)
|
||||||
|
.sass-cache/
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
[Ee]xpress/
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
DocProject/buildhelp/
|
||||||
|
DocProject/Help/*.HxT
|
||||||
|
DocProject/Help/*.HxC
|
||||||
|
DocProject/Help/*.hhc
|
||||||
|
DocProject/Help/*.hhk
|
||||||
|
DocProject/Help/*.hhp
|
||||||
|
DocProject/Help/Html2
|
||||||
|
DocProject/Help/html
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
publish/
|
||||||
|
|
||||||
|
# Publish Web Output
|
||||||
|
*.[Pp]ublish.xml
|
||||||
|
*.azurePubxml
|
||||||
|
# TODO: Comment the next line if you want to checkin your web deploy settings
|
||||||
|
# but database connection strings (with potential passwords) will be unencrypted
|
||||||
|
*.pubxml
|
||||||
|
*.publishproj
|
||||||
|
|
||||||
|
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||||
|
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||||
|
# in these scripts will be unencrypted
|
||||||
|
PublishScripts/
|
||||||
|
|
||||||
|
# NuGet Packages
|
||||||
|
*.nupkg
|
||||||
|
# The packages folder can be ignored because of Package Restore
|
||||||
|
**/packages/*
|
||||||
|
# except build/, which is used as an MSBuild target.
|
||||||
|
!**/packages/build/
|
||||||
|
# Uncomment if necessary however generally it will be regenerated when needed
|
||||||
|
#!**/packages/repositories.config
|
||||||
|
# NuGet v3's project.json files produces more ignoreable files
|
||||||
|
*.nuget.props
|
||||||
|
*.nuget.targets
|
||||||
|
|
||||||
|
# Microsoft Azure Build Output
|
||||||
|
csx/
|
||||||
|
*.build.csdef
|
||||||
|
|
||||||
|
# Microsoft Azure Emulator
|
||||||
|
ecf/
|
||||||
|
rcf/
|
||||||
|
|
||||||
|
# Windows Store app package directories and files
|
||||||
|
AppPackages/
|
||||||
|
BundleArtifacts/
|
||||||
|
Package.StoreAssociation.xml
|
||||||
|
_pkginfo.txt
|
||||||
|
|
||||||
|
# Visual Studio cache files
|
||||||
|
# files ending in .cache can be ignored
|
||||||
|
*.[Cc]ache
|
||||||
|
# but keep track of directories ending in .cache
|
||||||
|
!*.[Cc]ache/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
ClientBin/
|
||||||
|
~$*
|
||||||
|
*~
|
||||||
|
*.dbmdl
|
||||||
|
*.dbproj.schemaview
|
||||||
|
*.jfm
|
||||||
|
*.pfx
|
||||||
|
*.publishsettings
|
||||||
|
node_modules/
|
||||||
|
orleans.codegen.cs
|
||||||
|
|
||||||
|
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||||
|
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||||
|
#bower_components/
|
||||||
|
|
||||||
|
# RIA/Silverlight projects
|
||||||
|
Generated_Code/
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file
|
||||||
|
# to a newer Visual Studio version. Backup files are not needed,
|
||||||
|
# because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
UpgradeLog*.htm
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
*.mdf
|
||||||
|
*.ldf
|
||||||
|
|
||||||
|
# Business Intelligence projects
|
||||||
|
*.rdl.data
|
||||||
|
*.bim.layout
|
||||||
|
*.bim_*.settings
|
||||||
|
|
||||||
|
# Microsoft Fakes
|
||||||
|
FakesAssemblies/
|
||||||
|
|
||||||
|
# GhostDoc plugin setting file
|
||||||
|
*.GhostDoc.xml
|
||||||
|
|
||||||
|
# Node.js Tools for Visual Studio
|
||||||
|
.ntvs_analysis.dat
|
||||||
|
|
||||||
|
# Visual Studio 6 build log
|
||||||
|
*.plg
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace options file
|
||||||
|
*.opt
|
||||||
|
|
||||||
|
# Visual Studio LightSwitch build output
|
||||||
|
**/*.HTMLClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/ModelManifest.xml
|
||||||
|
**/*.Server/GeneratedArtifacts
|
||||||
|
**/*.Server/ModelManifest.xml
|
||||||
|
_Pvt_Extensions
|
||||||
|
|
||||||
|
# Paket dependency manager
|
||||||
|
.paket/paket.exe
|
||||||
|
paket-files/
|
||||||
|
|
||||||
|
# FAKE - F# Make
|
||||||
|
.fake/
|
||||||
|
|
||||||
|
# JetBrains Rider
|
||||||
|
.idea/
|
||||||
|
*.sln.iml
|
||||||
|
|
||||||
|
# CodeRush
|
||||||
|
.cr/
|
||||||
|
|
||||||
|
# Python Tools for Visual Studio (PTVS)
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Cake - Uncomment if you are using it
|
||||||
|
# tools/
|
||||||
|
|
||||||
|
# End of https://www.gitignore.io/api/aspnetcore
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace OFBButte.Api.Controllers
|
||||||
|
{
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
[ApiController]
|
||||||
|
public class ValuesController : ControllerBase
|
||||||
|
{
|
||||||
|
// GET api/values
|
||||||
|
[HttpGet]
|
||||||
|
public ActionResult<IEnumerable<string>> Get()
|
||||||
|
{
|
||||||
|
return new string[] { "value1", "value2" };
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET api/values/5
|
||||||
|
[HttpGet("{id}")]
|
||||||
|
public ActionResult<string> Get(int id)
|
||||||
|
{
|
||||||
|
return "value";
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST api/values
|
||||||
|
[HttpPost]
|
||||||
|
public void Post([FromBody] string value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// PUT api/values/5
|
||||||
|
[HttpPut("{id}")]
|
||||||
|
public void Put(int id, [FromBody] string value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// DELETE api/values/5
|
||||||
|
[HttpDelete("{id}")]
|
||||||
|
public void Delete(int id)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||||
|
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.App" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\OFBButte.Application\OFBButte.Application.csproj" />
|
||||||
|
<ProjectReference Include="..\OFBButte.Database\OFBButte.Database.csproj" />
|
||||||
|
<ProjectReference Include="..\OFBButte.Entities\OFBButte.Entities.csproj" />
|
||||||
|
<ProjectReference Include="..\OFBButte.Infrastructure\OFBButte.Infrastructure.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace OFBButte.Api
|
||||||
|
{
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
CreateWebHostBuilder(args).Build().Run();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
|
||||||
|
WebHost.CreateDefaultBuilder(args)
|
||||||
|
.UseStartup<Startup>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||||
|
"iisSettings": {
|
||||||
|
"windowsAuthentication": false,
|
||||||
|
"anonymousAuthentication": true,
|
||||||
|
"iisExpress": {
|
||||||
|
"applicationUrl": "http://localhost:50405",
|
||||||
|
"sslPort": 44314
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"IIS Express": {
|
||||||
|
"commandName": "IISExpress",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "api/values",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"OFBButte.Api": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "api/values",
|
||||||
|
"applicationUrl": "https://localhost:5001;http://localhost:5000",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.HttpsPolicy;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using OFBButte.Application.Configuration;
|
||||||
|
using OFBButte.Application.Database;
|
||||||
|
using OFBButte.Database;
|
||||||
|
|
||||||
|
namespace OFBButte.Api
|
||||||
|
{
|
||||||
|
public class Startup
|
||||||
|
{
|
||||||
|
public Startup(IConfiguration configuration)
|
||||||
|
{
|
||||||
|
Configuration = configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IConfiguration Configuration { get; }
|
||||||
|
|
||||||
|
// This method gets called by the runtime. Use this method to add services to the container.
|
||||||
|
public void ConfigureServices(IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
|
||||||
|
|
||||||
|
// App Settings
|
||||||
|
services.Configure<AppSettings>(Configuration.GetSection("App"));
|
||||||
|
|
||||||
|
// DB Context
|
||||||
|
services.AddDbContext<OFBContext>(o =>
|
||||||
|
{
|
||||||
|
OFBContext.UseMySql(o, Configuration.GetConnectionString("OFBContext"));
|
||||||
|
});
|
||||||
|
services.AddScoped<IOFBContext, OFBContext>(s => s.GetService<OFBContext>());
|
||||||
|
}
|
||||||
|
|
||||||
|
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||||
|
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
|
||||||
|
{
|
||||||
|
if (env.IsDevelopment())
|
||||||
|
{
|
||||||
|
app.UseDeveloperExceptionPage();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
||||||
|
app.UseHsts();
|
||||||
|
}
|
||||||
|
|
||||||
|
app.UseHttpsRedirection();
|
||||||
|
app.UseMvc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Debug",
|
||||||
|
"System": "Information",
|
||||||
|
"Microsoft": "Information"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"App": {
|
||||||
|
"Environment": "Dev"
|
||||||
|
},
|
||||||
|
"ConnectionStrings": {
|
||||||
|
"OFBContext": "server=localhost;database=ofbtest;user=ofbapi;password=87hjdusiodksyeunsjkdis7"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft": "Warning",
|
||||||
|
"Microsoft.Hosting.Lifetime": "Information"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*",
|
||||||
|
"App": {
|
||||||
|
"Environment": "Local"
|
||||||
|
},
|
||||||
|
"ConnectionStrings": {
|
||||||
|
"OFBContext": "server=localhost;database=ofbtest;user=ofbapi;password=87hjdusiodksyeunsjkdis7"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
using OFBButte.Application.Configuration;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace OFBButte.Application.Base
|
||||||
|
{
|
||||||
|
public class AuthorizeCommand
|
||||||
|
{
|
||||||
|
private readonly IAccess access;
|
||||||
|
protected readonly IEnumerable<Permission> allowedPermissions;
|
||||||
|
private AuthorizeCommand(IAccess access)
|
||||||
|
{
|
||||||
|
this.access = access;
|
||||||
|
}
|
||||||
|
public AuthorizeCommand(IAccess access, Permission permission): this(access)
|
||||||
|
{
|
||||||
|
this.allowedPermissions = new Permission[] { permission };
|
||||||
|
}
|
||||||
|
public AuthorizeCommand(IAccess access, IEnumerable<Permission> permissions): this(access)
|
||||||
|
{
|
||||||
|
this.allowedPermissions = permissions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Authorize()
|
||||||
|
{
|
||||||
|
if (allowedPermissions == null)
|
||||||
|
{
|
||||||
|
throw new UnauthorizedAccessException("Permission Denied");
|
||||||
|
}
|
||||||
|
foreach(var permission in allowedPermissions)
|
||||||
|
{
|
||||||
|
if (access.CanDo(permission))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new UnauthorizedAccessException($"Permission Denied: {string.Join(", ", allowedPermissions)}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
using OFBButte.Application.Configuration;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace OFBButte.Application.Base
|
||||||
|
{
|
||||||
|
public interface IAccess
|
||||||
|
{
|
||||||
|
bool CanDo(Permission permission);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace OFBButte.Application.Codes
|
||||||
|
{
|
||||||
|
public interface ICodeGenerator
|
||||||
|
{
|
||||||
|
string Generate(int byteSize = 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace OFBButte.Application.Configuration
|
||||||
|
{
|
||||||
|
public class AppSettings
|
||||||
|
{
|
||||||
|
public string Environment { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace OFBButte.Application.Configuration
|
||||||
|
{
|
||||||
|
public class ConnectionStrings
|
||||||
|
{
|
||||||
|
public string OFBContext { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace OFBButte.Application.Configuration
|
||||||
|
{
|
||||||
|
public class Permission
|
||||||
|
{
|
||||||
|
public string Value { get; private set; }
|
||||||
|
private Permission(string permission)
|
||||||
|
{
|
||||||
|
Value = permission;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Permission AddProfile { get; } = new Permission("Add Profile");
|
||||||
|
|
||||||
|
private static Dictionary<string, Permission> values = typeof(Permission).GetProperties(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static).Where(p => p.PropertyType == typeof(Permission)).ToDictionary(p => p.Name, p => (Permission)p.GetValue(null));
|
||||||
|
public static bool TryCast(string input, out Permission permission)
|
||||||
|
{
|
||||||
|
permission = null;
|
||||||
|
if (values.TryGetValue(input, out Permission perm))
|
||||||
|
{
|
||||||
|
permission = perm;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static implicit operator string(Permission input)
|
||||||
|
{
|
||||||
|
return input.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static explicit operator Permission(string input)
|
||||||
|
{
|
||||||
|
if (TryCast(input, out Permission permission))
|
||||||
|
{
|
||||||
|
return permission;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
throw new InvalidCastException($"Could not cast {input} to {typeof(Permission).ToString()}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using OFBButte.Entities;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace OFBButte.Application.Database
|
||||||
|
{
|
||||||
|
public interface IOFBContext
|
||||||
|
{
|
||||||
|
DbSet<User> Users { get; set; }
|
||||||
|
DbSet<Profile> Profiles { get; set; }
|
||||||
|
DbSet<EmailVerificationCode> EmailVerificationCodes { get; set; }
|
||||||
|
DbSet<PasswordResetCode> PasswordResetCodes { get; set; }
|
||||||
|
DbSet<MissionarySupport> MissionarySupportForms { get; set; }
|
||||||
|
int SaveChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace OFBButte.Application.Email
|
||||||
|
{
|
||||||
|
public abstract class EmailMessage
|
||||||
|
{
|
||||||
|
public virtual string To { get; set; }
|
||||||
|
public virtual string From { get; set; } = "donotreply@ofbbutte.com";
|
||||||
|
public virtual string Subject { get; set; }
|
||||||
|
public virtual string Body { get; set; }
|
||||||
|
public abstract string TemplateName { get; set; }
|
||||||
|
|
||||||
|
public void SetBodyFromTemplate(bool isTest = true)
|
||||||
|
{
|
||||||
|
string baseHtml = System.IO.File.ReadAllText($@"Email\Templates\base.html");
|
||||||
|
string template = System.IO.File.ReadAllText($@"Email\Templates\{TemplateName}.html");
|
||||||
|
baseHtml = baseHtml.Replace("{{TEST}}", isTest ? "THIS IS A TEST" : "");
|
||||||
|
template = baseHtml.Replace("{{CONTENT}}", template);
|
||||||
|
var props = GetType().GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public).ToDictionary(p => p.Name, p => p.GetValue(this)?.ToString() ?? "");
|
||||||
|
foreach (var property in props)
|
||||||
|
{
|
||||||
|
template = template.Replace($"{{{{{property.Key}}}}}", property.Value);
|
||||||
|
}
|
||||||
|
Body = template;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace OFBButte.Application.Email
|
||||||
|
{
|
||||||
|
public interface IEmailSender
|
||||||
|
{
|
||||||
|
Task Send(EmailMessage message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace OFBButte.Application.Email
|
||||||
|
{
|
||||||
|
public class PasswordResetEmailMessage : EmailMessage
|
||||||
|
{
|
||||||
|
public override string TemplateName { get; set; } = "PasswordResetEmailMessage";
|
||||||
|
public override string Subject { get; set; } = "OFB Butte: Password Reset";
|
||||||
|
|
||||||
|
public string Url { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
<p>
|
||||||
|
Please click the link below to reset your password.
|
||||||
|
</p>
|
||||||
|
<a href="{{Url}}">{{Url}}</a>
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
<p>
|
||||||
|
Please verify your email by clicking the link below.
|
||||||
|
</p>
|
||||||
|
<a href="{{VerificationUrl}}">{{VerificationUrl}}</a>
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
|
||||||
|
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title></title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{{TEST}}
|
||||||
|
{{CONTENT}}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace OFBButte.Application.Email
|
||||||
|
{
|
||||||
|
public class VerifyEmailMessage : EmailMessage
|
||||||
|
{
|
||||||
|
public override string TemplateName { get; set; } = "VerifyEmailMessage";
|
||||||
|
public override string Subject { get; set; } = "OFB Butte: Email Verification";
|
||||||
|
|
||||||
|
// Email Properties
|
||||||
|
public string VerificationUrl { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace OFBButte.Application.Exceptions
|
||||||
|
{
|
||||||
|
public class ValidationException: Exception
|
||||||
|
{
|
||||||
|
public ValidationException(string message): base(message)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
using OFBButte.Application.Database;
|
||||||
|
using OFBButte.Entities;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace OFBButte.Application.Missionary
|
||||||
|
{
|
||||||
|
public class AddMissionarySupportForm
|
||||||
|
{
|
||||||
|
private readonly IOFBContext context;
|
||||||
|
public AddMissionarySupportForm(IOFBContext context)
|
||||||
|
{
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MissionarySupport Handle(MissionarySupport support)
|
||||||
|
{
|
||||||
|
support.Id = 0;
|
||||||
|
context.MissionarySupportForms.Add(support);
|
||||||
|
context.SaveChanges();
|
||||||
|
return support;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
using OFBButte.Application.Database;
|
||||||
|
using OFBButte.Entities;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace OFBButte.Application.Missionary
|
||||||
|
{
|
||||||
|
public class GetMissionarySupportForm
|
||||||
|
{
|
||||||
|
private readonly IOFBContext context;
|
||||||
|
public GetMissionarySupportForm(IOFBContext context)
|
||||||
|
{
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MissionarySupport Handle(int id)
|
||||||
|
{
|
||||||
|
return context.MissionarySupportForms.FirstOrDefault(m => m.Id == id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.2.6" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\OFBButte.Entities\OFBButte.Entities.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
using OFBButte.Application.Base;
|
||||||
|
using OFBButte.Application.Codes;
|
||||||
|
using OFBButte.Application.Configuration;
|
||||||
|
using OFBButte.Application.Database;
|
||||||
|
using OFBButte.Entities;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace OFBButte.Application.Users
|
||||||
|
{
|
||||||
|
public class AddProfile: AuthorizeCommand
|
||||||
|
{
|
||||||
|
private readonly IOFBContext context;
|
||||||
|
private readonly ICodeGenerator codes;
|
||||||
|
public AddProfile(IAccess access, IOFBContext context, ICodeGenerator codes)
|
||||||
|
:base(access, Permission.AddProfile)
|
||||||
|
{
|
||||||
|
this.context = context;
|
||||||
|
this.codes = codes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Profile Handle(Profile profile)
|
||||||
|
{
|
||||||
|
base.Authorize();
|
||||||
|
|
||||||
|
return Handle(profile.FirstName, profile.LastName, profile.Street, profile.City, profile.State, profile.Zip, profile.Country);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Profile Handle(string first, string last, string street, string city, string state, string zip, string country)
|
||||||
|
{
|
||||||
|
base.Authorize();
|
||||||
|
|
||||||
|
var profile = new Profile()
|
||||||
|
{
|
||||||
|
FirstName = first,
|
||||||
|
LastName = last,
|
||||||
|
Street = street,
|
||||||
|
City = city,
|
||||||
|
State = state,
|
||||||
|
Zip = zip,
|
||||||
|
Country = country,
|
||||||
|
ModifiedDate = DateTime.Now,
|
||||||
|
ProfileFederationCode = new ProfileFederationCode()
|
||||||
|
{
|
||||||
|
CreatedDate = DateTime.Now,
|
||||||
|
Code = codes.Generate()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
context.Profiles.Add(profile);
|
||||||
|
context.SaveChanges();
|
||||||
|
return profile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
using OFBButte.Application.Database;
|
||||||
|
using OFBButte.Application.Exceptions;
|
||||||
|
using OFBButte.Entities;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace OFBButte.Application.Users
|
||||||
|
{
|
||||||
|
public class AddUser
|
||||||
|
{
|
||||||
|
private readonly IOFBContext context;
|
||||||
|
private readonly IPasswordHasher hasher;
|
||||||
|
public AddUser(IOFBContext context, IPasswordHasher hasher)
|
||||||
|
{
|
||||||
|
this.context = context;
|
||||||
|
this.hasher = hasher;
|
||||||
|
}
|
||||||
|
|
||||||
|
public User Handle(string email, string password)
|
||||||
|
{
|
||||||
|
// Check to see if email already exists
|
||||||
|
var existingUser = context.Users.FirstOrDefault(u => u.Email == email);
|
||||||
|
if (existingUser != null)
|
||||||
|
throw new ValidationException("User with email already exists");
|
||||||
|
|
||||||
|
// Create the new user
|
||||||
|
var user = new Entities.User()
|
||||||
|
{
|
||||||
|
Email = email,
|
||||||
|
CreatedDate = DateTime.Now,
|
||||||
|
Password = hasher.HashPassword(password)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Save to db
|
||||||
|
context.Users.Add(user);
|
||||||
|
context.SaveChanges();
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
using OFBButte.Application.Database;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace OFBButte.Application.Users
|
||||||
|
{
|
||||||
|
public class DeleteUser
|
||||||
|
{
|
||||||
|
private readonly IOFBContext context;
|
||||||
|
public DeleteUser(IOFBContext context)
|
||||||
|
{
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
public void Handle(string email)
|
||||||
|
{
|
||||||
|
// Get the user
|
||||||
|
var user = context.Users.FirstOrDefault(u => u.Email == email && u.DeletedDate == null);
|
||||||
|
if (user == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
user.DeletedDate = DateTime.Now;
|
||||||
|
context.SaveChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
using OFBButte.Application.Database;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace OFBButte.Application.Users
|
||||||
|
{
|
||||||
|
public class FederateProfile
|
||||||
|
{
|
||||||
|
private readonly IOFBContext context;
|
||||||
|
public FederateProfile(IOFBContext context)
|
||||||
|
{
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Handle(string email, string lastName, string federationCode)
|
||||||
|
{
|
||||||
|
// Get the user
|
||||||
|
var user = context.Users.FirstOrDefault(u => u.Email == email && u.Profile == null);
|
||||||
|
if (user == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Get the profile
|
||||||
|
var profile = context.Profiles.FirstOrDefault(p => p.ProfileFederationCode.Code == federationCode && p.LastName == lastName);
|
||||||
|
if (profile == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
user.Profile = profile;
|
||||||
|
context.SaveChanges();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace OFBButte.Application.Users
|
||||||
|
{
|
||||||
|
public interface IPasswordHasher
|
||||||
|
{
|
||||||
|
string HashPassword(string password);
|
||||||
|
bool VerifyPassword(string hashedPassword, string password);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
using OFBButte.Application.Database;
|
||||||
|
using OFBButte.Entities;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace OFBButte.Application.Users
|
||||||
|
{
|
||||||
|
public class LoginUser
|
||||||
|
{
|
||||||
|
private readonly IOFBContext context;
|
||||||
|
private readonly IPasswordHasher hasher;
|
||||||
|
public LoginUser(IOFBContext context, IPasswordHasher hasher)
|
||||||
|
{
|
||||||
|
this.context = context;
|
||||||
|
this.hasher = hasher;
|
||||||
|
}
|
||||||
|
public User Handle(string email, string password)
|
||||||
|
{
|
||||||
|
var user = context.Users.FirstOrDefault(u => u.Email == email);
|
||||||
|
if (user == null)
|
||||||
|
throw new Exception("Could not find email: " + email);
|
||||||
|
|
||||||
|
var validPassword = hasher.VerifyPassword(user.Password, password);
|
||||||
|
|
||||||
|
if (validPassword)
|
||||||
|
{
|
||||||
|
return user;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
using OFBButte.Application.Database;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace OFBButte.Application.Users
|
||||||
|
{
|
||||||
|
public class ResetPassword
|
||||||
|
{
|
||||||
|
private readonly IOFBContext context;
|
||||||
|
private readonly IPasswordHasher hasher;
|
||||||
|
public ResetPassword(IOFBContext context, IPasswordHasher hasher)
|
||||||
|
{
|
||||||
|
this.context = context;
|
||||||
|
this.hasher = hasher;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Handle(int codeId, string code, string newPassword)
|
||||||
|
{
|
||||||
|
// Get user
|
||||||
|
var dte = DateTime.Now.AddHours(-1);
|
||||||
|
var user = context.Users.FirstOrDefault(u => u.PassswordResetCode.Id == codeId && u.PassswordResetCode.Code == code && u.PassswordResetCode.CreatedDate >= dte);
|
||||||
|
if (user == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
user.Password = hasher.HashPassword(newPassword);
|
||||||
|
context.PasswordResetCodes.Remove(user.PassswordResetCode);
|
||||||
|
user.PassswordResetCode = null;
|
||||||
|
context.SaveChanges();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using OFBButte.Application.Codes;
|
||||||
|
using OFBButte.Application.Database;
|
||||||
|
using OFBButte.Application.Email;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace OFBButte.Application.Users
|
||||||
|
{
|
||||||
|
public class SendEmailValidation
|
||||||
|
{
|
||||||
|
private readonly IOFBContext context;
|
||||||
|
private readonly IEmailSender emailSender;
|
||||||
|
private readonly ICodeGenerator codes;
|
||||||
|
public SendEmailValidation(IOFBContext context, IEmailSender emailSender, ICodeGenerator codes)
|
||||||
|
{
|
||||||
|
this.context = context;
|
||||||
|
this.emailSender = emailSender;
|
||||||
|
this.codes = codes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Handle(string email)
|
||||||
|
{
|
||||||
|
var user = context.Users.Include(u => u.EmailVerificationCode).FirstOrDefault(u => u.Email == email);
|
||||||
|
if (user == null)
|
||||||
|
throw new Exception($"Could not find user with email: {email}");
|
||||||
|
|
||||||
|
if (user.EmailVerificationCode != null)
|
||||||
|
{
|
||||||
|
context.EmailVerificationCodes.Remove(user.EmailVerificationCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate verification code
|
||||||
|
user.EmailVerificationCode = new Entities.EmailVerificationCode()
|
||||||
|
{
|
||||||
|
Code = codes.Generate(),
|
||||||
|
CreatedDate = DateTime.Now
|
||||||
|
};
|
||||||
|
|
||||||
|
context.SaveChanges();
|
||||||
|
|
||||||
|
// Create the email
|
||||||
|
var emailMessage = new VerifyEmailMessage()
|
||||||
|
{
|
||||||
|
To = email,
|
||||||
|
From = "donotreply@ofbbutte.com",
|
||||||
|
VerificationUrl = $"https://ofbbutte.com/user/emailverification?c={user.EmailVerificationCode.Id}&code={user.EmailVerificationCode.Code}"
|
||||||
|
};
|
||||||
|
|
||||||
|
await emailSender.Send(emailMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using OFBButte.Application.Codes;
|
||||||
|
using OFBButte.Application.Database;
|
||||||
|
using OFBButte.Application.Email;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace OFBButte.Application.Users
|
||||||
|
{
|
||||||
|
public class SendResetPasswordEmail
|
||||||
|
{
|
||||||
|
private readonly IOFBContext context;
|
||||||
|
private readonly IEmailSender emailSender;
|
||||||
|
private readonly ICodeGenerator codes;
|
||||||
|
|
||||||
|
public SendResetPasswordEmail(IOFBContext context, IEmailSender emailSender, ICodeGenerator codes)
|
||||||
|
{
|
||||||
|
this.context = context;
|
||||||
|
this.emailSender = emailSender;
|
||||||
|
this.codes = codes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Handle(string email, string lastName)
|
||||||
|
{
|
||||||
|
// Get the user
|
||||||
|
var user = context.Users.Include(u => u.PassswordResetCode).FirstOrDefault(u => u.Email == email && u.Profile.LastName == lastName);
|
||||||
|
if (user == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (user.PassswordResetCode != null)
|
||||||
|
{
|
||||||
|
context.PasswordResetCodes.Remove(user.PassswordResetCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
user.PassswordResetCode = new Entities.PasswordResetCode()
|
||||||
|
{
|
||||||
|
CreatedDate = DateTime.Now,
|
||||||
|
Code = codes.Generate()
|
||||||
|
};
|
||||||
|
|
||||||
|
context.SaveChanges();
|
||||||
|
|
||||||
|
var message = new PasswordResetEmailMessage()
|
||||||
|
{
|
||||||
|
To = user.Email,
|
||||||
|
Url = $"http://ofbbutte.com/user/resetpassword?c={user.PassswordResetCode.Id}&code={user.PassswordResetCode.Code}"
|
||||||
|
};
|
||||||
|
|
||||||
|
await emailSender.Send(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using OFBButte.Application.Database;
|
||||||
|
using OFBButte.Entities;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace OFBButte.Application.Users
|
||||||
|
{
|
||||||
|
public class UpdateProfile
|
||||||
|
{
|
||||||
|
private readonly IOFBContext context;
|
||||||
|
public UpdateProfile(IOFBContext context)
|
||||||
|
{
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Profile Handle(string email, Profile profile)
|
||||||
|
{
|
||||||
|
// Get the user
|
||||||
|
var user = context.Users.Include(u => u.Profile).FirstOrDefault(u => u.Email == email);
|
||||||
|
if (user == null || user.Profile == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
user.Profile.City = profile.City;
|
||||||
|
user.Profile.Country = profile.Country;
|
||||||
|
user.Profile.FirstName = profile.FirstName;
|
||||||
|
user.Profile.LastName = profile.LastName;
|
||||||
|
user.Profile.ModifiedDate = DateTime.Now;
|
||||||
|
user.Profile.State = profile.State;
|
||||||
|
user.Profile.Street = profile.Street;
|
||||||
|
user.Profile.Zip = profile.Zip;
|
||||||
|
|
||||||
|
context.SaveChanges();
|
||||||
|
return user.Profile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
using OFBButte.Application.Database;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace OFBButte.Application.Users
|
||||||
|
{
|
||||||
|
public class VerifyEmail
|
||||||
|
{
|
||||||
|
private readonly IOFBContext context;
|
||||||
|
public VerifyEmail(IOFBContext context)
|
||||||
|
{
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Handle(int codeId, string code)
|
||||||
|
{
|
||||||
|
var dte = DateTime.Now.AddHours(-1);
|
||||||
|
var user = context.Users.FirstOrDefault(u => u.EmailVerificationCode.Id == codeId && u.EmailVerificationCode.Code == code && u.EmailVerifiedDate > dte);
|
||||||
|
if (user == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
user.EmailVerifiedDate = DateTime.Now;
|
||||||
|
context.SaveChanges();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using OFBButte.Application.Configuration;
|
||||||
|
using OFBButte.Application.Database;
|
||||||
|
using OFBButte.Application.Email;
|
||||||
|
using OFBButte.Application.Users;
|
||||||
|
using OFBButte.Infrastructure.Hashing;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace OFBButte.Console
|
||||||
|
{
|
||||||
|
public class App
|
||||||
|
{
|
||||||
|
private readonly AppSettings appSettings;
|
||||||
|
private readonly IEmailSender emailSender;
|
||||||
|
private readonly IOFBContext context;
|
||||||
|
private string user = "djabsher@gmail.com";
|
||||||
|
private string pass = "my password";
|
||||||
|
|
||||||
|
public App(IOptions<AppSettings> appSettings, IOFBContext context, IEmailSender emailSender)
|
||||||
|
{
|
||||||
|
this.appSettings = appSettings.Value;
|
||||||
|
this.emailSender = emailSender;
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Run()
|
||||||
|
{
|
||||||
|
var env = appSettings.Environment;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await SendVerificationEmail();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddUser()
|
||||||
|
{
|
||||||
|
var adder = new AddUser(context, new PasswordHasher());
|
||||||
|
var result = adder.Handle(user, pass);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SendVerificationEmail()
|
||||||
|
{
|
||||||
|
var adder = new SendEmailValidation(context, emailSender, new Infrastructure.Codes.CodeGenerator());
|
||||||
|
await adder.Handle(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoginUser()
|
||||||
|
{
|
||||||
|
var login = new LoginUser(context, new PasswordHasher());
|
||||||
|
var result = login.Handle(user, pass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.2.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\OFBButte.Application\OFBButte.Application.csproj" />
|
||||||
|
<ProjectReference Include="..\OFBButte.Database\OFBButte.Database.csproj" />
|
||||||
|
<ProjectReference Include="..\OFBButte.Infrastructure\OFBButte.Infrastructure.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using OFBButte.Application.Configuration;
|
||||||
|
using OFBButte.Application.Database;
|
||||||
|
using OFBButte.Application.Email;
|
||||||
|
using OFBButte.Database;
|
||||||
|
using OFBButte.Infrastructure.Email;
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace OFBButte.Console
|
||||||
|
{
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
|
||||||
|
static ServiceProvider services;
|
||||||
|
|
||||||
|
static async Task Main(string[] args)
|
||||||
|
{
|
||||||
|
SetupDI();
|
||||||
|
|
||||||
|
await services.GetService<App>().Run();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SetupDI()
|
||||||
|
{
|
||||||
|
var builder = new ConfigurationBuilder();
|
||||||
|
builder.AddJsonFile("appsettings.json", false, true);
|
||||||
|
var config = builder.Build();
|
||||||
|
|
||||||
|
|
||||||
|
var serviceCollection = new ServiceCollection();
|
||||||
|
serviceCollection.Configure<AppSettings>(a => config.GetSection("app").Bind(a));
|
||||||
|
|
||||||
|
serviceCollection.AddSingleton<IEmailSender>(new EmailSender());
|
||||||
|
serviceCollection.AddDbContext<OFBContext>(o => OFBContext.UseMySql(o, config.GetConnectionString("OFBContext")));
|
||||||
|
serviceCollection.AddScoped<IOFBContext, OFBContext>(s => s.GetService<OFBContext>());
|
||||||
|
serviceCollection.AddTransient<App>();
|
||||||
|
services = serviceCollection.BuildServiceProvider();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,318 @@
|
||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using OFBButte.Database;
|
||||||
|
|
||||||
|
namespace OFBButte.Database.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(OFBContext))]
|
||||||
|
[Migration("20190717032510_Initial")]
|
||||||
|
partial class Initial
|
||||||
|
{
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "2.2.6-servicing-10079")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||||
|
|
||||||
|
modelBuilder.Entity("OFBButte.Entities.EmailVerificationCode", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("Code");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedDate");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("EmailVerificationCodes");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("OFBButte.Entities.MissionaryChild", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int?>("MissionarySupportId");
|
||||||
|
|
||||||
|
b.Property<string>("Name");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("MissionarySupportId");
|
||||||
|
|
||||||
|
b.ToTable("MissionaryChild");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("OFBButte.Entities.MissionaryCollegeRecommendation", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int?>("MissionarySupportId");
|
||||||
|
|
||||||
|
b.Property<string>("Name");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("MissionarySupportId");
|
||||||
|
|
||||||
|
b.ToTable("MissionaryCollegeRecommendation");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("OFBButte.Entities.MissionarySupport", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("AdmittedWrong");
|
||||||
|
|
||||||
|
b.Property<bool>("Alcohol");
|
||||||
|
|
||||||
|
b.Property<string>("AloneOrTeam");
|
||||||
|
|
||||||
|
b.Property<string>("BibleVersionOpinion");
|
||||||
|
|
||||||
|
b.Property<string>("BibleVersionsUsed");
|
||||||
|
|
||||||
|
b.Property<string>("BillsOnTime");
|
||||||
|
|
||||||
|
b.Property<string>("CallToField");
|
||||||
|
|
||||||
|
b.Property<string>("CellPhone");
|
||||||
|
|
||||||
|
b.Property<string>("Charasmaticism");
|
||||||
|
|
||||||
|
b.Property<int>("ChildrenSchool");
|
||||||
|
|
||||||
|
b.Property<bool>("ContemporaryMusic");
|
||||||
|
|
||||||
|
b.Property<string>("CorrectWrongOfAnotherMissionary");
|
||||||
|
|
||||||
|
b.Property<decimal>("CurrentMonthlySupport");
|
||||||
|
|
||||||
|
b.Property<int>("DailyBible");
|
||||||
|
|
||||||
|
b.Property<bool>("Dance");
|
||||||
|
|
||||||
|
b.Property<bool>("Divorced");
|
||||||
|
|
||||||
|
b.Property<string>("EvaluationOfNationals");
|
||||||
|
|
||||||
|
b.Property<string>("FellowshipAssociation");
|
||||||
|
|
||||||
|
b.Property<bool>("FemaleDressStandard");
|
||||||
|
|
||||||
|
b.Property<int>("FemaleShorts");
|
||||||
|
|
||||||
|
b.Property<int>("FemaleSlacks");
|
||||||
|
|
||||||
|
b.Property<string>("FieldOfService");
|
||||||
|
|
||||||
|
b.Property<string>("FieldPhone");
|
||||||
|
|
||||||
|
b.Property<string>("FinancialStatementPrevYear");
|
||||||
|
|
||||||
|
b.Property<bool>("Fundamentalist");
|
||||||
|
|
||||||
|
b.Property<bool>("GroundsForRemarry");
|
||||||
|
|
||||||
|
b.Property<string>("HomePhone");
|
||||||
|
|
||||||
|
b.Property<string>("LateBillActionTaken");
|
||||||
|
|
||||||
|
b.Property<string>("LateBills");
|
||||||
|
|
||||||
|
b.Property<bool>("MaleHair");
|
||||||
|
|
||||||
|
b.Property<bool>("MarryADivorcee");
|
||||||
|
|
||||||
|
b.Property<bool>("MasonicLodge");
|
||||||
|
|
||||||
|
b.Property<decimal>("MonthlySupportNeeded");
|
||||||
|
|
||||||
|
b.Property<bool>("MovieTheaters");
|
||||||
|
|
||||||
|
b.Property<string>("Name");
|
||||||
|
|
||||||
|
b.Property<double>("NumberLedToChrist");
|
||||||
|
|
||||||
|
b.Property<double>("NumberWeeklyTracts");
|
||||||
|
|
||||||
|
b.Property<double>("NumberWitnessedTo");
|
||||||
|
|
||||||
|
b.Property<string>("Plans");
|
||||||
|
|
||||||
|
b.Property<string>("PotentialHarvest");
|
||||||
|
|
||||||
|
b.Property<string>("Predestination");
|
||||||
|
|
||||||
|
b.Property<string>("RateOfSuccess");
|
||||||
|
|
||||||
|
b.Property<string>("RepentanceDefinition");
|
||||||
|
|
||||||
|
b.Property<bool>("RepentanceNecessary");
|
||||||
|
|
||||||
|
b.Property<string>("RestAndRelaxation");
|
||||||
|
|
||||||
|
b.Property<string>("SendingChurch");
|
||||||
|
|
||||||
|
b.Property<bool>("Smoking");
|
||||||
|
|
||||||
|
b.Property<string>("SwimmingClothing");
|
||||||
|
|
||||||
|
b.Property<string>("Television");
|
||||||
|
|
||||||
|
b.Property<string>("Testimony");
|
||||||
|
|
||||||
|
b.Property<string>("TimeInCountry");
|
||||||
|
|
||||||
|
b.Property<bool>("Tongues");
|
||||||
|
|
||||||
|
b.Property<string>("WifesName");
|
||||||
|
|
||||||
|
b.Property<bool>("WorldlyMusic");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("MissionarySupportForms");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("OFBButte.Entities.PasswordResetCode", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("Code");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedDate");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("PasswordResetCodes");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("OFBButte.Entities.Profile", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("City");
|
||||||
|
|
||||||
|
b.Property<string>("Country");
|
||||||
|
|
||||||
|
b.Property<string>("FirstName");
|
||||||
|
|
||||||
|
b.Property<string>("LastName");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ModifiedDate");
|
||||||
|
|
||||||
|
b.Property<int?>("ProfileFederationCodeId");
|
||||||
|
|
||||||
|
b.Property<string>("State");
|
||||||
|
|
||||||
|
b.Property<string>("Street");
|
||||||
|
|
||||||
|
b.Property<string>("Zip");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ProfileFederationCodeId");
|
||||||
|
|
||||||
|
b.ToTable("Profiles");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("OFBButte.Entities.ProfileFederationCode", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("Code");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedDate");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("ProfileFederationCodes");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("OFBButte.Entities.User", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedDate");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DeletedDate");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Property<int?>("EmailVerificationCodeId");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("EmailVerifiedDate");
|
||||||
|
|
||||||
|
b.Property<int?>("PassswordResetCodeId");
|
||||||
|
|
||||||
|
b.Property<string>("Password")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Property<int?>("ProfileId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("Email");
|
||||||
|
|
||||||
|
b.HasIndex("EmailVerificationCodeId");
|
||||||
|
|
||||||
|
b.HasIndex("PassswordResetCodeId");
|
||||||
|
|
||||||
|
b.HasIndex("ProfileId");
|
||||||
|
|
||||||
|
b.ToTable("Users");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("OFBButte.Entities.MissionaryChild", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("OFBButte.Entities.MissionarySupport")
|
||||||
|
.WithMany("Children")
|
||||||
|
.HasForeignKey("MissionarySupportId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("OFBButte.Entities.MissionaryCollegeRecommendation", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("OFBButte.Entities.MissionarySupport")
|
||||||
|
.WithMany("CollegeRecommendations")
|
||||||
|
.HasForeignKey("MissionarySupportId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("OFBButte.Entities.Profile", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("OFBButte.Entities.ProfileFederationCode", "ProfileFederationCode")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("ProfileFederationCodeId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("OFBButte.Entities.User", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("OFBButte.Entities.EmailVerificationCode", "EmailVerificationCode")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("EmailVerificationCodeId");
|
||||||
|
|
||||||
|
b.HasOne("OFBButte.Entities.PasswordResetCode", "PassswordResetCode")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("PassswordResetCodeId");
|
||||||
|
|
||||||
|
b.HasOne("OFBButte.Entities.Profile", "Profile")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("ProfileId");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,287 @@
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
namespace OFBButte.Database.Migrations
|
||||||
|
{
|
||||||
|
public partial class Initial : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "EmailVerificationCodes",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(nullable: false)
|
||||||
|
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||||
|
Code = table.Column<string>(nullable: true),
|
||||||
|
CreatedDate = table.Column<DateTime>(nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_EmailVerificationCodes", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "MissionarySupportForms",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(nullable: false)
|
||||||
|
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||||
|
Name = table.Column<string>(nullable: true),
|
||||||
|
HomePhone = table.Column<string>(nullable: true),
|
||||||
|
CellPhone = table.Column<string>(nullable: true),
|
||||||
|
FieldPhone = table.Column<string>(nullable: true),
|
||||||
|
WifesName = table.Column<string>(nullable: true),
|
||||||
|
Testimony = table.Column<string>(nullable: true),
|
||||||
|
CallToField = table.Column<string>(nullable: true),
|
||||||
|
SendingChurch = table.Column<string>(nullable: true),
|
||||||
|
FieldOfService = table.Column<string>(nullable: true),
|
||||||
|
Plans = table.Column<string>(nullable: true),
|
||||||
|
EvaluationOfNationals = table.Column<string>(nullable: true),
|
||||||
|
TimeInCountry = table.Column<string>(nullable: true),
|
||||||
|
CorrectWrongOfAnotherMissionary = table.Column<string>(nullable: true),
|
||||||
|
FinancialStatementPrevYear = table.Column<string>(nullable: true),
|
||||||
|
CurrentMonthlySupport = table.Column<decimal>(nullable: false),
|
||||||
|
MonthlySupportNeeded = table.Column<decimal>(nullable: false),
|
||||||
|
RestAndRelaxation = table.Column<string>(nullable: true),
|
||||||
|
AloneOrTeam = table.Column<string>(nullable: true),
|
||||||
|
ChildrenSchool = table.Column<int>(nullable: false),
|
||||||
|
Dance = table.Column<bool>(nullable: false),
|
||||||
|
WorldlyMusic = table.Column<bool>(nullable: false),
|
||||||
|
MovieTheaters = table.Column<bool>(nullable: false),
|
||||||
|
Alcohol = table.Column<bool>(nullable: false),
|
||||||
|
Smoking = table.Column<bool>(nullable: false),
|
||||||
|
MaleHair = table.Column<bool>(nullable: false),
|
||||||
|
FemaleSlacks = table.Column<int>(nullable: false),
|
||||||
|
FemaleShorts = table.Column<int>(nullable: false),
|
||||||
|
FemaleDressStandard = table.Column<bool>(nullable: false),
|
||||||
|
SwimmingClothing = table.Column<string>(nullable: true),
|
||||||
|
Television = table.Column<string>(nullable: true),
|
||||||
|
DailyBible = table.Column<int>(nullable: false),
|
||||||
|
NumberLedToChrist = table.Column<double>(nullable: false),
|
||||||
|
NumberWitnessedTo = table.Column<double>(nullable: false),
|
||||||
|
NumberWeeklyTracts = table.Column<double>(nullable: false),
|
||||||
|
RateOfSuccess = table.Column<string>(nullable: true),
|
||||||
|
Predestination = table.Column<string>(nullable: true),
|
||||||
|
FellowshipAssociation = table.Column<string>(nullable: true),
|
||||||
|
AdmittedWrong = table.Column<string>(nullable: true),
|
||||||
|
Divorced = table.Column<bool>(nullable: false),
|
||||||
|
GroundsForRemarry = table.Column<bool>(nullable: false),
|
||||||
|
MarryADivorcee = table.Column<bool>(nullable: false),
|
||||||
|
MasonicLodge = table.Column<bool>(nullable: false),
|
||||||
|
BibleVersionsUsed = table.Column<string>(nullable: true),
|
||||||
|
BibleVersionOpinion = table.Column<string>(nullable: true),
|
||||||
|
ContemporaryMusic = table.Column<bool>(nullable: false),
|
||||||
|
Charasmaticism = table.Column<string>(nullable: true),
|
||||||
|
Tongues = table.Column<bool>(nullable: false),
|
||||||
|
RepentanceNecessary = table.Column<bool>(nullable: false),
|
||||||
|
RepentanceDefinition = table.Column<string>(nullable: true),
|
||||||
|
Fundamentalist = table.Column<bool>(nullable: false),
|
||||||
|
BillsOnTime = table.Column<string>(nullable: true),
|
||||||
|
LateBills = table.Column<string>(nullable: true),
|
||||||
|
LateBillActionTaken = table.Column<string>(nullable: true),
|
||||||
|
PotentialHarvest = table.Column<string>(nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_MissionarySupportForms", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "PasswordResetCodes",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(nullable: false)
|
||||||
|
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||||
|
Code = table.Column<string>(nullable: true),
|
||||||
|
CreatedDate = table.Column<DateTime>(nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_PasswordResetCodes", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ProfileFederationCodes",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(nullable: false)
|
||||||
|
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||||
|
Code = table.Column<string>(nullable: true),
|
||||||
|
CreatedDate = table.Column<DateTime>(nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ProfileFederationCodes", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "MissionaryChild",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(nullable: false)
|
||||||
|
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||||
|
Name = table.Column<string>(nullable: true),
|
||||||
|
MissionarySupportId = table.Column<int>(nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_MissionaryChild", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_MissionaryChild_MissionarySupportForms_MissionarySupportId",
|
||||||
|
column: x => x.MissionarySupportId,
|
||||||
|
principalTable: "MissionarySupportForms",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "MissionaryCollegeRecommendation",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(nullable: false)
|
||||||
|
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||||
|
Name = table.Column<string>(nullable: true),
|
||||||
|
MissionarySupportId = table.Column<int>(nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_MissionaryCollegeRecommendation", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_MissionaryCollegeRecommendation_MissionarySupportForms_Missi~",
|
||||||
|
column: x => x.MissionarySupportId,
|
||||||
|
principalTable: "MissionarySupportForms",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Profiles",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(nullable: false)
|
||||||
|
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||||
|
ModifiedDate = table.Column<DateTime>(nullable: false),
|
||||||
|
FirstName = table.Column<string>(nullable: true),
|
||||||
|
LastName = table.Column<string>(nullable: true),
|
||||||
|
Street = table.Column<string>(nullable: true),
|
||||||
|
City = table.Column<string>(nullable: true),
|
||||||
|
State = table.Column<string>(nullable: true),
|
||||||
|
Zip = table.Column<string>(nullable: true),
|
||||||
|
Country = table.Column<string>(nullable: true),
|
||||||
|
ProfileFederationCodeId = table.Column<int>(nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Profiles", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Profiles_ProfileFederationCodes_ProfileFederationCodeId",
|
||||||
|
column: x => x.ProfileFederationCodeId,
|
||||||
|
principalTable: "ProfileFederationCodes",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Users",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(nullable: false)
|
||||||
|
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||||
|
Email = table.Column<string>(nullable: false),
|
||||||
|
Password = table.Column<string>(nullable: false),
|
||||||
|
EmailVerifiedDate = table.Column<DateTime>(nullable: true),
|
||||||
|
CreatedDate = table.Column<DateTime>(nullable: false),
|
||||||
|
DeletedDate = table.Column<DateTime>(nullable: true),
|
||||||
|
ProfileId = table.Column<int>(nullable: true),
|
||||||
|
EmailVerificationCodeId = table.Column<int>(nullable: true),
|
||||||
|
PassswordResetCodeId = table.Column<int>(nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Users", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Users_EmailVerificationCodes_EmailVerificationCodeId",
|
||||||
|
column: x => x.EmailVerificationCodeId,
|
||||||
|
principalTable: "EmailVerificationCodes",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Users_PasswordResetCodes_PassswordResetCodeId",
|
||||||
|
column: x => x.PassswordResetCodeId,
|
||||||
|
principalTable: "PasswordResetCodes",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Users_Profiles_ProfileId",
|
||||||
|
column: x => x.ProfileId,
|
||||||
|
principalTable: "Profiles",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_MissionaryChild_MissionarySupportId",
|
||||||
|
table: "MissionaryChild",
|
||||||
|
column: "MissionarySupportId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_MissionaryCollegeRecommendation_MissionarySupportId",
|
||||||
|
table: "MissionaryCollegeRecommendation",
|
||||||
|
column: "MissionarySupportId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Profiles_ProfileFederationCodeId",
|
||||||
|
table: "Profiles",
|
||||||
|
column: "ProfileFederationCodeId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Users_Email",
|
||||||
|
table: "Users",
|
||||||
|
column: "Email");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Users_EmailVerificationCodeId",
|
||||||
|
table: "Users",
|
||||||
|
column: "EmailVerificationCodeId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Users_PassswordResetCodeId",
|
||||||
|
table: "Users",
|
||||||
|
column: "PassswordResetCodeId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Users_ProfileId",
|
||||||
|
table: "Users",
|
||||||
|
column: "ProfileId");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "MissionaryChild");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "MissionaryCollegeRecommendation");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Users");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "MissionarySupportForms");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "EmailVerificationCodes");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "PasswordResetCodes");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Profiles");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ProfileFederationCodes");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,316 @@
|
||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using OFBButte.Database;
|
||||||
|
|
||||||
|
namespace OFBButte.Database.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(OFBContext))]
|
||||||
|
partial class OFBContextModelSnapshot : ModelSnapshot
|
||||||
|
{
|
||||||
|
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "2.2.6-servicing-10079")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||||
|
|
||||||
|
modelBuilder.Entity("OFBButte.Entities.EmailVerificationCode", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("Code");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedDate");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("EmailVerificationCodes");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("OFBButte.Entities.MissionaryChild", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int?>("MissionarySupportId");
|
||||||
|
|
||||||
|
b.Property<string>("Name");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("MissionarySupportId");
|
||||||
|
|
||||||
|
b.ToTable("MissionaryChild");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("OFBButte.Entities.MissionaryCollegeRecommendation", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int?>("MissionarySupportId");
|
||||||
|
|
||||||
|
b.Property<string>("Name");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("MissionarySupportId");
|
||||||
|
|
||||||
|
b.ToTable("MissionaryCollegeRecommendation");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("OFBButte.Entities.MissionarySupport", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("AdmittedWrong");
|
||||||
|
|
||||||
|
b.Property<bool>("Alcohol");
|
||||||
|
|
||||||
|
b.Property<string>("AloneOrTeam");
|
||||||
|
|
||||||
|
b.Property<string>("BibleVersionOpinion");
|
||||||
|
|
||||||
|
b.Property<string>("BibleVersionsUsed");
|
||||||
|
|
||||||
|
b.Property<string>("BillsOnTime");
|
||||||
|
|
||||||
|
b.Property<string>("CallToField");
|
||||||
|
|
||||||
|
b.Property<string>("CellPhone");
|
||||||
|
|
||||||
|
b.Property<string>("Charasmaticism");
|
||||||
|
|
||||||
|
b.Property<int>("ChildrenSchool");
|
||||||
|
|
||||||
|
b.Property<bool>("ContemporaryMusic");
|
||||||
|
|
||||||
|
b.Property<string>("CorrectWrongOfAnotherMissionary");
|
||||||
|
|
||||||
|
b.Property<decimal>("CurrentMonthlySupport");
|
||||||
|
|
||||||
|
b.Property<int>("DailyBible");
|
||||||
|
|
||||||
|
b.Property<bool>("Dance");
|
||||||
|
|
||||||
|
b.Property<bool>("Divorced");
|
||||||
|
|
||||||
|
b.Property<string>("EvaluationOfNationals");
|
||||||
|
|
||||||
|
b.Property<string>("FellowshipAssociation");
|
||||||
|
|
||||||
|
b.Property<bool>("FemaleDressStandard");
|
||||||
|
|
||||||
|
b.Property<int>("FemaleShorts");
|
||||||
|
|
||||||
|
b.Property<int>("FemaleSlacks");
|
||||||
|
|
||||||
|
b.Property<string>("FieldOfService");
|
||||||
|
|
||||||
|
b.Property<string>("FieldPhone");
|
||||||
|
|
||||||
|
b.Property<string>("FinancialStatementPrevYear");
|
||||||
|
|
||||||
|
b.Property<bool>("Fundamentalist");
|
||||||
|
|
||||||
|
b.Property<bool>("GroundsForRemarry");
|
||||||
|
|
||||||
|
b.Property<string>("HomePhone");
|
||||||
|
|
||||||
|
b.Property<string>("LateBillActionTaken");
|
||||||
|
|
||||||
|
b.Property<string>("LateBills");
|
||||||
|
|
||||||
|
b.Property<bool>("MaleHair");
|
||||||
|
|
||||||
|
b.Property<bool>("MarryADivorcee");
|
||||||
|
|
||||||
|
b.Property<bool>("MasonicLodge");
|
||||||
|
|
||||||
|
b.Property<decimal>("MonthlySupportNeeded");
|
||||||
|
|
||||||
|
b.Property<bool>("MovieTheaters");
|
||||||
|
|
||||||
|
b.Property<string>("Name");
|
||||||
|
|
||||||
|
b.Property<double>("NumberLedToChrist");
|
||||||
|
|
||||||
|
b.Property<double>("NumberWeeklyTracts");
|
||||||
|
|
||||||
|
b.Property<double>("NumberWitnessedTo");
|
||||||
|
|
||||||
|
b.Property<string>("Plans");
|
||||||
|
|
||||||
|
b.Property<string>("PotentialHarvest");
|
||||||
|
|
||||||
|
b.Property<string>("Predestination");
|
||||||
|
|
||||||
|
b.Property<string>("RateOfSuccess");
|
||||||
|
|
||||||
|
b.Property<string>("RepentanceDefinition");
|
||||||
|
|
||||||
|
b.Property<bool>("RepentanceNecessary");
|
||||||
|
|
||||||
|
b.Property<string>("RestAndRelaxation");
|
||||||
|
|
||||||
|
b.Property<string>("SendingChurch");
|
||||||
|
|
||||||
|
b.Property<bool>("Smoking");
|
||||||
|
|
||||||
|
b.Property<string>("SwimmingClothing");
|
||||||
|
|
||||||
|
b.Property<string>("Television");
|
||||||
|
|
||||||
|
b.Property<string>("Testimony");
|
||||||
|
|
||||||
|
b.Property<string>("TimeInCountry");
|
||||||
|
|
||||||
|
b.Property<bool>("Tongues");
|
||||||
|
|
||||||
|
b.Property<string>("WifesName");
|
||||||
|
|
||||||
|
b.Property<bool>("WorldlyMusic");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("MissionarySupportForms");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("OFBButte.Entities.PasswordResetCode", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("Code");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedDate");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("PasswordResetCodes");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("OFBButte.Entities.Profile", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("City");
|
||||||
|
|
||||||
|
b.Property<string>("Country");
|
||||||
|
|
||||||
|
b.Property<string>("FirstName");
|
||||||
|
|
||||||
|
b.Property<string>("LastName");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ModifiedDate");
|
||||||
|
|
||||||
|
b.Property<int?>("ProfileFederationCodeId");
|
||||||
|
|
||||||
|
b.Property<string>("State");
|
||||||
|
|
||||||
|
b.Property<string>("Street");
|
||||||
|
|
||||||
|
b.Property<string>("Zip");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ProfileFederationCodeId");
|
||||||
|
|
||||||
|
b.ToTable("Profiles");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("OFBButte.Entities.ProfileFederationCode", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("Code");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedDate");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("ProfileFederationCodes");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("OFBButte.Entities.User", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedDate");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DeletedDate");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Property<int?>("EmailVerificationCodeId");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("EmailVerifiedDate");
|
||||||
|
|
||||||
|
b.Property<int?>("PassswordResetCodeId");
|
||||||
|
|
||||||
|
b.Property<string>("Password")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Property<int?>("ProfileId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("Email");
|
||||||
|
|
||||||
|
b.HasIndex("EmailVerificationCodeId");
|
||||||
|
|
||||||
|
b.HasIndex("PassswordResetCodeId");
|
||||||
|
|
||||||
|
b.HasIndex("ProfileId");
|
||||||
|
|
||||||
|
b.ToTable("Users");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("OFBButte.Entities.MissionaryChild", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("OFBButte.Entities.MissionarySupport")
|
||||||
|
.WithMany("Children")
|
||||||
|
.HasForeignKey("MissionarySupportId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("OFBButte.Entities.MissionaryCollegeRecommendation", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("OFBButte.Entities.MissionarySupport")
|
||||||
|
.WithMany("CollegeRecommendations")
|
||||||
|
.HasForeignKey("MissionarySupportId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("OFBButte.Entities.Profile", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("OFBButte.Entities.ProfileFederationCode", "ProfileFederationCode")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("ProfileFederationCodeId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("OFBButte.Entities.User", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("OFBButte.Entities.EmailVerificationCode", "EmailVerificationCode")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("EmailVerificationCodeId");
|
||||||
|
|
||||||
|
b.HasOne("OFBButte.Entities.PasswordResetCode", "PassswordResetCode")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("PassswordResetCodeId");
|
||||||
|
|
||||||
|
b.HasOne("OFBButte.Entities.Profile", "Profile")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("ProfileId");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.2.6" />
|
||||||
|
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="2.2.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\OFBButte.Application\OFBButte.Application.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using OFBButte.Application.Database;
|
||||||
|
using OFBButte.Entities;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace OFBButte.Database
|
||||||
|
{
|
||||||
|
public class OFBContext : DbContext, IOFBContext
|
||||||
|
{
|
||||||
|
public OFBContext(DbContextOptions<OFBContext> options)
|
||||||
|
: base(options)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
public static void UseMySql(DbContextOptionsBuilder optionsBuilder, string connString)
|
||||||
|
{
|
||||||
|
optionsBuilder.UseMySql(connString);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DbSet<User> Users { get; set; }
|
||||||
|
public DbSet<Profile> Profiles { get; set; }
|
||||||
|
public DbSet<ProfileFederationCode> ProfileFederationCodes { get; set; }
|
||||||
|
public DbSet<EmailVerificationCode> EmailVerificationCodes { get; set; }
|
||||||
|
public DbSet<PasswordResetCode> PasswordResetCodes { get; set; }
|
||||||
|
public DbSet<MissionarySupport> MissionarySupportForms { get; set; }
|
||||||
|
|
||||||
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
base.OnModelCreating(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity<User>(e =>
|
||||||
|
{
|
||||||
|
e.HasKey(x => x.Id);
|
||||||
|
e.HasIndex(x => x.Email);
|
||||||
|
e.Property(x => x.Email).IsRequired();
|
||||||
|
e.Property(x => x.Password).IsRequired();
|
||||||
|
e.Property(x => x.CreatedDate).IsRequired();
|
||||||
|
e.HasOne(x => x.EmailVerificationCode);
|
||||||
|
e.HasOne(x => x.Profile);
|
||||||
|
e.HasOne(x => x.PassswordResetCode);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity<EmailVerificationCode>(e => {
|
||||||
|
e.HasKey(x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity<Profile>(e => {
|
||||||
|
e.HasKey(x => x.Id);
|
||||||
|
e.HasOne(x => x.ProfileFederationCode);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity<ProfileFederationCode>(e => {
|
||||||
|
e.HasKey(x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity<PasswordResetCode>(e => {
|
||||||
|
e.HasKey(x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity<MissionarySupport>(e => {
|
||||||
|
e.HasKey(x => x.Id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace OFBButte.Entities
|
||||||
|
{
|
||||||
|
public class EmailVerificationCode
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Code { get; set; }
|
||||||
|
public DateTime CreatedDate { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
namespace OFBButte.Entities
|
||||||
|
{
|
||||||
|
public enum BibleReading
|
||||||
|
{
|
||||||
|
Always = 1,
|
||||||
|
Sometimes = 2,
|
||||||
|
Rarely = 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
namespace OFBButte.Entities
|
||||||
|
{
|
||||||
|
public enum ChildrenSchool
|
||||||
|
{
|
||||||
|
Christian = 1,
|
||||||
|
Public = 2,
|
||||||
|
Home = 3,
|
||||||
|
Other = 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
namespace OFBButte.Entities
|
||||||
|
{
|
||||||
|
public enum FemaleShorts
|
||||||
|
{
|
||||||
|
Rarely = 1,
|
||||||
|
Often = 2,
|
||||||
|
Sports = 3,
|
||||||
|
Never = 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
namespace OFBButte.Entities
|
||||||
|
{
|
||||||
|
public enum FemaleSlacks
|
||||||
|
{
|
||||||
|
Rarely = 1,
|
||||||
|
Often = 2,
|
||||||
|
Sports = 3,
|
||||||
|
Never = 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace OFBButte.Entities
|
||||||
|
{
|
||||||
|
public class MissionaryChild
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace OFBButte.Entities
|
||||||
|
{
|
||||||
|
public class MissionaryCollegeRecommendation
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace OFBButte.Entities
|
||||||
|
{
|
||||||
|
public class MissionarySupport
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string HomePhone { get; set; }
|
||||||
|
public string CellPhone { get; set; }
|
||||||
|
public string FieldPhone { get; set; }
|
||||||
|
public string WifesName { get; set; }
|
||||||
|
public MissionaryChild[] Children { get; set; }
|
||||||
|
public string Testimony { get; set; }
|
||||||
|
public string CallToField { get; set; }
|
||||||
|
public string SendingChurch { get; set; }
|
||||||
|
public string FieldOfService { get; set; }
|
||||||
|
public string Plans { get; set; }
|
||||||
|
public string EvaluationOfNationals { get; set; }
|
||||||
|
public string TimeInCountry { get; set; }
|
||||||
|
public string CorrectWrongOfAnotherMissionary { get; set; }
|
||||||
|
public string FinancialStatementPrevYear { get; set; }
|
||||||
|
public decimal CurrentMonthlySupport { get; set; }
|
||||||
|
public decimal MonthlySupportNeeded { get; set; }
|
||||||
|
public string RestAndRelaxation { get; set; }
|
||||||
|
public string AloneOrTeam { get; set; }
|
||||||
|
public ChildrenSchool ChildrenSchool { get; set; }
|
||||||
|
public bool Dance { get; set; }
|
||||||
|
public bool WorldlyMusic { get; set; }
|
||||||
|
public bool MovieTheaters { get; set; }
|
||||||
|
public bool Alcohol { get; set; }
|
||||||
|
public bool Smoking { get; set; }
|
||||||
|
public bool MaleHair { get; set; }
|
||||||
|
public FemaleSlacks FemaleSlacks { get; set; }
|
||||||
|
public FemaleShorts FemaleShorts { get; set; }
|
||||||
|
public bool FemaleDressStandard { get; set; }
|
||||||
|
public string SwimmingClothing { get; set; }
|
||||||
|
public string Television { get; set; }
|
||||||
|
public BibleReading DailyBible { get; set; }
|
||||||
|
public double NumberLedToChrist { get; set; }
|
||||||
|
public double NumberWitnessedTo { get; set; }
|
||||||
|
public double NumberWeeklyTracts { get; set; }
|
||||||
|
public string RateOfSuccess { get; set; }
|
||||||
|
public string Predestination { get; set; }
|
||||||
|
public string FellowshipAssociation { get; set; }
|
||||||
|
public MissionaryCollegeRecommendation[] CollegeRecommendations { get; set; }
|
||||||
|
public string AdmittedWrong { get; set; }
|
||||||
|
public bool Divorced { get; set; }
|
||||||
|
public bool GroundsForRemarry { get; set; }
|
||||||
|
public bool MarryADivorcee { get; set; }
|
||||||
|
public bool MasonicLodge { get; set; }
|
||||||
|
public string BibleVersionsUsed { get; set; }
|
||||||
|
public string BibleVersionOpinion { get; set; }
|
||||||
|
public bool ContemporaryMusic { get; set; }
|
||||||
|
public string Charasmaticism { get; set; }
|
||||||
|
public bool Tongues { get; set; }
|
||||||
|
public bool RepentanceNecessary { get; set; }
|
||||||
|
public string RepentanceDefinition { get; set; }
|
||||||
|
public bool Fundamentalist { get; set; }
|
||||||
|
public string BillsOnTime { get; set; }
|
||||||
|
public string LateBills { get; set; }
|
||||||
|
public string LateBillActionTaken { get; set; }
|
||||||
|
public string PotentialHarvest { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace OFBButte.Entities
|
||||||
|
{
|
||||||
|
public class PasswordResetCode
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Code { get; set; }
|
||||||
|
public DateTime CreatedDate { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace OFBButte.Entities
|
||||||
|
{
|
||||||
|
public class Profile
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public DateTime ModifiedDate { get; set; }
|
||||||
|
public string FirstName { get; set; }
|
||||||
|
public string LastName { get; set; }
|
||||||
|
public string Street { get; set; }
|
||||||
|
public string City { get; set; }
|
||||||
|
public string State { get; set; }
|
||||||
|
public string Zip { get; set; }
|
||||||
|
public string Country { get; set; }
|
||||||
|
|
||||||
|
public ProfileFederationCode ProfileFederationCode { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace OFBButte.Entities
|
||||||
|
{
|
||||||
|
public class ProfileFederationCode
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Code { get; set; }
|
||||||
|
public DateTime CreatedDate { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace OFBButte.Entities
|
||||||
|
{
|
||||||
|
public class User
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Email { get; set; }
|
||||||
|
public string Password { get; set; }
|
||||||
|
public DateTime? EmailVerifiedDate { get; set; }
|
||||||
|
|
||||||
|
public DateTime CreatedDate { get; set; }
|
||||||
|
public DateTime? DeletedDate { get; set; }
|
||||||
|
|
||||||
|
public Profile Profile { get; set; }
|
||||||
|
public EmailVerificationCode EmailVerificationCode { get; set; }
|
||||||
|
public PasswordResetCode PassswordResetCode { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
using OFBButte.Application.Codes;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace OFBButte.Infrastructure.Codes
|
||||||
|
{
|
||||||
|
public class CodeGenerator: ICodeGenerator
|
||||||
|
{
|
||||||
|
public string Generate(int byteSize = 16)
|
||||||
|
{
|
||||||
|
byte[] codeBytes = new byte[byteSize];
|
||||||
|
RandomNumberGenerator.Create().GetBytes(codeBytes);
|
||||||
|
string code = Convert.ToBase64String(codeBytes).TrimEnd('=').Replace('+', '-').Replace('/', '_');
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
using MailKit.Net.Smtp;
|
||||||
|
using MimeKit;
|
||||||
|
using OFBButte.Application.Email;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace OFBButte.Infrastructure.Email
|
||||||
|
{
|
||||||
|
public class EmailSender : IEmailSender, IDisposable
|
||||||
|
{
|
||||||
|
private readonly SmtpClient client;
|
||||||
|
private readonly bool isTest;
|
||||||
|
|
||||||
|
public EmailSender(bool isTest = true)
|
||||||
|
{
|
||||||
|
this.isTest = isTest;
|
||||||
|
client = new SmtpClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Send(EmailMessage message)
|
||||||
|
{
|
||||||
|
message.SetBodyFromTemplate(isTest);
|
||||||
|
var mm = new MimeMessage();
|
||||||
|
mm.From.Add(new MailboxAddress(message.From));
|
||||||
|
mm.To.Add(new MailboxAddress(message.To));
|
||||||
|
mm.Subject = message.Subject;
|
||||||
|
var bodyBuilder = new BodyBuilder();
|
||||||
|
bodyBuilder.HtmlBody = message.Body;
|
||||||
|
mm.Body = bodyBuilder.ToMessageBody();
|
||||||
|
|
||||||
|
client.Connect("smtp.webfaction.com", 465, true);
|
||||||
|
client.Authenticate("ofbcontact", "2014OfbPwd");
|
||||||
|
await client.SendAsync(mm);
|
||||||
|
client.Disconnect(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
client.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,215 @@
|
||||||
|
using Microsoft.AspNetCore.Cryptography.KeyDerivation;
|
||||||
|
using OFBButte.Application.Users;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace OFBButte.Infrastructure.Hashing
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Implements the standard Identity password hashing.
|
||||||
|
/// </summary>
|
||||||
|
public class PasswordHasher: IPasswordHasher
|
||||||
|
{
|
||||||
|
/* =======================
|
||||||
|
* HASHED PASSWORD FORMATS
|
||||||
|
* =======================
|
||||||
|
*
|
||||||
|
* Version 3:
|
||||||
|
* PBKDF2 with HMAC-SHA256, 128-bit salt, 256-bit subkey, 10000 iterations.
|
||||||
|
* Format: { 0x01, prf (UInt32), iter count (UInt32), salt length (UInt32), salt, subkey }
|
||||||
|
* (All UInt32s are stored big-endian.)
|
||||||
|
*/
|
||||||
|
|
||||||
|
private readonly int _iterCount;
|
||||||
|
private readonly RandomNumberGenerator _rng;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of <see cref="PasswordHasher{TUser}"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="optionsAccessor">The options for this instance.</param>
|
||||||
|
public PasswordHasher(PasswordHasherOptions optionsAccessor = null)
|
||||||
|
{
|
||||||
|
var options = optionsAccessor ?? new PasswordHasherOptions();
|
||||||
|
|
||||||
|
_iterCount = options.IterationCount;
|
||||||
|
_rng = options.Rng;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compares two byte arrays for equality. The method is specifically written so that the loop is not optimized.
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
|
||||||
|
private static bool ByteArraysEqual(byte[] a, byte[] b)
|
||||||
|
{
|
||||||
|
if (a == null && b == null)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (a == null || b == null || a.Length != b.Length)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var areSame = true;
|
||||||
|
for (var i = 0; i < a.Length; i++)
|
||||||
|
{
|
||||||
|
areSame &= (a[i] == b[i]);
|
||||||
|
}
|
||||||
|
return areSame;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a hashed representation of the supplied <paramref name="password"/> for the specified <paramref name="user"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="password">The password to hash.</param>
|
||||||
|
/// <returns>A hashed representation of the supplied <paramref name="password"/> for the specified <paramref name="user"/>.</returns>
|
||||||
|
public virtual string HashPassword(string password)
|
||||||
|
{
|
||||||
|
if (password == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(password));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Convert.ToBase64String(HashPasswordV3(password, _rng));
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] HashPasswordV3(string password, RandomNumberGenerator rng)
|
||||||
|
{
|
||||||
|
return HashPasswordV3(password, rng,
|
||||||
|
prf: KeyDerivationPrf.HMACSHA256,
|
||||||
|
iterCount: _iterCount,
|
||||||
|
saltSize: 128 / 8,
|
||||||
|
numBytesRequested: 256 / 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] HashPasswordV3(string password, RandomNumberGenerator rng, KeyDerivationPrf prf, int iterCount, int saltSize, int numBytesRequested)
|
||||||
|
{
|
||||||
|
// Produce a version 3 (see comment above) text hash.
|
||||||
|
byte[] salt = new byte[saltSize];
|
||||||
|
rng.GetBytes(salt);
|
||||||
|
byte[] subkey = KeyDerivation.Pbkdf2(password, salt, prf, iterCount, numBytesRequested);
|
||||||
|
|
||||||
|
var outputBytes = new byte[13 + salt.Length + subkey.Length];
|
||||||
|
outputBytes[0] = 0x01; // format marker
|
||||||
|
WriteNetworkByteOrder(outputBytes, 1, (uint)prf);
|
||||||
|
WriteNetworkByteOrder(outputBytes, 5, (uint)iterCount);
|
||||||
|
WriteNetworkByteOrder(outputBytes, 9, (uint)saltSize);
|
||||||
|
Buffer.BlockCopy(salt, 0, outputBytes, 13, salt.Length);
|
||||||
|
Buffer.BlockCopy(subkey, 0, outputBytes, 13 + saltSize, subkey.Length);
|
||||||
|
return outputBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint ReadNetworkByteOrder(byte[] buffer, int offset)
|
||||||
|
{
|
||||||
|
return ((uint)(buffer[offset + 0]) << 24)
|
||||||
|
| ((uint)(buffer[offset + 1]) << 16)
|
||||||
|
| ((uint)(buffer[offset + 2]) << 8)
|
||||||
|
| ((uint)(buffer[offset + 3]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool VerifyPassword(string hashedPassword, string password)
|
||||||
|
{
|
||||||
|
var result = VerifyHashedPassword(hashedPassword, password);
|
||||||
|
return result == PasswordVerificationResult.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a <see cref="PasswordVerificationResult"/> indicating the result of a password hash comparison.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user">The user whose password should be verified.</param>
|
||||||
|
/// <param name="hashedPassword">The hash value for a user's stored password.</param>
|
||||||
|
/// <param name="providedPassword">The password supplied for comparison.</param>
|
||||||
|
/// <returns>A <see cref="PasswordVerificationResult"/> indicating the result of a password hash comparison.</returns>
|
||||||
|
/// <remarks>Implementations of this method should be time consistent.</remarks>
|
||||||
|
public virtual PasswordVerificationResult VerifyHashedPassword(string hashedPassword, string providedPassword)
|
||||||
|
{
|
||||||
|
if (hashedPassword == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(hashedPassword));
|
||||||
|
}
|
||||||
|
if (providedPassword == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(providedPassword));
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] decodedHashedPassword = Convert.FromBase64String(hashedPassword);
|
||||||
|
|
||||||
|
// read the format marker from the hashed password
|
||||||
|
if (decodedHashedPassword.Length == 0)
|
||||||
|
{
|
||||||
|
return PasswordVerificationResult.Failed;
|
||||||
|
}
|
||||||
|
switch (decodedHashedPassword[0])
|
||||||
|
{
|
||||||
|
case 0x00:
|
||||||
|
return PasswordVerificationResult.Failed;
|
||||||
|
|
||||||
|
case 0x01:
|
||||||
|
int embeddedIterCount;
|
||||||
|
if (VerifyHashedPasswordV3(decodedHashedPassword, providedPassword, out embeddedIterCount))
|
||||||
|
{
|
||||||
|
// If this hasher was configured with a higher iteration count, change the entry now.
|
||||||
|
return (embeddedIterCount < _iterCount)
|
||||||
|
? PasswordVerificationResult.SuccessRehashNeeded
|
||||||
|
: PasswordVerificationResult.Success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return PasswordVerificationResult.Failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return PasswordVerificationResult.Failed; // unknown format marker
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool VerifyHashedPasswordV3(byte[] hashedPassword, string password, out int iterCount)
|
||||||
|
{
|
||||||
|
iterCount = default(int);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Read header information
|
||||||
|
KeyDerivationPrf prf = (KeyDerivationPrf)ReadNetworkByteOrder(hashedPassword, 1);
|
||||||
|
iterCount = (int)ReadNetworkByteOrder(hashedPassword, 5);
|
||||||
|
int saltLength = (int)ReadNetworkByteOrder(hashedPassword, 9);
|
||||||
|
|
||||||
|
// Read the salt: must be >= 128 bits
|
||||||
|
if (saltLength < 128 / 8)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
byte[] salt = new byte[saltLength];
|
||||||
|
Buffer.BlockCopy(hashedPassword, 13, salt, 0, salt.Length);
|
||||||
|
|
||||||
|
// Read the subkey (the rest of the payload): must be >= 128 bits
|
||||||
|
int subkeyLength = hashedPassword.Length - 13 - salt.Length;
|
||||||
|
if (subkeyLength < 128 / 8)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
byte[] expectedSubkey = new byte[subkeyLength];
|
||||||
|
Buffer.BlockCopy(hashedPassword, 13 + salt.Length, expectedSubkey, 0, expectedSubkey.Length);
|
||||||
|
|
||||||
|
// Hash the incoming password and verify it
|
||||||
|
byte[] actualSubkey = KeyDerivation.Pbkdf2(password, salt, prf, iterCount, subkeyLength);
|
||||||
|
return ByteArraysEqual(actualSubkey, expectedSubkey);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// This should never occur except in the case of a malformed payload, where
|
||||||
|
// we might go off the end of the array. Regardless, a malformed payload
|
||||||
|
// implies verification failed.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void WriteNetworkByteOrder(byte[] buffer, int offset, uint value)
|
||||||
|
{
|
||||||
|
buffer[offset + 0] = (byte)(value >> 24);
|
||||||
|
buffer[offset + 1] = (byte)(value >> 16);
|
||||||
|
buffer[offset + 2] = (byte)(value >> 8);
|
||||||
|
buffer[offset + 3] = (byte)(value >> 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace OFBButte.Infrastructure.Hashing
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies options for password hashing.
|
||||||
|
/// </summary>
|
||||||
|
public class PasswordHasherOptions
|
||||||
|
{
|
||||||
|
private static readonly RandomNumberGenerator _defaultRng = RandomNumberGenerator.Create(); // secure PRNG
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the number of iterations used when hashing passwords using PBKDF2. Default is 10,000.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>
|
||||||
|
/// The number of iterations used when hashing passwords using PBKDF2.
|
||||||
|
/// </value>
|
||||||
|
/// <remarks>
|
||||||
|
/// This value is only used when the compatibility mode is set to 'V3'.
|
||||||
|
/// The value must be a positive integer.
|
||||||
|
/// </remarks>
|
||||||
|
public int IterationCount { get; set; } = 10000;
|
||||||
|
|
||||||
|
// for unit testing
|
||||||
|
internal RandomNumberGenerator Rng { get; set; } = _defaultRng;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace OFBButte.Infrastructure.Hashing
|
||||||
|
{
|
||||||
|
// <summary>
|
||||||
|
/// Specifies the results for password verification.
|
||||||
|
/// </summary>
|
||||||
|
public enum PasswordVerificationResult
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates password verification failed.
|
||||||
|
/// </summary>
|
||||||
|
Failed = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates password verification was successful.
|
||||||
|
/// </summary>
|
||||||
|
Success = 1,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates password verification was successful however the password was encoded using a deprecated algorithm
|
||||||
|
/// and should be rehashed and updated.
|
||||||
|
/// </summary>
|
||||||
|
SuccessRehashNeeded = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="MailKit" Version="2.2.0" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="2.2.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\OFBButte.Application\OFBButte.Application.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 16
|
||||||
|
VisualStudioVersion = 16.0.29102.190
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OFBButte.Api", "OFBButte.Api\OFBButte.Api.csproj", "{FF5F4D78-6419-47E4-A7CE-5B25503BE2D7}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OFBButte.Application", "OFBButte.Application\OFBButte.Application.csproj", "{8BCA31C6-C10D-4865-BAE8-6DC932B4797D}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OFBButte.Console", "OFBButte.Console\OFBButte.Console.csproj", "{82FA36EC-1CBC-4F9A-B436-A61D9AA6D1C7}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OFBButte.Database", "OFBButte.Database\OFBButte.Database.csproj", "{64A05F44-4BB2-45CE-A343-6307EA8F099A}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OFBButte.Infrastructure", "OFBButte.Infrastructure\OFBButte.Infrastructure.csproj", "{157A1CB3-9320-4FE6-ABC5-1C9EF1952540}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OFBButte.Entities", "OFBButte.Entities\OFBButte.Entities.csproj", "{39A5BAF9-E108-4B81-8DBB-A7C1A6CEA9EF}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{FF5F4D78-6419-47E4-A7CE-5B25503BE2D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{FF5F4D78-6419-47E4-A7CE-5B25503BE2D7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{FF5F4D78-6419-47E4-A7CE-5B25503BE2D7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{FF5F4D78-6419-47E4-A7CE-5B25503BE2D7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{8BCA31C6-C10D-4865-BAE8-6DC932B4797D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{8BCA31C6-C10D-4865-BAE8-6DC932B4797D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{8BCA31C6-C10D-4865-BAE8-6DC932B4797D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{8BCA31C6-C10D-4865-BAE8-6DC932B4797D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{82FA36EC-1CBC-4F9A-B436-A61D9AA6D1C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{82FA36EC-1CBC-4F9A-B436-A61D9AA6D1C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{82FA36EC-1CBC-4F9A-B436-A61D9AA6D1C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{82FA36EC-1CBC-4F9A-B436-A61D9AA6D1C7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{64A05F44-4BB2-45CE-A343-6307EA8F099A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{64A05F44-4BB2-45CE-A343-6307EA8F099A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{64A05F44-4BB2-45CE-A343-6307EA8F099A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{64A05F44-4BB2-45CE-A343-6307EA8F099A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{157A1CB3-9320-4FE6-ABC5-1C9EF1952540}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{157A1CB3-9320-4FE6-ABC5-1C9EF1952540}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{157A1CB3-9320-4FE6-ABC5-1C9EF1952540}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{157A1CB3-9320-4FE6-ABC5-1C9EF1952540}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{39A5BAF9-E108-4B81-8DBB-A7C1A6CEA9EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{39A5BAF9-E108-4B81-8DBB-A7C1A6CEA9EF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{39A5BAF9-E108-4B81-8DBB-A7C1A6CEA9EF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{39A5BAF9-E108-4B81-8DBB-A7C1A6CEA9EF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {CD719D07-AABB-44D8-84CD-705EDF224EB9}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
Loading…
Reference in New Issue