Compare commits

...

3 Commits
dev ... dev2

  1. 6
      Client/Biskilog Accounting.Client.csproj
  2. 17
      Client/Pages/Auth/Login.razor
  3. 32
      Client/Pages/Auth/Login.razor.cs
  4. 8
      Client/Properties/launchSettings.json
  5. 10
      Server/BiskAcdbContext.cs
  6. 7
      Server/Biskilog Accounting.Server.csproj
  7. 24
      Server/BiskilogContext.cs
  8. 39
      Server/Controllers/AuthenticationController.cs
  9. 85
      Server/Program.cs
  10. 40
      Server/Properties/launchSettings.json
  11. 144
      Server/Services/AuthenticationService.cs
  12. 52
      Server/Services/ConnectionService.cs
  13. 11
      Server/appsettings.json
  14. 9
      Shared/Biskilog Accounting.Shared.csproj
  15. 3
      Shared/ClientContractModels/Databasemap.cs
  16. 6
      Shared/ClientContractModels/Userauth.cs
  17. 17
      Shared/Enums/AuthEnums.cs
  18. 14
      Shared/Enums/ConnectionEnums.cs
  19. 19
      Shared/Interfaces/IAuthService.cs
  20. 24
      Shared/Interfaces/IConnectionService.cs
  21. 15
      Shared/Interfaces/ITokenService.cs
  22. 3
      Shared/POSModels/Tbluser.cs
  23. 124
      Shared/ServiceRepo/TokenService.cs

6
Client/Biskilog Accounting.Client.csproj

@ -8,6 +8,12 @@
<AssemblyName>$(AssemblyName.Replace(' ', '_'))</AssemblyName>
</PropertyGroup>
<PropertyGroup Condition=" '$(RunConfiguration)' == 'https' ">
<ExternalConsole>true</ExternalConsole>
</PropertyGroup>
<PropertyGroup Condition=" '$(RunConfiguration)' == 'http' ">
<ExternalConsole>true</ExternalConsole>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="7.0.5" />

17
Client/Pages/Auth/Login.razor

@ -1,5 +1,8 @@
@layout AuthLayout
@page "/"
@inject HttpClient httpclient
@inject NavigationManager navigationmanager
<style>
.gradient-custom-2 {
/* fallback for old browsers */
@ -58,6 +61,9 @@
}
</style>
<section class="h-100 gradient-form" style="background-color: #eee;">
<div class="container py-5 h-100">
<div class="row d-flex justify-content-center align-items-center h-100">
@ -75,20 +81,25 @@
<form>
<p>Please login to your account</p>
@if (!string.IsNullOrEmpty(errormessage))
{
<p class="text-danger">@errormessage</p>
}
<div class="form-outline mb-4">
<input type="email" id="form2Example11" class="form-control"
placeholder="Phone number or email address" />
placeholder="Phone number or email address" @bind-value="@m_userAuth.Username" />
<label class="form-label" for="form2Example11">Username</label>
</div>
<div class="form-outline mb-4">
<input type="password" id="form2Example22" class="form-control" />
<input type="password" id="form2Example22" class="form-control"
@bind-value="@m_userAuth.Passsword" />
<label class="form-label" for="form2Example22">Password</label>
</div>
<div class="text-center pt-1 mb-5 pb-1">
<button class="btn btn-primary btn-block fa-lg gradient-custom-2 mb-3" type="button">
<button class="btn btn-primary btn-block fa-lg gradient-custom-2 mb-3" @onclick="AuthUser" type="button">
Log
in
</button>

32
Client/Pages/Auth/Login.razor.cs

@ -0,0 +1,32 @@
using System.Net.Http.Json;
using Biskilog_Accounting.Shared.ClientContractModels;
namespace Biskilog_Accounting.Client.Pages.Auth
{
public partial class Login
{
private Userauth m_userAuth;
private string errormessage;
protected override void OnInitialized()
{
m_userAuth = new();
base.OnInitialized();
}
public async Task AuthUser()
{
var response = await httpclient.PostAsJsonAsync("api/authentication", m_userAuth);
if (response.IsSuccessStatusCode)
{
navigationmanager.NavigateTo("/welcome");
}
else
{
errormessage = "Invalid Username or password";
}
}
}
}

8
Client/Properties/launchSettings.json

