Added first app template

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

64
.gitignore vendored Normal file
View File

@ -0,0 +1,64 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
*/dist
*/tmp
*/out-tsc
# Only exists if Bazel was run
*/bazel-out
# dependencies
*node_modules*
# profiling files
*chrome-profiler-events*.json
*speed-measure-plugin*.json
# IDEs and editors
*/.idea
*.project
*.classpath
*.c9/
*.launch
*.settings/
*.sublime-workspace
# IDE - VSCode
*.vscode/*
!*.vscode/settings.json
!*.vscode/tasks.json
!*.vscode/launch.json
!*.vscode/extensions.json
*.history/*
# misc
*/.sass-cache
*/connect.lock
*/coverage
*/libpeerconnection.log
*npm-debug.log
*yarn-error.log
*testem.log
*/typings
# System Files
*.DS_Store
*Thumbs.db
# .Net Env
*Debug*
obj
logs
-nlog.txt
.vs
.vscode
# Python Env
*__pycache__*
*.pyc
*.idea*
*.log
# angular & .net
dist
bin

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 sh-edraft.de
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

2
README.md Normal file
View File

@ -0,0 +1,2 @@
# Web-App template

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]

View File

@ -0,0 +1,18 @@
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# For the full list of supported browsers by the Angular framework, please see:
# https://angular.io/guide/browser-support
# You can see what browsers were selected by your queries by running:
# npx browserslist
last 1 Chrome version
last 1 Firefox version
last 2 Edge major versions
last 2 Safari major versions
last 2 iOS major versions
Firefox ESR
not IE 9-10 # Angular support for IE 9-10 has been deprecated and will be removed as of Angular v11. To opt-in, remove the 'not' prefix on this line.
not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line.

View File

@ -0,0 +1,16 @@
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.ts]
quote_type = single
[*.md]
max_line_length = off
trim_trailing_whitespace = false

View File

@ -0,0 +1,27 @@
# LoginCounter
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 10.1.3.
## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
## Code scaffolding
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).

View File

@ -0,0 +1,140 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"app": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"style": "scss"
}
},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/app",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"assets": [
"src/assets/images/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.scss",
"node_modules/primeicons/primeicons.css",
"node_modules/primeng/resources/themes/saga-blue/theme.css",
"node_modules/primeng/resources/primeng.min.css"
],
"scripts": [],
"vendorChunk": true,
"extractLicenses": false,
"buildOptimizer": false,
"sourceMap": true,
"optimization": false,
"namedChunks": true
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb",
"maximumError": "10kb"
}
]
}
},
"defaultConfiguration": ""
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "app:build"
},
"configurations": {
"production": {
"browserTarget": "app:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "app:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.scss"
],
"scripts": []
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"tsconfig.app.json",
"tsconfig.spec.json",
"e2e/tsconfig.json"
],
"exclude": [
"**/node_modules/**"
]
}
},
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "app:serve"
},
"configurations": {
"production": {
"devServerTarget": "app:serve:production"
}
}
}
}
}
},
"defaultProject": "app",
"cli": {
"analytics": "dcc5d68a-d438-4579-bac1-5bc2d574bd1a"
}
}

View File

@ -0,0 +1,36 @@
// @ts-check
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter');
/**
* @type { import("protractor").Config }
*/
exports.config = {
allScriptsTimeout: 11000,
specs: [
'./src/**/*.e2e-spec.ts'
],
capabilities: {
browserName: 'chrome'
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
onPrepare() {
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.json')
});
jasmine.getEnv().addReporter(new SpecReporter({
spec: {
displayStacktrace: StacktraceOption.PRETTY
}
}));
}
};

View File

@ -0,0 +1,23 @@
import { AppPage } from './app.po';
import { browser, logging } from 'protractor';
describe('workspace-project App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getTitleText()).toEqual('app app is running!');
});
afterEach(async () => {
// Assert that there are no errors emitted from the browser
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
expect(logs).not.toContain(jasmine.objectContaining({
level: logging.Level.SEVERE,
} as logging.Entry));
});
});

View File

@ -0,0 +1,11 @@
import { browser, by, element } from 'protractor';
export class AppPage {
navigateTo(): Promise<unknown> {
return browser.get(browser.baseUrl) as Promise<unknown>;
}
getTitleText(): Promise<string> {
return element(by.css('app-root .content span')).getText() as Promise<string>;
}
}

View File

@ -0,0 +1,14 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/e2e",
"module": "commonjs",
"target": "es2018",
"types": [
"jasmine",
"jasminewd2",
"node"
]
}
}

View File

@ -0,0 +1,32 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, './coverage/app'),
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true
});
};

