Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/user #4

Merged
merged 7 commits into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions src/Application/Auth/Commands/ForgotPasswordCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ public async Task<bool> Handle(ForgotPasswordCommand request, CancellationToken
{
x.Id,
x.Email,
CompanyName = x.Company.Name,
Name = x.Employee != null ? x.Employee.FullName : "there",
})
.FirstAsync(cancellationToken);
Expand All @@ -43,7 +42,6 @@ public async Task<bool> Handle(ForgotPasswordCommand request, CancellationToken

var emailText = await staticContentReader.ReadContentAsync("email-templates/forgot-password.html");
emailText = emailText.Replace("[User Name]", user.Name);
emailText = emailText.Replace("[Company Name]", user.CompanyName);
emailText = emailText.Replace("[Year]", DateTime.Now.Year.ToString());
emailText = emailText.Replace("[Reset Password Link]", link);
emailService.Send(user.Email!.ToValueObject<Email>(), "Reset Your Password", emailText);
Expand Down
2 changes: 1 addition & 1 deletion src/Application/Common/Abstract/IIdentityService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,5 @@ public interface IIdentityService

Task<bool> IsEmailConfirmedAsync(int userId);

Task AddSeedDataAsync(Company company);
Task AddSeedDataAsync();
}
3 changes: 2 additions & 1 deletion src/Application/Users/Commands/AddUserCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ private async Task<int> CreateUserAsync(AddUserCommand request, CancellationToke
private async Task<User> CreateIdentity(Company company, AddUserCommand request)
{
var role = await identityService.GetRoleAsync(request.Role.AsEnum<RoleName>());
var user = User.Create(request.Email.ToValueObject<Email>(), request.PhoneNumber.ToValueObject<PhoneNumber>(), company.Id);
var user = User.Create(request.Email.ToValueObject<Email>(), request.PhoneNumber.ToValueObject<PhoneNumber>());

var claims = new Dictionary<string, string>
{
Expand All @@ -71,6 +71,7 @@ private async Task<User> CreateIdentity(Company company, AddUserCommand request)
{
UserId = user.Id,
});

return user;
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Application/Users/Events/UserCreatedEvent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public async Task Handle(UserCreatedEvent notification, CancellationToken cancel
{
x.Id,
x.Email,
CompanyName = x.Company.Name,
CompanyName = x.Employee != null ? x.Employee.Company.Name : "",
Name = x.Employee != null ? x.Employee.FullName : "there",
})
.FirstAsync(cancellationToken);
Expand Down
4 changes: 1 addition & 3 deletions src/Database/Scripts/20240407.sql
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ CREATE TABLE domain_events (
-- users
CREATE TABLE public.users (
id int4 GENERATED BY DEFAULT AS IDENTITY( INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START 1 CACHE 1 NO CYCLE) NOT NULL,
company_id int NOT NULL,
user_name text NULL,
normalized_user_name text NULL,
email text NULL,
Expand All @@ -49,8 +48,7 @@ CREATE TABLE public.users (
created_on timestamptz NOT NULL,
modified_by int NULL,
modified_on timestamptz NULL,
CONSTRAINT pk_users PRIMARY KEY (id),
CONSTRAINT fk_users_company_id FOREIGN KEY (company_id) REFERENCES companies (id) MATCH SIMPLE
CONSTRAINT pk_users PRIMARY KEY (id)
);
CREATE UNIQUE INDEX uk_users_email ON public.users USING btree (email);
CREATE UNIQUE INDEX uk_users_phone_number ON public.users USING btree (phone_number);
Expand Down
8 changes: 1 addition & 7 deletions src/Domain/Identity/User.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using Domain.Companies;
using Domain.Employees;
using Microsoft.AspNetCore.Identity;

Expand All @@ -11,22 +10,17 @@ private User()
Claims = [];
}

public int CompanyId { get; private set; }

public Company Company { get; private set; } = null!;

public Employee? Employee { get; private set; } = null!;

public ICollection<IdentityUserClaim<int>> Claims { get; private set; }

public static User Create(Email email, PhoneNumber phoneNumber, int companyId)
public static User Create(Email email, PhoneNumber phoneNumber)
{
var user = new User
{
UserName = email.Value,
Email = email.Value,
PhoneNumber = phoneNumber.Value,
CompanyId = companyId
};

return user;
Expand Down
5 changes: 2 additions & 3 deletions src/Infrastructure/BackgroundJobs/SeedDataJob.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public async Task Run()
{
if (!await dbContext.Companies.AnyAsync())
{
var company = Company.Create("Acme", "admin@example.com".ToValueObject<Email>(), "9876543210".ToValueObject<PhoneNumber>(), "Acme Admin", 3.ToValueObject<FinancialMonth>());
var company = Company.Create("Example Company", "admin@example.com".ToValueObject<Email>(), "9876543210".ToValueObject<PhoneNumber>(), "Super Admin", 3.ToValueObject<FinancialMonth>());
await dbContext.Companies.AddAsync(company);
await dbContext.SaveChangesAsync(default);
}
Expand All @@ -18,8 +18,7 @@ public async Task Run()
using var transaction = await dbContext.Database.BeginTransactionAsync();
try
{
var company = await dbContext.Companies.FirstAsync();
await identityService.AddSeedDataAsync(company);
await identityService.AddSeedDataAsync();
await transaction.CommitAsync();
}
catch (Exception)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,6 @@ public void Configure(EntityTypeBuilder<User> builder)
.WithOne()
.HasForeignKey(uc => uc.UserId)
.IsRequired();

builder
.Property(x => x.CompanyId)
.HasColumnName("company_id");
}
}

Expand Down
31 changes: 0 additions & 31 deletions src/Infrastructure/Services/CustomClaimsTransformation.cs

This file was deleted.

4 changes: 2 additions & 2 deletions src/Infrastructure/Services/IdentityService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ public async Task<bool> IsEmailConfirmedAsync(int userId)
return await userManager.IsEmailConfirmedAsync(user!);
}

public async Task AddSeedDataAsync(Company company)
public async Task AddSeedDataAsync()
{
foreach (var roleName in (RoleName[])Enum.GetValues(typeof(RoleName)))
{
Expand All @@ -118,7 +118,7 @@ public async Task AddSeedDataAsync(Company company)
VerifyResult(result);
}

User user = User.Create("admin@example.com".ToValueObject<Email>(), "9876543210".ToValueObject<PhoneNumber>(), company.Id);
User user = User.Create("admin@example.com".ToValueObject<Email>(), "9876543210".ToValueObject<PhoneNumber>());
var role = await GetRoleAsync(RoleName.SuperAdmin);
await CreateUserAsync(user, role);
var token = await GenerateAccountVerificationTokenAsync(user.Id);
Expand Down
16 changes: 5 additions & 11 deletions src/Infrastructure/Services/JwtService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Security.Cryptography;
using System.Text;
using Application.Auth.Commands;
using Domain.Identity;
using Microsoft.AspNetCore.Http;
using Microsoft.IdentityModel.Tokens;

Expand Down Expand Up @@ -123,31 +124,24 @@ private async Task<string> GenerateRefreshTokenAsync(User user, CancellationToke
private async Task AddCustomClaimsAsync(List<Claim> claims, CancellationToken cancellationToken)
{
var userId = int.Parse(claims.Find(x => x.Type == ClaimTypes.NameIdentifier)!.Value);
var user = await dbContext
.Users
.Select(x => new
{
x.Id,
x.CompanyId,
})
.FirstAsync(x => x.Id == userId);

var employee = await dbContext
.Employees
.IgnoreQueryFilters()
.Where(x => x.UserId == userId && x.CompanyId == user.CompanyId)
.Where(x => x.UserId == userId)
.Select(x => new
{
x.Id,
x.FullName
x.FullName,
x.CompanyId
})
.FirstOrDefaultAsync(cancellationToken);

claims.Add(new Claim("companyId", user.CompanyId.ToString()));
if (employee != null)
{
claims.Add(new Claim("employeeId", employee.Id.ToString()!));
claims.Add(new Claim("name", employee.FullName.ToString()!));
claims.Add(new Claim("companyId", employee.CompanyId.ToString()));
}
}
}
34 changes: 33 additions & 1 deletion src/Presentation.Server/DependencyRegistration/AuthPolicy.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,44 @@
using static Domain.Common.Enums;

namespace Presentation.DependencyRegistration
{
public static class AuthPolicy
internal static class AuthPolicy
{
public const string AllRoles = "ALL-ROLES-CAN-ACCESS";
public const string SuperAdmin = "ONLY-SUPER-ADMIN-CAN-ACCESS";
public const string CompanyAdmin = "ONLY-COMPANY-ADMIN-CAN-ACCESS";
public const string CompanyAdminOrEmployee = "COMPANY-ADMIN-AND-EMPLOYEE-CAN-ACCESS";
public const string Employee = "ONLY-EMPLOYEE-CAN-ACCESS";
public const string AllowAnonymous = "ALLOW-ANONYMOUS";

internal static IServiceCollection AddAuthPolicy(this IServiceCollection services)
{
services.AddAuthorizationBuilder()
.AddPolicy(AllRoles, policy => policy.RequireAssertion(context =>
{
return context.User.IsInRole(RoleName.SuperAdmin.ToString()) ||
context.User.IsInRole(RoleName.CompanyAdmin.ToString()) ||
context.User.IsInRole(RoleName.Employee.ToString());
}))
.AddPolicy(SuperAdmin, policy => policy.RequireAssertion(context =>
{
return context.User.IsInRole(RoleName.SuperAdmin.ToString());
}))
.AddPolicy(CompanyAdmin, policy => policy.RequireAssertion(context =>
{
return context.User.IsInRole(RoleName.CompanyAdmin.ToString());
}))
.AddPolicy(CompanyAdminOrEmployee, policy => policy.RequireAssertion(context =>
{
return context.User.IsInRole(RoleName.CompanyAdmin.ToString()) ||
context.User.IsInRole(RoleName.Employee.ToString());
}))
.AddPolicy(Employee, policy => policy.RequireAssertion(context =>
{
return context.User.IsInRole(RoleName.Employee.ToString());
}));

return services;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using Microsoft.AspNetCore.Mvc.Authorization;

namespace Presentation.DependencyRegistration
{
public static class DependencyRegistration
Expand All @@ -9,7 +11,7 @@ public static IServiceCollection AddPresentationServices(this IServiceCollection
builder.Services.AddSwagger();
services.AddHttpContextAccessor();
services.AddSerilogUI(builder.Configuration);
services.AddMvcWithAuthPolicy();
services.AddAuthPolicy();
services.AddHealthChecks();

services.AddCors(options => options.AddPolicy("CORS",
Expand All @@ -23,6 +25,11 @@ public static IServiceCollection AddPresentationServices(this IServiceCollection
}
}));

services.AddMvc(options =>
{
options.Filters.Add(new AuthorizeFilter());
});

return services;
}
}
Expand Down
41 changes: 0 additions & 41 deletions src/Presentation.Server/DependencyRegistration/Mvc.cs

This file was deleted.

14 changes: 9 additions & 5 deletions src/Presentation.Server/DependencyRegistration/SerilogUI.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using Serilog;
using Serilog.Ui.Core.Extensions;
using Serilog.Ui.PostgreSqlProvider.Extensions;
using Serilog.Ui.Web.Extensions;
Expand All @@ -11,11 +10,16 @@ internal static void AddSerilogUI(this IServiceCollection services, IConfigurati
{
services.AddSerilog();

var logTable = configuration.GetValue<string>("Serilog:WriteTo:0:Args:tableName")!;
var connectionString = configuration.GetValue<string>("Serilog:WriteTo:0:Args:connectionString")!;

services.AddSerilogUi(options =>
options.UseNpgSql(options => options.WithConnectionString(connectionString).WithTable(logTable)));
{
var logTable = configuration.GetValue<string>("Serilog:WriteTo:0:Args:tableName")!;
var connectionString = configuration.GetValue<string>("Serilog:WriteTo:0:Args:connectionString")!;

options.UseNpgSql(options => options.WithConnectionString(connectionString)
.WithTable(logTable))
.AddScopedBasicAuthFilter();
})
.AddControllersWithViews();
}
}
}
6 changes: 3 additions & 3 deletions src/Presentation.Server/DependencyRegistration/Swagger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ public static void AddSwagger(this IServiceCollection services)
options.SwaggerDoc("v1", new OpenApiInfo
{
Version = "v1",
Title = "Bit Xplorer HR",
Description = "Rest API for accessing CAB Portal data",
Title = "BIT-HRMS",
Description = "Rest API for accessing BIT-HRMS data",
TermsOfService = new Uri(EXAMPLE_DOT_COM),
Contact = new OpenApiContact
{
Name = "Rawae API",
Name = "BIT-HRMS API",
Url = new Uri(EXAMPLE_DOT_COM)
},
});
Expand Down
13 changes: 7 additions & 6 deletions src/Presentation.Server/Middleware/Middleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,15 @@ public static void ConfigureMiddleware(this WebApplication app)
app.UseSwaggerUI();
}

if (!app.Environment.IsDevelopment())
{
app.UseHttpsRedirection();
}

app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.UseSerilogUi();

app.UseSerilogUi(options =>
{
options.WithAuthenticationType(Serilog.Ui.Web.Models.AuthenticationType.Basic)
.WithExpandedDropdownsByDefault();
});
app.MapControllers();
app.MapFallbackToFile("/index.html");
app.UseHealthChecks("/health");
Expand Down
Loading