@ -8,23 +8,23 @@
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
"applicationUrl": "http://localhost:5136",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"dotnetRunMessages": true
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
"applicationUrl": "https://localhost:7247;http://localhost:5136",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"dotnetRunMessages": true
},
"IIS Express": {
"commandName": "IISExpress",

10
Server/DevBiskacdbContext.cs → Server/BiskAcdbContext.cs

@ -5,13 +5,13 @@ using Microsoft.EntityFrameworkCore;
namespace Biskilog_Accounting.Server.POSModels;
public partial class DevBiskacdbContext : DbContext
public partial class BiskAcdbContext : DbContext
{
public DevBiskacdbContext()
public BiskAcdbContext()
{
}
public DevBiskacdbContext(DbContextOptions<DevBiskacdbContext> options)
public BiskAcdbContext(DbContextOptions<BiskAcdbContext> options)
: base(options)
{
}
@ -76,10 +76,6 @@ public partial class DevBiskacdbContext : DbContext
public virtual DbSet<Unitofmeasure> Unitofmeasures { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see http://go.microsoft.com/fwlink/?LinkId=723263.
=> optionsBuilder.UseMySql("server=54.37.19.162;database=dev_biskacdb;user=biskilog;password=mefbuk-6niFsu-fytrew", Microsoft.EntityFrameworkCore.ServerVersion.Parse("10.3.38-mariadb"));
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder

7
Server/Biskilog Accounting.Server.csproj

@ -8,6 +8,8 @@
<AssemblyName>$(AssemblyName.Replace(' ', '_'))</AssemblyName>
</PropertyGroup>
<PropertyGroup Condition=" '$(RunConfiguration)' == 'https' " />
<PropertyGroup Condition=" '$(RunConfiguration)' == 'http' " />
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="7.0.5" />
<PackageReference Include="BCrypt.Net" Version="0.1.0" />
@ -15,6 +17,7 @@
<PackageReference Include="AutoMapper" Version="12.0.1" />
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
<PackageReference Include="CsvHelper" Version="30.0.1" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
@ -35,8 +38,4 @@
<ProjectReference Include="..\Shared\Biskilog Accounting.Shared.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Services\" />
</ItemGroup>
</Project>

24
Server/DevBiskilogclientsContext.cs → Server/BiskilogContext.cs

@ -4,14 +4,15 @@ using Biskilog_Accounting.Shared.ClientContractModels;
using Microsoft.EntityFrameworkCore;
namespace Biskilog_Accounting.Server;
public partial class DevBiskilogclientsContext : DbContext
/// <summary>
/// This is the main EF DbContext for the Biskilog Accounting
/// </summary>
public partial class BiskilogContext : DbContext
{
public DevBiskilogclientsContext()
public BiskilogContext()
{
}
public DevBiskilogclientsContext(DbContextOptions<DevBiskilogclientsContext> options)
public BiskilogContext(DbContextOptions<BiskilogContext> options)
: base(options)
{
}
@ -30,10 +31,6 @@ public partial class DevBiskilogclientsContext : DbContext
public virtual DbSet<Userauth> Userauths { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see http://go.microsoft.com/fwlink/?LinkId=723263.
=> optionsBuilder.UseMySql("server=54.37.19.162;database=dev_biskilogclients;user=biskilog;password=mefbuk-6niFsu-fytrew", ServerVersion.Parse("10.3.38-mariadb"));
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder
@ -182,6 +179,15 @@ public partial class DevBiskilogclientsContext : DbContext
.HasColumnName("db_name")
.UseCollation("utf8mb4_general_ci")
.HasCharSet("utf8mb4");
entity.Property(e => e.Domain)
.HasMaxLength(50)
.HasDefaultValueSql("''")
.HasColumnName("domain")
.UseCollation("utf8mb4_general_ci")
.HasCharSet("utf8mb4");
entity.Property(e => e.LastSyncDate)
.HasColumnType("datetime")
.HasColumnName("last_sync_date");
});
modelBuilder.Entity<Siteaccesspermission>(entity =>

39
Server/Controllers/AuthenticationController.cs

@ -0,0 +1,39 @@
using Biskilog_Accounting.Shared.ClientContractModels;
using Biskilog_Accounting.Shared.Interfaces;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.DotNet.Scaffolding.Shared.CodeModifier.CodeChange;
namespace Biskilog_Accounting.Server.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class AuthenticationController : ControllerBase
{
private readonly IAuthService m_authService;
private readonly ITokenService m_tokenService;
public AuthenticationController(IAuthService a_authService, ITokenService a_tokenService)
{
m_authService = a_authService;
m_tokenService = a_tokenService;
}
[HttpPost, Route("type-a")]
public async Task<IActionResult> AuthUser(Userauth a_userData)
{
if (a_userData != null && a_userData.Username != null && a_userData.Passsword != null)
{
var token = await m_authService.AuthenticateClient(a_userData.Username, a_userData.Passsword);
if (token == null)
return BadRequest("Invalid credentials");
return Ok(token.ToString());
}
else
{
return BadRequest();
}
}
}
}

85
Server/Program.cs

@ -1,7 +1,78 @@
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.ResponseCompression;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using System.Text.Json.Serialization;
using System.Text;
using Biskilog_Accounting.Server;
using Biskilog_Accounting.ServiceRepo;
using Biskilog_Accounting.Shared.Interfaces;
using Biskilog_Accounting.Server.Services;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers().AddJsonOptions(x => x.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles);
builder.Services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
builder.Logging.ClearProviders();
builder.Logging.AddConsole();
builder.Services.AddEntityFrameworkMySql().AddDbContext<BiskilogContext>(options =>
{
options.UseMySql(builder.Configuration.GetConnectionString("Connection"), new MariaDbServerVersion(new Version()));
});
builder.Services.AddScoped<IAuthService, AuthenticationService>();
builder.Services.AddScoped<ITokenService, TokenService>();
builder.Services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
);
});
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
{
options.RequireHttpsMetadata = false;
options.SaveToken = true;
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
ValidateAudience = true,
ValidAudience = builder.Configuration["Jwt:Audience"],
ValidIssuer = builder.Configuration["Jwt:Issuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]))
};
});
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "MyBlazor", Version = "v1" });
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
In = ParameterLocation.Header,
Description = "Please enter a valid token",
Name = "Authorization",
Type = SecuritySchemeType.Http,
BearerFormat = "JWT",
Scheme = "Bearer"
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type=ReferenceType.SecurityScheme,
Id="Bearer"
}
},
new string[]{}
}
});
});
builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();
@ -10,19 +81,31 @@ var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseWebAssemblyDebugging();
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "MyBlazor v1");
c.RoutePrefix = "api/docs";
});
}
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.UseHttpsRedirection();
app.UseBlazorFrameworkFiles();
app.UseStaticFiles();
app.UseRouting();
app.UseCors("CorsPolicy");
app.UseAuthentication();
app.UseAuthorization();
app.MapRazorPages();
app.MapControllers();

