Added first app template

This commit is contained in:
2022-02-20 13:43:25 +01:00
commit fb2a0080bc
238 changed files with 45673 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
using System;
namespace app.Configuration
{
public class APISettings
{
public bool RedirectToHTTPS { get; set; }
public APIVersionSettings ApiVersion { get; set; }
}
}

View File

@@ -0,0 +1,15 @@
using System;
namespace app.Configuration
{
public class APIVersionSettings
{
public string Major { get; set; }
public string Minor { get; set; }
public string Micro { get; set; }
public override string ToString() {
return $"{this.Major}.{this.Minor}.{this.Micro}";
}
}
}

View File

@@ -0,0 +1,11 @@
namespace app.Configuration
{
public class AuthentificationSettings
{
public string SecretKey { get; set; }
public string Issuer { get; set; }
public string Audience { get; set; }
public int TokenExpireTime { get; set; }
public int RefreshTokenExpireTime { get; set; }
}
}

View File

@@ -0,0 +1,36 @@
using Microsoft.Extensions.Configuration;
using System;
namespace app.Configuration {
public static class ConfigurationExtensions {
public static TSetting GetSetting<TSetting>(this IConfiguration config, string sectionName)
where TSetting : new() {
var settings = new TSetting();
config.GetSection(sectionName).Bind(settings);
return settings;
}
public static string GetDbConnectionString(this IConfiguration config, string sectionName) {
var connectionString = config[$"{sectionName}:ConnectionString"];
if (string.IsNullOrEmpty(connectionString))
throw new Exception($"ConnectionString is not set! SectionName:{sectionName}");
var dbCredentials = config[$"{sectionName}:Credentials"];
if (!string.IsNullOrEmpty(dbCredentials))
connectionString += dbCredentials;
return connectionString;
}
public static string GetCustomer(this IConfiguration config) {
return config["CUSTOMER"];
}
public static string GetComputerName(this IConfiguration config) {
return config["COMPUTERNAME"];
}
}
}

View File

@@ -0,0 +1,7 @@
namespace app.Configuration {
public static class Constants {
public const string CustomerEnvironmentVariable = "CUSTOMER";
public const string ComputerNameEnvironmentVariable = "COMPUTERNAME";
public const string CustomersDirectoryName = "customers";
}
}

View File

@@ -0,0 +1,8 @@
namespace app.Configuration
{
public class DatabaseSettings
{
public string ConnectionString { get; set; }
public string Credentials { get; set; }
}
}

View File

@@ -0,0 +1,14 @@
namespace app.Configuration
{
public class EMailSettings
{
public string FromName { get; set; }
public string FromAddress { get; set; }
public string MailServerAddress { get; set; }
public int MailServerPort { get; set; }
public string Username { get; set; }
public string Credentials { get; set; }
}
}

View File

@@ -0,0 +1,7 @@
namespace app.Configuration
{
public class FrontendSettings
{
public string URL { get; set; }
}
}

View File

@@ -0,0 +1,115 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.EventLog;
using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
namespace app.Configuration {
public static class HostExtensions {
// copied from https://github.com/dotnet/extensions/blob/release/3.1/src/Hosting/Hosting/src/Host.cs,
// modified to use customer environment
public static IHostBuilder CreateBuilder(string[] args) {
var builder = new HostBuilder();
builder.UseContentRoot(Directory.GetCurrentDirectory()); // UseWindowsService overrides content root to AppContext.BaseDirectory
builder.ConfigureHostConfiguration(config => {
config.AddEnvironmentVariables(prefix: "DOTNET_");
config.AddEnvironmentVariables(prefix: "LOGINCOUNTER_");
if (args != null) {
config.AddCommandLine(args);
}
});
builder.ConfigureAppConfiguration((hostingContext, config) => {
var env = hostingContext.HostingEnvironment;
var customerEnv = hostingContext.Configuration.GetValue<string>(Constants.CustomerEnvironmentVariable);
//var customerEnv = Environment.GetEnvironmentVariable(Constants.CustomerEnvironmentVariable);
if (!string.IsNullOrEmpty(customerEnv))
config.SetBasePath(Path.Combine(hostingContext.HostingEnvironment.ContentRootPath, Constants.CustomersDirectoryName, customerEnv));
config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
config.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: false, reloadOnChange: true);
var computerName = Environment.GetEnvironmentVariable(Constants.ComputerNameEnvironmentVariable);
if (!string.IsNullOrEmpty(computerName))
config.AddJsonFile($"appsettings.{computerName}.json", optional: true, reloadOnChange: true);
if (env.IsDevelopment() && !string.IsNullOrEmpty(env.ApplicationName)) {
var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName));
if (appAssembly != null) {
config.AddUserSecrets(appAssembly, optional: true);
}
}
config.AddEnvironmentVariables();
config.AddEnvironmentVariables("IOTHUB_");
if (args != null) {
config.AddCommandLine(args);
}
})
.ConfigureLogging((hostingContext, logging) => {
var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
// IMPORTANT: This needs to be added *before* configuration is loaded, this lets
// the defaults be overridden by the configuration.
if (isWindows) {
// Default the EventLogLoggerProvider to warning or above
logging.AddFilter<EventLogLoggerProvider>(level => level >= LogLevel.Warning);
}
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddConsole();
logging.AddDebug();
logging.AddEventSourceLogger();
if (isWindows) {
// Add the EventLogLoggerProvider on windows machines
logging.AddEventLog();
}
})
.UseDefaultServiceProvider((context, options) => {
var isDevelopment = context.HostingEnvironment.IsDevelopment();
options.ValidateScopes = isDevelopment;
options.ValidateOnBuild = isDevelopment;
});
return builder;
}
public static void LogHostingEnvironment<T>(this IHost host) {
var loggerFactory = host.Services.GetRequiredService<ILoggerFactory>();
var configuration = host.Services.GetRequiredService<IConfiguration>();
var hostEnvironment = host.Services.GetRequiredService<IHostEnvironment>();
var logger = loggerFactory.CreateLogger<T>();
logger.LogInformation("Running Host...");
logger.LogInformation($"EnvironmentName:{hostEnvironment.EnvironmentName}");
logger.LogInformation($"ContentRootPath:{hostEnvironment.ContentRootPath}");
logger.LogInformation($"CurrentDirectory:{Environment.CurrentDirectory}");
logger.LogInformation($"ApplicationName:{hostEnvironment.ApplicationName}");
logger.LogInformation($"Customer:{configuration.GetCustomer()}");
logger.LogInformation($"ComputerName:{configuration.GetComputerName()}");
}
public static void LogStartError(Exception ex) {
var logDir = Path.Combine(AppContext.BaseDirectory, "logs");
if (!Directory.Exists(logDir))
Directory.CreateDirectory(logDir);
var logFile = Path.Combine(logDir, "start_error.txt");
File.AppendAllText(logFile, DateTime.Now.ToString() + ": " + ex.ToString() + Environment.NewLine);
}
public static void ChangeCurrentDirToProjectDir() {
var currentDir = Directory.GetCurrentDirectory();
var ix = currentDir.IndexOf("\\bin"); // goto project dir, if we are running from VS
if (ix >= 0) {
var newDir = currentDir.Substring(0, ix);
Directory.SetCurrentDirectory(newDir);
}
}
}
}

View File

@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0"/>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.1"/>
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="6.0.0"/>
<PackageReference Include="NLog" Version="4.7.13"/>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,25 @@
using System;
namespace app.CredentialManager
{
public static class Base64
{
public static string Encode(string credentials)
{
var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(credentials);
return System.Convert.ToBase64String(plainTextBytes);
}
public static string Decode(string credentials)
{
var base64EncodedBytes = System.Convert.FromBase64String(credentials);
return System.Text.Encoding.UTF8.GetString(base64EncodedBytes);
}
public static string DecodeConnectionString(string connectionString, string credentials)
{
string decodedCredentials = Decode(credentials);
return connectionString += $"password={decodedCredentials};";
}
}
}

View File

@@ -0,0 +1,89 @@
using System;
using System.IO;
using Microsoft.Extensions.Configuration;
using app.Configuration;
namespace app.CredentialManager
{
class Program
{
static IConfiguration Configuration { get; set; }
static void Menu()
{
bool end = false;
while (!end)
{
Console.Clear();
Console.WriteLine("Credential Manager: \n");
Console.WriteLine("[1] Encode");
Console.WriteLine("[2] Decode");
Console.WriteLine("[x] Exit");
Console.Write("\n> ");
string input = Console.ReadLine();
switch (input)
{
case "1":
Console.Clear();
Encode();
break;
case "2":
Console.Clear();
Decode();
break;
case "x":
end = true;
Continue();
break;
case "X":
end = true;
Continue();
break;
default:
Console.WriteLine("Invalid input");
Continue();
break;
}
}
}
static void Continue()
{
Console.WriteLine("\n\nPress any key to continue...");
Console.ReadLine();
}
static void Main(string[] args)
{
Menu();
}
static void Encode()
{
Console.Write("String to encode: ");
string input = Console.ReadLine();
Console.WriteLine($"{input} encoded with Base64 is: {Base64.Encode(input)}");
Continue();
}
static void Decode()
{
Console.Write("String to decode: ");
string input = Console.ReadLine();
Console.WriteLine($"{input} decoded with Base64 is: {Base64.Decode(input)}");
Continue();
}
protected static SettingType GetTypedSettingsFromSection<SettingType>(string sectionName) where SettingType : new()
{
var settings = new SettingType();
Configuration.GetSection(sectionName).Bind(settings);
return settings;
}
}
}

View File

@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.0"/>
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0"/>
<PackageReference Include="NLog" Version="4.7.13"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\app.Configuration\app.Configuration.csproj"/>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,73 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using app.Interface.Repositories;
using app.Model;
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace app.Data
{
public class DatabaseContext : DbContext, IUnitOfWork
{
public DatabaseContext(DbContextOptions<DatabaseContext> options) : base(options) { }
public DbSet<AuthUser> AuthUsers { get; set; }
public Task<int> SaveChangesAsync()
{
var changedList = ChangeTracker.Entries<IAutoGenerateDateFields>().Where(x => x.State == EntityState.Added || x.State == EntityState.Modified);
foreach (var item in changedList)
{
item.Entity.LastModifiedOn = DateTime.UtcNow;
if (item.State == EntityState.Added)
item.Entity.CreatedOn = DateTime.UtcNow;
}
return base.SaveChangesAsync();
}
public override int SaveChanges()
{
var changedList = ChangeTracker.Entries<IAutoGenerateDateFields>().Where(x => x.State == EntityState.Added || x.State == EntityState.Modified);
foreach (var item in changedList)
{
item.Entity.LastModifiedOn = DateTime.UtcNow;
if (item.State == EntityState.Added)
item.Entity.CreatedOn = DateTime.UtcNow;
}
return base.SaveChanges();
}
public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken))
{
// Datum der letzten Änderung, etc. setzen
DateTimeOffset now = DateTimeOffset.UtcNow;
var changedList = ChangeTracker.Entries<IAutoGenerateDateFields>().Where(x => x.State == EntityState.Added || x.State == EntityState.Modified);
foreach (var item in changedList)
{
item.Entity.LastModifiedOn = now;
if (item.State == EntityState.Added)
item.Entity.CreatedOn = now;
}
return base.SaveChangesAsync();
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<AuthUser>()
.HasIndex(au => au.EMail)
.IsUnique(true);
modelBuilder.Entity<AuthUser>()
.HasIndex(au => au.ConfirmationId)
.IsUnique(true);
modelBuilder.Entity<AuthUser>()
.HasIndex(au => au.ForgotPasswordId)
.IsUnique(true);
}
}
}

View File

@@ -0,0 +1,22 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using System;
using System.Collections.Generic;
using System.Text;
namespace app.Data
{
public class DesignTimeContextFactory : IDesignTimeDbContextFactory<DatabaseContext>
{
public DatabaseContext CreateDbContext(string[] args)
{
var optionsBuilder = new DbContextOptionsBuilder<DatabaseContext>();
optionsBuilder.UseMySql(
"server=localhost;user=sh_login_counter;password=mZaKBHY1lhzQoCAv;database=sh_login_counter",
new MySqlServerVersion(new Version(10, 3, 32))
);
return new DatabaseContext(optionsBuilder.Options);
}
}
}

View File

@@ -0,0 +1,30 @@
using System;
using System.Linq;
using System.Linq.Expressions;
namespace app.Data
{
public static class LinqExtension
{
public static IQueryable<T> OrderByMember<T>(this IQueryable<T> q, string SortField)
{
var param = Expression.Parameter(typeof(T), "p");
var prop = Expression.Property(param, SortField);
var exp = Expression.Lambda(prop, param);
Type[] types = new Type[] { q.ElementType, exp.Body.Type };
var mce = Expression.Call(typeof(Queryable), "OrderBy", types, q.Expression, exp);
return q.Provider.CreateQuery<T>(mce);
}
public static IQueryable<T> OrderByMemberDescending<T>(this IQueryable<T> q, string SortField)
{
var param = Expression.Parameter(typeof(T), "p");
var prop = Expression.Property(param, SortField);
var exp = Expression.Lambda(prop, param);
Type[] types = new Type[] { q.ElementType, exp.Body.Type };
var mce = Expression.Call(typeof(Queryable), "OrderByDescending", types, q.Expression, exp);
return q.Provider.CreateQuery<T>(mce);
}
}
}

View File

@@ -0,0 +1,291 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using app.Data;
namespace app.Data.Migrations
{
[DbContext(typeof(DatabaseContext))]
[Migration("20210213133159_2021_03_10")]
partial class _2021_03_10
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "3.1.9")
.HasAnnotation("Relational:MaxIdentifierLength", 64);
modelBuilder.Entity("app.Model.AuthUser", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<string>("ConfirmationId")
.HasColumnType("varchar(255) CHARACTER SET utf8mb4");
b.Property<DateTimeOffset>("CreatedOn")
.HasColumnType("datetime(6)");
b.Property<string>("EMail")
.HasColumnType("varchar(255) CHARACTER SET utf8mb4");
b.Property<string>("FirstName")
.HasColumnType("longtext CHARACTER SET utf8mb4");
b.Property<string>("ForgotPasswordId")
.HasColumnType("varchar(255) CHARACTER SET utf8mb4");
b.Property<DateTimeOffset>("LastModifiedOn")
.HasColumnType("datetime(6)");
b.Property<string>("LastName")
.HasColumnType("longtext CHARACTER SET utf8mb4");
b.Property<string>("Password")
.HasColumnType("longtext CHARACTER SET utf8mb4");
b.Property<string>("RefreshToken")
.HasColumnType("longtext CHARACTER SET utf8mb4");
b.Property<DateTime>("RefreshTokenExpiryTime")
.HasColumnType("datetime(6)");
b.HasKey("Id");
b.HasIndex("ConfirmationId")
.IsUnique();
b.HasIndex("EMail")
.IsUnique();
b.HasIndex("ForgotPasswordId")
.IsUnique();
b.ToTable("AuthUsers");
});
modelBuilder.Entity("app.Model.Domain", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<DateTimeOffset>("CreatedOn")
.HasColumnType("datetime(6)");
b.Property<DateTimeOffset>("LastModifiedOn")
.HasColumnType("datetime(6)");
b.Property<string>("Name")
.HasColumnType("longtext CHARACTER SET utf8mb4");
b.Property<bool>("NotifyWhenLogin")
.HasColumnType("tinyint(1)");
b.HasKey("Id");
b.ToTable("Domains");
});
modelBuilder.Entity("app.Model.Host", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<DateTimeOffset>("CreatedOn")
.HasColumnType("datetime(6)");
b.Property<long?>("DomainId")
.HasColumnType("bigint");
b.Property<byte[]>("IPAddress")
.HasColumnType("varbinary(1400)")
.HasMaxLength(1400);
b.Property<DateTimeOffset>("LastModifiedOn")
.HasColumnType("datetime(6)");
b.Property<string>("Name")
.HasColumnType("varchar(255) CHARACTER SET utf8mb4");
b.Property<bool>("NotifyWhenLogin")
.HasColumnType("tinyint(1)");
b.Property<long>("OSId")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("DomainId");
b.HasIndex("OSId");
b.HasIndex("DomainId", "Name")
.IsUnique();
b.HasIndex("IPAddress", "Name")
.IsUnique();
b.ToTable("Hosts");
});
modelBuilder.Entity("app.Model.Login", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<DateTimeOffset>("CreatedOn")
.HasColumnType("datetime(6)");
b.Property<long>("HostId")
.HasColumnType("bigint");
b.Property<DateTimeOffset>("LastModifiedOn")
.HasColumnType("datetime(6)");
b.Property<DateTimeOffset>("Time")
.HasColumnType("datetime(6)");
b.Property<long>("UserId")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("HostId");
b.HasIndex("UserId");
b.ToTable("Logins");
});
modelBuilder.Entity("app.Model.OperatingSystem", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<DateTimeOffset>("CreatedOn")
.HasColumnType("datetime(6)");
b.Property<DateTimeOffset>("LastModifiedOn")
.HasColumnType("datetime(6)");
b.Property<string>("Name")
.HasColumnType("varchar(255) CHARACTER SET utf8mb4");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("OperatingSystems");
});
modelBuilder.Entity("app.Model.User", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<DateTimeOffset>("CreatedOn")
.HasColumnType("datetime(6)");
b.Property<string>("Discriminator")
.IsRequired()
.HasColumnType("varchar(255) CHARACTER SET utf8mb4");
b.Property<DateTimeOffset>("LastModifiedOn")
.HasColumnType("datetime(6)");
b.Property<string>("Name")
.HasColumnType("varchar(255) CHARACTER SET utf8mb4");
b.Property<bool>("NotifyWhenLogin")
.HasColumnType("tinyint(1)");
b.HasKey("Id");
b.HasIndex("Discriminator", "Name")
.IsUnique();
b.ToTable("Users");
b.HasDiscriminator<string>("Discriminator").HasValue("User");
});
modelBuilder.Entity("app.Model.DomainUser", b =>
{
b.HasBaseType("app.Model.User");
b.Property<long?>("DomainId")
.HasColumnType("bigint");
b.HasIndex("DomainId");
b.HasDiscriminator().HasValue("DomainUser");
});
modelBuilder.Entity("app.Model.HostUser", b =>
{
b.HasBaseType("app.Model.User");
b.Property<long?>("HostId")
.HasColumnType("bigint");
b.HasIndex("HostId");
b.HasDiscriminator().HasValue("HostUser");
});
modelBuilder.Entity("app.Model.Host", b =>
{
b.HasOne("app.Model.Domain", "Domain")
.WithMany("Hosts")
.HasForeignKey("DomainId");
b.HasOne("app.Model.OperatingSystem", "OS")
.WithMany()
.HasForeignKey("OSId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("app.Model.Login", b =>
{
b.HasOne("app.Model.Host", "Host")
.WithMany("Logins")
.HasForeignKey("HostId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("app.Model.User", "User")
.WithMany("Logins")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("app.Model.DomainUser", b =>
{
b.HasOne("app.Model.Domain", "Domain")
.WithMany("Users")
.HasForeignKey("DomainId");
});
modelBuilder.Entity("app.Model.HostUser", b =>
{
b.HasOne("app.Model.Host", "Host")
.WithMany("Users")
.HasForeignKey("HostId");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,249 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
namespace app.Data.Migrations
{
public partial class _2021_03_10 : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "AuthUsers",
columns: table => new
{
Id = table.Column<long>(nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
FirstName = table.Column<string>(nullable: true),
LastName = table.Column<string>(nullable: true),
EMail = table.Column<string>(nullable: true),
Password = table.Column<string>(nullable: true),
RefreshToken = table.Column<string>(nullable: true),
ConfirmationId = table.Column<string>(nullable: true),
ForgotPasswordId = table.Column<string>(nullable: true),
RefreshTokenExpiryTime = table.Column<DateTime>(nullable: false),
CreatedOn = table.Column<DateTimeOffset>(nullable: false),
LastModifiedOn = table.Column<DateTimeOffset>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AuthUsers", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Domains",
columns: table => new
{
Id = table.Column<long>(nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
Name = table.Column<string>(nullable: true),
NotifyWhenLogin = table.Column<bool>(nullable: false),
CreatedOn = table.Column<DateTimeOffset>(nullable: false),
LastModifiedOn = table.Column<DateTimeOffset>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Domains", x => x.Id);
});
migrationBuilder.CreateTable(
name: "OperatingSystems",
columns: table => new
{
Id = table.Column<long>(nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
Name = table.Column<string>(nullable: true),
CreatedOn = table.Column<DateTimeOffset>(nullable: false),
LastModifiedOn = table.Column<DateTimeOffset>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_OperatingSystems", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Hosts",
columns: table => new
{
Id = table.Column<long>(nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
Name = table.Column<string>(nullable: true),
IPAddress = table.Column<byte[]>(maxLength: 1400, nullable: true),
NotifyWhenLogin = table.Column<bool>(nullable: false),
DomainId = table.Column<long>(nullable: true),
OSId = table.Column<long>(nullable: false),
CreatedOn = table.Column<DateTimeOffset>(nullable: false),
LastModifiedOn = table.Column<DateTimeOffset>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Hosts", x => x.Id);
table.ForeignKey(
name: "FK_Hosts_Domains_DomainId",
column: x => x.DomainId,
principalTable: "Domains",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_Hosts_OperatingSystems_OSId",
column: x => x.OSId,
principalTable: "OperatingSystems",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "Users",
columns: table => new
{
Id = table.Column<long>(nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
Name = table.Column<string>(nullable: true),
NotifyWhenLogin = table.Column<bool>(nullable: false),
CreatedOn = table.Column<DateTimeOffset>(nullable: false),
LastModifiedOn = table.Column<DateTimeOffset>(nullable: false),
Discriminator = table.Column<string>(nullable: false),
DomainId = table.Column<long>(nullable: true),
HostId = table.Column<long>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Users", x => x.Id);
table.ForeignKey(
name: "FK_Users_Domains_DomainId",
column: x => x.DomainId,
principalTable: "Domains",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_Users_Hosts_HostId",
column: x => x.HostId,
principalTable: "Hosts",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateTable(
name: "Logins",
columns: table => new
{
Id = table.Column<long>(nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
Time = table.Column<DateTimeOffset>(nullable: false),
HostId = table.Column<long>(nullable: false),
UserId = table.Column<long>(nullable: false),
CreatedOn = table.Column<DateTimeOffset>(nullable: false),
LastModifiedOn = table.Column<DateTimeOffset>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Logins", x => x.Id);
table.ForeignKey(
name: "FK_Logins_Hosts_HostId",
column: x => x.HostId,
principalTable: "Hosts",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_Logins_Users_UserId",
column: x => x.UserId,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_AuthUsers_ConfirmationId",
table: "AuthUsers",
column: "ConfirmationId",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_AuthUsers_EMail",
table: "AuthUsers",
column: "EMail",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_AuthUsers_ForgotPasswordId",
table: "AuthUsers",
column: "ForgotPasswordId",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_Hosts_DomainId",
table: "Hosts",
column: "DomainId");
migrationBuilder.CreateIndex(
name: "IX_Hosts_OSId",
table: "Hosts",
column: "OSId");
migrationBuilder.CreateIndex(
name: "IX_Hosts_DomainId_Name",
table: "Hosts",
columns: new[] { "DomainId", "Name" },
unique: true);
migrationBuilder.CreateIndex(
name: "IX_Hosts_IPAddress_Name",
table: "Hosts",
columns: new[] { "IPAddress", "Name" },
unique: true);
migrationBuilder.CreateIndex(
name: "IX_Logins_HostId",
table: "Logins",
column: "HostId");
migrationBuilder.CreateIndex(
name: "IX_Logins_UserId",
table: "Logins",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_OperatingSystems_Name",
table: "OperatingSystems",
column: "Name",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_Users_DomainId",
table: "Users",
column: "DomainId");
migrationBuilder.CreateIndex(
name: "IX_Users_HostId",
table: "Users",
column: "HostId");
migrationBuilder.CreateIndex(
name: "IX_Users_Discriminator_Name",
table: "Users",
columns: new[] { "Discriminator", "Name" },
unique: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "AuthUsers");
migrationBuilder.DropTable(
name: "Logins");
migrationBuilder.DropTable(
name: "Users");
migrationBuilder.DropTable(
name: "Hosts");
migrationBuilder.DropTable(
name: "Domains");
migrationBuilder.DropTable(
name: "OperatingSystems");
}
}
}

View File

@@ -0,0 +1,294 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using app.Data;
namespace app.Data.Migrations
{
[DbContext(typeof(DatabaseContext))]
[Migration("20210217201310_2021_03_13")]
partial class _2021_03_13
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "3.1.9")
.HasAnnotation("Relational:MaxIdentifierLength", 64);
modelBuilder.Entity("app.Model.AuthUser", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<int>("AuthRole")
.HasColumnType("int");
b.Property<string>("ConfirmationId")
.HasColumnType("varchar(255) CHARACTER SET utf8mb4");
b.Property<DateTimeOffset>("CreatedOn")
.HasColumnType("datetime(6)");
b.Property<string>("EMail")
.HasColumnType("varchar(255) CHARACTER SET utf8mb4");
b.Property<string>("FirstName")
.HasColumnType("longtext CHARACTER SET utf8mb4");
b.Property<string>("ForgotPasswordId")
.HasColumnType("varchar(255) CHARACTER SET utf8mb4");
b.Property<DateTimeOffset>("LastModifiedOn")
.HasColumnType("datetime(6)");
b.Property<string>("LastName")
.HasColumnType("longtext CHARACTER SET utf8mb4");
b.Property<string>("Password")
.HasColumnType("longtext CHARACTER SET utf8mb4");
b.Property<string>("RefreshToken")
.HasColumnType("longtext CHARACTER SET utf8mb4");
b.Property<DateTime>("RefreshTokenExpiryTime")
.HasColumnType("datetime(6)");
b.HasKey("Id");
b.HasIndex("ConfirmationId")
.IsUnique();
b.HasIndex("EMail")
.IsUnique();
b.HasIndex("ForgotPasswordId")
.IsUnique();
b.ToTable("AuthUsers");
});
modelBuilder.Entity("app.Model.Domain", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<DateTimeOffset>("CreatedOn")
.HasColumnType("datetime(6)");
b.Property<DateTimeOffset>("LastModifiedOn")
.HasColumnType("datetime(6)");
b.Property<string>("Name")
.HasColumnType("longtext CHARACTER SET utf8mb4");
b.Property<bool>("NotifyWhenLogin")
.HasColumnType("tinyint(1)");
b.HasKey("Id");
b.ToTable("Domains");
});
modelBuilder.Entity("app.Model.Host", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<DateTimeOffset>("CreatedOn")
.HasColumnType("datetime(6)");
b.Property<long?>("DomainId")
.HasColumnType("bigint");
b.Property<byte[]>("IPAddress")
.HasColumnType("varbinary(1400)")
.HasMaxLength(1400);
b.Property<DateTimeOffset>("LastModifiedOn")
.HasColumnType("datetime(6)");
b.Property<string>("Name")
.HasColumnType("varchar(255) CHARACTER SET utf8mb4");
b.Property<bool>("NotifyWhenLogin")
.HasColumnType("tinyint(1)");
b.Property<long>("OSId")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("DomainId");
b.HasIndex("OSId");
b.HasIndex("DomainId", "Name")
.IsUnique();
b.HasIndex("IPAddress", "Name")
.IsUnique();
b.ToTable("Hosts");
});
modelBuilder.Entity("app.Model.Login", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<DateTimeOffset>("CreatedOn")
.HasColumnType("datetime(6)");
b.Property<long>("HostId")
.HasColumnType("bigint");
b.Property<DateTimeOffset>("LastModifiedOn")
.HasColumnType("datetime(6)");
b.Property<DateTimeOffset>("Time")
.HasColumnType("datetime(6)");
b.Property<long>("UserId")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("HostId");
b.HasIndex("UserId");
b.ToTable("Logins");
});
modelBuilder.Entity("app.Model.OperatingSystem", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<DateTimeOffset>("CreatedOn")
.HasColumnType("datetime(6)");
b.Property<DateTimeOffset>("LastModifiedOn")
.HasColumnType("datetime(6)");
b.Property<string>("Name")
.HasColumnType("varchar(255) CHARACTER SET utf8mb4");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("OperatingSystems");
});
modelBuilder.Entity("app.Model.User", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<DateTimeOffset>("CreatedOn")
.HasColumnType("datetime(6)");
b.Property<string>("Discriminator")
.IsRequired()
.HasColumnType("varchar(255) CHARACTER SET utf8mb4");
b.Property<DateTimeOffset>("LastModifiedOn")
.HasColumnType("datetime(6)");
b.Property<string>("Name")
.HasColumnType("varchar(255) CHARACTER SET utf8mb4");
b.Property<bool>("NotifyWhenLogin")
.HasColumnType("tinyint(1)");
b.HasKey("Id");
b.HasIndex("Discriminator", "Name")
.IsUnique();
b.ToTable("Users");
b.HasDiscriminator<string>("Discriminator").HasValue("User");
});
modelBuilder.Entity("app.Model.DomainUser", b =>
{
b.HasBaseType("app.Model.User");
b.Property<long?>("DomainId")
.HasColumnType("bigint");
b.HasIndex("DomainId");
b.HasDiscriminator().HasValue("DomainUser");
});
modelBuilder.Entity("app.Model.HostUser", b =>
{
b.HasBaseType("app.Model.User");
b.Property<long?>("HostId")
.HasColumnType("bigint");
b.HasIndex("HostId");
b.HasDiscriminator().HasValue("HostUser");
});
modelBuilder.Entity("app.Model.Host", b =>
{
b.HasOne("app.Model.Domain", "Domain")
.WithMany("Hosts")
.HasForeignKey("DomainId");
b.HasOne("app.Model.OperatingSystem", "OS")
.WithMany()
.HasForeignKey("OSId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("app.Model.Login", b =>
{
b.HasOne("app.Model.Host", "Host")
.WithMany("Logins")
.HasForeignKey("HostId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("app.Model.User", "User")
.WithMany("Logins")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("app.Model.DomainUser", b =>
{
b.HasOne("app.Model.Domain", "Domain")
.WithMany("Users")
.HasForeignKey("DomainId");
});
modelBuilder.Entity("app.Model.HostUser", b =>
{
b.HasOne("app.Model.Host", "Host")
.WithMany("Users")
.HasForeignKey("HostId");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,23 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace app.Data.Migrations
{
public partial class _2021_03_13 : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "AuthRole",
table: "AuthUsers",
nullable: false,
defaultValue: 0);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "AuthRole",
table: "AuthUsers");
}
}
}

View File

@@ -0,0 +1,291 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using app.Data;
namespace app.Data.Migrations
{
[DbContext(typeof(DatabaseContext))]
[Migration("20210220140309_2021_03_14")]
partial class _2021_03_14
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "3.1.9")
.HasAnnotation("Relational:MaxIdentifierLength", 64);
modelBuilder.Entity("app.Model.AuthUser", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<int>("AuthRole")
.HasColumnType("int");
b.Property<string>("ConfirmationId")
.HasColumnType("varchar(255) CHARACTER SET utf8mb4");
b.Property<DateTimeOffset>("CreatedOn")
.HasColumnType("datetime(6)");
b.Property<string>("EMail")
.HasColumnType("varchar(255) CHARACTER SET utf8mb4");
b.Property<string>("FirstName")
.HasColumnType("longtext CHARACTER SET utf8mb4");
b.Property<string>("ForgotPasswordId")
.HasColumnType("varchar(255) CHARACTER SET utf8mb4");
b.Property<DateTimeOffset>("LastModifiedOn")
.HasColumnType("datetime(6)");
b.Property<string>("LastName")
.HasColumnType("longtext CHARACTER SET utf8mb4");
b.Property<string>("Password")
.HasColumnType("longtext CHARACTER SET utf8mb4");
b.Property<string>("RefreshToken")
.HasColumnType("longtext CHARACTER SET utf8mb4");
b.Property<DateTime>("RefreshTokenExpiryTime")
.HasColumnType("datetime(6)");
b.HasKey("Id");
b.HasIndex("ConfirmationId")
.IsUnique();
b.HasIndex("EMail")
.IsUnique();
b.HasIndex("ForgotPasswordId")
.IsUnique();
b.ToTable("AuthUsers");
});
modelBuilder.Entity("app.Model.Domain", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<DateTimeOffset>("CreatedOn")
.HasColumnType("datetime(6)");
b.Property<DateTimeOffset>("LastModifiedOn")
.HasColumnType("datetime(6)");
b.Property<string>("Name")
.HasColumnType("longtext CHARACTER SET utf8mb4");
b.Property<bool>("NotifyWhenLogin")
.HasColumnType("tinyint(1)");
b.HasKey("Id");
b.ToTable("Domains");
});
modelBuilder.Entity("app.Model.Host", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<DateTimeOffset>("CreatedOn")
.HasColumnType("datetime(6)");
b.Property<long?>("DomainId")
.HasColumnType("bigint");
b.Property<byte[]>("IPAddress")
.HasColumnType("varbinary(1400)")
.HasMaxLength(1400);
b.Property<DateTimeOffset>("LastModifiedOn")
.HasColumnType("datetime(6)");
b.Property<string>("Name")
.HasColumnType("varchar(255) CHARACTER SET utf8mb4");
b.Property<bool>("NotifyWhenLogin")
.HasColumnType("tinyint(1)");
b.Property<long>("OSId")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("DomainId");
b.HasIndex("OSId");
b.HasIndex("DomainId", "Name")
.IsUnique();
b.HasIndex("IPAddress", "Name")
.IsUnique();
b.ToTable("Hosts");
});
modelBuilder.Entity("app.Model.Login", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<DateTimeOffset>("CreatedOn")
.HasColumnType("datetime(6)");
b.Property<long>("HostId")
.HasColumnType("bigint");
b.Property<DateTimeOffset>("LastModifiedOn")
.HasColumnType("datetime(6)");
b.Property<DateTimeOffset>("Time")
.HasColumnType("datetime(6)");
b.Property<long>("UserId")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("HostId");
b.HasIndex("UserId");
b.ToTable("Logins");
});
modelBuilder.Entity("app.Model.OperatingSystem", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<DateTimeOffset>("CreatedOn")
.HasColumnType("datetime(6)");
b.Property<DateTimeOffset>("LastModifiedOn")
.HasColumnType("datetime(6)");
b.Property<string>("Name")
.HasColumnType("varchar(255) CHARACTER SET utf8mb4");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("OperatingSystems");
});
modelBuilder.Entity("app.Model.User", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<DateTimeOffset>("CreatedOn")
.HasColumnType("datetime(6)");
b.Property<string>("Discriminator")
.IsRequired()
.HasColumnType("longtext CHARACTER SET utf8mb4");
b.Property<DateTimeOffset>("LastModifiedOn")
.HasColumnType("datetime(6)");
b.Property<string>("Name")
.HasColumnType("longtext CHARACTER SET utf8mb4");
b.Property<bool>("NotifyWhenLogin")
.HasColumnType("tinyint(1)");
b.HasKey("Id");
b.ToTable("Users");
b.HasDiscriminator<string>("Discriminator").HasValue("User");
});
modelBuilder.Entity("app.Model.DomainUser", b =>
{
b.HasBaseType("app.Model.User");
b.Property<long?>("DomainId")
.HasColumnType("bigint");
b.HasIndex("DomainId");
b.HasDiscriminator().HasValue("DomainUser");
});
modelBuilder.Entity("app.Model.HostUser", b =>
{
b.HasBaseType("app.Model.User");
b.Property<long?>("HostId")
.HasColumnType("bigint");
b.HasIndex("HostId");
b.HasDiscriminator().HasValue("HostUser");
});
modelBuilder.Entity("app.Model.Host", b =>
{
b.HasOne("app.Model.Domain", "Domain")
.WithMany("Hosts")
.HasForeignKey("DomainId");
b.HasOne("app.Model.OperatingSystem", "OS")
.WithMany()
.HasForeignKey("OSId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("app.Model.Login", b =>
{
b.HasOne("app.Model.Host", "Host")
.WithMany("Logins")
.HasForeignKey("HostId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("app.Model.User", "User")
.WithMany("Logins")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("app.Model.DomainUser", b =>
{
b.HasOne("app.Model.Domain", "Domain")
.WithMany("Users")
.HasForeignKey("DomainId");
});
modelBuilder.Entity("app.Model.HostUser", b =>
{
b.HasOne("app.Model.Host", "Host")
.WithMany("Users")
.HasForeignKey("HostId");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,53 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace app.Data.Migrations
{
public partial class _2021_03_14 : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_Users_Discriminator_Name",
table: "Users");
migrationBuilder.AlterColumn<string>(
name: "Name",
table: "Users",
nullable: true,
oldClrType: typeof(string),
oldType: "varchar(255) CHARACTER SET utf8mb4",
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "Discriminator",
table: "Users",
nullable: false,
oldClrType: typeof(string),
oldType: "varchar(255) CHARACTER SET utf8mb4");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "Name",
table: "Users",
type: "varchar(255) CHARACTER SET utf8mb4",
nullable: true,
oldClrType: typeof(string),
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "Discriminator",
table: "Users",
type: "varchar(255) CHARACTER SET utf8mb4",
nullable: false,
oldClrType: typeof(string));
migrationBuilder.CreateIndex(
name: "IX_Users_Discriminator_Name",
table: "Users",
columns: new[] { "Discriminator", "Name" },
unique: true);
}
}
}

View File

@@ -0,0 +1,289 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using app.Data;
namespace app.Data.Migrations
{
[DbContext(typeof(DatabaseContext))]
partial class DatabaseContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "3.1.9")
.HasAnnotation("Relational:MaxIdentifierLength", 64);
modelBuilder.Entity("app.Model.AuthUser", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<int>("AuthRole")
.HasColumnType("int");
b.Property<string>("ConfirmationId")
.HasColumnType("varchar(255) CHARACTER SET utf8mb4");
b.Property<DateTimeOffset>("CreatedOn")
.HasColumnType("datetime(6)");
b.Property<string>("EMail")
.HasColumnType("varchar(255) CHARACTER SET utf8mb4");
b.Property<string>("FirstName")
.HasColumnType("longtext CHARACTER SET utf8mb4");
b.Property<string>("ForgotPasswordId")
.HasColumnType("varchar(255) CHARACTER SET utf8mb4");
b.Property<DateTimeOffset>("LastModifiedOn")
.HasColumnType("datetime(6)");
b.Property<string>("LastName")
.HasColumnType("longtext CHARACTER SET utf8mb4");
b.Property<string>("Password")
.HasColumnType("longtext CHARACTER SET utf8mb4");
b.Property<string>("RefreshToken")
.HasColumnType("longtext CHARACTER SET utf8mb4");
b.Property<DateTime>("RefreshTokenExpiryTime")
.HasColumnType("datetime(6)");
b.HasKey("Id");
b.HasIndex("ConfirmationId")
.IsUnique();
b.HasIndex("EMail")
.IsUnique();
b.HasIndex("ForgotPasswordId")
.IsUnique();
b.ToTable("AuthUsers");
});
modelBuilder.Entity("app.Model.Domain", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<DateTimeOffset>("CreatedOn")
.HasColumnType("datetime(6)");
b.Property<DateTimeOffset>("LastModifiedOn")
.HasColumnType("datetime(6)");
b.Property<string>("Name")
.HasColumnType("longtext CHARACTER SET utf8mb4");
b.Property<bool>("NotifyWhenLogin")
.HasColumnType("tinyint(1)");
b.HasKey("Id");
b.ToTable("Domains");
});
modelBuilder.Entity("app.Model.Host", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<DateTimeOffset>("CreatedOn")
.HasColumnType("datetime(6)");
b.Property<long?>("DomainId")
.HasColumnType("bigint");
b.Property<byte[]>("IPAddress")
.HasColumnType("varbinary(1400)")
.HasMaxLength(1400);
b.Property<DateTimeOffset>("LastModifiedOn")
.HasColumnType("datetime(6)");
b.Property<string>("Name")
.HasColumnType("varchar(255) CHARACTER SET utf8mb4");
b.Property<bool>("NotifyWhenLogin")
.HasColumnType("tinyint(1)");
b.Property<long>("OSId")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("DomainId");
b.HasIndex("OSId");
b.HasIndex("DomainId", "Name")
.IsUnique();
b.HasIndex("IPAddress", "Name")
.IsUnique();
b.ToTable("Hosts");
});
modelBuilder.Entity("app.Model.Login", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<DateTimeOffset>("CreatedOn")
.HasColumnType("datetime(6)");
b.Property<long>("HostId")
.HasColumnType("bigint");
b.Property<DateTimeOffset>("LastModifiedOn")
.HasColumnType("datetime(6)");
b.Property<DateTimeOffset>("Time")
.HasColumnType("datetime(6)");
b.Property<long>("UserId")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("HostId");
b.HasIndex("UserId");
b.ToTable("Logins");
});
modelBuilder.Entity("app.Model.OperatingSystem", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<DateTimeOffset>("CreatedOn")
.HasColumnType("datetime(6)");
b.Property<DateTimeOffset>("LastModifiedOn")
.HasColumnType("datetime(6)");
b.Property<string>("Name")
.HasColumnType("varchar(255) CHARACTER SET utf8mb4");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("OperatingSystems");
});
modelBuilder.Entity("app.Model.User", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<DateTimeOffset>("CreatedOn")
.HasColumnType("datetime(6)");
b.Property<string>("Discriminator")
.IsRequired()
.HasColumnType("longtext CHARACTER SET utf8mb4");
b.Property<DateTimeOffset>("LastModifiedOn")
.HasColumnType("datetime(6)");
b.Property<string>("Name")
.HasColumnType("longtext CHARACTER SET utf8mb4");
b.Property<bool>("NotifyWhenLogin")
.HasColumnType("tinyint(1)");
b.HasKey("Id");
b.ToTable("Users");
b.HasDiscriminator<string>("Discriminator").HasValue("User");
});
modelBuilder.Entity("app.Model.DomainUser", b =>
{
b.HasBaseType("app.Model.User");
b.Property<long?>("DomainId")
.HasColumnType("bigint");
b.HasIndex("DomainId");
b.HasDiscriminator().HasValue("DomainUser");
});
modelBuilder.Entity("app.Model.HostUser", b =>
{
b.HasBaseType("app.Model.User");
b.Property<long?>("HostId")
.HasColumnType("bigint");
b.HasIndex("HostId");
b.HasDiscriminator().HasValue("HostUser");
});
modelBuilder.Entity("app.Model.Host", b =>
{
b.HasOne("app.Model.Domain", "Domain")
.WithMany("Hosts")
.HasForeignKey("DomainId");
b.HasOne("app.Model.OperatingSystem", "OS")
.WithMany()
.HasForeignKey("OSId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("app.Model.Login", b =>
{
b.HasOne("app.Model.Host", "Host")
.WithMany("Logins")
.HasForeignKey("HostId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("app.Model.User", "User")
.WithMany("Logins")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("app.Model.DomainUser", b =>
{
b.HasOne("app.Model.Domain", "Domain")
.WithMany("Users")
.HasForeignKey("DomainId");
});
modelBuilder.Entity("app.Model.HostUser", b =>
{
b.HasOne("app.Model.Host", "Host")
.WithMany("Users")
.HasForeignKey("HostId");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,125 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using app.Interface.Repositories;
using app.Model;
using app.Share.Common;
namespace app.Data.Repositories
{
public class AuthUserRepositoryImpl : IAuthUserRepository
{
private readonly DatabaseContext _databaseContext;
private DbSet<AuthUser> _authUsers { get { return _databaseContext.AuthUsers; } }
public AuthUserRepositoryImpl(DatabaseContext databaseContext)
{
_databaseContext = databaseContext;
}
public async Task<List<AuthUser>> GetAllAuthUsersAsync()
{
return await _authUsers
.ToListAsync();
}
public async Task<(List<AuthUser>, int totalCount)> GetFilteredAuthUsersAsync(AuthUserSelectCriterion selectCriterion)
{
var query = _authUsers
.Select(u => new AuthUser
{
Id = u.Id,
FirstName = u.FirstName,
LastName = u.LastName,
EMail = u.EMail,
ConfirmationId = u.ConfirmationId,
AuthRole = u.AuthRole
});
// FirstName filter
if (!string.IsNullOrWhiteSpace(selectCriterion.FirstName))
query = query.Where(u => u.FirstName.Contains(selectCriterion.FirstName) || u.FirstName == selectCriterion.FirstName);
// LastName filter
if (!string.IsNullOrWhiteSpace(selectCriterion.LastName))
query = query.Where(u => u.LastName.Contains(selectCriterion.LastName) || u.LastName == selectCriterion.LastName);
// EMail filter
if (!string.IsNullOrWhiteSpace(selectCriterion.EMail))
query = query.Where(u => u.EMail.Contains(selectCriterion.EMail) || u.EMail == selectCriterion.EMail);
// AuthRole filter
if (selectCriterion.AuthRole != null)
query = query.Where(u => u.AuthRole == (AuthRoles)selectCriterion.AuthRole);
// sort
if (!string.IsNullOrWhiteSpace(selectCriterion.SortColumn) && !string.IsNullOrWhiteSpace(selectCriterion.SortDirection))
{
var critSortDirection = selectCriterion.SortDirection?.ToLower();
if (critSortDirection == "desc" || critSortDirection == "descending")
query = query.OrderByMemberDescending(selectCriterion.SortColumn);
else
query = query.OrderByMember(selectCriterion.SortColumn);
}
var skip = selectCriterion.PageSize * selectCriterion.PageIndex;
if (skip < 0)
skip = 0;
var totalCount = await query.CountAsync();
var list = await query.Skip(skip).Take(selectCriterion.PageSize).AsNoTracking().ToListAsync();
return (list, totalCount);
}
public async Task<AuthUser> GetAuthUserByEMailAsync(string email)
{
return await _authUsers
.Where(au => au.EMail == email)
.FirstAsync();
}
public async Task<AuthUser> FindAuthUserByEMailAsync(string email)
{
return await _authUsers
.Where(au => au.EMail == email)
.FirstOrDefaultAsync();
}
public async Task<AuthUser> FindAuthUserByEMailConfirmationIdAsync(string id)
{
return await _authUsers
.Where(au => au.ConfirmationId == id)
.FirstOrDefaultAsync();
}
public async Task<AuthUser> FindAuthUserByEMailForgotPasswordIdAsync(string id)
{
return await _authUsers
.Where(au => au.ForgotPasswordId == id)
.FirstOrDefaultAsync();
}
public void AddAuthUser(AuthUser user)
{
_authUsers.Add(user);
}
public async Task DeleteAuthUserByEMailAsync(string email)
{
var au = await _authUsers
.Where(au => au.EMail == email)
.FirstAsync();
_authUsers.Remove(au);
}
public void DeleteAuthUser(AuthUser user)
{
_authUsers.Remove(user);
}
}
}

View File

@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.2"/>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="NLog" Version="4.7.13"/>
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="6.0.1"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\app.Model\app.Model.csproj"/>
<ProjectReference Include="..\app.Interface\app.Interface.csproj"/>
<ProjectReference Include="..\app.Share.Common\app.Share.Common.csproj"/>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,20 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using app.Model;
using app.Share.Common;
namespace app.Interface.Repositories
{
public interface IAuthUserRepository
{
Task<List<AuthUser>> GetAllAuthUsersAsync();
Task<(List<AuthUser>, int totalCount)> GetFilteredAuthUsersAsync(AuthUserSelectCriterion selectCriterion);
Task<AuthUser> GetAuthUserByEMailAsync(string email);
Task<AuthUser> FindAuthUserByEMailAsync(string email);
Task<AuthUser> FindAuthUserByEMailConfirmationIdAsync(string id);
Task<AuthUser> FindAuthUserByEMailForgotPasswordIdAsync(string id);
void AddAuthUser(AuthUser user);
Task DeleteAuthUserByEMailAsync(string email);
void DeleteAuthUser(AuthUser user);
}
}

View File

@@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace app.Interface.Repositories {
public interface IUnitOfWork : IDisposable {
int SaveChanges();
Task<int> SaveChangesAsync();
}
}

View File

@@ -0,0 +1,28 @@
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Threading.Tasks;
using app.Model.DTOs;
using app.Share.Common;
namespace app.Interface.Services
{
public interface IAuthService
{
Task<List<AuthUserDTO>> GetAllAuthUsersAsync();
Task<GetFilteredAuthUsersResultDTO> GetFilteredAuthUsersAsync(AuthUserSelectCriterion selectCriterion);
Task<AuthUserDTO> GetAuthUserByEMailAsync(string email);
Task<AuthUserDTO> FindAuthUserByEMailAsync(string email);
Task<long> AddAuthUserAsync(AuthUserDTO userDTO);
Task<bool> ConfirmEMail(string id);
Task<TokenDTO> Login(AuthUserDTO userDTO);
Task ForgotPassword(string email);
Task<EMailStringDTO> ConfirmForgotPassword(string id);
Task ResetPassword(ResetPasswordDTO rpDTO);
Task UpdateUser(UpdateUserDTO updateUserDTO);
Task UpdateUserAsAdmin(AdminUpdateUserDTO updateUserDTO);
Task<TokenDTO> Refresh(TokenDTO tokenDTO);
Task Revoke(TokenDTO tokenDTO);
Task DeleteAuthUserByEMailAsync(string email);
Task DeleteAuthUserAsync(AuthUserDTO userDTO);
}
}

View File

@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\app.Model\app.Model.csproj"/>
<ProjectReference Include="..\app.Share.Common\app.Share.Common.csproj"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="NLog" Version="4.7.13"/>
</ItemGroup>
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,10 @@
using System;
namespace app.Model
{
public enum AuthRoles
{
Normal = 0,
Admin = 1
}
}

View File

@@ -0,0 +1,34 @@
using System;
using app.Model.DTOs;
namespace app.Model
{
public class AuthUser : IAutoGenerateDateFields
{
public long Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string EMail { get; set; }
public string Password { get; set; }
public AuthRoles AuthRole { get; set; }
public string RefreshToken { get; set; }
public string ConfirmationId { get; set; }
public string ForgotPasswordId { get; set; }
public DateTime RefreshTokenExpiryTime { get; set; }
public DateTimeOffset CreatedOn { get; set; }
public DateTimeOffset LastModifiedOn { get; set; }
public AuthUserDTO ToAuthUserDTO()
{
return new AuthUserDTO
{
Id = this.Id,
FirstName = this.FirstName,
LastName = this.LastName,
EMail = this.EMail,
IsConfirmed = this.ConfirmationId == null,
AuthRole = this.AuthRole
};
}
}
}

View File

@@ -0,0 +1,11 @@
using System;
namespace app.Model.DTOs
{
public class AdminUpdateUserDTO
{
public AuthUserDTO AuthUserDTO { get; set; }
public AuthUserDTO NewAuthUserDTO { get; set; }
public bool ChangePassword { get; set; }
}
}

View File

@@ -0,0 +1,11 @@
using System;
namespace app.Model.DTOs
{
public class ApiVersionDTO
{
public string Major { get; set; }
public string Minor { get; set; }
public string Micro { get; set; }
}
}

View File

@@ -0,0 +1,27 @@
namespace app.Model.DTOs
{
public class AuthUserDTO
{
public long Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string EMail { get; set; }
public string Password { get; set; }
public bool IsConfirmed { get; set; }
public AuthRoles AuthRole { get; set; }
public AuthUser ToAuthUser()
{
return new AuthUser
{
Id = this.Id,
FirstName = this.FirstName,
LastName = this.LastName,
EMail = this.EMail,
Password = this.Password,
AuthRole = this.AuthRole
};
}
}
}

View File

@@ -0,0 +1,9 @@
using System;
namespace app.Model.DTOs
{
public class EMailStringDTO
{
public string EMail { get; set; }
}
}

View File

@@ -0,0 +1,10 @@
using System.Collections.Generic;
namespace app.Model.DTOs
{
public class GetFilteredAuthUsersResultDTO
{
public List<AuthUserDTO> Users { get; set; }
public int TotalCount { get; set; }
}
}

View File

@@ -0,0 +1,10 @@
using System;
namespace app.Model.DTOs
{
public class ResetPasswordDTO
{
public string Id { get; set; }
public string Password { get; set; }
}
}

View File

@@ -0,0 +1,22 @@
using System;
namespace app.Model.DTOs
{
public class SettingsDTO
{
public string WebVersion { get; set; }
public string ApiVersion { get; set; }
public string ConfigPath { get; set; }
public string WebBaseURL { get; set; }
public string ApiBaseURL { get; set; }
public int TokenExpireTime { get; set; }
public int RefreshTokenExpireTime { get; set; }
public string MailUser { get; set; }
public int MailPort { get; set; }
public string MailHost { get; set; }
public string MailTransceiver { get; set; }
public string MailTransceiverAddress { get; set; }
}
}

View File

@@ -0,0 +1,8 @@
namespace app.Model.DTOs
{
public class TokenDTO
{
public string Token { get; set; }
public string RefreshToken { get; set; }
}
}

View File

@@ -0,0 +1,10 @@
using System;
namespace app.Model.DTOs
{
public class UpdateUserDTO
{
public AuthUserDTO AuthUserDTO { get; set; }
public AuthUserDTO NewAuthUserDTO { get; set; }
}
}

View File

@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace app.Model.DTOs
{
public abstract class UserDTO
{
public long Id { get; set; }
public string Name { get; set; }
public bool NotifyWhenLogin { get; set; }
}
}

View File

@@ -0,0 +1,21 @@
namespace app.Model.Filters
{
public enum ErrorType {
ValidationError,
Exception
}
public class ErrorDto {
public ServiceErrorCode ErrorCode { get; set; }
public string Message { get; set; }
public ErrorDto(string message)
: this(ServiceErrorCode.Unknown, message) {
}
public ErrorDto(ServiceErrorCode errorCode, string message) {
Message = message;
ErrorCode = errorCode;
}
}
}

View File

@@ -0,0 +1,21 @@
using System.Threading;
namespace app.Model.Filters
{
public enum ServiceErrorCode
{
Unknown = 0,
InvalidDependencies = 1,
InvalidData = 2,
NotFound = 3,
DataAlreadyExists = 4,
UnableToAdd = 5,
UnableToDelete = 6,
InvalidUser = 7,
ConnectionFailed = 8,
Timeout = 9,
MailError = 10
}
}

View File

@@ -0,0 +1,23 @@
using System;
namespace app.Model.Filters
{
public class ServiceException: Exception {
public ServiceErrorCode ErrorCode { get; set; }
public string ErrorMessage { get; set; }
public string Nameof { get; set; }
public ServiceException(ServiceErrorCode errorCode, string errorMessage) {
ErrorCode = errorCode;
ErrorMessage = errorMessage;
}
public string GetMessage() {
return ErrorMessage;
}
public string GetDetailedMessage() {
return $"{nameof(ServiceException)} - ErrorCode: {ErrorCode} - ErrorMessage: {ErrorMessage}";
}
}
}

View File

@@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace app.Model {
public interface IAutoGenerateDateFields {
DateTimeOffset CreatedOn { get; set; }
DateTimeOffset LastModifiedOn { get; set; }
}
}

View File

@@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NLog" Version="4.7.13"/>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,10 @@
using System.Threading.Tasks;
using app.SMTP.Model;
namespace app.SMTP.Interface
{
public interface ISMTPClient
{
Task SendEmailAsync(EMail email);
}
}

View File

@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\app.SMTP.Model\app.SMTP.Model.csproj" />
</ItemGroup>
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,11 @@
using System;
namespace app.SMTP.Model
{
public class EMail
{
public string Receiver { get; set; }
public string Subject { get; set; }
public string Message { get; set; }
}
}

View File

@@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,80 @@
using System;
using System.Net;
using System.Threading.Tasks;
using MailKit.Net.Smtp;
using MailKit.Security;
using MimeKit;
using MimeKit.Text;
using NLog;
using app.Configuration;
using app.Model.Filters;
using app.SMTP.Interface;
using app.SMTP.Model;
namespace app.SMTP.Service
{
public class SMTPClientImpl : ISMTPClient
{
private readonly EMailSettings _emailSettings;
private static Logger _logger = LogManager.GetCurrentClassLogger();
public SMTPClientImpl(EMailSettings emailSettings)
{
this._emailSettings = emailSettings;
}
public async Task SendEmailAsync(EMail email)
{
var emailMessage = new MimeMessage();
emailMessage.From.Add(new MailboxAddress(_emailSettings.FromName, _emailSettings.FromAddress));
emailMessage.To.Add(new MailboxAddress("", email.Receiver));
emailMessage.Subject = email.Subject;
emailMessage.Body = new TextPart(TextFormat.Html) { Text = $"{email.Message}<br><br>Dies ist eine Automatische E-Mail.<br>Gesendet von Login counter @ {System.Net.Dns.GetHostName()}" };
using (var client = new SmtpClient())
{
client.Timeout = 30000;
try
{
await client.ConnectAsync(_emailSettings.MailServerAddress, _emailSettings.MailServerPort, SecureSocketOptions.Auto).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.Error(ex);
throw new ServiceException(ServiceErrorCode.ConnectionFailed, "Connection to mail server failed");
}
try
{
await client.AuthenticateAsync(new NetworkCredential(_emailSettings.Username, _emailSettings.Credentials));
}
catch (Exception ex)
{
_logger.Error(ex);
throw new ServiceException(ServiceErrorCode.InvalidUser, "Authentification to mail server failed");
}
try
{
await client.SendAsync(emailMessage).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.Error(ex);
throw new ServiceException(ServiceErrorCode.MailError, "Unable to send email");
}
try
{
await client.DisconnectAsync(true).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.Error(ex);
throw new ServiceException(ServiceErrorCode.ConnectionFailed, "Unable to disconnect from mail server");
}
}
}
}
}

View File

@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\app.SMTP.Model\app.SMTP.Model.csproj"/>
<ProjectReference Include="..\app.SMTP.Interface\app.SMTP.Interface.csproj"/>
<ProjectReference Include="..\app.Configuration\app.Configuration.csproj"/>
<ProjectReference Include="..\app.Model\app.Model.csproj"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="NETCore.MailKit" Version="2.0.3"/>
<PackageReference Include="nlog" Version="4.7.13"/>
</ItemGroup>
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,604 @@
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Tokens;
using NLog;
using app.Configuration;
using app.Interface.Repositories;
using app.Interface.Services;
using app.Model;
using app.Model.DTOs;
using app.Model.Filters;
using app.Share.Common;
using app.SMTP.Interface;
using app.SMTP.Model;
namespace app.Service
{
public class AuthServiceImpl : IAuthService
{
private readonly IAuthUserRepository _authUserRepository;
private readonly IUnitOfWork _unitOfWork;
private readonly AuthentificationSettings _authSettings;
private readonly ISMTPClient _smtpClient;
private readonly FrontendSettings _frontendSettings;
private static Logger _logger = LogManager.GetCurrentClassLogger();
private static Random random = new Random();
public AuthServiceImpl(
IAuthUserRepository authUserRepository,
IUnitOfWork unitOfWork,
AuthentificationSettings authSettings,
ISMTPClient smtpClient,
FrontendSettings frontendSettings
)
{
_unitOfWork = unitOfWork;
_authUserRepository = authUserRepository;
_authSettings = authSettings;
_smtpClient = smtpClient;
_frontendSettings = frontendSettings;
}
private static string _randomString(int length)
{
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
return new string(Enumerable.Repeat(chars, length)
.Select(s => s[random.Next(s.Length)]).ToArray());
}
private string _generateToken(IEnumerable<Claim> claims)
{
var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_authSettings.SecretKey));
var signingCredentials = new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha256);
var tokenOptions = new JwtSecurityToken(
issuer: _authSettings.Issuer,
audience: _authSettings.Audience,
claims: claims,
expires: DateTime.Now.AddMinutes(_authSettings.TokenExpireTime),
signingCredentials: signingCredentials
);
return new JwtSecurityTokenHandler().WriteToken(tokenOptions);
}
private string _generateRefreshToken()
{
var randomNumber = new byte[32];
using (var rng = RandomNumberGenerator.Create())
{
rng.GetBytes(randomNumber);
return Convert.ToBase64String(randomNumber);
}
}
private async Task<string> _createAndSaveRefreshToken(AuthUser user)
{
var refreshToken = this._generateRefreshToken();
user.RefreshToken = refreshToken;
user.RefreshTokenExpiryTime = DateTime.Now.AddDays(_authSettings.RefreshTokenExpireTime);
await _unitOfWork.SaveChangesAsync();
return refreshToken;
}
private ClaimsPrincipal _getPrincipalFromExpiredToken(string token)
{
var tokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false, //you might want to validate the audience and issuer depending on your use case
ValidateIssuer = false,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(this._authSettings.SecretKey)),
ValidateLifetime = false //here we are saying that we don't care about the token's expiration date
};
var tokenHandler = new JwtSecurityTokenHandler();
SecurityToken securityToken;
var principal = tokenHandler.ValidateToken(token, tokenValidationParameters, out securityToken);
var jwtSecurityToken = securityToken as JwtSecurityToken;
if (jwtSecurityToken == null || !jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256, StringComparison.InvariantCultureIgnoreCase))
throw new SecurityTokenException("Invalid token");
return principal;
}
private async Task _createAndSaveConfirmationId(AuthUser user)
{
bool end = false;
while (!end)
{
string id = _randomString(16);
var userFromDb = await _authUserRepository.FindAuthUserByEMailConfirmationIdAsync(id);
if (userFromDb is null)
{
end = true;
user.ConfirmationId = id;
}
}
}
private async Task _createAndSaveForgotPasswordId(AuthUser user)
{
bool end = false;
while (!end)
{
string id = _randomString(16);
var userFromDb = await _authUserRepository.FindAuthUserByEMailForgotPasswordIdAsync(id);
if (userFromDb is null)
{
end = true;
user.ForgotPasswordId = id;
}
}
}
private async Task _sendConfirmationIdToUser(AuthUser user)
{
string url = _frontendSettings.URL.EndsWith("/") ? _frontendSettings.URL : $"{_frontendSettings}/";
await _smtpClient.SendEmailAsync(new EMail()
{
Receiver = user.EMail,
Subject = $"E-Mail für {user.FirstName} {user.LastName} bestätigen",
Message = $"{url}auth/register/{user.ConfirmationId}"
});
}
private async Task _sendForgotPasswordIdToUser(AuthUser user)
{
string url = _frontendSettings.URL.EndsWith("/") ? _frontendSettings.URL : $"{_frontendSettings}/";
await _smtpClient.SendEmailAsync(new EMail()
{
Receiver = user.EMail,
Subject = $"Passwort für {user.FirstName} {user.LastName} zurücksetzen",
Message = $"{url}auth/forgot-password/{user.ForgotPasswordId}"
});
}
public async Task<List<AuthUserDTO>> GetAllAuthUsersAsync()
{
var authUserDTOs = new List<AuthUserDTO>();
var authUsers = await _authUserRepository.GetAllAuthUsersAsync();
authUsers.ForEach(authUser =>
{
authUserDTOs.Add(authUser.ToAuthUserDTO());
});
return authUserDTOs;
}
public async Task<GetFilteredAuthUsersResultDTO> GetFilteredAuthUsersAsync(AuthUserSelectCriterion selectCriterion) {
(var users, var totalCount) = await _authUserRepository.GetFilteredAuthUsersAsync(selectCriterion);
var result = new List<AuthUserDTO>();
users.ForEach(user => {
result.Add(user.ToAuthUserDTO());
});
return new GetFilteredAuthUsersResultDTO() {
Users = result,
TotalCount = totalCount
};
}
public async Task<AuthUserDTO> GetAuthUserByEMailAsync(string email)
{
try
{
var authUser = await _authUserRepository.GetAuthUserByEMailAsync(email);
return authUser.ToAuthUserDTO();
}
catch (Exception e)
{
_logger.Error(e);
throw new ServiceException(ServiceErrorCode.InvalidData, $"AuthUser with email {email} not found");
}
}
public async Task<AuthUserDTO> FindAuthUserByEMailAsync(string email)
{
var authUser = await _authUserRepository.FindAuthUserByEMailAsync(email);
return authUser != null ? authUser.ToAuthUserDTO() : null;
}
public async Task<long> AddAuthUserAsync(AuthUserDTO authUserDTO)
{
var authUserDb = await _authUserRepository.FindAuthUserByEMailAsync(authUserDTO.EMail);
if (authUserDb != null)
{
throw new ServiceException(ServiceErrorCode.InvalidUser, "User already exists");
}
authUserDTO.Password = ComputeHash(authUserDTO.Password, new SHA256CryptoServiceProvider());
var authUser = authUserDTO.ToAuthUser();
if (!IsValidEmail(authUser.EMail))
{
throw new ServiceException(ServiceErrorCode.InvalidData, $"Invalid E-Mail");
}
try
{
_authUserRepository.AddAuthUser(authUser);
await _createAndSaveConfirmationId(authUser);
await _sendConfirmationIdToUser(authUser);
await _unitOfWork.SaveChangesAsync();
_logger.Info($"Added authUser with email: {authUser.EMail}");
return authUser.Id;
}
catch (Exception e)
{
_logger.Error(e);
throw new ServiceException(ServiceErrorCode.UnableToAdd, $"Cannot add authUser {authUserDTO.EMail}");
}
}
public async Task<bool> ConfirmEMail(string id)
{
var user = await _authUserRepository.FindAuthUserByEMailConfirmationIdAsync(id);
if (user.ConfirmationId == id)
{
user.ConfirmationId = null;
await _unitOfWork.SaveChangesAsync();
return true;
}
return false;
}
public async Task<TokenDTO> Login(AuthUserDTO userDTO)
{
if (userDTO == null)
{
throw new ServiceException(ServiceErrorCode.InvalidData, $"User is empty");
}
var userFromDb = await _authUserRepository.FindAuthUserByEMailAsync(userDTO.EMail);
if (userFromDb == null)
{
throw new ServiceException(ServiceErrorCode.InvalidUser, "User not found");
}
if (userFromDb.ConfirmationId != null)
{
throw new ServiceException(ServiceErrorCode.InvalidUser, "E-Mail not confirmed");
}
userDTO.Password = ComputeHash(userDTO.Password, new SHA256CryptoServiceProvider());
if (userFromDb.Password != userDTO.Password)
{
throw new ServiceException(ServiceErrorCode.InvalidUser, "Wrong password");
}
var tokenString = this._generateToken(new List<Claim>() {
new Claim(ClaimTypes.Name, userDTO.EMail),
new Claim(ClaimTypes.Role, userFromDb.AuthRole.ToString())
});
var refreshString = await this._createAndSaveRefreshToken(userFromDb);
if (userFromDb.ForgotPasswordId != null)
{
userFromDb.ForgotPasswordId = null;
await _unitOfWork.SaveChangesAsync();
}
return new TokenDTO
{
Token = tokenString,
RefreshToken = refreshString
};
}
public async Task ForgotPassword(string email)
{
var user = await _authUserRepository.FindAuthUserByEMailAsync(email);
if (user is null)
{
return;
}
await _createAndSaveForgotPasswordId(user);
await _sendForgotPasswordIdToUser(user);
await _unitOfWork.SaveChangesAsync();
}
public async Task<EMailStringDTO> ConfirmForgotPassword(string id)
{
var user = await _authUserRepository.FindAuthUserByEMailForgotPasswordIdAsync(id);
return new EMailStringDTO()
{
EMail = user.EMail
};
}
public async Task ResetPassword(ResetPasswordDTO rpDTO)
{
var user = await _authUserRepository.FindAuthUserByEMailForgotPasswordIdAsync(rpDTO.Id);
if (user == null)
{
throw new ServiceException(ServiceErrorCode.InvalidUser, "User not found");
}
if (user.ConfirmationId != null)
{
throw new ServiceException(ServiceErrorCode.InvalidUser, "E-Mail not confirmed");
}
if (rpDTO.Password == null || rpDTO.Password == "")
{
throw new ServiceException(ServiceErrorCode.InvalidData, "Password is empty");
}
rpDTO.Password = ComputeHash(rpDTO.Password, new SHA256CryptoServiceProvider());
user.Password = rpDTO.Password;
await _unitOfWork.SaveChangesAsync();
}
public async Task UpdateUser(UpdateUserDTO updateUserDTO)
{
if (updateUserDTO == null)
{
throw new ServiceException(ServiceErrorCode.InvalidData, $"User is empty");
}
if (updateUserDTO.AuthUserDTO == null)
{
throw new ServiceException(ServiceErrorCode.InvalidData, $"Existing user is empty");
}
if (updateUserDTO.NewAuthUserDTO == null)
{
throw new ServiceException(ServiceErrorCode.InvalidData, $"New user is empty");
}
if (!IsValidEmail(updateUserDTO.AuthUserDTO.EMail) || !IsValidEmail(updateUserDTO.NewAuthUserDTO.EMail))
{
throw new ServiceException(ServiceErrorCode.InvalidData, $"Invalid E-Mail");
}
var user = await _authUserRepository.FindAuthUserByEMailAsync(updateUserDTO.AuthUserDTO.EMail);
if (user == null)
{
throw new ServiceException(ServiceErrorCode.InvalidUser, "User not found");
}
if (user.ConfirmationId != null)
{
throw new ServiceException(ServiceErrorCode.InvalidUser, "E-Mail not confirmed");
}
// update first name
if (updateUserDTO.NewAuthUserDTO.FirstName != null && updateUserDTO.AuthUserDTO.FirstName != updateUserDTO.NewAuthUserDTO.FirstName)
{
user.FirstName = updateUserDTO.NewAuthUserDTO.FirstName;
}
// update last name
if (updateUserDTO.NewAuthUserDTO.LastName != null && updateUserDTO.NewAuthUserDTO.LastName != "" && updateUserDTO.AuthUserDTO.LastName != updateUserDTO.NewAuthUserDTO.LastName)
{
user.LastName = updateUserDTO.NewAuthUserDTO.LastName;
}
// update E-Mail
if (updateUserDTO.NewAuthUserDTO.EMail != null && updateUserDTO.NewAuthUserDTO.EMail != "" && updateUserDTO.AuthUserDTO.EMail != updateUserDTO.NewAuthUserDTO.EMail)
{
var userByNewEMail = await _authUserRepository.FindAuthUserByEMailAsync(updateUserDTO.NewAuthUserDTO.EMail);
if (userByNewEMail != null)
{
throw new ServiceException(ServiceErrorCode.InvalidUser, "User already exists");
}
user.EMail = updateUserDTO.NewAuthUserDTO.EMail;
}
bool isExistingPasswordSet = false;
bool isnewPasswordSet = false;
// hash passwords in DTOs
if (updateUserDTO.AuthUserDTO.Password != null && updateUserDTO.AuthUserDTO.Password != "")
{
isExistingPasswordSet = true;
updateUserDTO.AuthUserDTO.Password = ComputeHash(updateUserDTO.AuthUserDTO.Password, new SHA256CryptoServiceProvider());
}
if (updateUserDTO.AuthUserDTO.Password != user.Password)
{
throw new ServiceException(ServiceErrorCode.InvalidUser, "Wrong password");
}
if (updateUserDTO.NewAuthUserDTO.Password != null && updateUserDTO.NewAuthUserDTO.Password != "")
{
isnewPasswordSet = true;
updateUserDTO.NewAuthUserDTO.Password = ComputeHash(updateUserDTO.NewAuthUserDTO.Password, new SHA256CryptoServiceProvider());
}
// update password
if (isExistingPasswordSet && isnewPasswordSet && updateUserDTO.AuthUserDTO.Password != updateUserDTO.NewAuthUserDTO.Password)
{
user.Password = updateUserDTO.NewAuthUserDTO.Password;
}
await _unitOfWork.SaveChangesAsync();
}
public async Task UpdateUserAsAdmin(AdminUpdateUserDTO updateUserDTO)
{
if (updateUserDTO == null)
{
throw new ServiceException(ServiceErrorCode.InvalidData, $"User is empty");
}
if (updateUserDTO.AuthUserDTO == null)
{
throw new ServiceException(ServiceErrorCode.InvalidData, $"Existing user is empty");
}
if (updateUserDTO.NewAuthUserDTO == null)
{
throw new ServiceException(ServiceErrorCode.InvalidData, $"New user is empty");
}
if (!IsValidEmail(updateUserDTO.AuthUserDTO.EMail) || !IsValidEmail(updateUserDTO.NewAuthUserDTO.EMail))
{
throw new ServiceException(ServiceErrorCode.InvalidData, $"Invalid E-Mail");
}
var user = await _authUserRepository.FindAuthUserByEMailAsync(updateUserDTO.AuthUserDTO.EMail);
if (user == null)
{
throw new ServiceException(ServiceErrorCode.InvalidUser, "User not found");
}
if (user.ConfirmationId != null && updateUserDTO.NewAuthUserDTO.IsConfirmed)
{
user.ConfirmationId = null;
}
else if (user.ConfirmationId == null && !updateUserDTO.NewAuthUserDTO.IsConfirmed)
{
await _createAndSaveConfirmationId(user);
}
// else
// {
// throw new ServiceException(ServiceErrorCode.InvalidUser, "E-Mail not confirmed");
// }
// update first name
if (updateUserDTO.NewAuthUserDTO.FirstName != null && updateUserDTO.AuthUserDTO.FirstName != updateUserDTO.NewAuthUserDTO.FirstName)
{
user.FirstName = updateUserDTO.NewAuthUserDTO.FirstName;
}
// update last name
if (updateUserDTO.NewAuthUserDTO.LastName != null && updateUserDTO.NewAuthUserDTO.LastName != "" && updateUserDTO.AuthUserDTO.LastName != updateUserDTO.NewAuthUserDTO.LastName)
{
user.LastName = updateUserDTO.NewAuthUserDTO.LastName;
}
// update E-Mail
if (updateUserDTO.NewAuthUserDTO.EMail != null && updateUserDTO.NewAuthUserDTO.EMail != "" && updateUserDTO.AuthUserDTO.EMail != updateUserDTO.NewAuthUserDTO.EMail)
{
var userByNewEMail = await _authUserRepository.FindAuthUserByEMailAsync(updateUserDTO.NewAuthUserDTO.EMail);
if (userByNewEMail != null)
{
throw new ServiceException(ServiceErrorCode.InvalidUser, "User already exists");
}
user.EMail = updateUserDTO.NewAuthUserDTO.EMail;
}
// update password
if (updateUserDTO.ChangePassword && updateUserDTO.AuthUserDTO.Password != updateUserDTO.NewAuthUserDTO.Password)
{
user.Password = ComputeHash(updateUserDTO.NewAuthUserDTO.Password, new SHA256CryptoServiceProvider());
}
// update role
if (user.AuthRole == updateUserDTO.AuthUserDTO.AuthRole && user.AuthRole != updateUserDTO.NewAuthUserDTO.AuthRole)
{
user.AuthRole = updateUserDTO.NewAuthUserDTO.AuthRole;
}
await _unitOfWork.SaveChangesAsync();
}
public async Task<TokenDTO> Refresh(TokenDTO tokenDTO)
{
if (tokenDTO is null)
{
throw new ServiceException(ServiceErrorCode.InvalidData, $"Token is empty");
}
var principal = this._getPrincipalFromExpiredToken(tokenDTO.Token);
var email = principal.Identity.Name;
var user = await this._authUserRepository.FindAuthUserByEMailAsync(email);
if (user == null || user.RefreshToken != tokenDTO.RefreshToken || user.RefreshTokenExpiryTime <= DateTime.Now)
{
throw new ServiceException(ServiceErrorCode.InvalidData, $"Token is expired");
}
var newToken = this._generateToken(principal.Claims);
var newRefreshToken = await this._createAndSaveRefreshToken(user);
return new TokenDTO()
{
Token = newToken,
RefreshToken = newRefreshToken
};
}
public async Task Revoke(TokenDTO tokenDTO)
{
if (tokenDTO == null || tokenDTO.Token == null || tokenDTO.RefreshToken == null)
{
throw new ServiceException(ServiceErrorCode.InvalidData, $"Token is empty");
};
var principal = this._getPrincipalFromExpiredToken(tokenDTO.Token);
var email = principal.Identity.Name;
var user = await this._authUserRepository.FindAuthUserByEMailAsync(email);
if (user == null || user.RefreshToken != tokenDTO.RefreshToken || user.RefreshTokenExpiryTime <= DateTime.Now)
{
throw new ServiceException(ServiceErrorCode.InvalidData, $"Token is expired");
}
user.RefreshToken = null;
await _unitOfWork.SaveChangesAsync();
}
public async Task DeleteAuthUserByEMailAsync(string email)
{
try
{
await _authUserRepository.DeleteAuthUserByEMailAsync(email);
await _unitOfWork.SaveChangesAsync();
_logger.Info($"Deleted authUser with email: {email}");
}
catch (Exception e)
{
_logger.Error(e);
throw new ServiceException(ServiceErrorCode.UnableToDelete, $"Cannot delete authUser with email {email}");
}
}
public async Task DeleteAuthUserAsync(AuthUserDTO authUserDTO)
{
try
{
_authUserRepository.DeleteAuthUser(authUserDTO.ToAuthUser());
await _unitOfWork.SaveChangesAsync();
_logger.Info($"Deleted authUser {authUserDTO.EMail}");
}
catch (Exception e)
{
_logger.Error(e);
throw new ServiceException(ServiceErrorCode.UnableToDelete, $"Cannot delete authUser {authUserDTO.EMail}");
}
}
private string ComputeHash(string input, HashAlgorithm algorithm)
{
Byte[] inputBytes = Encoding.UTF8.GetBytes(input);
Byte[] hashedBytes = algorithm.ComputeHash(inputBytes);
return BitConverter.ToString(hashedBytes);
}
private bool IsValidEmail(string email)
{
try
{
var addr = new System.Net.Mail.MailAddress(email);
return addr.Address == email;
}
catch
{
return false;
}
}
}
}

View File

@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\app.Interface\app.Interface.csproj"/>
<ProjectReference Include="..\app.Model\app.Model.csproj"/>
<ProjectReference Include="..\app.Data\app.Data.csproj"/>
<ProjectReference Include="..\app.Configuration\app.Configuration.csproj"/>
<ProjectReference Include="..\app.SMTP.Interface\app.SMTP.Interface.csproj"/>
<ProjectReference Include="..\app.SMTP.Model\app.SMTP.Model.csproj"/>
<ProjectReference Include="..\app.Share.Common\app.Share.Common.csproj"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.2"/>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.2"/>
<PackageReference Include="NLog" Version="4.7.13"/>
</ItemGroup>
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,11 @@
namespace app.Share.Common
{
public class AuthUserSelectCriterion : SelectCriterion
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string EMail { get; set; }
public int? AuthRole { get; set; }
}
}

View File

@@ -0,0 +1,8 @@
using System;
namespace app.Share.Common
{
public class DomainSelectCriterion : SelectCriterion
{
public string Name { get; set; }
}
}

View File

@@ -0,0 +1,11 @@
using System;
namespace app.Share.Common
{
public class HostSelectCriterion : SelectCriterion
{
public string Name { get; set; }
public int[] IPAddress { get; set; }
public string DomainName { get; set; }
public string OSName { get; set; }
}
}

View File

@@ -0,0 +1,11 @@
using System;
namespace app.Share.Common
{
public class LoginSelectCriterion : SelectCriterion
{
public DateTimeOffset? TimeFrom { get; set; }
public DateTimeOffset? TimeTo { get; set; }
public string HostName { get; set; }
public string UserName { get; set; }
}
}

View File

@@ -0,0 +1,10 @@
namespace app.Share.Common
{
public class SelectCriterion
{
public int PageIndex { get; set; }
public int PageSize { get; set; }
public string SortDirection { get; set; }
public string SortColumn { get; set; }
}
}

View File

@@ -0,0 +1,10 @@
using System;
namespace app.Share.Common
{
public class UserSelectCriterion : SelectCriterion
{
public string Name { get; set; }
public string DomainName { get; set; }
public string HostName { get; set; }
}
}

View File

@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\app.Share.Common.Interface\app.Share.Common.Interface.csproj" />
</ItemGroup>
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,9 @@
using System;
using Microsoft.AspNetCore.SignalR;
namespace app.SignalR
{
public class NotifyHub : Hub
{
}
}

View File

@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.1.0" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,127 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30621.155
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "app", "app\app.csproj", "{9AB6095F-23EF-4925-9637-7F0B7DE57D4A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "app.Configuration", "app.Configuration\app.Configuration.csproj", "{6079B54E-42E1-4B68-8BE5-C831E6C0B3A3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "app.CredentialManager", "app.CredentialManager\app.CredentialManager.csproj", "{90969785-C60C-45DB-A84E-E92A65755DF0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "app.Data", "app.Data\app.Data.csproj", "{1F380FAE-D068-40BB-869A-80CE076A2C81}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "app.Interface", "app.Interface\app.Interface.csproj", "{447C2465-462F-4DBC-95F8-2330BBE36C4E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "app.Model", "app.Model\app.Model.csproj", "{185EDC35-E150-4404-90EC-562819397D80}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "app.Service", "app.Service\app.Service.csproj", "{F90A1288-0A41-449F-A856-F8988E77547F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "app.SMTP.Interface", "app.SMTP.Interface\app.SMTP.Interface.csproj", "{EAE98478-F76B-4A37-A428-3A4F9DF18458}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "app.SMTP.Service", "app.SMTP.Service\app.SMTP.Service.csproj", "{EC9B5EB4-EADC-4A95-8E67-4DF8B6BB8ED6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "app.SMTP.Model", "app.SMTP.Model\app.SMTP.Model.csproj", "{7A792724-5EBF-4B12-AB8B-ACEF17F3E752}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "app.SignalR", "app.SignalR\app.SignalR.csproj", "{B55249FD-3E4B-497B-810F-C7D60DB7A09C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "app.Share.Common", "app.Share.Common\app.Share.Common.csproj", "{53841805-BF4C-4197-98C8-79CA3609D670}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{31207121-94AB-4E14-9533-0E14E5A1D7B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{31207121-94AB-4E14-9533-0E14E5A1D7B7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{31207121-94AB-4E14-9533-0E14E5A1D7B7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{31207121-94AB-4E14-9533-0E14E5A1D7B7}.Release|Any CPU.Build.0 = Release|Any CPU
{24ADC76B-B4B8-4BE7-AA45-DDD70C703746}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{24ADC76B-B4B8-4BE7-AA45-DDD70C703746}.Debug|Any CPU.Build.0 = Debug|Any CPU
{24ADC76B-B4B8-4BE7-AA45-DDD70C703746}.Release|Any CPU.ActiveCfg = Release|Any CPU
{24ADC76B-B4B8-4BE7-AA45-DDD70C703746}.Release|Any CPU.Build.0 = Release|Any CPU
{256CDBD1-03A3-45B3-AEC2-D47C8E27D297}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{256CDBD1-03A3-45B3-AEC2-D47C8E27D297}.Debug|Any CPU.Build.0 = Debug|Any CPU
{256CDBD1-03A3-45B3-AEC2-D47C8E27D297}.Release|Any CPU.ActiveCfg = Release|Any CPU
{256CDBD1-03A3-45B3-AEC2-D47C8E27D297}.Release|Any CPU.Build.0 = Release|Any CPU
{1B2288C0-7376-417F-B6E7-4722674A3865}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1B2288C0-7376-417F-B6E7-4722674A3865}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1B2288C0-7376-417F-B6E7-4722674A3865}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1B2288C0-7376-417F-B6E7-4722674A3865}.Release|Any CPU.Build.0 = Release|Any CPU
{4EB016C1-4193-4935-9E83-C127F6B31E90}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4EB016C1-4193-4935-9E83-C127F6B31E90}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4EB016C1-4193-4935-9E83-C127F6B31E90}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4EB016C1-4193-4935-9E83-C127F6B31E90}.Release|Any CPU.Build.0 = Release|Any CPU
{DE15C470-100E-4D31-B81B-C28B53C244CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DE15C470-100E-4D31-B81B-C28B53C244CF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DE15C470-100E-4D31-B81B-C28B53C244CF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DE15C470-100E-4D31-B81B-C28B53C244CF}.Release|Any CPU.Build.0 = Release|Any CPU
{0D798502-DF95-453D-B3D4-A49D2536A83D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0D798502-DF95-453D-B3D4-A49D2536A83D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0D798502-DF95-453D-B3D4-A49D2536A83D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0D798502-DF95-453D-B3D4-A49D2536A83D}.Release|Any CPU.Build.0 = Release|Any CPU
{F62CDD28-A656-40DF-8D40-5CC8949A3A9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F62CDD28-A656-40DF-8D40-5CC8949A3A9E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F62CDD28-A656-40DF-8D40-5CC8949A3A9E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F62CDD28-A656-40DF-8D40-5CC8949A3A9E}.Release|Any CPU.Build.0 = Release|Any CPU
{9AB6095F-23EF-4925-9637-7F0B7DE57D4A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9AB6095F-23EF-4925-9637-7F0B7DE57D4A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9AB6095F-23EF-4925-9637-7F0B7DE57D4A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9AB6095F-23EF-4925-9637-7F0B7DE57D4A}.Release|Any CPU.Build.0 = Release|Any CPU
{6079B54E-42E1-4B68-8BE5-C831E6C0B3A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6079B54E-42E1-4B68-8BE5-C831E6C0B3A3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6079B54E-42E1-4B68-8BE5-C831E6C0B3A3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6079B54E-42E1-4B68-8BE5-C831E6C0B3A3}.Release|Any CPU.Build.0 = Release|Any CPU
{90969785-C60C-45DB-A84E-E92A65755DF0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{90969785-C60C-45DB-A84E-E92A65755DF0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{90969785-C60C-45DB-A84E-E92A65755DF0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{90969785-C60C-45DB-A84E-E92A65755DF0}.Release|Any CPU.Build.0 = Release|Any CPU
{1F380FAE-D068-40BB-869A-80CE076A2C81}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1F380FAE-D068-40BB-869A-80CE076A2C81}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1F380FAE-D068-40BB-869A-80CE076A2C81}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1F380FAE-D068-40BB-869A-80CE076A2C81}.Release|Any CPU.Build.0 = Release|Any CPU
{447C2465-462F-4DBC-95F8-2330BBE36C4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{447C2465-462F-4DBC-95F8-2330BBE36C4E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{447C2465-462F-4DBC-95F8-2330BBE36C4E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{447C2465-462F-4DBC-95F8-2330BBE36C4E}.Release|Any CPU.Build.0 = Release|Any CPU
{185EDC35-E150-4404-90EC-562819397D80}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{185EDC35-E150-4404-90EC-562819397D80}.Debug|Any CPU.Build.0 = Debug|Any CPU
{185EDC35-E150-4404-90EC-562819397D80}.Release|Any CPU.ActiveCfg = Release|Any CPU
{185EDC35-E150-4404-90EC-562819397D80}.Release|Any CPU.Build.0 = Release|Any CPU
{F90A1288-0A41-449F-A856-F8988E77547F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F90A1288-0A41-449F-A856-F8988E77547F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F90A1288-0A41-449F-A856-F8988E77547F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F90A1288-0A41-449F-A856-F8988E77547F}.Release|Any CPU.Build.0 = Release|Any CPU
{EAE98478-F76B-4A37-A428-3A4F9DF18458}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EAE98478-F76B-4A37-A428-3A4F9DF18458}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EAE98478-F76B-4A37-A428-3A4F9DF18458}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EAE98478-F76B-4A37-A428-3A4F9DF18458}.Release|Any CPU.Build.0 = Release|Any CPU
{EC9B5EB4-EADC-4A95-8E67-4DF8B6BB8ED6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EC9B5EB4-EADC-4A95-8E67-4DF8B6BB8ED6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EC9B5EB4-EADC-4A95-8E67-4DF8B6BB8ED6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EC9B5EB4-EADC-4A95-8E67-4DF8B6BB8ED6}.Release|Any CPU.Build.0 = Release|Any CPU
{7A792724-5EBF-4B12-AB8B-ACEF17F3E752}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7A792724-5EBF-4B12-AB8B-ACEF17F3E752}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7A792724-5EBF-4B12-AB8B-ACEF17F3E752}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7A792724-5EBF-4B12-AB8B-ACEF17F3E752}.Release|Any CPU.Build.0 = Release|Any CPU
{B55249FD-3E4B-497B-810F-C7D60DB7A09C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B55249FD-3E4B-497B-810F-C7D60DB7A09C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B55249FD-3E4B-497B-810F-C7D60DB7A09C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B55249FD-3E4B-497B-810F-C7D60DB7A09C}.Release|Any CPU.Build.0 = Release|Any CPU
{53841805-BF4C-4197-98C8-79CA3609D670}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{53841805-BF4C-4197-98C8-79CA3609D670}.Debug|Any CPU.Build.0 = Debug|Any CPU
{53841805-BF4C-4197-98C8-79CA3609D670}.Release|Any CPU.ActiveCfg = Release|Any CPU
{53841805-BF4C-4197-98C8-79CA3609D670}.Release|Any CPU.Build.0 = Release|Any CPU
{539E128A-E0B2-4593-A13A-9641922A50EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{539E128A-E0B2-4593-A13A-9641922A50EC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{539E128A-E0B2-4593-A13A-9641922A50EC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{539E128A-E0B2-4593-A13A-9641922A50EC}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {27320AC8-C9ED-452B-9C99-074215D41C63}
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,148 @@
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using app.Interface.Services;
using app.Model.DTOs;
using app.Share.Common;
namespace app.Controllers
{
[Route("api/auth")]
[ApiController]
public class AuthController : ControllerBase
{
private readonly IAuthService _authService;
public AuthController(
IAuthService authServce
)
{
_authService = authServce;
}
/* Data requests */
// Get /api/auth/users
[HttpGet("users")]
[Authorize]
public async Task<List<AuthUserDTO>> GetAllAuthUsers()
{
return await _authService.GetAllAuthUsersAsync();
}
// POST /api/auth/users/get/filtered
[HttpPost("users/get/filtered")]
[Authorize]
public async Task<GetFilteredAuthUsersResultDTO> GetFilteredAuthUsers(AuthUserSelectCriterion selectCriterion)
{
return await _authService.GetFilteredAuthUsersAsync(selectCriterion);
}
// Get /api/auth/users/get/<mail>
[HttpGet("users/get/{email}")]
[Authorize]
public async Task<AuthUserDTO> GetUserFromEMail(string email)
{
return await _authService.GetAuthUserByEMailAsync(email);
}
// Get /api/auth/users/find/<mail>
[HttpGet("users/find/{email}")]
[Authorize]
public async Task<AuthUserDTO> FindUserFromEMail(string email)
{
return await _authService.FindAuthUserByEMailAsync(email);
}
/* Auth requests */
// POST /api/auth/register
[HttpPost("register")]
public async Task Register(AuthUserDTO userDTO)
{
await _authService.AddAuthUserAsync(userDTO);
}
// POST /api/auth/register/<id>
[HttpPost("register/{id}")]
public async Task<bool> ConfirmEMail(string id)
{
return await _authService.ConfirmEMail(id);
}
// POST /api/auth/login
[HttpPost("login")]
public async Task<TokenDTO> Login(AuthUserDTO userDTO)
{
return await _authService.Login(userDTO);
}
// POST /api/auth/forgot-password
[HttpPost("forgot-password")]
public async Task ForgotPassword([FromBody] string email)
{
await _authService.ForgotPassword(email);
}
// POST /api/auth/confirm-forgot-password
[HttpPost("confirm-forgot-password")]
public async Task<EMailStringDTO> ConfirmForgotPassword([FromBody] string id)
{
return await _authService.ConfirmForgotPassword(id);
}
// POST /api/auth/reset-password
[HttpPost("reset-password")]
public async Task ResetPassword(ResetPasswordDTO rpDTO)
{
await _authService.ResetPassword(rpDTO);
}
// POST /api/auth/update-user
[HttpPost("update-user")]
public async Task UpdateUser(UpdateUserDTO updateUserDTO)
{
await _authService.UpdateUser(updateUserDTO);
}
// POST /api/auth/update-user-as-admin
[HttpPost("update-user-as-admin")]
[Authorize]
public async Task UpdateUserAsAdmin(AdminUpdateUserDTO updateUserDTO)
{
await _authService.UpdateUserAsAdmin(updateUserDTO);
}
// POST /api/auth/refresh
[HttpPost("refresh")]
public async Task<TokenDTO> Refresh(TokenDTO tokenDTO)
{
return await _authService.Refresh(tokenDTO);
}
// POST /api/auth/revoke
[HttpPost("revoke")]
public async Task Revoke(TokenDTO tokenDTO)
{
await _authService.Revoke(tokenDTO);
}
// POST /api/auth/delete-user
[HttpPost("delete-user")]
public async Task DeleteAuthUserAsync(AuthUserDTO userDTO)
{
await _authService.DeleteAuthUserAsync(userDTO);
}
// POST /api/auth/delete-user
[HttpPost("delete-user-by-mail/{mail}")]
public async Task DeleteAuthUserByEMailAsync(string mail)
{
await _authService.DeleteAuthUserByEMailAsync(mail);
}
}
}

View File

@@ -0,0 +1,92 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Hosting;
using app.Configuration;
using app.Model.DTOs;
using app.SMTP.Interface;
using app.SMTP.Model;
namespace app.Controllers
{
[Route("api/gui")]
[ApiController]
public class GUIController : ControllerBase
{
private APISettings _apiSettings;
private DatabaseSettings _databaseSettings;
private AuthentificationSettings _authSettings;
private EMailSettings _mailSettings;
private FrontendSettings _frontendSettings;
private IHostEnvironment _env;
private readonly ISMTPClient _smtpClient;
public GUIController(
APISettings apiSettings,
DatabaseSettings databaseSettings,
AuthentificationSettings authSettings,
EMailSettings mailSettings,
FrontendSettings frontendSettings,
IHostEnvironment env,
ISMTPClient smtpClient
)
{
this._apiSettings = apiSettings;
this._databaseSettings = databaseSettings;
this._authSettings = authSettings;
this._mailSettings = mailSettings;
this._frontendSettings = frontendSettings;
this._env = env;
this._smtpClient = smtpClient;
}
// GET /api/gui/api-version
[HttpGet("api-version")]
public ApiVersionDTO GetApiVersion()
{
return new ApiVersionDTO()
{
Major = this._apiSettings.ApiVersion.Major,
Minor = this._apiSettings.ApiVersion.Minor,
Micro = this._apiSettings.ApiVersion.Micro
};
}
// GET /api/gui/settings
[HttpGet("settings")]
[Authorize]
public SettingsDTO GetSettingsDTO()
{
return new SettingsDTO()
{
ApiVersion = this._apiSettings.ApiVersion.ToString(),
ConfigPath = this._env.ContentRootPath,
WebBaseURL = this._frontendSettings.URL,
ApiBaseURL = "",
TokenExpireTime = this._authSettings.TokenExpireTime,
RefreshTokenExpireTime = this._authSettings.RefreshTokenExpireTime,
MailUser = this._mailSettings.Username,
MailPort = this._mailSettings.MailServerPort,
MailHost = this._mailSettings.MailServerAddress,
MailTransceiver = this._mailSettings.FromName,
MailTransceiverAddress = this._mailSettings.FromAddress
};
}
// POST /api/gui/send-test-mail/<email>
[HttpPost("send-test-mail/{email}")]
[Authorize]
public async Task SendTestMail(string email)
{
await _smtpClient.SendEmailAsync(new EMail()
{
Receiver = email,
Subject = $"Login counter Test E-Mail",
Message = $"Login counter Test E-Mail"
});
}
}
}

View File

@@ -0,0 +1,76 @@
using System;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Security.Cryptography;
using Microsoft.EntityFrameworkCore;
using app.Data;
using app.Model;
namespace app
{
public class DataSeeder
{
private readonly DatabaseContext _databaseContext;
public DataSeeder(DatabaseContext databaseContext)
{
_databaseContext = databaseContext;
}
public void SeedData()
{
_databaseContext.Database.EnsureCreated();
if (!_databaseContext.AuthUsers.Any())
{
var admin = new AuthUser()
{
FirstName = "Admin",
LastName = "Administator",
EMail = "admin@localhost",
Password = ComputeHash("Administator", new SHA256CryptoServiceProvider()),
AuthRole = AuthRoles.Admin
};
var authUser = new AuthUser()
{
FirstName = "Max",
LastName = "Mustermann",
EMail = "max.mustermann@gmail.com",
Password = ComputeHash("test1234", new SHA256CryptoServiceProvider()),
};
var authUser1 = new AuthUser()
{
FirstName = "Max",
LastName = "Tester",
EMail = "max.mustermann@mustermail.com",
Password = ComputeHash("test1234", new SHA256CryptoServiceProvider()),
};
var authUser2 = new AuthUser()
{
FirstName = "Max",
LastName = "Muster",
EMail = "max.mustermann@yahoo.com",
Password = ComputeHash("test1234", new SHA256CryptoServiceProvider()),
};
_databaseContext.AuthUsers.Add(admin);
_databaseContext.AuthUsers.Add(authUser);
_databaseContext.AuthUsers.Add(authUser1);
_databaseContext.AuthUsers.Add(authUser2);
}
_databaseContext.SaveChanges();
}
public string ComputeHash(string input, HashAlgorithm algorithm)
{
Byte[] inputBytes = Encoding.UTF8.GetBytes(input);
Byte[] hashedBytes = algorithm.ComputeHash(inputBytes);
return BitConverter.ToString(hashedBytes);
}
}
}

View File

@@ -0,0 +1,55 @@
using System;
using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using NLog;
using app.Model.Filters;
namespace app.Filters
{
public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
{
private static Logger _logger = LogManager.GetCurrentClassLogger();
public CustomExceptionFilterAttribute() { }
public override void OnException(ExceptionContext context)
{
context.Result = CreateJsonErrorResult(context);
base.OnException(context);
}
public JsonResult CreateJsonErrorResult(ExceptionContext context)
{
JsonResult result = null;
// create jsonresult
var exception = context.Exception;
if (exception is ServiceException)
{
var bex = exception as ServiceException;
_logger.Error($"{bex.GetDetailedMessage()}");
var error = new ErrorDto(bex.ErrorCode, bex.GetMessage());
result = new JsonResult(error)
{
StatusCode = (int)HttpStatusCode.BadRequest
};
}
else
{
var trackingId = Guid.NewGuid();
string userMsg = $"Tracking Id: {trackingId}";
_logger.Error(exception, userMsg);
var error = new ErrorDto($"Tracking Id: {trackingId}");
result = new JsonResult(error)
{
StatusCode = (int)HttpStatusCode.InternalServerError
};
}
return result;
}
}
}

View File

@@ -0,0 +1,30 @@
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using app.Data;
namespace app
{
public static class HostExtensions
{
public static IHost SeedData(this IHost host)
{
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
var context = services.GetService<DatabaseContext>();
// now we have the DbContext. Run migrations
context.Database.Migrate();
#if DEBUG
// now that the database is up to date. Let's seed
new DataSeeder(context).SeedData();
#endif
}
return host;
}
}
}

View File

@@ -0,0 +1,54 @@
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NLog;
namespace app.JSONConverter
{
public abstract class JsonCreationConverter<T> : JsonConverter
{
static Logger _logger = LogManager.GetCurrentClassLogger();
protected abstract T Create(Type objectType, JObject jObject);
public override bool CanConvert(Type objectType)
{
return typeof(T).IsAssignableFrom(objectType);
}
public override bool CanWrite
{
get { return false; }
}
public override object ReadJson(JsonReader reader,
Type objectType,
object existingValue,
JsonSerializer serializer)
{
try
{
// Load JObject from stream
JObject jObject = JObject.Load(reader);
// Create target object based on JObject
T target = Create(objectType, jObject);
// Populate the object properties
serializer.Populate(jObject.CreateReader(), target);
return target;
}
catch (Exception e)
{
_logger.Error(e);
return null;
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
}
}
}

View File

@@ -0,0 +1,43 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using NLog.Extensions.Logging;
using app.Configuration;
using System;
namespace app
{
public class Program
{
static void Main(string[] args)
{
try
{
Configuration.HostExtensions.ChangeCurrentDirToProjectDir();
var host = CreateHostBuilder(args).Build();
host.LogHostingEnvironment<Program>();
host.SeedData();
host.Run();
}
catch (Exception ex)
{
Configuration.HostExtensions.LogStartError(ex);
Console.WriteLine(ex.ToString());
}
finally
{
NLog.LogManager.Shutdown();
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
}).ConfigureLogging((hostCtx, loggingBuilder) =>
{
loggingBuilder.SetMinimumLevel(LogLevel.Trace);
loggingBuilder.AddNLog($"nlog-{hostCtx.HostingEnvironment.EnvironmentName}.config");
});
}
}

View File

@@ -0,0 +1,28 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:50843",
"sslPort": 44360
}
},
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchUrl": "weatherforecast",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"login_counter": {
"commandName": "Project",
"launchUrl": "weatherforecast",
"environmentVariables": {
"DOTNET_ENVIRONMENT": "development"
},
"applicationUrl": "https://localhost:5001;http://localhost:5000"
}
}
}

View File

@@ -0,0 +1,186 @@
using System.Text;
using System;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using NLog.Extensions.Logging;
using NLog;
using app.Configuration;
using app.CredentialManager;
using app.Data;
using app.Data.Repositories;
using app.Filters;
using app.Interface.Repositories;
using app.Interface.Services;
using app.Service;
using app.SMTP.Interface;
using app.SMTP.Service;
using app.SignalR;
using Microsoft.AspNetCore.HttpOverrides;
namespace app
{
public class Startup
{
public IConfiguration Configuration { get; }
private static Logger _logger = LogManager.GetCurrentClassLogger();
public Startup(IHostEnvironment env)
{
try
{
var environmentName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
if (string.IsNullOrEmpty(environmentName))
environmentName = "production";
var builder = new ConfigurationBuilder().SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddJsonFile($"appsettings.{System.Net.Dns.GetHostName()}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
catch (Exception e)
{
_logger.Error(e);
}
}
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddLogging(builder =>
{
builder.ClearProviders();
builder.AddConfiguration(Configuration.GetSection("Logging"));
builder.AddNLog();
builder.AddConsole();
});
var frontend = GetTypedSettingsFromSection<FrontendSettings>("Frontend");
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy", builder => builder.WithOrigins(frontend.URL)
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials()
.SetIsOriginAllowed((host) => true));
});
var dbSettings = GetTypedSettingsFromSection<DatabaseSettings>("Database");
var authSettings = GetTypedSettingsFromSection<AuthentificationSettings>("Authentification");
var emailSettings = GetTypedSettingsFromSection<EMailSettings>("EMail");
var apiSettings = GetTypedSettingsFromSection<APISettings>("API");
emailSettings.Credentials = Base64.Decode(emailSettings.Credentials);
services.AddSingleton(dbSettings);
services.AddSingleton(authSettings);
services.AddSingleton(emailSettings);
services.AddSingleton(apiSettings);
services.AddSingleton(GetTypedSettingsFromSection<FrontendSettings>("Frontend"));
services.AddDbContextPool<DatabaseContext>(opt => opt.UseMySql(
Base64.DecodeConnectionString(dbSettings.ConnectionString, dbSettings.Credentials),
new MySqlServerVersion(new Version(10, 3, 32))
));
services.AddTransient<IAuthService, AuthServiceImpl>();
services.AddTransient<ISMTPClient, SMTPClientImpl>();
services.AddScoped<IUnitOfWork>((s) => s.GetRequiredService<DatabaseContext>());
services.AddScoped<IAuthUserRepository>((s) => new AuthUserRepositoryImpl((DatabaseContext)s.GetService<IUnitOfWork>()));
services.AddSignalR();
services.AddAuthentication(opt =>
{
opt.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
opt.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(opt =>
{
opt.RequireHttpsMetadata = false;
opt.SaveToken = true;
opt.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = authSettings.Issuer,
ValidAudience = authSettings.Audience,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(authSettings.SecretKey))
};
});
services.AddControllers()
.AddNewtonsoftJson(options =>
{
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
// options.SerializerSettings.TypeNameHandling = TypeNameHandling.All;
});
services.AddControllers(options =>
{
options.Filters.Add(typeof(CustomExceptionFilterAttribute));
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
{
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
NLog.LogManager.LoadConfiguration($"nlog-{env.EnvironmentName}.config");
app.UseCors("CorsPolicy");
var apiSettings = GetTypedSettingsFromSection<APISettings>("API");
if (apiSettings.RedirectToHTTPS)
{
app.UseHttpsRedirection();
}
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHub<NotifyHub>("/notify");
});
}
protected SettingType GetTypedSettingsFromSection<SettingType>(string sectionName) where SettingType : new()
{
try
{
var settings = new SettingType();
Configuration.GetSection(sectionName).Bind(settings);
return settings;
}
catch (Exception e)
{
_logger.Error(e);
return new SettingType();
}
}
}
}

View File

@@ -0,0 +1,43 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace>app</RootNamespace>
</PropertyGroup>
<ItemGroup>
<None Include="nlog-production.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="nlog-staging.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="nlog-development.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<Target Name="BeforeBuildEvent" BeforeTargets="BeforeBuild">
<Exec Command="powershell -executionpolicy remotesigned -File ./update-version.ps1" Condition="'$(OS)' == 'Windows_NT'"/>
<Exec Command="pwsh -executionpolicy remotesigned -File ./update-version.ps1" Condition="$([MSBuild]::IsOSPlatform('Linux'))"/>
</Target>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.2"/>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.2"/>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.2"/>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0"/>
<PackageReference Include="NLog" Version="4.7.13"/>
<PackageReference Include="NLog.Web.AspNetCore" Version="4.14.0"/>
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="6.0.1"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\app.Service\app.Service.csproj"/>
<ProjectReference Include="..\app.Data\app.Data.csproj"/>
<ProjectReference Include="..\app.Configuration\app.Configuration.csproj"/>
<ProjectReference Include="..\app.Model\app.Model.csproj"/>
<ProjectReference Include="..\app.Interface\app.Interface.csproj"/>
<ProjectReference Include="..\app.CredentialManager\app.CredentialManager.csproj"/>
<ProjectReference Include="..\app.SMTP.Model\app.SMTP.Model.csproj"/>
<ProjectReference Include="..\app.SMTP.Interface\app.SMTP.Interface.csproj"/>
<ProjectReference Include="..\app.SMTP.Service\app.SMTP.Service.csproj"/>
<ProjectReference Include="..\app.SignalR\app.SignalR.csproj"/>
<ProjectReference Include="..\app.Share.Common\app.Share.Common.csproj"/>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,13 @@
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Debug"
}
},
"Database": {
"ConnectionString": ""
}
}

