Skip to content

Commit

Permalink
Implement EmitterController + Fixes + Documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
SakuraIsayeki committed Mar 21, 2021
1 parent 2f112ef commit fcd4db1
Show file tree
Hide file tree
Showing 12 changed files with 168 additions and 42 deletions.
30 changes: 26 additions & 4 deletions Transcom.SocialGuard.Api/Controllers/AuthController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,44 @@ public AuthController(AuthenticationService authenticationService)
service = authenticationService;
}


[HttpPost, Route("login")]
/// <summary>
/// Provides JWT Authentication token for specified credentials.
/// </summary>
/// <response code="200">Authentication success response with JWT Token</response>
/// <response code="401">Authentication failure response</response>
/// <param name="model">Login credentials</param>
/// <returns>Auth response</returns>
[HttpPost("login")]
[ProducesResponseType(200), ProducesResponseType(401)]
public async Task<IActionResult> Login([FromBody] LoginModel model)
{
AuthServiceResponse result = await service.HandleLogin(model);
return StatusCode(result.StatusCode, result.Response);
}

[HttpPost, Route("register")]
/// <summary>
/// Creates user with specified credentials.
/// </summary>
/// <response code="201">Registration success response</response>
/// <param name="model">User signup details</param>
/// <returns>Registration status response</returns>
[HttpPost("register")]
[ProducesResponseType(201)]
public async Task<IActionResult> Register([FromBody] RegisterModel model)
{
AuthServiceResponse result = await service.HandleRegister(model);
return StatusCode(result.StatusCode, result.Response);
}

[HttpGet, Route("Whoami"), Authorize]

/// <summary>
/// Ftches full status/info for current Authentication method.
/// </summary>
/// <response code="200">Returns full authentication info</response>
/// <response code="401">Authentication failure response</response>
/// <returns>Full Auth info</returns>
[HttpGet("whoami"), Authorize]
[ProducesResponseType(200), ProducesResponseType(401)]
public IActionResult Whoami()
{
AuthServiceResponse result = service.Whoami(HttpContext);
Expand Down
50 changes: 50 additions & 0 deletions Transcom.SocialGuard.Api/Controllers/EmitterController.cs
Original file line number Diff line number Diff line change
@@ -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;
}

/// <summary>
/// Fetches currently logged-in user's Emitter profile.
/// </summary>
/// <response code="200">Returns Emitter profile</response>
/// <response code="204">If user's Emitter profile is not set up.</response>
/// <returns>Emitter profile</returns>
[HttpGet, ProducesResponseType(typeof(Emitter), 200), ProducesResponseType(204)]
public async Task<IActionResult> GetEmitterProfile()
{
Emitter emitter = await emitterService.GetEmitterAsync(HttpContext);
return emitter is null
? StatusCode(204)
: StatusCode(200, emitter);
}

/// <summary>
/// Creates or Updates currently logged-in user's Emitter profile.
/// </summary>
/// <param name="emitter">New or updated Emitter info</param>
/// <response code="200">Emitter profile was successfully created or updated.</response>
/// <returns></returns>
[HttpPost, ProducesResponseType(200)]
public async Task<IActionResult> UpdateEmitterProfile([FromBody] Emitter emitter)
{
await emitterService.CreateOrUpdateEmitterSelfAsync(emitter, HttpContext);
return StatusCode(200);
}
}
}
27 changes: 21 additions & 6 deletions Transcom.SocialGuard.Api/Controllers/UserController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

