Added first app template

This commit is contained in:
2022-02-20 13:43:25 +01:00
commit fb2a0080bc
238 changed files with 45673 additions and 0 deletions
+64
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
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
View File
@@ -0,0 +1,2 @@
# Web-App template
@@ -0,0 +1,10 @@
using System;
namespace app.Configuration
{
public class APISettings
{
public bool RedirectToHTTPS { get; set; }
public APIVersionSettings ApiVersion { get; set; }
}
}
@@ -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}";
}
}
}
@@ -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; }
}
}
@@ -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"];
}
}
}
@@ -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";
}
}
@@ -0,0 +1,8 @@
namespace app.Configuration
{
public class DatabaseSettings
{
public string ConnectionString { get; set; }
public string Credentials { get; set; }
}
}
@@ -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; }
}
}
@@ -0,0 +1,7 @@
namespace app.Configuration
{
public class FrontendSettings
{
public string URL { get; set; }
}
}
@@ -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);
}
}
}
}
@@ -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>
@@ -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};";
}
}
}
@@ -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;
}
}
}
@@ -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>
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -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
}
}
}
@@ -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");
}
}
}
@@ -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
}
}
}
@@ -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");
}
}
}
@@ -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
}
}
}
@@ -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);
}
}
}
@@ -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
}
}
}
@@ -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);
}
}
}
@@ -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>
@@ -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);
}
}
@@ -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();
}
}
@@ -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);
}
}
@@ -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>
@@ -0,0 +1,10 @@
using System;
namespace app.Model
{
public enum AuthRoles
{
Normal = 0,
Admin = 1
}
}
@@ -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
};
}
}
}
@@ -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; }
}
}
@@ -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; }
}
}
@@ -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
};
}
}
}
@@ -0,0 +1,9 @@
using System;
namespace app.Model.DTOs
{
public class EMailStringDTO
{
public string EMail { get; set; }
}
}
@@ -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; }
}
}
@@ -0,0 +1,10 @@
using System;
namespace app.Model.DTOs
{
public class ResetPasswordDTO
{
public string Id { get; set; }
public string Password { get; set; }
}
}
@@ -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; }
}
}
@@ -0,0 +1,8 @@
namespace app.Model.DTOs
{
public class TokenDTO
{
public string Token { get; set; }
public string RefreshToken { get; set; }
}
}
@@ -0,0 +1,10 @@
using System;
namespace app.Model.DTOs
{
public class UpdateUserDTO
{
public AuthUserDTO AuthUserDTO { get; set; }
public AuthUserDTO NewAuthUserDTO { get; set; }
}
}
@@ -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; }
}
}
@@ -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;
}
}
}
@@ -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
}
}
@@ -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}";
}
}
}
@@ -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; }
}
}
@@ -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>
@@ -0,0 +1,10 @@
using System.Threading.Tasks;
using app.SMTP.Model;
namespace app.SMTP.Interface
{
public interface ISMTPClient
{
Task SendEmailAsync(EMail email);
}
}
@@ -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>
@@ -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; }
}
}
@@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
</Project>
@@ -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");
}
}
}
}
}
@@ -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>
@@ -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;
}
}
}
}
@@ -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>
@@ -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; }
}
}
@@ -0,0 +1,8 @@
using System;
namespace app.Share.Common
{
public class DomainSelectCriterion : SelectCriterion
{
public string Name { get; set; }
}
}
@@ -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; }
}
}
@@ -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; }
}
}
@@ -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; }
}
}
@@ -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; }
}
}
@@ -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>
@@ -0,0 +1,9 @@
using System;
using Microsoft.AspNetCore.SignalR;
namespace app.SignalR
{
public class NotifyHub : Hub
{
}
}
@@ -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>
+127
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
@@ -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);
}
}
}
@@ -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"
});
}
}
}
+76
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);
}
}
}
@@ -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;
}
}
}
@@ -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;
}
}
}
@@ -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)
{
}
}
}
+43
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");
});
}
}
@@ -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"
}
}
}
+186
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();
}
}
}
}
+43
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>
@@ -0,0 +1,13 @@
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Debug"
}
},
"Database": {
"ConnectionString": ""
}
}
@@ -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": "*"
}
@@ -0,0 +1,13 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"Database": {
"ConnectionString": ""
}
}
@@ -0,0 +1,13 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"Database": {
"ConnectionString": ""
}
}
@@ -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>
@@ -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>
@@ -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>
@@ -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]
+18
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.
+16
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
+27
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).
+140
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"
}
}
@@ -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
}
}));
}
};
@@ -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));
});
});
+11
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>;
}
}
+14
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"
]
}
}
+32
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
});
};
File diff suppressed because it is too large Load Diff
+58
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"
}
}
@@ -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 { }
@@ -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>
@@ -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