33392
frontend/login-counter/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,58 @@
{
"name": "app",
"version": "1.0.0",
"scripts": {
"ng": "ng",
"update-version": "ts-node -O '{\"module\": \"commonjs\"}' update-version.ts",
"#prestart": "npm run update-version",
"start": "ng serve",
"#prebuild": "npm run update-version",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "~12.2.5",
"@angular/cdk": "^12.2.5",
"@angular/common": "~12.2.5",
"@angular/compiler": "~12.2.5",
"@angular/core": "~12.2.5",
"@angular/forms": "~12.2.5",
"@angular/platform-browser": "~12.2.5",
"@angular/platform-browser-dynamic": "~12.2.5",
"@angular/router": "~12.2.5",
"@aspnet/signalr": "^3.0.0-preview6.19307.2",
"@auth0/angular-jwt": "^5.0.2",
"@ngx-translate/core": "^13.0.0",
"@ngx-translate/http-loader": "^6.0.0",
"primeicons": "^4.1.0",
"primeng": "^12.1.1",
"rxjs": "^6.6.7",
"ts-lint": "^4.5.1",
"tslib": "^2.3.1",
"zone.js": "~0.11.4"
},
"devDependencies": {
"@angular-devkit/build-angular": "~12.2.5",
"@angular/cli": "~12.2.5",
"@angular/compiler-cli": "~12.2.5",
"@angular/localize": "^12.2.5",
"@types/jasmine": "^3.9.0",
"@types/jasminewd2": "^2.0.10",
"@types/node": "^12.20.24",
"codelyzer": "^6.0.2",
"jasmine-core": "~3.6.0",
"jasmine-spec-reporter": "~5.0.0",
"karma": "~6.3.4",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage-istanbul-reporter": "~3.0.2",
"karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "^1.7.0",
"protractor": "~7.0.0",
"ts-node": "~8.3.0",
"tslint": "~6.1.0",
"typescript": "~4.2.4"
}
}

View File

@ -0,0 +1,23 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { NotFoundComponent } from './components/error/not-found/not-found.component';
import { HomeComponent } from './components/home/home.component';
import { AuthRoles } from './models/auth/auth-roles.enum';
import { AuthGuard } from './modules/shared/guards/auth/auth.guard';
const routes: Routes = [
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'home', component: HomeComponent, pathMatch: 'full' },
{ path: 'change-password', loadChildren: () => import('./modules/view/change-password/change-password.module').then(m => m.ChangePasswordModule), canActivate: [AuthGuard] },
{ path: 'user-settings', loadChildren: () => import('./modules/view/user-settings/user-settings.module').then(m => m.UserSettingsModule), canActivate: [AuthGuard] },
{ path: 'auth', loadChildren: () => import('./modules/auth/auth.module').then(m => m.AuthModule) },
{ path: 'admin/settings', loadChildren: () => import('./modules/admin/settings/settings.module').then(m => m.SettingsModule), canActivate: [AuthGuard], data: { role: AuthRoles.Admin } },
{ path: 'admin/users', loadChildren: () => import('./modules/admin/auth-users/auth-user.module').then(m => m.AuthUserModule), canActivate: [AuthGuard], data: { role: AuthRoles.Admin } },
{ path: '404', component: NotFoundComponent}
];
@NgModule({
imports: [RouterModule.forRoot(routes, { relativeLinkResolution: 'legacy' })],
exports: [RouterModule]
})
export class AppRoutingModule { }

View File

@ -0,0 +1,36 @@
<main [class]="themeService.themeName">
<ng-container *ngIf="authService.isLoggedIn; else login">
<app-header (isSidebarFullWidth)="themeService.setSideWidth($event)"></app-header>
<section class="app">
<div>
<section class="sidebar" [style.width]="themeService.sidebarWidth">
<app-sidebar [isSidebarOpen]="themeService.isSidebarOpen"></app-sidebar>
</section>
</div>
<div class="component-wrapper">
<section class="component">
<router-outlet></router-outlet>
</section>
</div>
</section>
<app-footer></app-footer>
</ng-container>
<ng-template #login>
<router-outlet></router-outlet>
</ng-template>
<app-spinner></app-spinner>
<p-confirmDialog #cd key="confirmConfirmationDialog" [baseZIndex]="10000">
<ng-template pTemplate="footer">
<div class="wrapper-right btn-wrapper">
<button pButton label="{{'dialog.abort' | translate}}" class="btn icon-btn danger-icon-btn" icon="pi pi-times-circle" (click)="cd.reject()"></button>
<button pButton label="{{'dialog.confirm' | translate}}" class="btn" icon="pi pi-check-circle" (click)="cd.accept()"></button>
</div>
</ng-template>
</p-confirmDialog>
<p-toast></p-toast>
</main>

View File

@ -0,0 +1,35 @@
import { TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [
RouterTestingModule
],
declarations: [
AppComponent
],
}).compileComponents();
});
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app).toBeTruthy();
});
it(`should have as title 'app'`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app.title).toEqual('app');
});
it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement;
expect(compiled.querySelector('.content span').textContent).toContain('app app is running!');
});
});

Some files were not shown because too many files have changed in this diff Show More