40
Server/Properties/launchSettings.json

@ -1,44 +1,44 @@
{
"iisExpress": {
"applicationUrl": "http://localhost:24369",
"sslPort": 44366
},
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:54487/",
"sslPort": 44383
}
},
"profiles": {
"http": {
"commandName": "Project",
"launchBrowser": true,
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
"applicationUrl": "http://localhost:5136",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"dotnetRunMessages": true,
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
"applicationUrl": "http://localhost:5136"
"dotnetRunMessages": true
},
"https": {
"commandName": "Project",
"launchBrowser": true,
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
"applicationUrl": "https://localhost:7247;http://localhost:5136",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"dotnetRunMessages": true,
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
"applicationUrl": "https://localhost:7247;http://localhost:5136"
"dotnetRunMessages": true
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}"
}
},
"iisExpress": {
"applicationUrl": "http://localhost:24369",
"sslPort": 44366
},
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:54487/",
"sslPort": 44383
}
}
}
}

144
Server/Services/AuthenticationService.cs

@ -0,0 +1,144 @@
using Biskilog_Accounting.Shared.ClientContractModels;
using Biskilog_Accounting.Shared.Enums;
using Biskilog_Accounting.Shared.Interfaces;
using Microsoft.EntityFrameworkCore;
namespace Biskilog_Accounting.Server.Services
{
public class AuthenticationService : IAuthService
{
private readonly BiskilogContext m_context;
private readonly ITokenService m_tokenService;
public AuthenticationService( BiskilogContext a_context, ITokenService a_tokenService)
{
m_context = a_context;
m_tokenService = a_tokenService;
}
/// <summary>
/// Returns the status of a user account
/// </summary>
/// <returns>AuthEnums</returns>
public AuthEnums AccountStatus(int? a_id, string? a_username)
{
if (m_context.Userauths.Any(i => i.UserId == a_id || i.Username == a_username))
{
return AuthEnums.Found;
}
else
{
return AuthEnums.NotFound;
}
}
/// <summary>
/// Autenticates a user and returns a tokenized string
/// </summary>
/// <param name="a_username"></param>
/// <param name="a_password"></param>
/// <returns>strings</returns>
public async Task<string> AuthenticateClient(string a_username, string a_password)
{
var user = await GetUserAsync(a_username, a_password);
if (user == null)
{
return null;
}
user.LastLogin = DateTime.Now;
m_context.Userauths.Update(user);
m_context.SaveChanges();
Databasemap databasemap = GetClientDB(user.ClientId);
List<int> businessIds = GetSiteaccesspermission(user.ClientId, user.UserId).Select(t => t.BusinessId).ToList();
Contract? contract = GetContract(user.ClientId, businessIds);
if (contract == null)
return AuthEnums.Invalid.ToString();
return m_tokenService.GenerateToken(user, contract, databasemap);
}
/// <summary>
/// Creates a new user account
/// </summary>
/// returns AuthEnums.successful if the account was created successfully
public AuthEnums CreateUser(Userauth a_user)
{
throw new NotImplementedException();
//if (AccountStatus(null, a_user.Username) == AuthEnums.Found)
// return AuthEnums.Registered;
//a_user.Role = m_context.UserRoles.First(i => i.RoleId== a_user.RoleId);
//a_user.Password = BCrypt.Net.BCrypt.HashPassword(a_user.Password);
//m_context.Users.Add(a_user);
//var result = m_context.SaveChangesAsync();
//result.Wait();
//if(result.Result > 0)
//{
// return AuthEnums.Successful;
//}
//return AuthEnums.Error;
}
/// <summary>
/// Deletes a user account
/// </summary>
/// <exception cref="NotImplementedException"></exception>
public void DeleteUser(int a_id)
{
throw new NotImplementedException();
}
public List<Clientbusiness> GetClientbusiness(int a_clientId)
{
return m_context.Clientbusinesses.Where(i => i.ClientId == a_clientId).ToList();
}
public Databasemap GetClientDB(int a_clientId)
{
return m_context.Databasemaps.First(t => t.ClientId == a_clientId);
}
public Contract? GetContract(int a_clientId, List<int> a_businessId)
{
return m_context.Contracts.FirstOrDefault(c => c.ClientId == a_clientId && a_businessId.Contains(c.BusinessId!.Value) && c.EndDate >= DateTime.Now);
}
public List<Siteaccesspermission> GetSiteaccesspermission(int a_clientId, int a_userId)
{
return m_context.Siteaccesspermissions.Where(t => t.ClientId == a_clientId && t.UserId == a_userId).ToList();
}
private async Task<Userauth> GetUserAsync(string username, string password)
{
//Todo have complete implementation after means of creating user is done
//try
//{
// string pa = await m_context.Userauths.Where(u => u.Username == username).Select(u => u.Password).FirstAsync();
// bool verified = BCrypt.Net.BCrypt.Verify(password, pa);
// if (verified)
// {
//TODO have a complete implementation
return await m_context.Userauths.FirstAsync(u => u.Username == username && u.Passsword == password);
// }
// else
// {
// return null;
// }
//}catch(Exception ex)
//{
// //possible is user not found
// return null;
//}
}
}
}

