From 1c61f5ec63e2377cd06439d9b053ab54125fe861 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rich=C3=A1rd=20Kunkli?= Date: Sun, 25 Oct 2020 16:15:06 +0100 Subject: [PATCH] Added NLog, Added UserService, Added database and seed seed --- Birdmap.BLL/Birdmap.BLL.csproj | 1 + .../Exceptions/AuthenticationException.cs | 14 +++ .../Exceptions/EntityNotFoundException.cs | 25 ++++ Birdmap.BLL/Interfaces/IAuthService.cs | 1 + Birdmap.BLL/Interfaces/IUserService.cs | 15 +++ Birdmap.BLL/Services/AuthService.cs | 68 ++++------- Birdmap.BLL/Services/UserService.cs | 66 +++++++++++ Birdmap.BLL/Startup.cs | 1 + Birdmap.Common/Birdmap.Common.csproj | 7 ++ Birdmap.Common/PasswordHelper.cs | 35 ++++++ Birdmap.DAL/Birdmap.DAL.csproj | 8 ++ Birdmap.DAL/BirdmapContext.cs | 9 +- .../Configurations/ServiceConfiguration.cs | 33 ++++++ .../Configurations/UserConfiguration.cs | 24 ++++ Birdmap.DAL/Entities/Service.cs | 11 ++ .../20201025144409_InitialCreate.Designer.cs | 108 ++++++++++++++++++ .../20201025144409_InitialCreate.cs | 74 ++++++++++++ .../Migrations/BirdmapContextModelSnapshot.cs | 106 +++++++++++++++++ Birdmap.DAL/Startup.cs | 3 - Birdmap.sln | 10 +- Birdmap/Birdmap.API.csproj | 6 + Birdmap/Controllers/AuthController.cs | 22 +++- Birdmap/DTOs/RegisterRequest.cs | 17 +++ .../Middlewares/ExceptionHandlerMiddleware.cs | 56 +++++++++ Birdmap/Pages/Error.cshtml | 26 ----- Birdmap/Pages/Error.cshtml.cs | 30 ----- Birdmap/Pages/_ViewImports.cshtml | 3 - Birdmap/Program.cs | 33 ++++-- Birdmap/Startup.cs | 10 +- Birdmap/appsettings.json | 21 +--- Birdmap/nlog.config | 35 ++++++ 31 files changed, 728 insertions(+), 150 deletions(-) create mode 100644 Birdmap.BLL/Exceptions/EntityNotFoundException.cs create mode 100644 Birdmap.BLL/Interfaces/IUserService.cs create mode 100644 Birdmap.BLL/Services/UserService.cs create mode 100644 Birdmap.Common/Birdmap.Common.csproj create mode 100644 Birdmap.Common/PasswordHelper.cs create mode 100644 Birdmap.DAL/Entities/Configurations/ServiceConfiguration.cs create mode 100644 Birdmap.DAL/Entities/Service.cs create mode 100644 Birdmap.DAL/Migrations/20201025144409_InitialCreate.Designer.cs create mode 100644 Birdmap.DAL/Migrations/20201025144409_InitialCreate.cs create mode 100644 Birdmap.DAL/Migrations/BirdmapContextModelSnapshot.cs create mode 100644 Birdmap/DTOs/RegisterRequest.cs create mode 100644 Birdmap/Middlewares/ExceptionHandlerMiddleware.cs delete mode 100644 Birdmap/Pages/Error.cshtml delete mode 100644 Birdmap/Pages/Error.cshtml.cs delete mode 100644 Birdmap/Pages/_ViewImports.cshtml create mode 100644 Birdmap/nlog.config diff --git a/Birdmap.BLL/Birdmap.BLL.csproj b/Birdmap.BLL/Birdmap.BLL.csproj index 8191252..32cb887 100644 --- a/Birdmap.BLL/Birdmap.BLL.csproj +++ b/Birdmap.BLL/Birdmap.BLL.csproj @@ -5,6 +5,7 @@ + diff --git a/Birdmap.BLL/Exceptions/AuthenticationException.cs b/Birdmap.BLL/Exceptions/AuthenticationException.cs index 04ccb33..b0b2636 100644 --- a/Birdmap.BLL/Exceptions/AuthenticationException.cs +++ b/Birdmap.BLL/Exceptions/AuthenticationException.cs @@ -1,12 +1,26 @@ using System; +using System.Runtime.Serialization; namespace Birdmap.BLL.Exceptions { + [Serializable] public class AuthenticationException : Exception { public AuthenticationException() : base("Username or password is incorrect.") { } + + public AuthenticationException(string message) : this() + { + } + + public AuthenticationException(string message, Exception innerException) : this() + { + } + + protected AuthenticationException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } } } diff --git a/Birdmap.BLL/Exceptions/EntityNotFoundException.cs b/Birdmap.BLL/Exceptions/EntityNotFoundException.cs new file mode 100644 index 0000000..a0e7177 --- /dev/null +++ b/Birdmap.BLL/Exceptions/EntityNotFoundException.cs @@ -0,0 +1,25 @@ +using System; +using System.Runtime.Serialization; + +namespace Birdmap.BLL.Exceptions +{ + [Serializable] + public class EntityNotFoundException : Exception + { + public EntityNotFoundException() + { + } + + public EntityNotFoundException(string message) : base(message) + { + } + + public EntityNotFoundException(string message, Exception innerException) : base(message, innerException) + { + } + + protected EntityNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/Birdmap.BLL/Interfaces/IAuthService.cs b/Birdmap.BLL/Interfaces/IAuthService.cs index 757b2ca..c7212d0 100644 --- a/Birdmap.BLL/Interfaces/IAuthService.cs +++ b/Birdmap.BLL/Interfaces/IAuthService.cs @@ -6,5 +6,6 @@ namespace Birdmap.BLL.Interfaces public interface IAuthService { Task AuthenticateUserAsync(string username, string password); + Task RegisterUserAsync(string username, string password); } } diff --git a/Birdmap.BLL/Interfaces/IUserService.cs b/Birdmap.BLL/Interfaces/IUserService.cs new file mode 100644 index 0000000..4c4e4f7 --- /dev/null +++ b/Birdmap.BLL/Interfaces/IUserService.cs @@ -0,0 +1,15 @@ +using Birdmap.DAL.Entities; +using System.Threading.Tasks; + +namespace Birdmap.BLL.Interfaces +{ + public interface IUserService + { + Task GetUserAsync(int userId); + Task GetUserAsync(string username); + Task CreateUserAsync(User user); + Task UpdateUserAsync(User user); + Task DeleteUserAsync(int userId); + Task DeleteUserAsync(string username); + } +} diff --git a/Birdmap.BLL/Services/AuthService.cs b/Birdmap.BLL/Services/AuthService.cs index 7e73b51..76e1bbe 100644 --- a/Birdmap.BLL/Services/AuthService.cs +++ b/Birdmap.BLL/Services/AuthService.cs @@ -1,24 +1,19 @@ -using Birdmap.DAL.Entities; -using Birdmap.BLL.Interfaces; -using Microsoft.Extensions.Configuration; +using Birdmap.BLL.Interfaces; +using Birdmap.DAL.Entities; using System; -using System.Collections.Generic; -using System.Linq; using System.Security.Authentication; -using System.Text; using System.Threading.Tasks; -using Birdmap.DAL; -using Microsoft.EntityFrameworkCore; +using static Birdmap.Common.PasswordHelper; namespace Birdmap.BLL.Services { public class AuthService : IAuthService { - private readonly BirdmapContext _context; + private readonly IUserService _userService; - public AuthService(BirdmapContext context) + public AuthService(IUserService userService) { - _context = context; + _userService = userService; } public Task AuthenticateUserAsync(string username, string password) @@ -29,9 +24,17 @@ namespace Birdmap.BLL.Services return AuthenticateUserInternalAsync(username, password); } + public Task RegisterUserAsync(string username, string password) + { + if (string.IsNullOrWhiteSpace(username) || string.IsNullOrEmpty(password)) + throw new ArgumentException("Username or password cannot be null or empty."); + + return RegisterUserInternalAsync(username, password); + } + private async Task AuthenticateUserInternalAsync(string username, string password) { - var user = await _context.Users.SingleOrDefaultAsync(u => u.Name == username) + var user = await _userService.GetUserAsync(username) ?? throw new AuthenticationException(); if (!VerifyPasswordHash(password, user.PasswordHash, user.PasswordSalt)) @@ -40,45 +43,18 @@ namespace Birdmap.BLL.Services return user; } - private Task Temp_GetUserAsync(IConfiguration configuration) + private Task RegisterUserInternalAsync(string username, string password) { - var name = configuration["BasicAuth:Username"]; - var pass = configuration["BasicAuth:Password"]; - - CreatePasswordHash(pass, out var hash, out var salt); - return Task.FromResult(new User + CreatePasswordHash(password, out var hash, out var salt); + var user = new User { - Name = name, + Name = username, PasswordHash = hash, PasswordSalt = salt, - }); - } + Role = Roles.User, + }; - private static void CreatePasswordHash(string password, out byte[] passwordHash, out byte[] passwordSalt) - { - if (string.IsNullOrWhiteSpace(password)) throw new ArgumentException("Value cannot be null or empty.", "password"); - - using var hmac = new System.Security.Cryptography.HMACSHA512(); - - passwordSalt = hmac.Key; - passwordHash = hmac.ComputeHash(Encoding.UTF8.GetBytes(password)); - } - - private static bool VerifyPasswordHash(string password, byte[] storedHash, byte[] storedSalt) - { - if (string.IsNullOrWhiteSpace(password)) throw new ArgumentException("Value cannot be null or empty.", nameof(password)); - if (storedHash.Length != 64) throw new ArgumentException("Invalid length of password hash (64 bytes expected).", nameof(storedHash)); - if (storedSalt.Length != 128) throw new ArgumentException("Invalid length of password salt (128 bytes expected).", nameof(storedSalt)); - - using var hmac = new System.Security.Cryptography.HMACSHA512(storedSalt); - - var computedHash = hmac.ComputeHash(Encoding.UTF8.GetBytes(password)); - for (int i = 0; i < computedHash.Length; i++) - { - if (computedHash[i] != storedHash[i]) return false; - } - - return true; + return _userService.CreateUserAsync(user); } } } diff --git a/Birdmap.BLL/Services/UserService.cs b/Birdmap.BLL/Services/UserService.cs new file mode 100644 index 0000000..2a1a755 --- /dev/null +++ b/Birdmap.BLL/Services/UserService.cs @@ -0,0 +1,66 @@ +using Birdmap.BLL.Exceptions; +using Birdmap.BLL.Interfaces; +using Birdmap.DAL; +using Birdmap.DAL.Entities; +using Microsoft.EntityFrameworkCore; +using System.Threading.Tasks; + +namespace Birdmap.BLL.Services +{ + public class UserService : IUserService + { + private readonly BirdmapContext _context; + + public UserService(BirdmapContext context) + { + _context = context; + } + + public async Task CreateUserAsync(User user) + { + _context.Users.Add(user); + await _context.SaveChangesAsync(); + + return user; + } + + public Task DeleteUserAsync(int userId) + { + return DeleteUserInternalAsync(userId); + } + + public Task DeleteUserAsync(string username) + { + return DeleteUserInternalAsync(username); + } + + public async Task GetUserAsync(int userId) + { + return await _context.Users.SingleOrDefaultAsync(u => u.Id == userId) + ?? throw new EntityNotFoundException($"Cannot find user with user ID: {userId}"); + } + + public async Task GetUserAsync(string username) + { + return await _context.Users.SingleOrDefaultAsync(u => u.Name == username) + ?? throw new EntityNotFoundException($"Cannot find user with username: {username}"); + } + + public Task UpdateUserAsync(User user) + { + _context.Users.Update(user); + return _context.SaveChangesAsync(); + } + + private Task DeleteUserInternalAsync(object key) + { + var user = _context.Users.Find(key); + + if (user == null) + return Task.CompletedTask; + + _context.Users.Remove(user); + return _context.SaveChangesAsync(); + } + } +} diff --git a/Birdmap.BLL/Startup.cs b/Birdmap.BLL/Startup.cs index 253554a..e6f3dd3 100644 --- a/Birdmap.BLL/Startup.cs +++ b/Birdmap.BLL/Startup.cs @@ -10,6 +10,7 @@ namespace Birdmap.BLL public static IServiceCollection ConfigureBLL(this IServiceCollection services, IConfiguration configuration) { services.AddTransient(); + services.AddTransient(); return services; } diff --git a/Birdmap.Common/Birdmap.Common.csproj b/Birdmap.Common/Birdmap.Common.csproj new file mode 100644 index 0000000..cb63190 --- /dev/null +++ b/Birdmap.Common/Birdmap.Common.csproj @@ -0,0 +1,7 @@ + + + + netcoreapp3.1 + + + diff --git a/Birdmap.Common/PasswordHelper.cs b/Birdmap.Common/PasswordHelper.cs new file mode 100644 index 0000000..1227e49 --- /dev/null +++ b/Birdmap.Common/PasswordHelper.cs @@ -0,0 +1,35 @@ +using System; +using System.Text; + +namespace Birdmap.Common +{ + public static class PasswordHelper + { + public static void CreatePasswordHash(string password, out byte[] passwordHash, out byte[] passwordSalt) + { + if (string.IsNullOrWhiteSpace(password)) throw new ArgumentException("Value cannot be null or empty.", "password"); + + using var hmac = new System.Security.Cryptography.HMACSHA512(); + + passwordSalt = hmac.Key; + passwordHash = hmac.ComputeHash(Encoding.UTF8.GetBytes(password)); + } + + public static bool VerifyPasswordHash(string password, byte[] storedHash, byte[] storedSalt) + { + if (string.IsNullOrWhiteSpace(password)) throw new ArgumentException("Value cannot be null or empty.", nameof(password)); + if (storedHash.Length != 64) throw new ArgumentException("Invalid length of password hash (64 bytes expected).", nameof(storedHash)); + if (storedSalt.Length != 128) throw new ArgumentException("Invalid length of password salt (128 bytes expected).", nameof(storedSalt)); + + using var hmac = new System.Security.Cryptography.HMACSHA512(storedSalt); + + var computedHash = hmac.ComputeHash(Encoding.UTF8.GetBytes(password)); + for (int i = 0; i < computedHash.Length; i++) + { + if (computedHash[i] != storedHash[i]) return false; + } + + return true; + } + } +} diff --git a/Birdmap.DAL/Birdmap.DAL.csproj b/Birdmap.DAL/Birdmap.DAL.csproj index 2dfa7bf..9e084dd 100644 --- a/Birdmap.DAL/Birdmap.DAL.csproj +++ b/Birdmap.DAL/Birdmap.DAL.csproj @@ -7,7 +7,15 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + diff --git a/Birdmap.DAL/BirdmapContext.cs b/Birdmap.DAL/BirdmapContext.cs index e4cfce3..39f27fb 100644 --- a/Birdmap.DAL/BirdmapContext.cs +++ b/Birdmap.DAL/BirdmapContext.cs @@ -8,20 +8,13 @@ namespace Birdmap.DAL { public DbSet Users { get; set; } - public BirdmapContext([NotNull] DbContextOptions options) : base(options) + public BirdmapContext([NotNull] DbContextOptions options) : base(options) { } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.ApplyConfigurationsFromAssembly(typeof(BirdmapContext).Assembly); - - SeedDatabase(modelBuilder); - } - - private void SeedDatabase(ModelBuilder modelBuilder) - { - } } } diff --git a/Birdmap.DAL/Entities/Configurations/ServiceConfiguration.cs b/Birdmap.DAL/Entities/Configurations/ServiceConfiguration.cs new file mode 100644 index 0000000..eee33f5 --- /dev/null +++ b/Birdmap.DAL/Entities/Configurations/ServiceConfiguration.cs @@ -0,0 +1,33 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using System; + +namespace Birdmap.DAL.Entities.Configurations +{ + public class ServiceConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.Property(s => s.Name) + .IsRequired(); + + builder.Property(s => s.Uri) + .HasConversion(u => u.ToString(), u => new Uri(u)) + .IsRequired(); + + builder.HasData( + new Service + { + Id = 1, + Name = "KMLabz services", + Uri = new Uri("https://birb.k8s.kmlabz.com/devices") + }, + new Service + { + Id = 2, + Name = "Local Database", + Uri = new Uri("/health", UriKind.Relative) + }); + } + } +} diff --git a/Birdmap.DAL/Entities/Configurations/UserConfiguration.cs b/Birdmap.DAL/Entities/Configurations/UserConfiguration.cs index fb2b6df..2a2928b 100644 --- a/Birdmap.DAL/Entities/Configurations/UserConfiguration.cs +++ b/Birdmap.DAL/Entities/Configurations/UserConfiguration.cs @@ -1,5 +1,6 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; +using static Birdmap.Common.PasswordHelper; namespace Birdmap.DAL.Entities.Configurations { @@ -7,6 +8,9 @@ namespace Birdmap.DAL.Entities.Configurations { public void Configure(EntityTypeBuilder builder) { + builder.HasIndex(u => u.Name) + .IsUnique(); + builder.Property(u => u.Name) .IsRequired(); @@ -18,6 +22,26 @@ namespace Birdmap.DAL.Entities.Configurations builder.Property(u => u.Role) .IsRequired(); + + CreatePasswordHash("pass", out var hash, out var salt); + + builder.HasData( + new User + { + Id = 1, + Name = "admin", + PasswordHash = hash, + PasswordSalt = salt, + Role = Roles.Admin, + }, + new User + { + Id = 2, + Name = "user", + PasswordHash = hash, + PasswordSalt = salt, + Role = Roles.User, + }); } } } diff --git a/Birdmap.DAL/Entities/Service.cs b/Birdmap.DAL/Entities/Service.cs new file mode 100644 index 0000000..ba7e747 --- /dev/null +++ b/Birdmap.DAL/Entities/Service.cs @@ -0,0 +1,11 @@ +using System; + +namespace Birdmap.DAL.Entities +{ + public class Service + { + public int Id { get; set; } + public string Name { get; set; } + public Uri Uri { get; set; } + } +} diff --git a/Birdmap.DAL/Migrations/20201025144409_InitialCreate.Designer.cs b/Birdmap.DAL/Migrations/20201025144409_InitialCreate.Designer.cs new file mode 100644 index 0000000..a5add08 --- /dev/null +++ b/Birdmap.DAL/Migrations/20201025144409_InitialCreate.Designer.cs @@ -0,0 +1,108 @@ +// +using System; +using Birdmap.DAL; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace Birdmap.DAL.Migrations +{ + [DbContext(typeof(BirdmapContext))] + [Migration("20201025144409_InitialCreate")] + partial class InitialCreate + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "3.1.9") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Birdmap.DAL.Entities.Service", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Uri") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Service"); + + b.HasData( + new + { + Id = 1, + Name = "KMLabz services", + Uri = "https://birb.k8s.kmlabz.com/devices" + }, + new + { + Id = 2, + Name = "Local Database", + Uri = "/health" + }); + }); + + modelBuilder.Entity("Birdmap.DAL.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("PasswordSalt") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("Role") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("Users"); + + b.HasData( + new + { + Id = 1, + Name = "admin", + PasswordHash = new byte[] { 182, 156, 97, 101, 71, 25, 1, 147, 227, 96, 95, 0, 109, 186, 93, 70, 175, 182, 11, 77, 231, 91, 77, 46, 38, 241, 58, 9, 217, 227, 116, 97, 153, 200, 188, 237, 136, 224, 209, 148, 211, 183, 193, 59, 248, 219, 7, 64, 215, 234, 35, 102, 226, 124, 253, 113, 109, 120, 48, 158, 62, 75, 49, 25 }, + PasswordSalt = new byte[] { 113, 114, 194, 22, 187, 116, 36, 33, 217, 100, 136, 225, 215, 1, 130, 131, 192, 198, 22, 50, 117, 244, 84, 85, 171, 4, 191, 0, 110, 145, 128, 150, 48, 226, 10, 149, 26, 111, 235, 125, 150, 120, 1, 168, 228, 110, 171, 2, 202, 54, 48, 214, 65, 75, 62, 245, 108, 82, 23, 226, 118, 218, 182, 177, 223, 124, 238, 67, 191, 103, 17, 159, 139, 44, 33, 62, 183, 22, 8, 193, 222, 60, 119, 240, 85, 179, 3, 35, 125, 23, 123, 232, 214, 193, 190, 13, 53, 131, 126, 124, 15, 238, 179, 202, 98, 173, 58, 29, 118, 253, 35, 34, 80, 79, 61, 185, 226, 99, 123, 195, 50, 46, 101, 45, 246, 1, 152, 61 }, + Role = 1 + }, + new + { + Id = 2, + Name = "user", + PasswordHash = new byte[] { 182, 156, 97, 101, 71, 25, 1, 147, 227, 96, 95, 0, 109, 186, 93, 70, 175, 182, 11, 77, 231, 91, 77, 46, 38, 241, 58, 9, 217, 227, 116, 97, 153, 200, 188, 237, 136, 224, 209, 148, 211, 183, 193, 59, 248, 219, 7, 64, 215, 234, 35, 102, 226, 124, 253, 113, 109, 120, 48, 158, 62, 75, 49, 25 }, + PasswordSalt = new byte[] { 113, 114, 194, 22, 187, 116, 36, 33, 217, 100, 136, 225, 215, 1, 130, 131, 192, 198, 22, 50, 117, 244, 84, 85, 171, 4, 191, 0, 110, 145, 128, 150, 48, 226, 10, 149, 26, 111, 235, 125, 150, 120, 1, 168, 228, 110, 171, 2, 202, 54, 48, 214, 65, 75, 62, 245, 108, 82, 23, 226, 118, 218, 182, 177, 223, 124, 238, 67, 191, 103, 17, 159, 139, 44, 33, 62, 183, 22, 8, 193, 222, 60, 119, 240, 85, 179, 3, 35, 125, 23, 123, 232, 214, 193, 190, 13, 53, 131, 126, 124, 15, 238, 179, 202, 98, 173, 58, 29, 118, 253, 35, 34, 80, 79, 61, 185, 226, 99, 123, 195, 50, 46, 101, 45, 246, 1, 152, 61 }, + Role = 0 + }); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Birdmap.DAL/Migrations/20201025144409_InitialCreate.cs b/Birdmap.DAL/Migrations/20201025144409_InitialCreate.cs new file mode 100644 index 0000000..2a011cd --- /dev/null +++ b/Birdmap.DAL/Migrations/20201025144409_InitialCreate.cs @@ -0,0 +1,74 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Birdmap.DAL.Migrations +{ + public partial class InitialCreate : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Service", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + Name = table.Column(nullable: false), + Uri = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Service", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Users", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + Name = table.Column(nullable: false), + PasswordHash = table.Column(nullable: false), + PasswordSalt = table.Column(nullable: false), + Role = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Users", x => x.Id); + }); + + migrationBuilder.InsertData( + table: "Service", + columns: new[] { "Id", "Name", "Uri" }, + values: new object[,] + { + { 1, "KMLabz services", "https://birb.k8s.kmlabz.com/devices" }, + { 2, "Local Database", "/health" } + }); + + migrationBuilder.InsertData( + table: "Users", + columns: new[] { "Id", "Name", "PasswordHash", "PasswordSalt", "Role" }, + values: new object[,] + { + { 1, "admin", new byte[] { 182, 156, 97, 101, 71, 25, 1, 147, 227, 96, 95, 0, 109, 186, 93, 70, 175, 182, 11, 77, 231, 91, 77, 46, 38, 241, 58, 9, 217, 227, 116, 97, 153, 200, 188, 237, 136, 224, 209, 148, 211, 183, 193, 59, 248, 219, 7, 64, 215, 234, 35, 102, 226, 124, 253, 113, 109, 120, 48, 158, 62, 75, 49, 25 }, new byte[] { 113, 114, 194, 22, 187, 116, 36, 33, 217, 100, 136, 225, 215, 1, 130, 131, 192, 198, 22, 50, 117, 244, 84, 85, 171, 4, 191, 0, 110, 145, 128, 150, 48, 226, 10, 149, 26, 111, 235, 125, 150, 120, 1, 168, 228, 110, 171, 2, 202, 54, 48, 214, 65, 75, 62, 245, 108, 82, 23, 226, 118, 218, 182, 177, 223, 124, 238, 67, 191, 103, 17, 159, 139, 44, 33, 62, 183, 22, 8, 193, 222, 60, 119, 240, 85, 179, 3, 35, 125, 23, 123, 232, 214, 193, 190, 13, 53, 131, 126, 124, 15, 238, 179, 202, 98, 173, 58, 29, 118, 253, 35, 34, 80, 79, 61, 185, 226, 99, 123, 195, 50, 46, 101, 45, 246, 1, 152, 61 }, 1 }, + { 2, "user", new byte[] { 182, 156, 97, 101, 71, 25, 1, 147, 227, 96, 95, 0, 109, 186, 93, 70, 175, 182, 11, 77, 231, 91, 77, 46, 38, 241, 58, 9, 217, 227, 116, 97, 153, 200, 188, 237, 136, 224, 209, 148, 211, 183, 193, 59, 248, 219, 7, 64, 215, 234, 35, 102, 226, 124, 253, 113, 109, 120, 48, 158, 62, 75, 49, 25 }, new byte[] { 113, 114, 194, 22, 187, 116, 36, 33, 217, 100, 136, 225, 215, 1, 130, 131, 192, 198, 22, 50, 117, 244, 84, 85, 171, 4, 191, 0, 110, 145, 128, 150, 48, 226, 10, 149, 26, 111, 235, 125, 150, 120, 1, 168, 228, 110, 171, 2, 202, 54, 48, 214, 65, 75, 62, 245, 108, 82, 23, 226, 118, 218, 182, 177, 223, 124, 238, 67, 191, 103, 17, 159, 139, 44, 33, 62, 183, 22, 8, 193, 222, 60, 119, 240, 85, 179, 3, 35, 125, 23, 123, 232, 214, 193, 190, 13, 53, 131, 126, 124, 15, 238, 179, 202, 98, 173, 58, 29, 118, 253, 35, 34, 80, 79, 61, 185, 226, 99, 123, 195, 50, 46, 101, 45, 246, 1, 152, 61 }, 0 } + }); + + migrationBuilder.CreateIndex( + name: "IX_Users_Name", + table: "Users", + column: "Name", + unique: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Service"); + + migrationBuilder.DropTable( + name: "Users"); + } + } +} diff --git a/Birdmap.DAL/Migrations/BirdmapContextModelSnapshot.cs b/Birdmap.DAL/Migrations/BirdmapContextModelSnapshot.cs new file mode 100644 index 0000000..c69d945 --- /dev/null +++ b/Birdmap.DAL/Migrations/BirdmapContextModelSnapshot.cs @@ -0,0 +1,106 @@ +// +using System; +using Birdmap.DAL; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace Birdmap.DAL.Migrations +{ + [DbContext(typeof(BirdmapContext))] + partial class BirdmapContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "3.1.9") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Birdmap.DAL.Entities.Service", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Uri") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Service"); + + b.HasData( + new + { + Id = 1, + Name = "KMLabz services", + Uri = "https://birb.k8s.kmlabz.com/devices" + }, + new + { + Id = 2, + Name = "Local Database", + Uri = "/health" + }); + }); + + modelBuilder.Entity("Birdmap.DAL.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("PasswordSalt") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("Role") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("Users"); + + b.HasData( + new + { + Id = 1, + Name = "admin", + PasswordHash = new byte[] { 182, 156, 97, 101, 71, 25, 1, 147, 227, 96, 95, 0, 109, 186, 93, 70, 175, 182, 11, 77, 231, 91, 77, 46, 38, 241, 58, 9, 217, 227, 116, 97, 153, 200, 188, 237, 136, 224, 209, 148, 211, 183, 193, 59, 248, 219, 7, 64, 215, 234, 35, 102, 226, 124, 253, 113, 109, 120, 48, 158, 62, 75, 49, 25 }, + PasswordSalt = new byte[] { 113, 114, 194, 22, 187, 116, 36, 33, 217, 100, 136, 225, 215, 1, 130, 131, 192, 198, 22, 50, 117, 244, 84, 85, 171, 4, 191, 0, 110, 145, 128, 150, 48, 226, 10, 149, 26, 111, 235, 125, 150, 120, 1, 168, 228, 110, 171, 2, 202, 54, 48, 214, 65, 75, 62, 245, 108, 82, 23, 226, 118, 218, 182, 177, 223, 124, 238, 67, 191, 103, 17, 159, 139, 44, 33, 62, 183, 22, 8, 193, 222, 60, 119, 240, 85, 179, 3, 35, 125, 23, 123, 232, 214, 193, 190, 13, 53, 131, 126, 124, 15, 238, 179, 202, 98, 173, 58, 29, 118, 253, 35, 34, 80, 79, 61, 185, 226, 99, 123, 195, 50, 46, 101, 45, 246, 1, 152, 61 }, + Role = 1 + }, + new + { + Id = 2, + Name = "user", + PasswordHash = new byte[] { 182, 156, 97, 101, 71, 25, 1, 147, 227, 96, 95, 0, 109, 186, 93, 70, 175, 182, 11, 77, 231, 91, 77, 46, 38, 241, 58, 9, 217, 227, 116, 97, 153, 200, 188, 237, 136, 224, 209, 148, 211, 183, 193, 59, 248, 219, 7, 64, 215, 234, 35, 102, 226, 124, 253, 113, 109, 120, 48, 158, 62, 75, 49, 25 }, + PasswordSalt = new byte[] { 113, 114, 194, 22, 187, 116, 36, 33, 217, 100, 136, 225, 215, 1, 130, 131, 192, 198, 22, 50, 117, 244, 84, 85, 171, 4, 191, 0, 110, 145, 128, 150, 48, 226, 10, 149, 26, 111, 235, 125, 150, 120, 1, 168, 228, 110, 171, 2, 202, 54, 48, 214, 65, 75, 62, 245, 108, 82, 23, 226, 118, 218, 182, 177, 223, 124, 238, 67, 191, 103, 17, 159, 139, 44, 33, 62, 183, 22, 8, 193, 222, 60, 119, 240, 85, 179, 3, 35, 125, 23, 123, 232, 214, 193, 190, 13, 53, 131, 126, 124, 15, 238, 179, 202, 98, 173, 58, 29, 118, 253, 35, 34, 80, 79, 61, 185, 226, 99, 123, 195, 50, 46, 101, 45, 246, 1, 152, 61 }, + Role = 0 + }); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Birdmap.DAL/Startup.cs b/Birdmap.DAL/Startup.cs index 31e52fa..dcc6cc6 100644 --- a/Birdmap.DAL/Startup.cs +++ b/Birdmap.DAL/Startup.cs @@ -1,9 +1,6 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using System; -using System.Collections.Generic; -using System.Text; namespace Birdmap.DAL { diff --git a/Birdmap.sln b/Birdmap.sln index 2c40501..009cd6b 100644 --- a/Birdmap.sln +++ b/Birdmap.sln @@ -5,9 +5,11 @@ VisualStudioVersion = 16.0.30611.23 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Birdmap.API", "Birdmap\Birdmap.API.csproj", "{88855E5F-9555-49E5-92F2-4E8C1194F60B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Birdmap.BLL", "Birdmap.BLL\Birdmap.BLL.csproj", "{879D7B8D-6865-4EBE-B346-E0CA37D3C06A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Birdmap.BLL", "Birdmap.BLL\Birdmap.BLL.csproj", "{879D7B8D-6865-4EBE-B346-E0CA37D3C06A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Birdmap.DAL", "Birdmap.DAL\Birdmap.DAL.csproj", "{543FAB06-B960-41A9-8865-1624A2ED2170}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Birdmap.DAL", "Birdmap.DAL\Birdmap.DAL.csproj", "{543FAB06-B960-41A9-8865-1624A2ED2170}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Birdmap.Common", "Birdmap.Common\Birdmap.Common.csproj", "{CE96BAFA-A0FD-4010-8EF2-700451091F71}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -27,6 +29,10 @@ Global {543FAB06-B960-41A9-8865-1624A2ED2170}.Debug|Any CPU.Build.0 = Debug|Any CPU {543FAB06-B960-41A9-8865-1624A2ED2170}.Release|Any CPU.ActiveCfg = Release|Any CPU {543FAB06-B960-41A9-8865-1624A2ED2170}.Release|Any CPU.Build.0 = Release|Any CPU + {CE96BAFA-A0FD-4010-8EF2-700451091F71}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CE96BAFA-A0FD-4010-8EF2-700451091F71}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CE96BAFA-A0FD-4010-8EF2-700451091F71}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CE96BAFA-A0FD-4010-8EF2-700451091F71}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Birdmap/Birdmap.API.csproj b/Birdmap/Birdmap.API.csproj index 2ff7ec8..1ef8321 100644 --- a/Birdmap/Birdmap.API.csproj +++ b/Birdmap/Birdmap.API.csproj @@ -11,12 +11,18 @@ + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + all runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/Birdmap/Controllers/AuthController.cs b/Birdmap/Controllers/AuthController.cs index 48aee62..76d6872 100644 --- a/Birdmap/Controllers/AuthController.cs +++ b/Birdmap/Controllers/AuthController.cs @@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; using Microsoft.IdentityModel.Tokens; using System; using System.IdentityModel.Tokens.Jwt; @@ -23,12 +24,14 @@ namespace Birdmap.Controllers private readonly IAuthService _service; private readonly IConfiguration _configuration; private readonly IMapper _mapper; + private readonly ILogger _logger; - public AuthController(IAuthService service, IConfiguration configuration, IMapper mapper) + public AuthController(IAuthService service, IConfiguration configuration, IMapper mapper, ILogger logger) { _service = service; _configuration = configuration; _mapper = mapper; + _logger = logger; } [AllowAnonymous] @@ -36,7 +39,12 @@ namespace Birdmap.Controllers [ProducesResponseType(typeof(object), StatusCodes.Status200OK)] public async Task AuthenticateAsync([FromBody] AuthenticateRequest model) { + _logger.LogInformation($"Authenticating user [{model.Username}] with password [*******]..."); + var user = await _service.AuthenticateUserAsync(model.Username, model.Password); + + _logger.LogInformation($"Authenticated user [{user.Name}]. Returning token..."); + var expiresInSeconds = TimeSpan.FromHours(2).TotalSeconds; var tokenHandler = new JwtSecurityTokenHandler(); var key = Encoding.ASCII.GetBytes(_configuration["Secret"]); @@ -60,5 +68,17 @@ namespace Birdmap.Controllers return Ok(response); } + + [AllowAnonymous] + [HttpPost("register")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + public async Task RegisterAsync([FromBody] RegisterRequest model) + { + _logger.LogInformation($"Registering user [{model.Username}]..."); + var created = await _service.RegisterUserAsync(model.Username, model.Password); + + _logger.LogInformation($"Registered user [{created.Id}."); + return NoContent(); + } } } diff --git a/Birdmap/DTOs/RegisterRequest.cs b/Birdmap/DTOs/RegisterRequest.cs new file mode 100644 index 0000000..a6c42d5 --- /dev/null +++ b/Birdmap/DTOs/RegisterRequest.cs @@ -0,0 +1,17 @@ +using System.ComponentModel.DataAnnotations; + +namespace Birdmap.API.DTOs +{ + public class RegisterRequest + { + [Required(AllowEmptyStrings = false, ErrorMessage = "Username is required.")] + public string Username { get; set; } + + [Required(AllowEmptyStrings = false, ErrorMessage = "Password is required."), DataType(DataType.Password)] + public string Password { get; set; } + + [Required(AllowEmptyStrings = false, ErrorMessage = "Please confirm password.")] + [DataType(DataType.Password), Compare(nameof(Password), ErrorMessage = "Passwords did not match.")] + public string ConfirmPassword { get; set; } + } +} diff --git a/Birdmap/Middlewares/ExceptionHandlerMiddleware.cs b/Birdmap/Middlewares/ExceptionHandlerMiddleware.cs new file mode 100644 index 0000000..70af1fd --- /dev/null +++ b/Birdmap/Middlewares/ExceptionHandlerMiddleware.cs @@ -0,0 +1,56 @@ +using Birdmap.BLL.Exceptions; +using Microsoft.AspNetCore.Http; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using System; +using System.Net; +using System.Threading.Tasks; + +namespace Birdmap.API.Middlewares +{ + public class ExceptionHandlerMiddleware + { + private readonly RequestDelegate _next; + private readonly ILogger _logger; + + public ExceptionHandlerMiddleware(RequestDelegate next, ILogger logger) + { + _next = next; + _logger = logger; + } + + public async Task Invoke(HttpContext context) + { + try + { + await _next(context); + } + catch (Exception ex) + { + await HandleException(context, ex); + } + } + + private Task HandleException(HttpContext context, Exception ex) + { + _logger.LogWarning(ex, ""); + + var code = ex switch + { + AuthenticationException _ => HttpStatusCode.Unauthorized, + EntityNotFoundException _ => HttpStatusCode.NotFound, + ArgumentException _ => HttpStatusCode.BadRequest, + DbUpdateConcurrencyException _ => HttpStatusCode.Conflict, + _ => HttpStatusCode.InternalServerError, + }; + + var result = JsonConvert.SerializeObject(new { error = ex.Message, exception = ex.ToString() }); + + context.Response.ContentType = "application/json"; + context.Response.StatusCode = (int)code; + + return context.Response.WriteAsync(result); + } + } +} \ No newline at end of file diff --git a/Birdmap/Pages/Error.cshtml b/Birdmap/Pages/Error.cshtml deleted file mode 100644 index 6f92b95..0000000 --- a/Birdmap/Pages/Error.cshtml +++ /dev/null @@ -1,26 +0,0 @@ -@page -@model ErrorModel -@{ - ViewData["Title"] = "Error"; -} - -

