1- database tables
2- program.cs
3- claimtransformation class
4- controller example
Create application can be done with VS Code too. MVC, is an architecture based on Model-Controller-Views and easy to work with it. the business logic is placed in Controllers.
I created the database in SQL Server, and perform the connection with "Entity Framework Core" package.
The tables that relevant for this subject are Roles, UserRoles and Employees
the entities models that retrieve data from database and store in Models
folder:
Role.cs
[Table("Tbl_Roles")]
public class Role
{
[Key]
public int Id { get; set; }
[StringLength(50)]
public string Name { get; set; }
public virtual List<UserRole> UserRoles { get; set; } //foreign key to UserRoles table
}
UserRole.cs
[Table("Tbl_UserRoles")]
public class UserRole
{
[Key]
public int Id { get; set; }
public int RoleId { get; set; }
public int EmployeeId { get; set; }
public bool Active { get; set; }
//fk to employees table
public virtual Employee Employee { get; set; }
//fk to employees table
public virtual Role Role { get; set; }
}
employee.cs
[Table("Tbl_Employees")]
public class Employee
{
[Key]
public int Id { get; set; }
[StringLength(50)]
[Required]
public string UserName { get; set; } = string.Empty;
[StringLength(50)]
[Required]
public string FirstName { get; set; } = string.Empty;
[StringLength(50)]
[Required]
public string LastName { get; set; } = string.Empty;
[StringLength(50)]
[Required]
public string EmployeeNumber { get; set; } = string.Empty;
public int CompanyId { get; set; }
[StringLength(150)]
public string? Email { get; set; }
[StringLength(50)]
public string? PhoneNumber { get; set; }
public string? Department { get; set; }
public string? SubDepartment { get; set; }
public bool Active { get; set; }
//foreign key to UserRoles table
public virtual List<UserRole> UserRoles { get; set; } = new();
}
program.cs
:// Add DbContext
builder.Services.AddDbContext<VisitorDataContext>(options =>
{
var context = builder.Configuration.GetConnectionString("Your Connection");
options.UseSqlServer(eAESCrypt.AesMethods.DecryptString(key, context));
});
// Add Windows Authentication
builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
.AddNegotiate();
build configuration for each role (as preconfigured in DB) or policy for combined role
Since we use claims as role : use context.User.HasClaim()
instead of Use.IsInRole()
// Add Authorization
builder.Services.AddAuthorizationBuilder()
.AddPolicy("AdminOnly", policy => policy.RequireRole("Admin"))
.AddPolicy("SecurityAdminOnly", policy => policy.RequireRole("SecurityAdmin"))
.AddPolicy("SecurityOnly", policy => policy.RequireRole("Security"))
.AddPolicy("AdminAndSecurityAdminOnly", policy =>
policy.RequireAssertion(context =>
context.User.HasClaim(ClaimTypes.Role, "Admin") || context.User.HasClaim(ClaimTypes.Role, "SecurityAdmin")));
//it allow to permit access to a method in the controller for "Admin" or "SecurityAdmin"
ClaimsTranformation
class to retrieve the Claims role for each user authenticated by windows
public class WVMClaimsTransformation : IClaimsTransformation
{
private readonly VisitorDataContext _context;
public WVMClaimsTransformation(VisitorDataContext context)
{
_context = context;
}
public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
{
if (principal.Identity.IsAuthenticated)
{
var identity = (ClaimsIdentity)principal.Identity;
//get the username without the domain name
var username = Helper.GetUserName(identity.Name);
//get all roles of the specific user from database
var roles = await _context.UserRoles
.Include(ur => ur.Role)
.Where(ur => ur.Employee.UserName == username)
.Select(ur => ur.Role.Name)
.ToListAsync();
//insert it to Claims Identities
foreach (var role in roles)
{
if (!identity.HasClaim(c => c.Type == ClaimTypes.Role && c.Value == role))
{
identity.AddClaim(new Claim(ClaimTypes.Role, role));
}
}
}
return principal;
}
}
Home Controller
for Administration View: // Add the Authorization to Combine Role : **Admin** or **SecurityAdmin **
[Authorize(Policy = "AdminAndSecurityAdminOnly")]
public IActionResult Administration()
{
return View();
}
When you troobleshooting you can see the role as :
I hope that will help people like it help me. if I missed something please fell free to send me feedback.