52
Server/Services/ConnectionService.cs

@ -0,0 +1,52 @@
using Biskilog_Accounting.Server.POSModels;
using Biskilog_Accounting.Shared.ClientContractModels;
using Biskilog_Accounting.Shared.Enums;
using Biskilog_Accounting.Shared.Interfaces;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
namespace Biskilog_Accounting.Server.Services
{
public class ConnectionService : IConnectionService
{
private readonly BiskilogContext m_context;
private readonly ITokenService m_tokenService;
private readonly IConfiguration m_configuration;
public ConnectionService(BiskilogContext a_context, ITokenService a_tokenService, IConfiguration configuration)
{
m_context = a_context;
m_tokenService = a_tokenService;
m_configuration = configuration;
}
/// <summary>
/// Prepares and returns the connection string for a client using the specified database id
/// </summary>
/// <param name="a_databaseId">Specified database id to use</param>
/// <returns></returns>
public string GetClientConnectionString(int a_databaseId)
{
Databasemap? dbMap = m_context.Databasemaps.Find(a_databaseId);
if (dbMap != null)
{
string rawConString = m_configuration.GetConnectionString("PrivateConnection")!.ToString();
return String.Format(rawConString, dbMap.Domain, dbMap.DbName);
}
return ConnectionEnums.ConnectionNotEstablished.ToString();
}
/// <summary>
/// Prepare the DB context from the specified connection string
/// </summary>
/// <param name="a_context"></param>
/// <param name="a_connectionString"></param>
/// <returns>A configured BiskAcdbContext</returns>
public object PrepareDBContext(string a_connectionString)
{
DbContextOptionsBuilder<BiskAcdbContext> acdbContext = new DbContextOptionsBuilder<BiskAcdbContext>();
acdbContext.UseMySql(a_connectionString, new MariaDbServerVersion(new Version()));
return acdbContext;
}
}
}