namespace Transcom.SocialGuard.Api.Controllers
{

[ApiController, Route("api/[controller]")]
public class UserController : ControllerBase
{
Expand All @@ -28,7 +29,7 @@ public UserController(TrustlistUserService trustlistService, EmitterService emit
/// <response code="200">Returns List</response>
/// <response code="204">If Trustlist is empty</response>
/// <returns>List of user IDs</returns>
[HttpGet("list"), ProducesResponseType(200), ProducesResponseType(204)]
[HttpGet("list"), ProducesResponseType(typeof(IEnumerable<ulong>), 200), ProducesResponseType(204)]
public IActionResult ListUsersIds()
{
IEnumerable<ulong> users = trustlistService.ListUserIds();
Expand All @@ -45,7 +46,7 @@ public IActionResult ListUsersIds()
/// <response code="200">Returns record</response>
/// <response code="404">If user ID is not found in DB</response>
/// <returns>Trustlist info</returns>
[HttpGet("{id}"), ProducesResponseType(200), ProducesResponseType(404)]
[HttpGet("{id}"), ProducesResponseType(typeof(TrustlistUser), 200), ProducesResponseType(404)]
public async Task<IActionResult> FetchUser(ulong id)
{
TrustlistUser user = await trustlistService.FetchUserAsync(id);
Expand All @@ -59,13 +60,20 @@ public async Task<IActionResult> FetchUser(ulong id)
/// <param name="userRecord">User record to insert</param>
/// <response code="201">User was created</response>
/// <response code="409">If User record already exists</response>
[HttpPost, Authorize(Roles = UserRole.Insert + "," + UserRole.Admin)]
[HttpPost, Authorize(Roles = UserRole.Emitter)]
[ProducesResponseType(201), ProducesResponseType(409)]
public async Task<IActionResult> 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 trustlistService.InsertNewUserAsync(userRecord, await GetEmitterAsync());
await trustlistService.InsertNewUserAsync(userRecord, emitter);
}
catch (ArgumentOutOfRangeException)
{
Expand All @@ -81,13 +89,20 @@ public async Task<IActionResult> InsertUserRecord([FromBody] TrustlistUser userR
/// <param name="userRecord">User record to escalate</param>
/// <response code="202">Record escalation request was accepted</response>
/// <response code="404">If user ID is not found in DB</response>
[HttpPut, Authorize(Roles = UserRole.Escalate + "," + UserRole.Admin)]
[HttpPut, Authorize(Roles = UserRole.Emitter)]
[ProducesResponseType(202), ProducesResponseType(404)]
public async Task<IActionResult> 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 trustlistService.EscalateUserAsync(userRecord, await GetEmitterAsync());
await trustlistService.EscalateUserAsync(userRecord, emitter);
}
catch (ArgumentOutOfRangeException)
{
Expand Down
33 changes: 26 additions & 7 deletions Transcom.SocialGuard.Api/Data/Models/Emitter.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using System.ComponentModel.DataAnnotations;



namespace Transcom.SocialGuard.Api.Data.Models
{

/// <summary>
/// Represents an Emitter profile.
/// </summary>
public record Emitter
{
[BsonId]
[BsonId, BsonRepresentation(BsonType.String)]
public string Login { get; init; }

[Required]
Expand All @@ -19,10 +24,24 @@ public record Emitter
public string DisplayName { get; init; }
}

public enum EmitterType
{
Unknown,
User,
Server
/// <summary>
/// Represents types of Emitter profiles.
/// </summary>
public enum EmitterType : byte
{
/// <summary>
/// Represents an unknown, undefined, or other Emitter profile type.
/// </summary>
Unknown = 0,

/// <summary>
/// Emitter is a singular user.
/// </summary>
User = 1,

/// <summary>
/// Emitter is a Discord server.
/// </summary>
Server = 2
}
}
3 changes: 3 additions & 0 deletions Transcom.SocialGuard.Api/Data/Models/EscalationLevel.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
namespace Transcom.SocialGuard.Api.Data.Models
{
/// <summary>
/// Represents severity levels for Trustlist User entries.
/// </summary>
public enum EscalationLevel : byte
{
Neutral = 0,
Expand Down
3 changes: 3 additions & 0 deletions Transcom.SocialGuard.Api/Data/Models/TrustlistUser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@

namespace Transcom.SocialGuard.Api.Data.Models
{
/// <summary>
/// Represents a Trustlist User entry.
/// </summary>
public record TrustlistUser
{
[Required, BsonRequired]
Expand Down
4 changes: 3 additions & 1 deletion Transcom.SocialGuard.Api/Program.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using MongoDB.Bson.Serialization.Conventions;
using System.Threading.Tasks;

namespace Transcom.SocialGuard.Api
Expand All @@ -9,6 +9,8 @@ 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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,12 @@

namespace Transcom.SocialGuard.Api.Services.Authentication
{
public class ApplicationUser : MongoUser
public class ApplicationUser : MongoUser<string>
{
public ApplicationUser() : base()
public ApplicationUser() : base() { }
public ApplicationUser(string username) : base(username)
{
Id = Id == default ? new() : Id;
}
public ApplicationUser(string username) : base(username)
{
Id = Id == default ? new() : Id;
Id = username;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public async Task<AuthServiceResponse> HandleRegister(RegisterModel model)
}

bool firstUser = userManager.Users.Count() is 0;
ApplicationUser user = new() { Email = model.Email, SecurityStamp = Guid.NewGuid().ToString(), UserName = model.Username };
ApplicationUser user = new(model.Username) { Email = model.Email, SecurityStamp = Guid.NewGuid().ToString() };
IdentityResult result = await userManager.CreateAsync(user, model.Password);

if (!result.Succeeded)
Expand All @@ -52,10 +52,7 @@ public async Task<AuthServiceResponse> HandleRegister(RegisterModel model)
await ProvisionFirstUseAsync(user);
}

await userManager.AddToRoleAsync(user, UserRole.User);


return new() { StatusCode = 200, Response = Response.SuccessResponse() with { Message = "User created successfuly." } };
return new() { StatusCode = 201, Response = Response.SuccessResponse() with { Message = "User created successfuly." } };
}

public async Task<AuthServiceResponse> HandleLogin(LoginModel model)
Expand Down Expand Up @@ -94,7 +91,7 @@ private async Task ProvisionFirstUseAsync(ApplicationUser firstUser)
if (roleManager.Roles.Count() is 0)
{
await roleManager.CreateAsync(new(UserRole.Admin));
await roleManager.CreateAsync(new(UserRole.User));
await roleManager.CreateAsync(new(UserRole.Emitter));

await userManager.AddToRoleAsync(firstUser, UserRole.Admin);
}
Expand Down
11 changes: 6 additions & 5 deletions Transcom.SocialGuard.Api/Services/Authentication/UserRole.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@

namespace Transcom.SocialGuard.Api.Services.Authentication
{
public class UserRole : MongoRole
public class UserRole : MongoRole<string>
{
public UserRole() : base() { }
public UserRole(string name) : base(name) { }
public UserRole(string name) : base(name)
{
Id = name;
}


public const string User = "user";
public const string Insert = "insert";
public const string Escalate = "escalate";
public const string Emitter = "emitter";
public const string Admin = "admin";
}
}
18 changes: 13 additions & 5 deletions Transcom.SocialGuard.Api/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public void ConfigureServices(IServiceCollection services)
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Natsecure SocialGuard", Version = "v1" });
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";
Expand Down Expand Up @@ -65,13 +65,20 @@ public void ConfigureServices(IServiceCollection services)

c.AddSecurityRequirement(new OpenApiSecurityRequirement()
{
{ securityScheme, Array.Empty<string>()},
{ securityScheme, Array.Empty<string>() },
});
});

services.AddIdentityMongoDbProvider<ApplicationUser, UserRole>(
services.AddIdentityMongoDbProvider<ApplicationUser, UserRole, string>(
options => { },
mongo => { });
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 =>
{
Expand Down Expand Up @@ -99,6 +106,7 @@ public void ConfigureServices(IServiceCollection services)
services.AddTransient<AuthenticationService>();

services.AddSingleton(s => new MongoClient(Configuration["MongoDatabase:ConnectionString"]).GetDatabase(Configuration["MongoDatabase:DatabaseName"]));

services.AddSingleton<TrustlistUserService>()
.AddSingleton<EmitterService>();
}
Expand All @@ -114,7 +122,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
app.UseStaticFiles();

app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Natsecure SocialGuard v1"));
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/2.0/swagger.json", "Natsecure SocialGuard 2.0"));

app.UseHttpsRedirection();

Expand Down
11 changes: 10 additions & 1 deletion Transcom.SocialGuard.Api/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@

"MongoDatabase": {
"ConnectionString": "mongodb://localhost:27017",
"DatabaseName": "socialguard-api"
"DatabaseName": "transcom-socialguard-api"
},

"Auth": {
"ConnectionString": "mongodb://localhost:27017/transcom-auth",
"Tables": {
"User": "User",
"Role": "Role",
"Migration": "_Migration"
}
}
}

0 comments on commit fcd4db1

Please sign in to comment.