View File

@@ -0,0 +1,18 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"API": {
"RedirectToHTTPS": true,
"ApiVersion": {
"Major": "HEAD",
"Minor": "0",
"Micro": "0"
}
},
"AllowedHosts": "*"
}

View File

@@ -0,0 +1,13 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"Database": {
"ConnectionString": ""
}
}

View File

@@ -0,0 +1,13 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"Database": {
"ConnectionString": ""
}
}

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true"
internalLogLevel="Warn"
internalLogFile="logs/internal-nlog.txt">
<targets>
<target name="login_counter-specific-file" xsi:type="AsyncWrapper" overflowAction="Grow" >
<target xsi:type="File" archiveFileName="logs/${logger}/${logger}-lc.{##}.txt" fileName="logs/${logger}/${logger}-lc.txt"
layout="${longdate} [${threadid}] ${level:uppercase=true} ${logger} - ${message} ${exception:format=ToString}"
maxArchiveFiles="10" archiveNumbering="Rolling" archiveEvery="Day" />
</target>
<target name="common-file" xsi:type="AsyncWrapper" overflowAction="Grow" >
<target xsi:type="File" archiveFileName="logs/login-counter.{##}.txt" fileName="logs/login-counter.txt"
layout="${longdate} [${threadid}] ${level:uppercase=true} ${logger} - ${message} ${exception:format=ToString}"
maxArchiveFiles="10" archiveNumbering="Rolling" archiveEvery="Day" />
</target>
</targets>
<rules>
<logger name="LC-*" minlevel="Warn" writeTo="common-file" />
<logger name="LC-*" minlevel="Info" writeTo="gateway-specific-file" final="true"/>
<logger name="*" minLevel="Trace" writeTo="common-file"/>
</rules>
</nlog>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true"
internalLogLevel="Warn"
internalLogFile="logs/internal-nlog.txt">
<targets>
<target name="login_counter-specific-file" xsi:type="AsyncWrapper" overflowAction="Grow" >
<target xsi:type="File" archiveFileName="logs/${logger}/${logger}-lc.{##}.txt" fileName="logs/${logger}/${logger}-lc.txt"
layout="${longdate} [${threadid}] ${level:uppercase=true} ${logger} - ${message} ${exception:format=ToString}"
maxArchiveFiles="10" archiveNumbering="Rolling" archiveEvery="Day" />
</target>
<target name="common-file" xsi:type="AsyncWrapper" overflowAction="Grow" >
<target xsi:type="File" archiveFileName="logs/login-counter.{##}.txt" fileName="logs/login-counter.txt"
layout="${longdate} [${threadid}] ${level:uppercase=true} ${logger} - ${message} ${exception:format=ToString}"
maxArchiveFiles="10" archiveNumbering="Rolling" archiveEvery="Day" />
</target>
</targets>
<rules>
<logger name="LC-*" minlevel="Warn" writeTo="common-file" />
<logger name="LC-*" minlevel="Trace" writeTo="gateway-specific-file" final="true"/>
<logger name="*" minLevel="Trace" writeTo="common-file"/>
</rules>
</nlog>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true"
internalLogLevel="Warn"
internalLogFile="logs/internal-nlog.txt">
<targets>
<target name="login_counter-specific-file" xsi:type="AsyncWrapper" overflowAction="Grow" >
<target xsi:type="File" archiveFileName="logs/${logger}/${logger}-lc.{##}.txt" fileName="logs/${logger}/${logger}-lc.txt"
layout="${longdate} [${threadid}] ${level:uppercase=true} ${logger} - ${message} ${exception:format=ToString}"
maxArchiveFiles="10" archiveNumbering="Rolling" archiveEvery="Day" />
</target>
<target name="common-file" xsi:type="AsyncWrapper" overflowAction="Grow" >
<target xsi:type="File" archiveFileName="logs/login-counter.{##}.txt" fileName="logs/login-counter.txt"
layout="${longdate} [${threadid}] ${level:uppercase=true} ${logger} - ${message} ${exception:format=ToString}"
maxArchiveFiles="10" archiveNumbering="Rolling" archiveEvery="Day" />
</target>
</targets>
<rules>
<logger name="LC-*" minlevel="Warn" writeTo="common-file" />
<logger name="LC-*" minlevel="Trace" writeTo="gateway-specific-file" final="true"/>
<logger name="*" minLevel="Trace" writeTo="common-file"/>
</rules>
</nlog>