11
Server/appsettings.json

@ -5,5 +5,14 @@
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
"ConnectionStrings": {
"Connection": "server=54.37.19.162;database=dev_biskilogclients;user=biskilog;password=mefbuk-6niFsu-fytrew",
"PrivateConnection": "server={0};database={1};user=biskilog;password=mefbuk-6niFsu-fytrew"
},
"AllowedHosts": "*",
"JWT": {
"Key": "@@BISKILOGACCOUNTING2023DEV??//##$",
"Issuer": "AUTH SERVER",
"Audience": "BISKILOG"
}
}

9
Shared/Biskilog Accounting.Shared.csproj

@ -8,14 +8,13 @@
<ItemGroup>
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
<PackageReference Include="EntityFramework" Version="6.4.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.5" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.30.1" />
</ItemGroup>
<ItemGroup>
<SupportedPlatform Include="browser" />
</ItemGroup>
<ItemGroup>
<Folder Include="Interfaces\" />
<Folder Include="Enums\" />
</ItemGroup>
</Project>

3
Shared/ClientContractModels/Databasemap.cs

@ -10,4 +10,7 @@ public partial class Databasemap
public string DbName { get; set; } = null!;
public int ClientId { get; set; }
public string Domain { get; set; }
public DateTime LastSyncDate { get; set; }
}

