243 lines
9.9 KiB
C#
243 lines
9.9 KiB
C#
using AutoMapper;
|
|
using Marco.Pms.DataAccess.Data;
|
|
using Marco.Pms.Model.Dtos.Tenant;
|
|
using Marco.Pms.Model.Employees;
|
|
using Marco.Pms.Model.Entitlements;
|
|
using Marco.Pms.Model.Roles;
|
|
using Marco.Pms.Model.Utilities;
|
|
using Marco.Pms.Model.ViewModels.Activities;
|
|
using Marco.Pms.Model.ViewModels.Tenant;
|
|
using Marco.Pms.Services.Service;
|
|
using MarcoBMS.Services.Helpers;
|
|
using MarcoBMS.Services.Service;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Identity;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using System.Net;
|
|
using System.Text.RegularExpressions;
|
|
|
|
// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
|
|
|
|
namespace Marco.Pms.Services.Controllers
|
|
{
|
|
[Route("api/[controller]")]
|
|
[ApiController]
|
|
[Authorize]
|
|
public class TenantController : ControllerBase
|
|
{
|
|
private readonly IDbContextFactory<ApplicationDbContext> _dbContextFactory;
|
|
private readonly IServiceScopeFactory _serviceScopeFactory;
|
|
private readonly ILoggingService _logger;
|
|
private readonly UserManager<ApplicationUser> _userManager;
|
|
private readonly IMapper _mapper;
|
|
private readonly static Guid activeStatus = Guid.Parse("62b05792-5115-4f99-8ff5-e8374859b191");
|
|
public TenantController(IDbContextFactory<ApplicationDbContext> dbContextFactory,
|
|
IServiceScopeFactory serviceScopeFactory,
|
|
ILoggingService logger,
|
|
UserManager<ApplicationUser> userManager,
|
|
IMapper mapper)
|
|
{
|
|
_dbContextFactory = dbContextFactory;
|
|
_serviceScopeFactory = serviceScopeFactory;
|
|
_logger = logger;
|
|
_userManager = userManager;
|
|
_mapper = mapper;
|
|
}
|
|
// GET: api/<TenantController>
|
|
[HttpGet]
|
|
public IEnumerable<string> Get()
|
|
{
|
|
return new string[] { "value1", "value2" };
|
|
}
|
|
|
|
// GET api/<TenantController>/5
|
|
[HttpGet("{id}")]
|
|
public string Get(int id)
|
|
{
|
|
return "value";
|
|
}
|
|
|
|
// POST api/<TenantController>
|
|
[HttpPost("create")]
|
|
public async Task<IActionResult> Post([FromBody] CreateTenantDto model)
|
|
{
|
|
using var scope = _serviceScopeFactory.CreateScope();
|
|
await using var _context = await _dbContextFactory.CreateDbContextAsync();
|
|
|
|
var _configuration = scope.ServiceProvider.GetRequiredService<IConfiguration>();
|
|
var _emailSender = scope.ServiceProvider.GetRequiredService<IEmailSender>();
|
|
|
|
var userHelper = scope.ServiceProvider.GetRequiredService<UserHelper>();
|
|
var loggedInEmployee = await userHelper.GetCurrentEmployeeAsync();
|
|
|
|
var permissionService = scope.ServiceProvider.GetRequiredService<PermissionServices>();
|
|
var hasPermission = await permissionService.HasPermission(PermissionsMaster.ManageTenants, loggedInEmployee.Id);
|
|
if (!hasPermission || !(loggedInEmployee.ApplicationUser?.IsRootUser ?? false))
|
|
{
|
|
_logger.LogWarning("User {EmployeeId} attmpted to create new tenant but not have permissions", loggedInEmployee.Id);
|
|
return StatusCode(403, ApiResponse<object>.ErrorResponse("Access denied", "User don't have rights for this action", 403));
|
|
}
|
|
|
|
var existingUser = await _userManager.FindByEmailAsync(model.Email);
|
|
if (existingUser != null)
|
|
{
|
|
_logger.LogWarning("User {EmployeeId} attempted to create tenant with email {Email} but already exists in database", loggedInEmployee.Id, model.Email);
|
|
return StatusCode(409, ApiResponse<object>.ErrorResponse("Tenant can't be created", "User with same email already exists", 409));
|
|
}
|
|
var isTenantExists = await _context.Tenants.AnyAsync(t => t.TaxId != null && t.TaxId == model.TaxId);
|
|
if (isTenantExists)
|
|
{
|
|
_logger.LogWarning("User {EmployeeId} attempted to create tenant with duplicate taxId", loggedInEmployee.Id);
|
|
return StatusCode(409, ApiResponse<object>.ErrorResponse("Tenant can't be created", "User with same taxId already exists", 409));
|
|
}
|
|
if (!string.IsNullOrWhiteSpace(model.logoImage) && !IsBase64String(model.logoImage))
|
|
{
|
|
_logger.LogWarning("User {EmployeeId} attempted to create tenant with Invalid logoImage", loggedInEmployee.Id);
|
|
return StatusCode(400, ApiResponse<object>.ErrorResponse("Tenant can't be created", "User with same taxId already exists", 400));
|
|
}
|
|
await using var transaction = await _context.Database.BeginTransactionAsync();
|
|
try
|
|
{
|
|
var tenant = new Tenant
|
|
{
|
|
Name = model.OragnizationName,
|
|
ContactName = $"{model.FirstName} {model.LastName}",
|
|
ContactNumber = model.ContactNumber,
|
|
Email = model.Email,
|
|
IndustryId = model.IndustryId,
|
|
TenantStatusId = activeStatus,
|
|
Description = model.Description,
|
|
OnBoardingDate = model.OnBoardingDate,
|
|
OragnizationSize = model.OragnizationSize,
|
|
Reference = model.Reference,
|
|
CreatedById = loggedInEmployee.Id,
|
|
BillingAddress = model.BillingAddress,
|
|
TaxId = model.TaxId,
|
|
logoImage = model.logoImage,
|
|
DomainName = model.DomainName,
|
|
IsSuperTenant = false
|
|
};
|
|
|
|
_context.Tenants.Add(tenant);
|
|
|
|
await _context.SaveChangesAsync();
|
|
var designation = new JobRole
|
|
{
|
|
Name = "Admin",
|
|
Description = "Root degination for tenant only",
|
|
TenantId = tenant.Id
|
|
};
|
|
var applicationUser = new ApplicationUser
|
|
{
|
|
Email = model.Email,
|
|
UserName = model.Email,
|
|
IsRootUser = true,
|
|
EmailConfirmed = true,
|
|
TenantId = tenant.Id
|
|
};
|
|
_context.JobRoles.Add(designation);
|
|
|
|
var result = await _userManager.CreateAsync(applicationUser, "User@123");
|
|
if (!result.Succeeded)
|
|
return BadRequest(ApiResponse<object>.ErrorResponse("Failed to create user", result.Errors, 400));
|
|
|
|
await _context.SaveChangesAsync();
|
|
|
|
var employeeUser = new Employee
|
|
{
|
|
FirstName = model.FirstName,
|
|
LastName = model.LastName,
|
|
Email = model.Email,
|
|
PhoneNumber = model.ContactNumber,
|
|
ApplicationUserId = applicationUser.Id,
|
|
JobRole = designation,
|
|
CurrentAddress = model.BillingAddress,
|
|
TenantId = tenant.Id
|
|
};
|
|
|
|
await _context.SaveChangesAsync();
|
|
|
|
var token = await _userManager.GeneratePasswordResetTokenAsync(applicationUser);
|
|
var resetLink = $"{_configuration["AppSettings:WebFrontendUrl"]}/reset-password?token={WebUtility.UrlEncode(token)}";
|
|
if (employeeUser.FirstName != null)
|
|
{
|
|
await _emailSender.SendResetPasswordEmailOnRegister(applicationUser.Email, employeeUser.FirstName, resetLink);
|
|
}
|
|
var vm = _mapper.Map<TenantVM>(tenant);
|
|
vm.CreatedBy = _mapper.Map<BasicEmployeeVM>(loggedInEmployee);
|
|
return Ok(tenant);
|
|
}
|
|
catch (DbUpdateException dbEx)
|
|
{
|
|
_logger.LogError(dbEx, "Database Exception occured while creating tenant");
|
|
return StatusCode(500, ApiResponse<object>.ErrorResponse("Internal Error occured", ExceptionMapper(dbEx), 500));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Exception occured while creating tenant");
|
|
return StatusCode(500, ApiResponse<object>.ErrorResponse("Internal Error occured", ExceptionMapper(ex), 500));
|
|
}
|
|
}
|
|
|
|
// PUT api/<TenantController>/5
|
|
[HttpPut("{id}")]
|
|
public void Put(int id, [FromBody] string value)
|
|
{
|
|
}
|
|
|
|
// DELETE api/<TenantController>/5
|
|
[HttpDelete("{id}")]
|
|
public void Delete(int id)
|
|
{
|
|
}
|
|
|
|
|
|
|
|
private static object ExceptionMapper(Exception ex)
|
|
{
|
|
return new
|
|
{
|
|
Message = ex.Message,
|
|
StackTrace = ex.StackTrace,
|
|
Source = ex.Source,
|
|
InnerException = new
|
|
{
|
|
Message = ex.InnerException?.Message,
|
|
StackTrace = ex.InnerException?.StackTrace,
|
|
Source = ex.InnerException?.Source,
|
|
}
|
|
};
|
|
}
|
|
private bool IsBase64String(string? input)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(input))
|
|
return false;
|
|
|
|
// Normalize string
|
|
input = input.Trim();
|
|
|
|
// Length must be multiple of 4
|
|
if (input.Length % 4 != 0)
|
|
return false;
|
|
|
|
// Valid Base64 characters with correct padding
|
|
var base64Regex = new Regex(@"^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$");
|
|
if (!base64Regex.IsMatch(input))
|
|
return false;
|
|
|
|
try
|
|
{
|
|
// Decode and re-encode to confirm validity
|
|
var bytes = Convert.FromBase64String(input);
|
|
var reEncoded = Convert.ToBase64String(bytes);
|
|
return input == reEncoded;
|
|
}
|
|
catch
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|