View File

@@ -0,0 +1,52 @@
#! /snap/bin/pwsh
function Get-VersionFromBranch {
$branch = git rev-parse --abbrev-ref HEAD
$versions = $branch.Split('.')
$major = "0"
$minor = "0"
$micro = "0"
if ($versions.Count -gt 0) {
$major = $versions[0]
}
if ($versions.Count -gt 1) {
$minor = $versions[1]
}
if ($versions.Count -gt 2) {
$micro = $versions[2]
}
return $major, $minor, $micro
}
function Get-VersionAsJSON([string]$major, [string]$minor, [string]$micro) {
return @{
"Major" = "$major"
"Minor" = "$minor"
"Micro" = "$micro"
} | ConvertTo-Json
}
function Get-AppsettingsAsJSON() {
return Get-Content -Raw -Path "./appsettings.json" | ConvertFrom-Json
}
function Set-AppsettingsAsJSON($settings) {
$settings | ConvertTo-Json | Out-File "./appsettings.json"
}
function Set-VersionFromBranch([string]$major, [string]$minor, [string]$micro) {
$versionJSON = Get-VersionAsJSON $major $minor $micro
$appsettings = Get-AppsettingsAsJSON
$appsettings.API.ApiVersion.Major = $major
$appsettings.API.ApiVersion.Minor = $minor
$appsettings.API.ApiVersion.Micro = $micro
Set-AppsettingsAsJSON $appsettings
}
$res = Get-VersionFromBranch
Set-VersionFromBranch $res[0] $res[1] $res[2]