6
Shared/ClientContractModels/Userauth.cs

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace Biskilog_Accounting.Shared.ClientContractModels;
@ -9,10 +10,15 @@ public partial class Userauth
public int ClientId { get; set; }
[Required]
[Display(Name = "Username")]
public string? Username { get; set; }
public string? Email { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string? Passsword { get; set; }
public string? PhoneNumber { get; set; }

17
Shared/Enums/AuthEnums.cs

@ -0,0 +1,17 @@
namespace Biskilog_Accounting.Shared.Enums
{
public enum AuthEnums
{
Registered,
AleadyLoggedin,
WrongPassword,
NotFound,
Found,
Expired,
Invalid,
Valid,
Successful,
Error
}
}

14
Shared/Enums/ConnectionEnums.cs

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Biskilog_Accounting.Shared.Enums
{
public enum ConnectionEnums
{
ConnectionEstablished,
ConnectionNotEstablished,
}
}

19
Shared/Interfaces/IAuthService.cs

@ -0,0 +1,19 @@
using Biskilog_Accounting.Shared.ClientContractModels;
namespace Biskilog_Accounting.Shared.Interfaces
{
public interface IAuthService
{
/// <summary>
/// Authenticates user or client
/// </summary>
/// <param name="a_username"></param>
/// <param name="a_password"></param>
/// <returns>A tokenized string with relevant information on the authenticated user</returns>
Task<string> AuthenticateClient(string a_username, string a_password);
Contract? GetContract(int a_clientId, List<int> a_businessId);
Databasemap GetClientDB(int a_clientId);
List<Siteaccesspermission> GetSiteaccesspermission(int a_clientId, int a_userId);
List<Clientbusiness> GetClientbusiness(int a_clientId);
}
}

24
Shared/Interfaces/IConnectionService.cs

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Biskilog_Accounting.Shared.Interfaces
{
public interface IConnectionService
{
/// <summary>
/// Prepares and returns the connection string for a client using the specified database id
/// </summary>
/// <param name="a_databaseId">Specified database id to use</param>
/// <returns></returns>
string GetClientConnectionString(int a_databaseId);
/// <summary>
/// Prepare the DB context from the specified connection string
/// </summary>
/// <param name="a_connectionString"></param>
/// <returns>A configured BiskAcdbContext</returns>
object PrepareDBContext(string a_connectionString);
}
}

15
Shared/Interfaces/ITokenService.cs

@ -0,0 +1,15 @@
using Biskilog_Accounting.Shared.ClientContractModels;
using Biskilog_Accounting.Shared.Enums;
namespace Biskilog_Accounting.Shared.Interfaces
{
public interface ITokenService
{
AuthEnums ValidateToken(string a_token);
string GenerateToken(Userauth a_user, Contract a_clientContract, Databasemap a_database);
int? GetDatabaseIdFromToken(string a_token);
int? GetUserIdFromToken(string a_token);
string? GetUserNameFromToken(string a_token);
}
}

3
Shared/POSModels/Tbluser.cs

@ -1,14 +1,17 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace Biskilog_Accounting.Shared.POSModels;
public partial class Tbluser
{
[Required]
public string Username { get; set; } = null!;
public string BranchId { get; set; } = null!;
[Required]
public string? Password { get; set; }
public string? Firstname { get; set; }

124
Shared/ServiceRepo/TokenService.cs

@ -0,0 +1,124 @@
using Biskilog_Accounting.Shared.ClientContractModels;
using Biskilog_Accounting.Shared.Enums;
using Biskilog_Accounting.Shared.Interfaces;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
namespace Biskilog_Accounting.ServiceRepo
{
public class TokenService : ITokenService
{
private IConfiguration m_configuration { get; }
public TokenService(IConfiguration a_configuration)
{
m_configuration = a_configuration;
}
/// <summary>
/// Validates a user access token
/// </summary>
/// <returns>AuthEnums.Valid if token is a valid and unexpired token</returns>
public AuthEnums ValidateToken(string a_token)
{
try
{
string token = a_token.Substring(6).Trim();
var handler = new JwtSecurityTokenHandler();
JwtSecurityToken jwtToken = (JwtSecurityToken)handler.ReadToken(token);
if (jwtToken.ValidFrom <= DateTime.Now && jwtToken.ValidTo > DateTime.Now)
return AuthEnums.Valid;
return AuthEnums.Expired;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return AuthEnums.Invalid;
}
}
/// <summary>
/// Generates an access token based on the user
/// </summary>
/// <returns>A tokenized string</returns>
public string GenerateToken(Userauth a_user, Contract a_clientContract, Databasemap a_database)
{
try
{
//create claims details based on the user information
var claims = new[] {
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.Iat, DateTime.UtcNow.ToString()),
new Claim("ContractStart",a_clientContract.StartDate !.Value.ToString()),
new Claim("ContractEnd",a_clientContract.EndDate!.Value.ToString()),
new Claim("UserId", a_user.UserId.ToString()),
new Claim("Username", a_user.Username.ToString()),
new Claim("DbId",a_database.DbNo.ToString()),
new Claim("ClientId", a_user.ClientId.ToString()),
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(m_configuration["Jwt:Key"]!));
var signIn = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(m_configuration["Jwt:Issuer"], m_configuration["Jwt:Audience"], claims, expires: DateTime.UtcNow.AddDays(14), signingCredentials: signIn);
return $"{new JwtSecurityTokenHandler().WriteToken(token)}";
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return AuthEnums.Error.ToString();
}
}
/// <summary>
///Deserializes the token string if valid to return the specified user role id in the token string
/// </summary>
/// <param name="a_token"></param>
/// <returns>RoleId</returns>
public int? GetDatabaseIdFromToken(string a_token)
{
if (ValidateToken(a_token) == AuthEnums.Valid)
{
string token = a_token.Substring(6).Trim();
var handler = new JwtSecurityTokenHandler();
JwtSecurityToken jwtToken = (JwtSecurityToken)handler.ReadToken(token);
return int.Parse(jwtToken.Claims.First(claim => claim.Type == "DbId").Value);
}
return null;
}
/// <summary>
///Deserializes the token string if valid to return the specified user id in the token string
/// </summary>
/// <param name="a_token"></param>
/// <returns>UserId</returns>
public int? GetUserIdFromToken(string a_token)
{
if (ValidateToken(a_token) == AuthEnums.Valid)
{
string token = a_token.Substring(6).Trim();
var handler = new JwtSecurityTokenHandler();
JwtSecurityToken jwtToken = (JwtSecurityToken)handler.ReadToken(token);
return int.Parse(jwtToken.Claims.First(claim => claim.Type == "UserId").Value);
}
return null;
}
/// <summary>
///Deserializes the token string if valid to return the specified username in the token string
/// </summary>
/// <param name="a_token"></param>
/// <returns>Username</returns>
public string? GetUserNameFromToken(string a_token)
{
if (ValidateToken(a_token) == AuthEnums.Valid)
{
string token = a_token.Substring(6).Trim();
var handler = new JwtSecurityTokenHandler();
JwtSecurityToken jwtToken = (JwtSecurityToken)handler.ReadToken(token);
return jwtToken.Claims.First(claim => claim.Type == "Username").Value;
}
return null;
}
}
}
Loading…
Cancel
Save