Error.

-

An error occurred while processing your request.

- -@if (Model.ShowRequestId) -{ -

- Request ID: @Model.RequestId -

-} - -

Development Mode

-

- Swapping to the Development environment displays detailed information about the error that occurred. -

-

- The Development environment shouldn't be enabled for deployed applications. - It can result in displaying sensitive information from exceptions to end users. - For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development - and restarting the app. -

diff --git a/Birdmap/Pages/Error.cshtml.cs b/Birdmap/Pages/Error.cshtml.cs deleted file mode 100644 index 980f3df..0000000 --- a/Birdmap/Pages/Error.cshtml.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; - -namespace Birdmap.Pages -{ - [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] - public class ErrorModel : PageModel - { - private readonly ILogger logger; - - public ErrorModel(ILogger _logger) - { - logger = _logger; - } - public string RequestId { get; set; } - - public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); - - public void OnGet() - { - RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; - } - } -} diff --git a/Birdmap/Pages/_ViewImports.cshtml b/Birdmap/Pages/_ViewImports.cshtml deleted file mode 100644 index c37e33d..0000000 --- a/Birdmap/Pages/_ViewImports.cshtml +++ /dev/null @@ -1,3 +0,0 @@ -@using Birdmap -@namespace Birdmap.Pages -@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers diff --git a/Birdmap/Program.cs b/Birdmap/Program.cs index 9aa8ff0..9fea490 100644 --- a/Birdmap/Program.cs +++ b/Birdmap/Program.cs @@ -1,11 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using NLog; +using NLog.Web; +using System; namespace Birdmap { @@ -13,7 +11,22 @@ namespace Birdmap { public static void Main(string[] args) { - CreateHostBuilder(args).Build().Run(); + var logger = NLogBuilder.ConfigureNLog("NLog.config").GetCurrentClassLogger(); + + try + { + logger.Debug("Main called..."); + CreateHostBuilder(args).Build().Run(); + } + catch (Exception ex) + { + logger.Error(ex, "Exception occurred in Main."); + throw; + } + finally + { + LogManager.Shutdown(); + } } public static IHostBuilder CreateHostBuilder(string[] args) => @@ -21,6 +34,12 @@ namespace Birdmap .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); - }); + }).ConfigureLogging(logging => + { + logging.ClearProviders(); + logging.AddConsole(); + logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace); + }) + .UseNLog(); } } diff --git a/Birdmap/Startup.cs b/Birdmap/Startup.cs index 7683c3f..2043c0b 100644 --- a/Birdmap/Startup.cs +++ b/Birdmap/Startup.cs @@ -1,5 +1,6 @@ +using AutoMapper; +using Birdmap.API.Middlewares; using Birdmap.BLL; -using Birdmap.BLL.Interfaces; using Birdmap.DAL; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Builder; @@ -34,6 +35,8 @@ namespace Birdmap services.ConfigureBLL(Configuration); services.ConfigureDAL(Configuration); + services.AddAutoMapper(typeof(Startup)); + var key = Encoding.ASCII.GetBytes(Configuration["Secret"]); services.AddAuthentication(opt => { @@ -70,9 +73,7 @@ namespace Birdmap } else { - app.UseExceptionHandler("/Error"); - // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. - app.UseHsts(); + app.UseMiddleware(); } app.UseHttpsRedirection(); @@ -86,6 +87,7 @@ namespace Birdmap app.UseEndpoints(endpoints => { + endpoints.MapHealthChecks("/health").RequireAuthorization(); endpoints.MapControllers(); }); diff --git a/Birdmap/appsettings.json b/Birdmap/appsettings.json index f9d3958..d2e0577 100644 --- a/Birdmap/appsettings.json +++ b/Birdmap/appsettings.json @@ -8,24 +8,5 @@ }, "AllowedHosts": "*", "Secret": "7vj.3KW.hYE!}4u6", - "LocalDbConnectionString": null, - "Default": { - "Users": [ - { - "Username": "user", - "Password": "pass", - "Role": "User" - }, - { - "Username": "admin", - "Password": "pass", - "Role": "Admin" - } - ], - "Endpoints": [ - "", - "", - "" - ] - } + "LocalDbConnectionString": "Data Source=DESKTOP-A6JQ6B5\\SQLEXPRESS;Initial Catalog=Birdmap;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False" } diff --git a/Birdmap/nlog.config b/Birdmap/nlog.config new file mode 100644 index 0000000..2ad336d --- /dev/null +++ b/Birdmap/nlog.config @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file