Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
H
HIAST-Clinics
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
almohanad.hafez
HIAST-Clinics
Commits
2ddc9eb6
Unverified
Commit
2ddc9eb6
authored
Aug 21, 2024
by
Almouhannad Hafez
Committed by
GitHub
Aug 21, 2024
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #11 from Almouhannad/B_Add-identity
B add identity
parents
1c65ec94
66384a51
Changes
36
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
36 changed files
with
2145 additions
and
6 deletions
+2145
-6
JWTBearerOptionsSetup.cs
Clinics.Backend/API/Options/JWT/JWTBearerOptionsSetup.cs
+27
-0
JWTOptionsSetup.cs
Clinics.Backend/API/Options/JWT/JWTOptionsSetup.cs
+22
-0
Program.cs
Clinics.Backend/API/Program.cs
+46
-2
SeedAdminUserHelper.cs
...ics.Backend/API/SeedDatabaseHelper/SeedAdminUserHelper.cs
+21
-0
SeedHelper.cs
Clinics.Backend/API/SeedDatabaseHelper/SeedHelper.cs
+5
-1
appsettings.json
Clinics.Backend/API/appsettings.json
+5
-0
IJWTProvider.cs
Clinics.Backend/Application/Abstractions/JWT/IJWTProvider.cs
+8
-0
LoginCommand.cs
....Backend/Application/Users/Commands/Login/LoginCommand.cs
+9
-0
LoginCommandHandler.cs
...d/Application/Users/Commands/Login/LoginCommandHandler.cs
+44
-0
Role.cs
Clinics.Backend/Domain/Entities/Identity/UserRoles/Role.cs
+32
-0
Roles.cs
Clinics.Backend/Domain/Entities/Identity/UserRoles/Roles.cs
+25
-0
DoctorUser.cs
Clinics.Backend/Domain/Entities/Identity/Users/DoctorUser.cs
+49
-0
ReceptionistUser.cs
...ackend/Domain/Entities/Identity/Users/ReceptionistUser.cs
+50
-0
User.cs
Clinics.Backend/Domain/Entities/Identity/Users/User.cs
+52
-0
IdentityErrors.cs
Clinics.Backend/Domain/Errors/IdentityErrors.cs
+10
-0
IUserRepository.cs
Clinics.Backend/Domain/Repositories/IUserRepository.cs
+13
-0
RoleConfiguration.cs
...stence/Configurations/Identity/Roles/RoleConfiguration.cs
+21
-0
DoctorUserConfiguration.cs
.../Configurations/Identity/Users/DoctorUserConfiguration.cs
+25
-0
ReceptionistUserConfiguration.cs
...gurations/Identity/Users/ReceptionistUserConfiguration.cs
+25
-0
UserConfiguration.cs
...stence/Configurations/Identity/Users/UserConfiguration.cs
+20
-0
JWTOptions.cs
...end/Persistence/Identity/Authentication/JWT/JWTOptions.cs
+9
-0
JWTProvider.cs
...nd/Persistence/Identity/Authentication/JWT/JWTProvider.cs
+47
-0
IPasswordHasher.cs
.../Persistence/Identity/PasswordsHashing/IPasswordHasher.cs
+8
-0
PasswordHasher.cs
...d/Persistence/Identity/PasswordsHashing/PasswordHasher.cs
+46
-0
20240820212742_Add_Identity.Designer.cs
...stence/Migrations/20240820212742_Add_Identity.Designer.cs
+1067
-0
20240820212742_Add_Identity.cs
...end/Persistence/Migrations/20240820212742_Add_Identity.cs
+136
-0
ClinicsDbContextModelSnapshot.cs
...d/Persistence/Migrations/ClinicsDbContextModelSnapshot.cs
+130
-0
Persistence.csproj
Clinics.Backend/Persistence/Persistence.csproj
+1
-0
SpecificationEvaluator.cs
...tories/Specifications/Evaluator/SpecificationEvaluator.cs
+2
-1
FullUserSpecification.cs
...d/Persistence/Repositories/Users/FullUserSpecification.cs
+13
-0
UserRepository.cs
....Backend/Persistence/Repositories/Users/UserRepository.cs
+59
-0
ISeedAdminUser.cs
...kend/Persistence/SeedDatabase/AdminUser/ISeedAdminUser.cs
+6
-0
SeedAdminUser.cs
...ckend/Persistence/SeedDatabase/AdminUser/SeedAdminUser.cs
+44
-0
UserRoles.cs
Clinics.Backend/Persistence/SeedDatabase/UserRoles.cs
+36
-0
EmployeesController.cs
...s.Backend/Presentation/Controllers/EmployeesController.cs
+4
-2
UsersController.cs
Clinics.Backend/Presentation/Controllers/UsersController.cs
+28
-0
No files found.
Clinics.Backend/API/Options/JWT/JWTBearerOptionsSetup.cs
0 → 100644
View file @
2ddc9eb6
using
Microsoft.AspNetCore.Authentication.JwtBearer
;
using
Microsoft.Extensions.Options
;
using
Microsoft.IdentityModel.Tokens
;
using
Persistence.Identity.Authentication.JWT
;
using
System.Text
;
namespace
API.Options.JWT
;
public
class
JWTBearerOptionsSetup
:
IPostConfigureOptions
<
JwtBearerOptions
>
{
#
region
JWT
Options
CTOR
DI
private
readonly
JWTOptions
_jwtOptions
;
public
JWTBearerOptionsSetup
(
IOptions
<
JWTOptions
>
jwtOptions
)
{
_jwtOptions
=
jwtOptions
.
Value
;
}
#
endregion
public
void
PostConfigure
(
string
?
name
,
JwtBearerOptions
options
)
{
options
.
TokenValidationParameters
.
ValidIssuer
=
_jwtOptions
.
Issuer
;
options
.
TokenValidationParameters
.
ValidAudience
=
_jwtOptions
.
Audience
;
options
.
TokenValidationParameters
.
IssuerSigningKey
=
new
SymmetricSecurityKey
(
Encoding
.
UTF8
.
GetBytes
(
_jwtOptions
.
SecretKey
));
}
}
Clinics.Backend/API/Options/JWT/JWTOptionsSetup.cs
0 → 100644
View file @
2ddc9eb6
using
Microsoft.Extensions.Options
;
using
Persistence.Identity.Authentication.JWT
;
namespace
API.Options.JWT
;
public
class
JWTOptionsSetup
:
IConfigureOptions
<
JWTOptions
>
{
private
const
string
_cofigurationSectionName
=
"JWTOptions"
;
// From appsettings.json
#
region
Using
ctor
DI
to
access
configuration
private
readonly
IConfiguration
_configuration
;
public
JWTOptionsSetup
(
IConfiguration
configuration
)
{
_configuration
=
configuration
;
}
#
endregion
public
void
Configure
(
JWTOptions
options
)
{
_configuration
.
GetSection
(
_cofigurationSectionName
).
Bind
(
options
);
}
}
Clinics.Backend/API/Program.cs
View file @
2ddc9eb6
using
API.Options.Database
;
using
API.Options.JWT
;
using
API.SeedDatabaseHelper
;
using
Application.Behaviors
;
using
FluentValidation
;
using
MediatR
;
using
Microsoft.AspNetCore.Authentication.JwtBearer
;
using
Microsoft.EntityFrameworkCore
;
using
Microsoft.Extensions.Options
;
using
Microsoft.OpenApi.Models
;
using
Persistence.Context
;
var
builder
=
WebApplication
.
CreateBuilder
(
args
);
...
...
@@ -65,14 +68,53 @@ builder.Services.AddControllers()
.
AddApplicationPart
(
Presentation
.
AssemblyReference
.
Assembly
);
#endregion
builder
.
Services
.
AddEndpointsApiExplorer
();
builder
.
Services
.
AddSwaggerGen
();
#region Swagger with JWT authorization
builder
.
Services
.
AddSwaggerGen
(
opt
=>
{
opt
.
SwaggerDoc
(
"v1"
,
new
OpenApiInfo
{
Title
=
"MyAPI"
,
Version
=
"v1"
});
opt
.
AddSecurityDefinition
(
"Bearer"
,
new
OpenApiSecurityScheme
{
In
=
ParameterLocation
.
Header
,
Description
=
"Please enter token"
,
Name
=
"Authorization"
,
Type
=
SecuritySchemeType
.
Http
,
BearerFormat
=
"JWT"
,
Scheme
=
"bearer"
});
opt
.
AddSecurityRequirement
(
new
OpenApiSecurityRequirement
{
{
new
OpenApiSecurityScheme
{
Reference
=
new
OpenApiReference
{
Type
=
ReferenceType
.
SecurityScheme
,
Id
=
"Bearer"
}
},
new
string
[]{}
}
});
});
#endregion
#region Authentication options
builder
.
Services
.
ConfigureOptions
<
JWTOptionsSetup
>();
builder
.
Services
.
ConfigureOptions
<
JWTBearerOptionsSetup
>();
builder
.
Services
.
AddAuthentication
(
JwtBearerDefaults
.
AuthenticationScheme
)
.
AddJwtBearer
();
#endregion
var
app
=
builder
.
Build
();
#region Seed database
await
SeedHelper
.
Seed
(
app
);
await
SeedAdminUserHelper
.
Seed
(
app
);
#endregion
// Configure the HTTP request pipeline.
...
...
@@ -84,6 +126,8 @@ if (app.Environment.IsDevelopment())
app
.
UseHttpsRedirection
();
app
.
UseAuthentication
();
app
.
UseAuthorization
();
app
.
MapControllers
();
...
...
Clinics.Backend/API/SeedDatabaseHelper/SeedAdminUserHelper.cs
0 → 100644
View file @
2ddc9eb6
using
Domain.Entities.Identity.UserRoles
;
using
Domain.Entities.Medicals.Medicines.MedicineFormValues
;
using
Domain.Entities.People.Doctors.Shared.Constants.DoctorStatusValues
;
using
Domain.Entities.People.Employees.Relations.EmployeeFamilyMembers.FamilyRoleValues
;
using
Domain.Entities.People.Shared.GenderValues
;
using
Persistence.SeedDatabase
;
using
Persistence.SeedDatabase.AdminUser
;
namespace
API.SeedDatabaseHelper
;
public
class
SeedAdminUserHelper
{
public
static
async
Task
Seed
(
IApplicationBuilder
applicationBuilder
)
{
using
(
var
serviceScope
=
applicationBuilder
.
ApplicationServices
.
CreateScope
())
{
var
seedAdminUser
=
serviceScope
.
ServiceProvider
.
GetRequiredService
<
ISeedAdminUser
>();
await
seedAdminUser
.
Seed
();
}
}
}
Clinics.Backend/API/SeedDatabaseHelper/SeedHelper.cs
View file @
2ddc9eb6
using
Domain.Entities.Medicals.Medicines.MedicineFormValues
;
using
Domain.Entities.Identity.UserRoles
;
using
Domain.Entities.Medicals.Medicines.MedicineFormValues
;
using
Domain.Entities.People.Doctors.Shared.Constants.DoctorStatusValues
;
using
Domain.Entities.People.Employees.Relations.EmployeeFamilyMembers.FamilyRoleValues
;
using
Domain.Entities.People.Shared.GenderValues
;
...
...
@@ -23,6 +24,9 @@ public class SeedHelper
var
seedMedicineForms
=
serviceScope
.
ServiceProvider
.
GetRequiredService
<
ISeed
<
MedicineForm
>>();
await
seedMedicineForms
.
Seed
();
var
seedUserRoles
=
serviceScope
.
ServiceProvider
.
GetRequiredService
<
ISeed
<
Role
>>();
await
seedUserRoles
.
Seed
();
}
}
}
Clinics.Backend/API/appsettings.json
View file @
2ddc9eb6
...
...
@@ -15,5 +15,10 @@
"MaxRetryCount"
:
3
,
"CommandTimeout"
:
30
,
"EnableDetailedErrors"
:
true
},
"JWTOptions"
:
{
"SecretKey"
:
"MoHafez11@SecretKeyAPIVeryLongSecretKeyMoHafez11@SecretKeyAPIVeryLongSecretKey"
,
"Audience"
:
"clinics-front-end.hiast.edy.sy"
,
"Issuer"
:
"clinics-back-end.hiast.edy.sy"
}
}
Clinics.Backend/Application/Abstractions/JWT/IJWTProvider.cs
0 → 100644
View file @
2ddc9eb6
using
Domain.Entities.Identity.Users
;
namespace
Application.Abstractions.JWT
;
public
interface
IJWTProvider
{
string
Generate
(
User
user
);
}
Clinics.Backend/Application/Users/Commands/Login/LoginCommand.cs
0 → 100644
View file @
2ddc9eb6
using
Application.Abstractions.CQRS.Commands
;
namespace
Application.Users.Commands.Login
;
public
class
LoginCommand
:
ICommand
<
string
>
{
public
string
UserName
{
get
;
set
;
}
=
null
!;
public
string
Password
{
get
;
set
;
}
=
null
!;
}
Clinics.Backend/Application/Users/Commands/Login/LoginCommandHandler.cs
0 → 100644
View file @
2ddc9eb6
using
Application.Abstractions.CQRS.Commands
;
using
Application.Abstractions.JWT
;
using
Domain.Entities.Identity.Users
;
using
Domain.Errors
;
using
Domain.Repositories
;
using
Domain.Shared
;
using
Domain.UnitOfWork
;
namespace
Application.Users.Commands.Login
;
public
class
LoginCommandHandler
:
CommandHandlerBase
<
LoginCommand
,
string
>
{
#
region
CTOR
DI
private
readonly
IUserRepository
_userRepository
;
private
readonly
IJWTProvider
_jwtProvider
;
public
LoginCommandHandler
(
IUnitOfWork
unitOfWork
,
IUserRepository
userRepository
,
IJWTProvider
jwtProvider
)
:
base
(
unitOfWork
)
{
_userRepository
=
userRepository
;
_jwtProvider
=
jwtProvider
;
}
#
endregion
public
override
async
Task
<
Result
<
string
>>
HandleHelper
(
LoginCommand
request
,
CancellationToken
cancellationToken
)
{
#
region
1.
Check
username
and
password
are
correct
Result
<
User
?>
loginResult
=
await
_userRepository
.
VerifyPasswordAsync
(
request
.
UserName
,
request
.
Password
);
if
(
loginResult
.
IsFailure
)
return
Result
.
Failure
<
string
>(
loginResult
.
Error
);
// Not found username
if
(
loginResult
.
Value
is
null
)
// Invalid password
return
Result
.
Failure
<
string
>(
IdentityErrors
.
PasswordMismatch
);
#
endregion
#
region
2.
Generate
JWT
User
user
=
loginResult
.
Value
!;
string
token
=
_jwtProvider
.
Generate
(
user
);
#
endregion
return
Result
.
Success
<
string
>(
token
);
}
}
Clinics.Backend/Domain/Entities/Identity/UserRoles/Role.cs
0 → 100644
View file @
2ddc9eb6
using
Domain.Primitives
;
namespace
Domain.Entities.Identity.UserRoles
;
public
sealed
class
Role
:
Entity
{
#
region
Private
ctor
private
Role
(
int
id
)
:
base
(
id
)
{
}
private
Role
(
int
id
,
string
name
)
:
base
(
id
)
{
Name
=
name
;
}
#
endregion
#
region
Properties
public
string
Name
{
get
;
private
set
;
}
=
null
!;
#
endregion
#
region
Methods
#
region
Static
factory
internal
static
Role
Create
(
int
id
,
string
name
)
{
return
new
Role
(
id
,
name
);
}
#
endregion
#
endregion
}
Clinics.Backend/Domain/Entities/Identity/UserRoles/Roles.cs
0 → 100644
View file @
2ddc9eb6
namespace
Domain.Entities.Identity.UserRoles
;
public
static
class
Roles
{
#
region
Constant
values
public
static
int
Count
=>
3
;
public
const
string
AdminName
=
"admin"
;
public
const
string
DoctorName
=
"doctor"
;
public
const
string
ReceptionistName
=
"receptionist"
;
public
static
Role
Admin
=>
Role
.
Create
(
1
,
AdminName
);
public
static
Role
Doctor
=>
Role
.
Create
(
2
,
DoctorName
);
public
static
Role
Receptionist
=>
Role
.
Create
(
3
,
ReceptionistName
);
public
static
List
<
Role
>
GetAll
()
{
List
<
Role
>
roles
=
new
();
roles
.
Add
(
Admin
);
roles
.
Add
(
Doctor
);
roles
.
Add
(
Receptionist
);
return
roles
;
}
#
endregion
}
Clinics.Backend/Domain/Entities/Identity/Users/DoctorUser.cs
0 → 100644
View file @
2ddc9eb6
using
Domain.Entities.Identity.UserRoles
;
using
Domain.Entities.People.Doctors
;
using
Domain.Primitives
;
using
Domain.Shared
;
namespace
Domain.Entities.Identity.Users
;
public
sealed
class
DoctorUser
:
Entity
{
#
region
Private
ctor
private
DoctorUser
(
int
id
)
:
base
(
id
)
{
}
private
DoctorUser
(
int
id
,
User
user
,
Doctor
doctor
)
:
base
(
id
)
{
User
=
user
;
Doctor
=
doctor
;
}
#
endregion
#
region
Properties
public
User
User
{
get
;
private
set
;
}
=
null
!;
public
Doctor
Doctor
{
get
;
private
set
;
}
=
null
!;
#
endregion
#
region
Methods
#
region
Static
factory
public
static
Result
<
DoctorUser
>
Create
(
string
username
,
string
hashedPassword
,
// User details
string
firstName
,
string
middleName
,
string
lastName
// Doctor details
)
{
Result
<
Doctor
>
doctorResult
=
Doctor
.
Create
(
firstName
,
middleName
,
lastName
);
if
(
doctorResult
.
IsFailure
)
return
Result
.
Failure
<
DoctorUser
>(
doctorResult
.
Error
);
Result
<
User
>
userResult
=
User
.
Create
(
username
,
hashedPassword
,
Roles
.
Doctor
.
Name
);
if
(
userResult
.
IsFailure
)
return
Result
.
Failure
<
DoctorUser
>(
userResult
.
Error
);
return
new
DoctorUser
(
0
,
userResult
.
Value
,
doctorResult
.
Value
);
}
#
endregion
#
endregion
}
Clinics.Backend/Domain/Entities/Identity/Users/ReceptionistUser.cs
0 → 100644
View file @
2ddc9eb6
using
Domain.Entities.Identity.UserRoles
;
using
Domain.Entities.People.Shared
;
using
Domain.Primitives
;
using
Domain.Shared
;
namespace
Domain.Entities.Identity.Users
;
public
sealed
class
ReceptionistUser
:
Entity
{
#
region
Private
ctor
private
ReceptionistUser
(
int
id
)
:
base
(
id
)
{
}
private
ReceptionistUser
(
int
id
,
User
user
,
PersonalInfo
personalInfo
)
:
base
(
id
)
{
User
=
user
;
PersonalInfo
=
personalInfo
;
}
#
endregion
#
region
Properties
public
User
User
{
get
;
private
set
;
}
=
null
!;
public
PersonalInfo
PersonalInfo
{
get
;
private
set
;
}
=
null
!;
#
endregion
#
region
Methods
#
region
Static
factory
public
static
Result
<
ReceptionistUser
>
Create
(
string
userName
,
string
hashedPassword
,
string
firstName
,
string
middleName
,
string
lastName
)
{
Result
<
PersonalInfo
>
personalInfoResult
=
PersonalInfo
.
Create
(
firstName
,
middleName
,
lastName
);
if
(
personalInfoResult
.
IsFailure
)
return
Result
.
Failure
<
ReceptionistUser
>(
personalInfoResult
.
Error
);
Result
<
User
>
userResult
=
User
.
Create
(
userName
,
hashedPassword
,
Roles
.
Receptionist
.
Name
);
if
(
userResult
.
IsFailure
)
return
Result
.
Failure
<
ReceptionistUser
>(
userResult
.
Error
);
return
new
ReceptionistUser
(
0
,
userResult
.
Value
,
personalInfoResult
.
Value
);
}
#
endregion
#
endregion
}
Clinics.Backend/Domain/Entities/Identity/Users/User.cs
0 → 100644
View file @
2ddc9eb6
using
Domain.Entities.Identity.UserRoles
;
using
Domain.Errors
;
using
Domain.Primitives
;
using
Domain.Shared
;
namespace
Domain.Entities.Identity.Users
;
public
sealed
class
User
:
Entity
{
#
region
Private
ctor
private
User
(
int
id
)
:
base
(
id
)
{
}
private
User
(
int
id
,
string
userName
,
string
hashedPassword
,
Role
role
)
:
base
(
id
)
{
UserName
=
userName
;
HashedPassword
=
hashedPassword
;
Role
=
role
;
}
#
endregion
#
region
Properties
public
string
UserName
{
get
;
private
set
;
}
=
null
!;
public
string
HashedPassword
{
get
;
private
set
;
}
=
null
!;
public
Role
Role
{
get
;
private
set
;
}
=
null
!;
#
endregion
#
region
Methods
public
static
Result
<
User
>
Create
(
string
userName
,
string
hashedPassword
,
string
role
)
{
if
(
userName
is
null
||
hashedPassword
is
null
||
role
is
null
)
{
return
Result
.
Failure
<
User
>(
DomainErrors
.
InvalidValuesError
);
}
#
region
Check
role
Result
<
Role
>
selectedRole
=
Result
.
Failure
<
Role
>(
IdentityErrors
.
InvalidRole
);
List
<
Role
>
roles
=
Roles
.
GetAll
();
foreach
(
Role
roleItem
in
roles
)
{
if
(
roleItem
.
Name
==
role
)
selectedRole
=
roleItem
;
}
if
(
selectedRole
.
IsFailure
)
return
Result
.
Failure
<
User
>(
selectedRole
.
Error
);
#
endregion
return
new
User
(
0
,
userName
,
hashedPassword
,
selectedRole
.
Value
);
}
#
endregion
}
Clinics.Backend/Domain/Errors/IdentityErrors.cs
0 → 100644
View file @
2ddc9eb6
using
Domain.Shared
;
namespace
Domain.Errors
;
public
static
class
IdentityErrors
{
public
static
Error
InvalidRole
=>
new
(
"Identity.InvalidRole"
,
"Role specified for user is invalid"
);
public
static
Error
NotFound
=>
new
(
"Identity.NotFound"
,
"المستخدم غير مسجّل في النظام"
);
public
static
Error
PasswordMismatch
=>
new
(
"Identity.PasswordMismatch"
,
"كلمة السر غير صحيحة"
);
}
Clinics.Backend/Domain/Repositories/IUserRepository.cs
0 → 100644
View file @
2ddc9eb6
using
Domain.Entities.Identity.Users
;
using
Domain.Repositories.Base
;
using
Domain.Shared
;
namespace
Domain.Repositories
;
public
interface
IUserRepository
:
IRepository
<
User
>
{
public
Task
<
Result
<
User
>>
GetByUserNameFullAsync
(
string
userName
);
public
Task
<
Result
<
User
?>>
VerifyPasswordAsync
(
string
userName
,
string
password
);
}
Clinics.Backend/Persistence/Configurations/Identity/Roles/RoleConfiguration.cs
0 → 100644
View file @
2ddc9eb6
using
Domain.Entities.Identity.UserRoles
;
using
Microsoft.EntityFrameworkCore
;
using
Microsoft.EntityFrameworkCore.Metadata.Builders
;
namespace
Persistence.Configurations.Identity.Roles
;
public
class
RoleConfiguration
:
IEntityTypeConfiguration
<
Role
>
{
public
void
Configure
(
EntityTypeBuilder
<
Role
>
builder
)
{
builder
.
ToTable
(
nameof
(
Role
));
builder
.
Property
(
role
=>
role
.
Id
).
ValueGeneratedNever
();
builder
.
Property
(
role
=>
role
.
Name
)
.
HasMaxLength
(
50
);
builder
.
HasIndex
(
role
=>
role
.
Name
)
.
IsUnique
();
}
}
Clinics.Backend/Persistence/Configurations/Identity/Users/DoctorUserConfiguration.cs
0 → 100644
View file @
2ddc9eb6
using
Domain.Entities.Identity.Users
;
using
Microsoft.EntityFrameworkCore
;
using
Microsoft.EntityFrameworkCore.Metadata.Builders
;
namespace
Persistence.Configurations.Identity.Users
;
public
class
DoctorUserConfiguration
:
IEntityTypeConfiguration
<
DoctorUser
>
{
public
void
Configure
(
EntityTypeBuilder
<
DoctorUser
>
builder
)
{
builder
.
ToTable
(
nameof
(
DoctorUser
));
builder
.
Property
(
doctorUser
=>
doctorUser
.
Id
).
ValueGeneratedOnAdd
();
builder
.
HasOne
(
doctorUser
=>
doctorUser
.
User
)
.
WithOne
()
.
HasForeignKey
<
DoctorUser
>(
doctorUser
=>
doctorUser
.
Id
)
.
OnDelete
(
DeleteBehavior
.
NoAction
);
builder
.
HasOne
(
doctorUser
=>
doctorUser
.
Doctor
)
.
WithOne
()
.
HasForeignKey
<
DoctorUser
>(
"DoctorId"
)
.
OnDelete
(
DeleteBehavior
.
NoAction
);
}
}
Clinics.Backend/Persistence/Configurations/Identity/Users/ReceptionistUserConfiguration.cs
0 → 100644
View file @
2ddc9eb6
using
Domain.Entities.Identity.Users
;
using
Microsoft.EntityFrameworkCore
;
using
Microsoft.EntityFrameworkCore.Metadata.Builders
;
namespace
Persistence.Configurations.Identity.Users
;
public
class
ReceptionistUserConfiguration
:
IEntityTypeConfiguration
<
ReceptionistUser
>
{
public
void
Configure
(
EntityTypeBuilder
<
ReceptionistUser
>
builder
)
{
builder
.
ToTable
(
nameof
(
ReceptionistUser
));
builder
.
Property
(
receptionistUser
=>
receptionistUser
.
Id
).
ValueGeneratedOnAdd
();
builder
.
HasOne
(
receptionistUser
=>
receptionistUser
.
User
)
.
WithOne
()
.
HasForeignKey
<
ReceptionistUser
>(
receptionistUser
=>
receptionistUser
.
Id
)
.
OnDelete
(
DeleteBehavior
.
NoAction
);
builder
.
HasOne
(
receptionistUser
=>
receptionistUser
.
PersonalInfo
)
.
WithOne
()
.
HasForeignKey
<
ReceptionistUser
>(
"PersonalInfoId"
)
.
OnDelete
(
DeleteBehavior
.
NoAction
);
}
}
Clinics.Backend/Persistence/Configurations/Identity/Users/UserConfiguration.cs
0 → 100644
View file @
2ddc9eb6
using
Domain.Entities.Identity.Users
;
using
Microsoft.EntityFrameworkCore
;
using
Microsoft.EntityFrameworkCore.Metadata.Builders
;
namespace
Persistence.Configurations.Identity.Users
;
public
class
UserConfiguration
:
IEntityTypeConfiguration
<
User
>
{
public
void
Configure
(
EntityTypeBuilder
<
User
>
builder
)
{
builder
.
ToTable
(
nameof
(
User
));
builder
.
HasIndex
(
user
=>
user
.
UserName
)
.
IsUnique
();
builder
.
HasOne
(
user
=>
user
.
Role
)
.
WithMany
()
.
HasForeignKey
(
"RoleId"
);
}
}
Clinics.Backend/Persistence/Identity/Authentication/JWT/JWTOptions.cs
0 → 100644
View file @
2ddc9eb6
namespace
Persistence.Identity.Authentication.JWT
;
// Using option pattern
public
class
JWTOptions
{
public
string
Issuer
{
get
;
init
;
}
=
null
!;
public
string
Audience
{
get
;
init
;
}
=
null
!;
public
string
SecretKey
{
get
;
init
;
}
=
null
!;
}
Clinics.Backend/Persistence/Identity/Authentication/JWT/JWTProvider.cs
0 → 100644
View file @
2ddc9eb6
using
Application.Abstractions.JWT
;
using
Domain.Entities.Identity.Users
;
using
Microsoft.Extensions.Options
;
using
Microsoft.IdentityModel.Tokens
;
using
System.IdentityModel.Tokens.Jwt
;
using
System.Security.Claims
;
using
System.Text
;
namespace
Persistence.Identity.Authentication.JWT
;
public
sealed
class
JWTProvider
:
IJWTProvider
{
private
readonly
JWTOptions
_options
;
public
JWTProvider
(
IOptions
<
JWTOptions
>
options
)
{
_options
=
options
.
Value
;
}
public
string
Generate
(
User
user
)
{
var
claims
=
new
Claim
[]
{
new
(
ClaimTypes
.
Name
,
user
.
UserName
),
new
(
ClaimTypes
.
Role
,
user
.
Role
.
Name
)
};
var
signingCredentials
=
new
SigningCredentials
(
new
SymmetricSecurityKey
(
Encoding
.
UTF8
.
GetBytes
(
_options
.
SecretKey
)),
SecurityAlgorithms
.
HmacSha256
);
var
token
=
new
JwtSecurityToken
(
_options
.
Issuer
,
_options
.
Audience
,
claims
,
null
,
DateTime
.
UtcNow
.
AddDays
(
30
),
signingCredentials
);
var
tokenValue
=
new
JwtSecurityTokenHandler
()
.
WriteToken
(
token
);
return
tokenValue
;
}
}
Clinics.Backend/Persistence/Identity/PasswordsHashing/IPasswordHasher.cs
0 → 100644
View file @
2ddc9eb6
namespace
Persistence.Identity.PasswordsHashing
;
public
interface
IPasswordHasher
{
public
string
Hash
(
string
password
);
public
bool
Verify
(
string
password
,
string
passwordHash
);
}
Clinics.Backend/Persistence/Identity/PasswordsHashing/PasswordHasher.cs
0 → 100644
View file @
2ddc9eb6
using
System.Security.Cryptography
;
namespace
Persistence.Identity.PasswordsHashing
;
public
class
PasswordHasher
:
IPasswordHasher
{
#
region
Hashing
// Salting is adding a random number to the hash
// to ensure that users with same passwords have
// different hash values.
// Best pactice is to use 128 bits = 16 bytes
private
const
int
SaltSize
=
16
;
// Output size
// Best practice is to use 256 bits
private
const
int
HashSize
=
32
;
private
const
int
Iterations
=
100000
;
private
readonly
HashAlgorithmName
Algorithm
=
HashAlgorithmName
.
SHA512
;
public
string
Hash
(
string
password
)
{
byte
[]
salt
=
RandomNumberGenerator
.
GetBytes
(
SaltSize
);
byte
[]
hash
=
Rfc2898DeriveBytes
.
Pbkdf2
(
password
,
salt
,
Iterations
,
Algorithm
,
HashSize
);
// Pdkdf ~ Password Based Key Derivation Function
return
$"
{
Convert
.
ToHexString
(
hash
)}
-
{
Convert
.
ToHexString
(
salt
)}
"
;
}
#
endregion
#
region
Verification
public
bool
Verify
(
string
password
,
string
passwordHash
)
{
string
[]
parts
=
passwordHash
.
Split
(
'-'
);
byte
[]
hash
=
Convert
.
FromHexString
(
parts
[
0
]);
byte
[]
salt
=
Convert
.
FromHexString
(
parts
[
1
]);
byte
[]
inputHash
=
Rfc2898DeriveBytes
.
Pbkdf2
(
password
,
salt
,
Iterations
,
Algorithm
,
HashSize
);
return
CryptographicOperations
.
FixedTimeEquals
(
hash
,
inputHash
);
}
#
endregion
}
Clinics.Backend/Persistence/Migrations/20240820212742_Add_Identity.Designer.cs
0 → 100644
View file @
2ddc9eb6
This diff is collapsed.
Click to expand it.
Clinics.Backend/Persistence/Migrations/20240820212742_Add_Identity.cs
0 → 100644
View file @
2ddc9eb6
using
Microsoft.EntityFrameworkCore.Migrations
;
#
nullable
disable
namespace
Persistence.Migrations
{
/// <inheritdoc />
public
partial
class
Add_Identity
:
Migration
{
/// <inheritdoc />
protected
override
void
Up
(
MigrationBuilder
migrationBuilder
)
{
migrationBuilder
.
CreateTable
(
name
:
"Role"
,
columns
:
table
=>
new
{
Id
=
table
.
Column
<
int
>(
type
:
"int"
,
nullable
:
false
),
Name
=
table
.
Column
<
string
>(
type
:
"nvarchar(50)"
,
maxLength
:
50
,
nullable
:
false
)
},
constraints
:
table
=>
{
table
.
PrimaryKey
(
"PK_Role"
,
x
=>
x
.
Id
);
});
migrationBuilder
.
CreateTable
(
name
:
"User"
,
columns
:
table
=>
new
{
Id
=
table
.
Column
<
int
>(
type
:
"int"
,
nullable
:
false
)
.
Annotation
(
"SqlServer:Identity"
,
"1, 1"
),
UserName
=
table
.
Column
<
string
>(
type
:
"nvarchar(450)"
,
nullable
:
false
),
HashedPassword
=
table
.
Column
<
string
>(
type
:
"nvarchar(max)"
,
nullable
:
false
),
RoleId
=
table
.
Column
<
int
>(
type
:
"int"
,
nullable
:
false
)
},
constraints
:
table
=>
{
table
.
PrimaryKey
(
"PK_User"
,
x
=>
x
.
Id
);
table
.
ForeignKey
(
name
:
"FK_User_Role_RoleId"
,
column
:
x
=>
x
.
RoleId
,
principalTable
:
"Role"
,
principalColumn
:
"Id"
,
onDelete
:
ReferentialAction
.
Cascade
);
});
migrationBuilder
.
CreateTable
(
name
:
"DoctorUser"
,
columns
:
table
=>
new
{
Id
=
table
.
Column
<
int
>(
type
:
"int"
,
nullable
:
false
),
DoctorId
=
table
.
Column
<
int
>(
type
:
"int"
,
nullable
:
false
)
},
constraints
:
table
=>
{
table
.
PrimaryKey
(
"PK_DoctorUser"
,
x
=>
x
.
Id
);
table
.
ForeignKey
(
name
:
"FK_DoctorUser_Doctor_DoctorId"
,
column
:
x
=>
x
.
DoctorId
,
principalTable
:
"Doctor"
,
principalColumn
:
"Id"
);
table
.
ForeignKey
(
name
:
"FK_DoctorUser_User_Id"
,
column
:
x
=>
x
.
Id
,
principalTable
:
"User"
,
principalColumn
:
"Id"
);
});
migrationBuilder
.
CreateTable
(
name
:
"ReceptionistUser"
,
columns
:
table
=>
new
{
Id
=
table
.
Column
<
int
>(
type
:
"int"
,
nullable
:
false
),
PersonalInfoId
=
table
.
Column
<
int
>(
type
:
"int"
,
nullable
:
false
)
},
constraints
:
table
=>
{
table
.
PrimaryKey
(
"PK_ReceptionistUser"
,
x
=>
x
.
Id
);
table
.
ForeignKey
(
name
:
"FK_ReceptionistUser_PersonalInfo_PersonalInfoId"
,
column
:
x
=>
x
.
PersonalInfoId
,
principalTable
:
"PersonalInfo"
,
principalColumn
:
"Id"
);
table
.
ForeignKey
(
name
:
"FK_ReceptionistUser_User_Id"
,
column
:
x
=>
x
.
Id
,
principalTable
:
"User"
,
principalColumn
:
"Id"
);
});
migrationBuilder
.
CreateIndex
(
name
:
"IX_DoctorUser_DoctorId"
,
table
:
"DoctorUser"
,
column
:
"DoctorId"
,
unique
:
true
);
migrationBuilder
.
CreateIndex
(
name
:
"IX_ReceptionistUser_PersonalInfoId"
,
table
:
"ReceptionistUser"
,
column
:
"PersonalInfoId"
,
unique
:
true
);
migrationBuilder
.
CreateIndex
(
name
:
"IX_Role_Name"
,
table
:
"Role"
,
column
:
"Name"
,
unique
:
true
);
migrationBuilder
.
CreateIndex
(
name
:
"IX_User_RoleId"
,
table
:
"User"
,
column
:
"RoleId"
);
migrationBuilder
.
CreateIndex
(
name
:
"IX_User_UserName"
,
table
:
"User"
,
column
:
"UserName"
,
unique
:
true
);
}
/// <inheritdoc />
protected
override
void
Down
(
MigrationBuilder
migrationBuilder
)
{
migrationBuilder
.
DropTable
(
name
:
"DoctorUser"
);
migrationBuilder
.
DropTable
(
name
:
"ReceptionistUser"
);
migrationBuilder
.
DropTable
(
name
:
"User"
);
migrationBuilder
.
DropTable
(
name
:
"Role"
);
}
}
}
Clinics.Backend/Persistence/Migrations/ClinicsDbContextModelSnapshot.cs
View file @
2ddc9eb6
...
...
@@ -22,6 +22,87 @@ namespace Persistence.Migrations
SqlServerModelBuilderExtensions
.
UseIdentityColumns
(
modelBuilder
);
modelBuilder
.
Entity
(
"Domain.Entities.Identity.UserRoles.Role"
,
b
=>
{
b
.
Property
<
int
>(
"Id"
)
.
HasColumnType
(
"int"
);
b
.
Property
<
string
>(
"Name"
)
.
IsRequired
()
.
HasMaxLength
(
50
)
.
HasColumnType
(
"nvarchar(50)"
);
b
.
HasKey
(
"Id"
);
b
.
HasIndex
(
"Name"
)
.
IsUnique
();
b
.
ToTable
(
"Role"
,
(
string
)
null
);
});
modelBuilder
.
Entity
(
"Domain.Entities.Identity.Users.DoctorUser"
,
b
=>
{
b
.
Property
<
int
>(
"Id"
)
.
ValueGeneratedOnAdd
()
.
HasColumnType
(
"int"
);
b
.
Property
<
int
>(
"DoctorId"
)
.
HasColumnType
(
"int"
);
b
.
HasKey
(
"Id"
);
b
.
HasIndex
(
"DoctorId"
)
.
IsUnique
();
b
.
ToTable
(
"DoctorUser"
,
(
string
)
null
);
});
modelBuilder
.
Entity
(
"Domain.Entities.Identity.Users.ReceptionistUser"
,
b
=>
{
b
.
Property
<
int
>(
"Id"
)
.
ValueGeneratedOnAdd
()
.
HasColumnType
(
"int"
);
b
.
Property
<
int
>(
"PersonalInfoId"
)
.
HasColumnType
(
"int"
);
b
.
HasKey
(
"Id"
);
b
.
HasIndex
(
"PersonalInfoId"
)
.
IsUnique
();
b
.
ToTable
(
"ReceptionistUser"
,
(
string
)
null
);
});
modelBuilder
.
Entity
(
"Domain.Entities.Identity.Users.User"
,
b
=>
{
b
.
Property
<
int
>(
"Id"
)
.
ValueGeneratedOnAdd
()
.
HasColumnType
(
"int"
);
SqlServerPropertyBuilderExtensions
.
UseIdentityColumn
(
b
.
Property
<
int
>(
"Id"
));
b
.
Property
<
string
>(
"HashedPassword"
)
.
IsRequired
()
.
HasColumnType
(
"nvarchar(max)"
);
b
.
Property
<
int
>(
"RoleId"
)
.
HasColumnType
(
"int"
);
b
.
Property
<
string
>(
"UserName"
)
.
IsRequired
()
.
HasColumnType
(
"nvarchar(450)"
);
b
.
HasKey
(
"Id"
);
b
.
HasIndex
(
"RoleId"
);
b
.
HasIndex
(
"UserName"
)
.
IsUnique
();
b
.
ToTable
(
"User"
,
(
string
)
null
);
});
modelBuilder
.
Entity
(
"Domain.Entities.Medicals.Diseases.Disease"
,
b
=>
{
b
.
Property
<
int
>(
"Id"
)
...
...
@@ -624,6 +705,55 @@ namespace Persistence.Migrations
b
.
ToTable
(
"EmployeeEmployee"
);
});
modelBuilder
.
Entity
(
"Domain.Entities.Identity.Users.DoctorUser"
,
b
=>
{
b
.
HasOne
(
"Domain.Entities.People.Doctors.Doctor"
,
"Doctor"
)
.
WithOne
()
.
HasForeignKey
(
"Domain.Entities.Identity.Users.DoctorUser"
,
"DoctorId"
)
.
OnDelete
(
DeleteBehavior
.
NoAction
)
.
IsRequired
();
b
.
HasOne
(
"Domain.Entities.Identity.Users.User"
,
"User"
)
.
WithOne
()
.
HasForeignKey
(
"Domain.Entities.Identity.Users.DoctorUser"
,
"Id"
)
.
OnDelete
(
DeleteBehavior
.
NoAction
)
.
IsRequired
();
b
.
Navigation
(
"Doctor"
);
b
.
Navigation
(
"User"
);
});
modelBuilder
.
Entity
(
"Domain.Entities.Identity.Users.ReceptionistUser"
,
b
=>
{
b
.
HasOne
(
"Domain.Entities.Identity.Users.User"
,
"User"
)
.
WithOne
()
.
HasForeignKey
(
"Domain.Entities.Identity.Users.ReceptionistUser"
,
"Id"
)
.
OnDelete
(
DeleteBehavior
.
NoAction
)
.
IsRequired
();
b
.
HasOne
(
"Domain.Entities.People.Shared.PersonalInfo"
,
"PersonalInfo"
)
.
WithOne
()
.
HasForeignKey
(
"Domain.Entities.Identity.Users.ReceptionistUser"
,
"PersonalInfoId"
)
.
OnDelete
(
DeleteBehavior
.
NoAction
)
.
IsRequired
();
b
.
Navigation
(
"PersonalInfo"
);
b
.
Navigation
(
"User"
);
});
modelBuilder
.
Entity
(
"Domain.Entities.Identity.Users.User"
,
b
=>
{
b
.
HasOne
(
"Domain.Entities.Identity.UserRoles.Role"
,
"Role"
)
.
WithMany
()
.
HasForeignKey
(
"RoleId"
)
.
OnDelete
(
DeleteBehavior
.
Cascade
)
.
IsRequired
();
b
.
Navigation
(
"Role"
);
});
modelBuilder
.
Entity
(
"Domain.Entities.Medicals.Medicines.Medicine"
,
b
=>
{
b
.
HasOne
(
"Domain.Entities.Medicals.Medicines.MedicineFormValues.MedicineForm"
,
"MedicineForm"
)
...
...
Clinics.Backend/Persistence/Persistence.csproj
View file @
2ddc9eb6
...
...
@@ -7,6 +7,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8">
<PrivateAssets>all</PrivateAssets>
...
...
Clinics.Backend/Persistence/Repositories/Specifications/Evaluator/SpecificationEvaluator.cs
View file @
2ddc9eb6
...
...
@@ -18,7 +18,8 @@ public static class SpecificationEvaluator
queryable
=
queryable
.
Where
(
specification
.
Criteria
);
}
specification
.
IncludeExpressions
.
Aggregate
(
queryable
=
specification
.
IncludeExpressions
.
Aggregate
(
queryable
,
(
current
,
includeExpression
)
=>
current
.
Include
(
includeExpression
)
...
...
Clinics.Backend/Persistence/Repositories/Users/FullUserSpecification.cs
0 → 100644
View file @
2ddc9eb6
using
Domain.Entities.Identity.Users
;
using
Persistence.Repositories.Specifications.Base
;
using
System.Linq.Expressions
;
namespace
Persistence.Repositories.Users
;
public
class
FullUserSpecification
:
Specification
<
User
>
{
public
FullUserSpecification
(
Expression
<
Func
<
User
,
bool
>>?
criteria
)
:
base
(
criteria
)
{
AddInclude
(
user
=>
user
.
Role
);
}
}
Clinics.Backend/Persistence/Repositories/Users/UserRepository.cs
0 → 100644
View file @
2ddc9eb6
using
Domain.Entities.Identity.Users
;
using
Domain.Errors
;
using
Domain.Repositories
;
using
Domain.Shared
;
using
Microsoft.EntityFrameworkCore
;
using
Persistence.Context
;
using
Persistence.Identity.PasswordsHashing
;
using
Persistence.Repositories.Base
;
namespace
Persistence.Repositories.Users
;
public
class
UserRepository
:
Repositroy
<
User
>,
IUserRepository
{
#
region
Ctor
DI
private
readonly
IPasswordHasher
_passwordHasher
;
public
UserRepository
(
ClinicsDbContext
context
,
IPasswordHasher
passwordHasher
)
:
base
(
context
)
{
_passwordHasher
=
passwordHasher
;
}
#
endregion
#
region
Create
method
public
override
Task
<
Result
<
User
>>
CreateAsync
(
User
entity
)
{
_context
.
Entry
(
entity
.
Role
).
State
=
EntityState
.
Unchanged
;
return
base
.
CreateAsync
(
entity
);
}
#
endregion
#
region
Get
by
username
public
async
Task
<
Result
<
User
>>
GetByUserNameFullAsync
(
string
userName
)
{
var
query
=
ApplySpecification
(
new
FullUserSpecification
(
user
=>
user
.
UserName
==
userName
));
var
result
=
await
query
.
ToListAsync
();
if
(
result
.
Count
==
0
)
{
return
Result
.
Failure
<
User
>(
IdentityErrors
.
NotFound
);
}
return
result
.
First
();
}
#
endregion
#
region
Verify
password
public
async
Task
<
Result
<
User
?>>
VerifyPasswordAsync
(
string
userName
,
string
password
)
{
var
userResult
=
await
GetByUserNameFullAsync
(
userName
);
if
(
userResult
.
IsFailure
)
return
Result
.
Failure
<
User
?>(
userResult
.
Error
);
if
(!
_passwordHasher
.
Verify
(
password
,
userResult
.
Value
.
HashedPassword
))
return
Result
.
Success
<
User
?>(
null
);
return
Result
.
Success
<
User
?>(
userResult
.
Value
);
}
#
endregion
}
Clinics.Backend/Persistence/SeedDatabase/AdminUser/ISeedAdminUser.cs
0 → 100644
View file @
2ddc9eb6
namespace
Persistence.SeedDatabase.AdminUser
;
public
interface
ISeedAdminUser
{
public
Task
Seed
();
}
Clinics.Backend/Persistence/SeedDatabase/AdminUser/SeedAdminUser.cs
0 → 100644
View file @
2ddc9eb6
using
Domain.Entities.Identity.UserRoles
;
using
Domain.Entities.Identity.Users
;
using
Domain.Exceptions.InvalidValue
;
using
Domain.Shared
;
using
Microsoft.EntityFrameworkCore
;
using
Persistence.Context
;
using
Persistence.Identity.PasswordsHashing
;
namespace
Persistence.SeedDatabase.AdminUser
;
public
class
SeedAdminUser
:
ISeedAdminUser
{
#
region
CTOR
DI
private
readonly
ClinicsDbContext
_context
;
private
readonly
IPasswordHasher
_passwordHasher
;
public
SeedAdminUser
(
ClinicsDbContext
context
,
IPasswordHasher
passwordHasher
)
{
_context
=
context
;
_passwordHasher
=
passwordHasher
;
}
#
endregion
public
async
Task
Seed
()
{
DbSet
<
User
>
users
=
_context
.
Set
<
User
>();
Result
<
User
>
adminUserResult
=
User
.
Create
(
"admin"
,
_passwordHasher
.
Hash
(
"123"
),
Roles
.
Admin
.
Name
);
if
(
adminUserResult
.
IsFailure
)
throw
new
Exception
(
"Unable to seed admin user"
);
if
(
users
.
Include
(
user
=>
user
.
Role
).
Where
(
user
=>
user
.
Role
==
Roles
.
Admin
).
ToList
().
Count
!=
1
)
{
var
adminUser
=
adminUserResult
.
Value
;
_context
.
Entry
(
adminUser
.
Role
).
State
=
EntityState
.
Unchanged
;
users
.
Add
(
adminUserResult
.
Value
);
await
_context
.
SaveChangesAsync
();
}
}
}
Clinics.Backend/Persistence/SeedDatabase/UserRoles.cs
0 → 100644
View file @
2ddc9eb6
using
Domain.Entities.Identity.UserRoles
;
using
Microsoft.EntityFrameworkCore
;
using
Persistence.Context
;
namespace
Persistence.SeedDatabase
;
public
class
UserRoles
:
ISeed
<
Role
>
{
#
region
CTOR
DI
private
readonly
ClinicsDbContext
_context
;
public
UserRoles
(
ClinicsDbContext
context
)
{
_context
=
context
;
}
#
endregion
public
async
Task
Seed
()
{
DbSet
<
Role
>
roles
=
_context
.
Set
<
Role
>();
var
current
=
await
roles
.
ToListAsync
();
// TODO: perform deep check on all seed operations
if
(
current
.
Count
!=
Roles
.
Count
)
{
roles
.
RemoveRange
(
current
);
roles
.
Add
(
Roles
.
Admin
);
roles
.
Add
(
Roles
.
Doctor
);
roles
.
Add
(
Roles
.
Receptionist
);
await
_context
.
SaveChangesAsync
();
}
}
}
Clinics.Backend/Presentation/Controllers/EmployeesController.cs
View file @
2ddc9eb6
using
Application.Employees.Commands.AttachFamilyMemberToEmployee
;
using
Application.Employees.Commands.CreateEmployee
;
using
Domain.Entities.Identity.UserRoles
;
using
MediatR
;
using
Microsoft.AspNetCore.Authorization
;
using
Microsoft.AspNetCore.Mvc
;
using
Presentation.Controllers.Base
;
...
...
@@ -16,7 +18,7 @@ public class EmployeesController : ApiController
}
#
endregion
[
Authorize
(
Roles
=
Roles
.
AdminName
)]
[
HttpPost
]
public
async
Task
<
IActionResult
>
Create
([
FromBody
]
CreateEmployeeCommand
command
)
{
...
...
@@ -25,7 +27,7 @@ public class EmployeesController : ApiController
return
HandleFailure
(
result
);
return
Created
();
}
[
Authorize
(
Roles
=
Roles
.
DoctorName
)]
[
HttpPut
(
"FamilyMembers"
)]
public
async
Task
<
IActionResult
>
AttachFamilyMember
([
FromBody
]
AttachFamilyMemberToEmployeeCommand
command
)
{
...
...
Clinics.Backend/Presentation/Controllers/UsersController.cs
0 → 100644
View file @
2ddc9eb6
using
Application.Users.Commands.Login
;
using
MediatR
;
using
Microsoft.AspNetCore.Mvc
;
using
Presentation.Controllers.Base
;
namespace
Presentation.Controllers
;
[Route("api/Users")]
public
class
UsersController
:
ApiController
{
#
region
CTOR
DI
public
UsersController
(
ISender
sender
)
:
base
(
sender
)
{
}
#
endregion
[
HttpPost
]
public
async
Task
<
IActionResult
>
LoginUser
([
FromBody
]
LoginCommand
command
)
{
var
result
=
await
_sender
.
Send
(
command
);
if
(
result
.
IsFailure
)
return
HandleFailure
(
result
);
return
Ok
(
result
.
Value
);
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment