diff --git a/.gitignore b/.gitignore index 572b177..8188d77 100644 --- a/.gitignore +++ b/.gitignore @@ -349,4 +349,4 @@ MigrationBackup/ # Ionide (cross platform F# VS Code tools) working folder .ionide/ -Natsecure.SocialGuard.Api/appsettings.Production.json +**/appsettings.Production.json diff --git a/Natsecure.SocialGuard.Api/Data/ApiDbContext.cs b/Natsecure.SocialGuard.Api/Data/ApiDbContext.cs deleted file mode 100644 index 898c8f8..0000000 --- a/Natsecure.SocialGuard.Api/Data/ApiDbContext.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Natsecure.SocialGuard.Api.Data.Models; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Natsecure.SocialGuard.Api.Data -{ - public class ApiDbContext : DbContext - { - public DbSet TrustlistUsers { get; set; } - - - public ApiDbContext(DbContextOptions options) : base(options) { } - -/* protected override void OnModelCreating(ModelBuilder modelBuilder) - { - base.OnModelCreating(modelBuilder); - } -*/ - } -} diff --git a/Natsecure.SocialGuard.Api/Data/Models/EscalationLevel.cs b/Natsecure.SocialGuard.Api/Data/Models/EscalationLevel.cs deleted file mode 100644 index 0642764..0000000 --- a/Natsecure.SocialGuard.Api/Data/Models/EscalationLevel.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Natsecure.SocialGuard.Api.Data.Models -{ - public enum EscalationLevel : byte - { - Neutral = 0, - Suspicious = 1, - Untrusted = 2, - Blacklisted = 3 - } -} diff --git a/Natsecure.SocialGuard.Api/Data/Models/TrustlistUser.cs b/Natsecure.SocialGuard.Api/Data/Models/TrustlistUser.cs deleted file mode 100644 index 1e647fc..0000000 --- a/Natsecure.SocialGuard.Api/Data/Models/TrustlistUser.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Threading.Tasks; - -namespace Natsecure.SocialGuard.Api.Data.Models -{ - public record TrustlistUser - { - [Key, DatabaseGenerated(DatabaseGeneratedOption.None)] - public ulong Id { get; init; } - - public DateTime EntryAt { get; set; } - - public DateTime LastEscalated { get; set; } - - [Required, Range(0, 3)] - public byte EscalationLevel { get; set; } - - [MinLength(5), MaxLength(2000)] - public string EscalationNote { get; set; } - } -} diff --git a/Natsecure.SocialGuard.Api/Migrations/20210120224703_InitialCreate.Designer.cs b/Natsecure.SocialGuard.Api/Migrations/20210120224703_InitialCreate.Designer.cs deleted file mode 100644 index e729020..0000000 --- a/Natsecure.SocialGuard.Api/Migrations/20210120224703_InitialCreate.Designer.cs +++ /dev/null @@ -1,48 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Natsecure.SocialGuard.Api.Data; - -namespace Natsecure.SocialGuard.Api.Migrations -{ - [DbContext(typeof(ApiDbContext))] - [Migration("20210120224703_InitialCreate")] - partial class InitialCreate - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .UseIdentityColumns() - .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("ProductVersion", "5.0.2"); - - modelBuilder.Entity("Natsecure.SocialGuard.Api.Data.Models.TrustlistUser", b => - { - b.Property("Id") - .HasColumnType("decimal(20,0)"); - - b.Property("EntryAt") - .HasColumnType("datetime2"); - - b.Property("EscalationLevel") - .HasColumnType("tinyint"); - - b.Property("EscalationNote") - .HasColumnType("nvarchar(max)"); - - b.Property("LastEscalated") - .HasColumnType("datetime2"); - - b.HasKey("Id"); - - b.ToTable("TrustlistUsers"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Natsecure.SocialGuard.Api/Migrations/20210120224703_InitialCreate.cs b/Natsecure.SocialGuard.Api/Migrations/20210120224703_InitialCreate.cs deleted file mode 100644 index 922f37f..0000000 --- a/Natsecure.SocialGuard.Api/Migrations/20210120224703_InitialCreate.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -namespace Natsecure.SocialGuard.Api.Migrations -{ - public partial class InitialCreate : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "TrustlistUsers", - columns: table => new - { - Id = table.Column(type: "decimal(20,0)", nullable: false), - EntryAt = table.Column(type: "datetime2", nullable: false), - LastEscalated = table.Column(type: "datetime2", nullable: false), - EscalationLevel = table.Column(type: "tinyint", nullable: false), - EscalationNote = table.Column(type: "nvarchar(max)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_TrustlistUsers", x => x.Id); - }); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "TrustlistUsers"); - } - } -} diff --git a/Natsecure.SocialGuard.Api/Migrations/ApiDbContextModelSnapshot.cs b/Natsecure.SocialGuard.Api/Migrations/ApiDbContextModelSnapshot.cs deleted file mode 100644 index e8ac7a7..0000000 --- a/Natsecure.SocialGuard.Api/Migrations/ApiDbContextModelSnapshot.cs +++ /dev/null @@ -1,46 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Natsecure.SocialGuard.Api.Data; - -namespace Natsecure.SocialGuard.Api.Migrations -{ - [DbContext(typeof(ApiDbContext))] - partial class ApiDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .UseIdentityColumns() - .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("ProductVersion", "5.0.2"); - - modelBuilder.Entity("Natsecure.SocialGuard.Api.Data.Models.TrustlistUser", b => - { - b.Property("Id") - .HasColumnType("decimal(20,0)"); - - b.Property("EntryAt") - .HasColumnType("datetime2"); - - b.Property("EscalationLevel") - .HasColumnType("tinyint"); - - b.Property("EscalationNote") - .HasColumnType("nvarchar(max)"); - - b.Property("LastEscalated") - .HasColumnType("datetime2"); - - b.HasKey("Id"); - - b.ToTable("TrustlistUsers"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Natsecure.SocialGuard.Api/Natsecure.SocialGuard.Api.csproj b/Natsecure.SocialGuard.Api/Natsecure.SocialGuard.Api.csproj deleted file mode 100644 index a28648e..0000000 --- a/Natsecure.SocialGuard.Api/Natsecure.SocialGuard.Api.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - - net5.0 - 328b09f5-4635-4228-b9af-a4f5bab47844 - - - - true - $(NoWarn);1591 - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - diff --git a/Natsecure.SocialGuard.Api/Program.cs b/Natsecure.SocialGuard.Api/Program.cs deleted file mode 100644 index 1bf87af..0000000 --- a/Natsecure.SocialGuard.Api/Program.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Microsoft.AspNetCore.Hosting; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Natsecure.SocialGuard.Api.Data; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Natsecure.SocialGuard.Api -{ - public class Program - { - public static async Task Main(string[] args) - { - using IHost host = CreateHostBuilder(args).Build(); - using IServiceScope scope = host.Services.CreateScope(); - - ApiDbContext context = host.Services.GetRequiredService>().CreateDbContext(); - await context.Database.MigrateAsync(); - - await host.RunAsync(); - } - - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseStartup(); - }); - } -} diff --git a/Natsecure.SocialGuard.Api/Services/Authentication/AccessKeyAttribute.cs b/Natsecure.SocialGuard.Api/Services/Authentication/AccessKeyAttribute.cs deleted file mode 100644 index e53a217..0000000 --- a/Natsecure.SocialGuard.Api/Services/Authentication/AccessKeyAttribute.cs +++ /dev/null @@ -1,71 +0,0 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Filters; -using Microsoft.Extensions.Primitives; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - - - -// https://github.com/DJDaemonix/WoWS-Karma/blob/main/WowsKarma.Api/Services/Authentication/AccessKeyAttribute.cs - -namespace Natsecure.SocialGuard.Api.Services.Authentication -{ - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)] - public sealed class AccessKeyAttribute : Attribute, IAsyncAuthorizationFilter - { - private const string AccessKeyHeaderName = "Access-Key"; - private static readonly string apiKeysLocation = Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + "access-keys.txt"; - - public string[] Scopes { get; init; } - - public AccessKeyAttribute() - { - Scopes = null; - } - - public AccessKeyAttribute(params string[] scope) - { - Scopes = scope; - } - - - public async Task OnAuthorizationAsync(AuthorizationFilterContext context) - { - // "AllowAnonymous" skips all authorization - if (context.ActionDescriptor.EndpointMetadata.OfType().Any()) - { - return; - } - - if (!context.HttpContext.Request.Headers.TryGetValue(AccessKeyHeaderName, out StringValues extractedApiKey)) - { - context.Result = new UnauthorizedResult(); - return; - } - - string[] apiKeys = await File.ReadAllLinesAsync(apiKeysLocation, Encoding.ASCII); - Dictionary apiKeysSplitted = apiKeys.Select(x => x.Split('|')).ToDictionary(x => x[0],x => x[1].Split(',')); - - if (apiKeysSplitted.TryGetValue(extractedApiKey, out string[] keyScopes)) - { - if (Scopes is not null) - { - if (!Scopes.Append("*").Intersect(keyScopes).Any()) - { - context.Result = new UnauthorizedResult(); - } - } - - return; - } - - context.Result = new UnauthorizedResult(); - return; - } - } -} diff --git a/Natsecure.SocialGuard.Api/Services/Authentication/AccessKeySwaggerFilter.cs b/Natsecure.SocialGuard.Api/Services/Authentication/AccessKeySwaggerFilter.cs deleted file mode 100644 index 96b0274..0000000 --- a/Natsecure.SocialGuard.Api/Services/Authentication/AccessKeySwaggerFilter.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Natsecure.SocialGuard.Api.Services.Authentication -{ - public class AccessKeySwaggerFilter : IOperationFilter - { - public void Apply(OpenApiOperation operation, OperationFilterContext context) - { - if (operation.Parameters is null) - { - operation.Parameters = new List(); - } - - operation.Parameters.Add(new OpenApiParameter - { - Name = "Access-Key", - In = ParameterLocation.Header, - Required = false, - Schema = new OpenApiSchema - { - Type = "String" - } - }); - } - } -} diff --git a/Natsecure.SocialGuard.Api/Services/Authentication/AccessScopes.cs b/Natsecure.SocialGuard.Api/Services/Authentication/AccessScopes.cs deleted file mode 100644 index ed512ef..0000000 --- a/Natsecure.SocialGuard.Api/Services/Authentication/AccessScopes.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Natsecure.SocialGuard.Api.Services.Authentication -{ - public static class AccessScopes - { - public const string - Read = "0", - Insert = "1", - Escalate = "2", - Delete = "3"; - } -} diff --git a/Natsecure.SocialGuard.Api/Services/TrustlistUserService.cs b/Natsecure.SocialGuard.Api/Services/TrustlistUserService.cs deleted file mode 100644 index 301ad21..0000000 --- a/Natsecure.SocialGuard.Api/Services/TrustlistUserService.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Natsecure.SocialGuard.Api.Data; -using Natsecure.SocialGuard.Api.Data.Models; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Natsecure.SocialGuard.Api.Services -{ - public class TrustlistUserService - { - private readonly ApiDbContext context; - - public TrustlistUserService(IDbContextFactory contextFactory) - { - context = contextFactory.CreateDbContext(); - } - - public IEnumerable ListUserIds() => from user in context.TrustlistUsers select user.Id; - - public async Task FetchUserAsync(ulong id) => await context.TrustlistUsers.FindAsync(id); - - public async Task InsertNewUserAsync(TrustlistUser user) - { - // Check if user exists already. - _ = await context.TrustlistUsers.FindAsync(user.Id) is null ? true : throw new ArgumentOutOfRangeException(nameof(user)); - - - user.EntryAt = DateTime.UtcNow; - user.LastEscalated = DateTime.UtcNow; - - context.TrustlistUsers.Add(user); - await context.SaveChangesAsync(); - } - - public async Task EscalateUserAsync(TrustlistUser updated) - { - TrustlistUser current = await context.TrustlistUsers.FindAsync(updated.Id) ?? throw new ArgumentOutOfRangeException(nameof(updated)); - - if (current.EscalationLevel < updated.EscalationLevel) - { - current.EscalationLevel = updated.EscalationLevel; - current.LastEscalated = DateTime.UtcNow; - current.EscalationNote = updated.EscalationNote; - - await context.SaveChangesAsync(); - } - } - - public async Task DeleteUserAsync(ulong id) - { - TrustlistUser user = await context.TrustlistUsers.FindAsync(id) ?? throw new ArgumentException($"No user found with ID {id}", nameof(id)); - - context.TrustlistUsers.Remove(user); - await context.SaveChangesAsync(); - } - } -} diff --git a/Natsecure.SocialGuard.Api/Startup.cs b/Natsecure.SocialGuard.Api/Startup.cs deleted file mode 100644 index 21eb9df..0000000 --- a/Natsecure.SocialGuard.Api/Startup.cs +++ /dev/null @@ -1,87 +0,0 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.HttpOverrides; -using Microsoft.AspNetCore.HttpsPolicy; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Microsoft.OpenApi.Models; -using Natsecure.SocialGuard.Api.Data; -using Natsecure.SocialGuard.Api.Data.Models; -using Natsecure.SocialGuard.Api.Services; -using Natsecure.SocialGuard.Api.Services.Authentication; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; - -namespace Natsecure.SocialGuard.Api -{ - public class Startup - { - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } - - public IConfiguration Configuration { get; } - - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) - { - - services.AddControllers(); - services.AddSwaggerGen(c => - { - c.SwaggerDoc("v1", new OpenApiInfo { Title = "Natsecure SocialGuard", Version = "v1" }); - c.OperationFilter(); - - // Set the comments path for the Swagger JSON and UI. - string xmlFile = $"{typeof(Startup).Assembly.GetName().Name}.xml"; - string xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); - c.IncludeXmlComments(xmlPath); - }); - - services.AddDbContextFactory(options => - options.UseSqlServer(Configuration.GetConnectionString($"ApiDbContext"), - providerOptions => providerOptions.EnableRetryOnFailure())); - - services.AddScoped(); - } - - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - - app.UseSwagger(); - app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Natsecure SocialGuard v1")); - - app.UseHttpsRedirection(); - - app.UseRouting(); - - if (env.IsProduction()) // Nginx configuration step - { - app.UseForwardedHeaders(new ForwardedHeadersOptions - { - ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto - }); - } - - app.UseAuthorization(); - - app.UseEndpoints(endpoints => - { - endpoints.MapControllers(); - }); - } - } -} diff --git a/Natsecure.SocialGuard.Api/appsettings.Development.json b/Natsecure.SocialGuard.Api/appsettings.Development.json deleted file mode 100644 index 8983e0f..0000000 --- a/Natsecure.SocialGuard.Api/appsettings.Development.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" - } - } -} diff --git a/Natsecure.SocialGuard.Api/appsettings.json b/Natsecure.SocialGuard.Api/appsettings.json deleted file mode 100644 index d9d9a9b..0000000 --- a/Natsecure.SocialGuard.Api/appsettings.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" - } - }, - "AllowedHosts": "*" -} diff --git a/Transcom.SocialGuard.Api/Controllers/AuthController.cs b/Transcom.SocialGuard.Api/Controllers/AuthController.cs new file mode 100644 index 0000000..fa4c71e --- /dev/null +++ b/Transcom.SocialGuard.Api/Controllers/AuthController.cs @@ -0,0 +1,65 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System.Threading.Tasks; +using Transcom.SocialGuard.Api.Services.Authentication; +using Transcom.SocialGuard.Api.Services.Authentication.Models; + + + +namespace Transcom.SocialGuard.Api.Controllers +{ + [ApiController, Route("api/[controller]")] + public class AuthController : ControllerBase + { + private readonly AuthenticationService service; + + public AuthController(AuthenticationService authenticationService) + { + service = authenticationService; + } + + /// + /// Provides JWT Authentication token for specified credentials. + /// + /// Authentication success response with JWT Token + /// Authentication failure response + /// Login credentials + /// Auth response + [HttpPost("login")] + [ProducesResponseType(200), ProducesResponseType(401)] + public async Task Login([FromBody] LoginModel model) + { + AuthServiceResponse result = await service.HandleLogin(model); + return StatusCode(result.StatusCode, result.Response); + } + + /// + /// Creates user with specified credentials. + /// + /// Registration success response + /// User signup details + /// Registration status response + [HttpPost("register")] + [ProducesResponseType(201)] + public async Task Register([FromBody] RegisterModel model) + { + AuthServiceResponse result = await service.HandleRegister(model); + return StatusCode(result.StatusCode, result.Response); + } + + + /// + /// Ftches full status/info for current Authentication method. + /// + /// Returns full authentication info + /// Authentication failure response + /// Full Auth info + [HttpGet("whoami"), Authorize] + [ProducesResponseType(200), ProducesResponseType(401)] + public IActionResult Whoami() + { + AuthServiceResponse result = service.Whoami(HttpContext); + return StatusCode(result.StatusCode, result.Response); + } + } +} diff --git a/Transcom.SocialGuard.Api/Controllers/EmitterController.cs b/Transcom.SocialGuard.Api/Controllers/EmitterController.cs new file mode 100644 index 0000000..e06dfb8 --- /dev/null +++ b/Transcom.SocialGuard.Api/Controllers/EmitterController.cs @@ -0,0 +1,50 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System.Threading.Tasks; +using Transcom.SocialGuard.Api.Data.Models; +using Transcom.SocialGuard.Api.Services; +using Transcom.SocialGuard.Api.Services.Authentication; + + + +namespace Transcom.SocialGuard.Api.Controllers +{ + [ApiController, Route("api/[controller]"), Authorize(Roles = UserRole.Emitter)] + public class EmitterController : ControllerBase + { + private readonly EmitterService emitterService; + + public EmitterController(EmitterService emitterService) + { + this.emitterService = emitterService; + } + + /// + /// Fetches currently logged-in user's Emitter profile. + /// + /// Returns Emitter profile + /// If user's Emitter profile is not set up. + /// Emitter profile + [HttpGet, ProducesResponseType(typeof(Emitter), 200), ProducesResponseType(204)] + public async Task GetEmitterProfile() + { + Emitter emitter = await emitterService.GetEmitterAsync(HttpContext); + return emitter is null + ? StatusCode(204) + : StatusCode(200, emitter); + } + + /// + /// Creates or Updates currently logged-in user's Emitter profile. + /// + /// New or updated Emitter info + /// Emitter profile was successfully created or updated. + /// + [HttpPost, ProducesResponseType(200)] + public async Task UpdateEmitterProfile([FromBody] Emitter emitter) + { + await emitterService.CreateOrUpdateEmitterSelfAsync(emitter, HttpContext); + return StatusCode(200); + } + } +} diff --git a/Natsecure.SocialGuard.Api/Controllers/UserController.cs b/Transcom.SocialGuard.Api/Controllers/UserController.cs similarity index 56% rename from Natsecure.SocialGuard.Api/Controllers/UserController.cs rename to Transcom.SocialGuard.Api/Controllers/UserController.cs index 56ac690..6e9def9 100644 --- a/Natsecure.SocialGuard.Api/Controllers/UserController.cs +++ b/Transcom.SocialGuard.Api/Controllers/UserController.cs @@ -1,24 +1,26 @@ using Microsoft.AspNetCore.Mvc; -using Natsecure.SocialGuard.Api.Data.Models; -using Natsecure.SocialGuard.Api.Services; -using Natsecure.SocialGuard.Api.Services.Authentication; +using Transcom.SocialGuard.Api.Data.Models; +using Transcom.SocialGuard.Api.Services; +using Transcom.SocialGuard.Api.Services.Authentication; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; - - -namespace Natsecure.SocialGuard.Api.Controllers +namespace Transcom.SocialGuard.Api.Controllers { + [ApiController, Route("api/[controller]")] public class UserController : ControllerBase { - private readonly TrustlistUserService service; + private readonly TrustlistUserService trustlistService; + private readonly EmitterService emitterService; - public UserController(TrustlistUserService service) + public UserController(TrustlistUserService trustlistService, EmitterService emitterService) { - this.service = service; + this.trustlistService = trustlistService; + this.emitterService = emitterService; } /// @@ -27,10 +29,10 @@ public UserController(TrustlistUserService service) /// Returns List /// If Trustlist is empty /// List of user IDs - [HttpGet("list"), ProducesResponseType(200), ProducesResponseType(204)] + [HttpGet("list"), ProducesResponseType(typeof(IEnumerable), 200), ProducesResponseType(204)] public IActionResult ListUsersIds() { - IEnumerable users = service.ListUserIds(); + IEnumerable users = trustlistService.ListUserIds(); return users.Any() ? StatusCode(200, users) : StatusCode(204); @@ -44,10 +46,10 @@ public IActionResult ListUsersIds() /// Returns record /// If user ID is not found in DB /// Trustlist info - [HttpGet("{id}"), ProducesResponseType(200), ProducesResponseType(404)] - public async Task FetchUser(ulong id) + [HttpGet("{id}"), ProducesResponseType(typeof(TrustlistUser), 200), ProducesResponseType(404)] + public async Task FetchUser(ulong id) { - TrustlistUser user = await service.FetchUserAsync(id); + TrustlistUser user = await trustlistService.FetchUserAsync(id); return StatusCode(user is not null ? 200 : 404, user); } @@ -58,12 +60,20 @@ public async Task FetchUser(ulong id) /// User record to insert /// User was created /// If User record already exists - [HttpPost, AccessKey(AccessScopes.Insert), ProducesResponseType(201), ProducesResponseType(409)] + [HttpPost, Authorize(Roles = UserRole.Emitter)] + [ProducesResponseType(201), ProducesResponseType(409)] public async Task InsertUserRecord([FromBody] TrustlistUser userRecord) { + Emitter emitter = await GetEmitterAsync(); + + if (emitter is null) + { + return StatusCode(401, "No emitter profile set for logged-in user. Please setup emitter first."); + } + try { - await service.InsertNewUserAsync(userRecord); + await trustlistService.InsertNewUserAsync(userRecord, emitter); } catch (ArgumentOutOfRangeException) { @@ -79,12 +89,20 @@ public async Task InsertUserRecord([FromBody] TrustlistUser userR /// User record to escalate /// Record escalation request was accepted /// If user ID is not found in DB - [HttpPut, AccessKey(AccessScopes.Escalate), ProducesResponseType(202), ProducesResponseType(404)] + [HttpPut, Authorize(Roles = UserRole.Emitter)] + [ProducesResponseType(202), ProducesResponseType(404)] public async Task EscalateUserRecord([FromBody] TrustlistUser userRecord) { + Emitter emitter = await GetEmitterAsync(); + + if (emitter is null) + { + return StatusCode(401, "No emitter profile set for logged-in user. Please setup emitter first."); + } + try { - await service.EscalateUserAsync(userRecord); + await trustlistService.EscalateUserAsync(userRecord, emitter); } catch (ArgumentOutOfRangeException) { @@ -100,11 +118,14 @@ public async Task EscalateUserRecord([FromBody] TrustlistUser use /// /// ID of User to wipe /// Record was wiped (if any) - [HttpDelete("{id}"), AccessKey(AccessScopes.Delete)] + [HttpDelete("{id}"), Authorize(Roles = UserRole.Admin)] public async Task DeleteUserRecord(ulong id) { - await service.DeleteUserAsync(id); + await trustlistService.DeleteUserRecordAsync(id); return StatusCode(200); } + + + private async Task GetEmitterAsync() => await emitterService.GetEmitterAsync(HttpContext); } } diff --git a/Transcom.SocialGuard.Api/Data/Models/AccessKey.cs b/Transcom.SocialGuard.Api/Data/Models/AccessKey.cs new file mode 100644 index 0000000..53ff736 --- /dev/null +++ b/Transcom.SocialGuard.Api/Data/Models/AccessKey.cs @@ -0,0 +1,13 @@ +namespace Transcom.SocialGuard.Api.Data.Models +{ + public record AccessKey + { + public string Id { get; init; } + + public string OwnerName { get; set; } + + public string[] Scopes { get; set; } + + public bool Active { get; set; } + } +} diff --git a/Transcom.SocialGuard.Api/Data/Models/Emitter.cs b/Transcom.SocialGuard.Api/Data/Models/Emitter.cs new file mode 100644 index 0000000..ce63207 --- /dev/null +++ b/Transcom.SocialGuard.Api/Data/Models/Emitter.cs @@ -0,0 +1,47 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using System.ComponentModel.DataAnnotations; + + + +namespace Transcom.SocialGuard.Api.Data.Models +{ + + /// + /// Represents an Emitter profile. + /// + public record Emitter + { + [BsonId, BsonRepresentation(BsonType.String)] + public string Login { get; init; } + + [Required] + public EmitterType EmitterType { get; init; } + + public ulong Snowflake { get; init; } + + [Required] + public string DisplayName { get; init; } + } + + /// + /// Represents types of Emitter profiles. + /// + public enum EmitterType : byte + { + /// + /// Represents an unknown, undefined, or other Emitter profile type. + /// + Unknown = 0, + + /// + /// Emitter is a singular user. + /// + User = 1, + + /// + /// Emitter is a Discord server. + /// + Server = 2 + } +} diff --git a/Transcom.SocialGuard.Api/Data/Models/EscalationLevel.cs b/Transcom.SocialGuard.Api/Data/Models/EscalationLevel.cs new file mode 100644 index 0000000..1a195c1 --- /dev/null +++ b/Transcom.SocialGuard.Api/Data/Models/EscalationLevel.cs @@ -0,0 +1,13 @@ +namespace Transcom.SocialGuard.Api.Data.Models +{ + /// + /// Represents severity levels for Trustlist User entries. + /// + public enum EscalationLevel : byte + { + Neutral = 0, + Suspicious = 1, + Untrusted = 2, + Blacklisted = 3 + } +} diff --git a/Transcom.SocialGuard.Api/Data/Models/TrustlistUser.cs b/Transcom.SocialGuard.Api/Data/Models/TrustlistUser.cs new file mode 100644 index 0000000..2756f1b --- /dev/null +++ b/Transcom.SocialGuard.Api/Data/Models/TrustlistUser.cs @@ -0,0 +1,33 @@ +using System; +using System.ComponentModel.DataAnnotations; +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; + + + +namespace Transcom.SocialGuard.Api.Data.Models +{ + /// + /// Represents a Trustlist User entry. + /// + public record TrustlistUser + { + [Required, BsonRequired] + public ulong Id { get; init; } + + [BsonRequired] + public DateTime EntryAt { get; set; } + + [BsonRequired] + public DateTime LastEscalated { get; set; } + + [Required, BsonRequired, Range(0, 3)] + public byte EscalationLevel { get; set; } + + [Required, BsonRequired, MinLength(5), MaxLength(2000)] + public string EscalationNote { get; set; } + + + public Emitter Emitter { get; set; } + } +} diff --git a/Transcom.SocialGuard.Api/Program.cs b/Transcom.SocialGuard.Api/Program.cs new file mode 100644 index 0000000..cc1ab2a --- /dev/null +++ b/Transcom.SocialGuard.Api/Program.cs @@ -0,0 +1,26 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using MongoDB.Bson.Serialization.Conventions; +using System.Threading.Tasks; + +namespace Transcom.SocialGuard.Api +{ + public class Program + { + public static async Task Main(string[] args) + { + ConventionRegistry.Register("Ignore null values", new ConventionPack { new IgnoreIfNullConvention(true) }, t => true); + + using IHost host = CreateHostBuilder(args).Build(); + + await host.RunAsync(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/Natsecure.SocialGuard.Api/Properties/launchSettings.json b/Transcom.SocialGuard.Api/Properties/launchSettings.json similarity index 100% rename from Natsecure.SocialGuard.Api/Properties/launchSettings.json rename to Transcom.SocialGuard.Api/Properties/launchSettings.json diff --git a/Transcom.SocialGuard.Api/Services/Authentication/ApplicationUser.cs b/Transcom.SocialGuard.Api/Services/Authentication/ApplicationUser.cs new file mode 100644 index 0000000..193e5f0 --- /dev/null +++ b/Transcom.SocialGuard.Api/Services/Authentication/ApplicationUser.cs @@ -0,0 +1,15 @@ +using AspNetCore.Identity.Mongo.Model; + + + +namespace Transcom.SocialGuard.Api.Services.Authentication +{ + public class ApplicationUser : MongoUser + { + public ApplicationUser() : base() { } + public ApplicationUser(string username) : base(username) + { + Id = username; + } + } +} \ No newline at end of file diff --git a/Transcom.SocialGuard.Api/Services/Authentication/AuthenticationService.cs b/Transcom.SocialGuard.Api/Services/Authentication/AuthenticationService.cs new file mode 100644 index 0000000..0e19bfd --- /dev/null +++ b/Transcom.SocialGuard.Api/Services/Authentication/AuthenticationService.cs @@ -0,0 +1,139 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.Configuration; +using Microsoft.IdentityModel.Tokens; +using System; +using System.Collections.Generic; +using System.IdentityModel.Tokens.Jwt; +using System.Linq; +using System.Security.Claims; +using System.Text; +using System.Threading.Tasks; +using Transcom.SocialGuard.Api.Services.Authentication.Models; + + + +namespace Transcom.SocialGuard.Api.Services.Authentication +{ + public class AuthenticationService + { + private readonly UserManager userManager; + private readonly RoleManager roleManager; + private static IConfiguration configuration; + private static SymmetricSecurityKey authSigningKey; + + public AuthenticationService(UserManager userManager, RoleManager roleManager, IConfiguration configuration) + { + this.userManager = userManager; + this.roleManager = roleManager; + AuthenticationService.configuration ??= configuration; + authSigningKey = new(Encoding.UTF8.GetBytes(configuration["JWT:Secret"])); + } + + public async Task HandleRegister(RegisterModel model) + { + ApplicationUser userExists = await userManager.FindByNameAsync(model.Username); + if (userExists is not null) + { + return new() { StatusCode = 409, Response = Response.ErrorResponse() with { Message = "User already exists." } }; + } + + bool firstUser = userManager.Users.Count() is 0; + ApplicationUser user = new(model.Username) { Email = model.Email, SecurityStamp = Guid.NewGuid().ToString() }; + IdentityResult result = await userManager.CreateAsync(user, model.Password); + + if (!result.Succeeded) + { + return new() { StatusCode = 500, Response = Response.ErrorResponse() with { Message = $"User creation has failed.", Details = result.Errors } }; + } + + if (firstUser) + { + await ProvisionFirstUseAsync(user); + } + + return new() { StatusCode = 201, Response = Response.SuccessResponse() with { Message = "User created successfuly." } }; + } + + public async Task HandleLogin(LoginModel model) + { + ApplicationUser user = await userManager.FindByNameAsync(model.Username); + + if (user is not null && await userManager.CheckPasswordAsync(user, model.Password)) + { + IList userRoles = await userManager.GetRolesAsync(user); + List authClaims = new() + { + new Claim(ClaimTypes.Name, user.UserName), + new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) + }; + authClaims.AddRange(from string userRole in userRoles select new Claim(ClaimTypes.Role, userRole)); + + JwtSecurityToken token = GetToken(authClaims); + + return new() + { + StatusCode = 200, + Response = Response.SuccessResponse() with + { + Message = "Login Successful.", + Details = new { token = new JwtSecurityTokenHandler().WriteToken(token), expiration = token.ValidTo } + } + }; + } + + return new() { StatusCode = 401, Response = Response.ErrorResponse() with { Message = "Login Failed." } }; + } + + + private async Task ProvisionFirstUseAsync(ApplicationUser firstUser) + { + if (roleManager.Roles.Count() is 0) + { + await roleManager.CreateAsync(new(UserRole.Admin)); + await roleManager.CreateAsync(new(UserRole.Emitter)); + + await userManager.AddToRoleAsync(firstUser, UserRole.Admin); + } + else + { + throw new ApplicationException("Inconsistent Authentication Database state."); + } + } + + public AuthServiceResponse Whoami(HttpContext context) + { + object identity = new { context.User.Identity.Name, context.User.Identity.AuthenticationType }; + + List claims = new(from claim in context.User.Claims select claim.Value); + + + return new() + { + StatusCode = 200, + Response = Response.SuccessResponse() with + { + Details = new + { + Identity = identity, + Claims = claims + } + } + }; + } + + private static JwtSecurityToken GetToken(List authClaims) => new( + issuer: configuration["JWT:ValidIssuer"], + audience: configuration["JWT:ValidAudience"], + expires: DateTime.Now.AddHours(1), + claims: authClaims, + signingCredentials: new SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256)); + } + + public record AuthServiceResponse + { + public int StatusCode { get; init; } + public Response Response { get; init; } + internal object InternalDetails { get; init; } + } +} diff --git a/Transcom.SocialGuard.Api/Services/Authentication/Models/LoginModel.cs b/Transcom.SocialGuard.Api/Services/Authentication/Models/LoginModel.cs new file mode 100644 index 0000000..3e3b9bc --- /dev/null +++ b/Transcom.SocialGuard.Api/Services/Authentication/Models/LoginModel.cs @@ -0,0 +1,15 @@ +using System.ComponentModel.DataAnnotations; + + + +namespace Transcom.SocialGuard.Api.Services.Authentication.Models +{ + public class LoginModel + { + [Required] + public string Username { get; set; } + + [Required] + public string Password { get; set; } + } +} diff --git a/Transcom.SocialGuard.Api/Services/Authentication/Models/RegisterModel.cs b/Transcom.SocialGuard.Api/Services/Authentication/Models/RegisterModel.cs new file mode 100644 index 0000000..f6b41f5 --- /dev/null +++ b/Transcom.SocialGuard.Api/Services/Authentication/Models/RegisterModel.cs @@ -0,0 +1,18 @@ +using System.ComponentModel.DataAnnotations; + + + +namespace Transcom.SocialGuard.Api +{ + public record RegisterModel + { + [Required] + public string Username { get; init; } + + [Required, EmailAddress] + public string Email { get; init; } + + [Required] + public string Password { get; init; } + } +} \ No newline at end of file diff --git a/Transcom.SocialGuard.Api/Services/Authentication/Models/Response.cs b/Transcom.SocialGuard.Api/Services/Authentication/Models/Response.cs new file mode 100644 index 0000000..cd4ebf3 --- /dev/null +++ b/Transcom.SocialGuard.Api/Services/Authentication/Models/Response.cs @@ -0,0 +1,12 @@ +namespace Transcom.SocialGuard.Api.Services.Authentication.Models +{ + public record Response + { + public string Status { get; init; } + public string Message { get; init; } + public object Details { get; init; } + + public static Response SuccessResponse() => new() { Status = "Success", Message = "Request carried out successfully." }; + public static Response ErrorResponse() => new() { Status = "Error", Message = "Something went wrong." }; + } +} diff --git a/Transcom.SocialGuard.Api/Services/Authentication/UserRole.cs b/Transcom.SocialGuard.Api/Services/Authentication/UserRole.cs new file mode 100644 index 0000000..c6fe0a4 --- /dev/null +++ b/Transcom.SocialGuard.Api/Services/Authentication/UserRole.cs @@ -0,0 +1,19 @@ +using AspNetCore.Identity.Mongo.Model; + + + +namespace Transcom.SocialGuard.Api.Services.Authentication +{ + public class UserRole : MongoRole + { + public UserRole() : base() { } + public UserRole(string name) : base(name) + { + Id = name; + } + + + public const string Emitter = "emitter"; + public const string Admin = "admin"; + } +} diff --git a/Transcom.SocialGuard.Api/Services/EmitterService.cs b/Transcom.SocialGuard.Api/Services/EmitterService.cs new file mode 100644 index 0000000..7772e44 --- /dev/null +++ b/Transcom.SocialGuard.Api/Services/EmitterService.cs @@ -0,0 +1,28 @@ +using Microsoft.AspNetCore.Http; +using MongoDB.Driver; +using System.Threading.Tasks; +using Transcom.SocialGuard.Api.Data.Models; + + + +namespace Transcom.SocialGuard.Api.Services +{ + public class EmitterService + { + private readonly IMongoCollection emitters; + + public EmitterService(IMongoDatabase database) + { + emitters = database.GetCollection(nameof(Emitter)); + } + + public async Task GetEmitterAsync(HttpContext context) => (await emitters.FindAsync(e => e.Login == context.User.Identity.Name)).FirstOrDefault(); + + public async Task CreateOrUpdateEmitterSelfAsync(Emitter emitter, HttpContext context) + { + await emitters.InsertOneAsync(emitter with { Login = context.User.Identity.Name }); + } + + public async Task DeleteEmitterAsync(string emitterLogin) => await emitters.FindOneAndDeleteAsync(e => e.Login == emitterLogin); + } +} \ No newline at end of file diff --git a/Transcom.SocialGuard.Api/Services/TrustlistUserService.cs b/Transcom.SocialGuard.Api/Services/TrustlistUserService.cs new file mode 100644 index 0000000..8e1a8d5 --- /dev/null +++ b/Transcom.SocialGuard.Api/Services/TrustlistUserService.cs @@ -0,0 +1,61 @@ +using Transcom.SocialGuard.Api.Data.Models; +using System; +using System.Linq; +using System.Threading.Tasks; +using MongoDB.Driver; + + + +namespace Transcom.SocialGuard.Api.Services +{ + public class TrustlistUserService + { + private readonly IMongoCollection trustlistUsers; + private readonly IMongoCollection emitters; + + public TrustlistUserService(IMongoDatabase database) + { + trustlistUsers = database.GetCollection(nameof(TrustlistUser)); + emitters = database.GetCollection(nameof(Emitter)); + } + + public IQueryable ListUserIds() => from user in trustlistUsers.AsQueryable() select user.Id; + + public async Task FetchUserAsync(ulong id) => await (await trustlistUsers.FindAsync(u => u.Id == id)).FirstOrDefaultAsync(); + + public async Task InsertNewUserAsync(TrustlistUser user, Emitter emitter) + { + // Check if user exists already. + _ = await (await trustlistUsers.FindAsync(u => u.Id == user.Id)).AnyAsync() ? throw new ArgumentOutOfRangeException(nameof(user)) : false; + + await trustlistUsers.InsertOneAsync(user with + { + EntryAt = DateTime.UtcNow, + LastEscalated = DateTime.UtcNow, + Emitter = emitter + }); + } + + public async Task EscalateUserAsync(TrustlistUser updated, Emitter emitter) + { + TrustlistUser current = await FetchUserAsync(updated.Id) ?? throw new ArgumentOutOfRangeException(nameof(updated)); + + if (current.EscalationLevel < updated.EscalationLevel) + { + await trustlistUsers.ReplaceOneAsync(u => u.Id == current.Id, current with + { + EscalationLevel = updated.EscalationLevel, + LastEscalated = DateTime.UtcNow, + EscalationNote = updated.EscalationNote, + Emitter = emitter + }); + } + } + + public async Task DeleteUserRecordAsync(ulong id) + { + TrustlistUser user = await FetchUserAsync(id) ?? throw new ArgumentException($"No user found with ID {id}", nameof(id)); + await trustlistUsers.DeleteOneAsync(u => u.Id == id); + } + } +} diff --git a/Transcom.SocialGuard.Api/Startup.cs b/Transcom.SocialGuard.Api/Startup.cs new file mode 100644 index 0000000..8754629 --- /dev/null +++ b/Transcom.SocialGuard.Api/Startup.cs @@ -0,0 +1,148 @@ +using System; +using AspNetCore.Identity.Mongo; +using AspNetCore.Identity.Mongo.Model; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.OpenApi.Models; +using Transcom.SocialGuard.Api.Services.Authentication; +using System.IO; +using MongoDB.Driver; +using Transcom.SocialGuard.Api.Services; +using Microsoft.AspNetCore.HttpOverrides; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.IdentityModel.Tokens; +using System.Text; + +namespace Transcom.SocialGuard.Api +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + + services.AddControllers(); + services.AddSwaggerGen(c => + { + c.SwaggerDoc("2.0", new OpenApiInfo { Title = "Natsecure SocialGuard", Version = "2.0" }); + + // Set the comments path for the Swagger JSON and UI. + string xmlFile = $"{typeof(Startup).Assembly.GetName().Name}.xml"; + string xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); + c.IncludeXmlComments(xmlPath); + + // Bearer token authentication + c.AddSecurityDefinition("jwt_auth", new OpenApiSecurityScheme() + { + Name = "bearer", + BearerFormat = "JWT", + Scheme = "bearer", + Description = "Specify the authorization token.", + In = ParameterLocation.Header, + Type = SecuritySchemeType.Http, + }); + + // Make sure swagger UI requires a Bearer token specified + OpenApiSecurityScheme securityScheme = new() + { + Reference = new() + { + Id = "jwt_auth", + Type = ReferenceType.SecurityScheme + } + }; + + c.AddSecurityRequirement(new OpenApiSecurityRequirement() + { + { securityScheme, Array.Empty() }, + }); + }); + + services.AddIdentityMongoDbProvider( + options => { }, + mongo => + { + IConfigurationSection config = Configuration.GetSection("Auth"); + mongo.ConnectionString = config["ConnectionString"]; + mongo.MigrationCollection = config["Tables:Migration"]; + mongo.RolesCollection = config["Tables:Role"]; + mongo.UsersCollection = config["Tables:User"]; + }); + + services.AddAuthentication(options => + { + options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; + options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; + }) + + // Adding Jwt Bearer + .AddJwtBearer(options => + { + options.SaveToken = true; + options.RequireHttpsMetadata = false; + options.TokenValidationParameters = new() + { + ValidateIssuer = true, + ValidateAudience = true, + ValidAudience = Configuration["JWT:ValidAudience"], + ValidIssuer = Configuration["JWT:ValidIssuer"], + IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JWT:Secret"])) + }; + }); + + + services.AddTransient(); + + services.AddSingleton(s => new MongoClient(Configuration["MongoDatabase:ConnectionString"]).GetDatabase(Configuration["MongoDatabase:DatabaseName"])); + + services.AddSingleton() + .AddSingleton(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseStaticFiles(); + + app.UseSwagger(); + app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/2.0/swagger.json", "Natsecure SocialGuard 2.0")); + + app.UseHttpsRedirection(); + + app.UseRouting(); + + if (env.IsProduction()) // Nginx configuration step + { + app.UseForwardedHeaders(new ForwardedHeadersOptions + { + ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto + }); + } + + app.UseAuthentication(); + app.UseAuthorization(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); + } + } +} diff --git a/Transcom.SocialGuard.Api/Transcom.SocialGuard.Api.csproj b/Transcom.SocialGuard.Api/Transcom.SocialGuard.Api.csproj new file mode 100644 index 0000000..4e70391 --- /dev/null +++ b/Transcom.SocialGuard.Api/Transcom.SocialGuard.Api.csproj @@ -0,0 +1,23 @@ + + + + net6.0 + 328b09f5-4635-4228-b9af-a4f5bab47844 + + + + true + $(NoWarn);1591 + + + + + + + + + + + + + diff --git a/Natsecure.SocialGuard.Api/access-keys.txt b/Transcom.SocialGuard.Api/access-keys.txt similarity index 100% rename from Natsecure.SocialGuard.Api/access-keys.txt rename to Transcom.SocialGuard.Api/access-keys.txt diff --git a/Transcom.SocialGuard.Api/appsettings.Development.json b/Transcom.SocialGuard.Api/appsettings.Development.json new file mode 100644 index 0000000..7d5732e --- /dev/null +++ b/Transcom.SocialGuard.Api/appsettings.Development.json @@ -0,0 +1,20 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + + "MongoDatabase": { + "ConnectionString": "mongodb://localhost:27017", + "DatabaseName": "socialguard-api" + }, + + "JWT": { + "ValidAudience": "https://localhost:5001", + "ValidIssuer": "https://localhost:5001", + "Secret": "FIXME : Placeholder value" + } +} diff --git a/Transcom.SocialGuard.Api/appsettings.json b/Transcom.SocialGuard.Api/appsettings.json new file mode 100644 index 0000000..0d8b7b8 --- /dev/null +++ b/Transcom.SocialGuard.Api/appsettings.json @@ -0,0 +1,24 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*", + + "MongoDatabase": { + "ConnectionString": "mongodb://localhost:27017", + "DatabaseName": "transcom-socialguard-api" + }, + + "Auth": { + "ConnectionString": "mongodb://localhost:27017/transcom-auth", + "Tables": { + "User": "User", + "Role": "Role", + "Migration": "_Migration" + } + } +} diff --git a/Natsecure.SocialGuard.sln b/Transcom.SocialGuard.sln similarity index 84% rename from Natsecure.SocialGuard.sln rename to Transcom.SocialGuard.sln index 965bdfc..e8ec541 100644 --- a/Natsecure.SocialGuard.sln +++ b/Transcom.SocialGuard.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.30803.129 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Natsecure.SocialGuard.Api", "Natsecure.SocialGuard.Api\Natsecure.SocialGuard.Api.csproj", "{A406CC3F-6F0E-474E-9F5D-826EF320724E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Transcom.SocialGuard.Api", "Transcom.SocialGuard.Api\Transcom.SocialGuard.Api.csproj", "{A406CC3F-6F0E-474E-9F5D-826EF320724E}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution