Skip to content
Open
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
405 changes: 403 additions & 2 deletions .gitignore

Large diffs are not rendered by default.

56 changes: 26 additions & 30 deletions Data/ApplicationDbContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,34 @@
using Microsoft.EntityFrameworkCore;
using UserManagementUiDemo.Models.Entities;

namespace UserManagementUiDemo.Data
namespace UserManagementUiDemo.Data;

public class ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : IdentityDbContext<ApplicationUser, ApplicationRole, string>(options)
{
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, string>
protected override void OnModelCreating(ModelBuilder builder)
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
base.OnModelCreating(builder);

builder
.Entity<ApplicationUser>()
.HasMany(user => user.UserClaims)
.WithOne()
.HasForeignKey(claim => claim.UserId);

protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder
.Entity<ApplicationUser>()
.HasMany(user => user.UserClaims)
.WithOne()
.HasForeignKey(claim => claim.UserId);
builder
.Entity<ApplicationUser>()
.HasMany(user => user.Roles)
.WithMany(role => role.Users)
.UsingEntity<IdentityUserRole<string>>(
builder => builder.HasOne<ApplicationRole>().WithMany().HasForeignKey(userRole => userRole.RoleId),
builder => builder.HasOne<ApplicationUser>().WithMany().HasForeignKey(userRole => userRole.UserId),
builder => builder.ToTable("AspNetUserRoles")
);
builder
.Entity<ApplicationRole>()
.HasMany(role => role.RoleClaims)
.WithOne()
.HasForeignKey(claim => claim.RoleId);
builder
.Entity<ApplicationUser>()
.HasMany(user => user.Roles)
.WithMany(role => role.Users)
.UsingEntity<IdentityUserRole<string>>(
builder => builder.HasOne<ApplicationRole>().WithMany().HasForeignKey(userRole => userRole.RoleId),
builder => builder.HasOne<ApplicationUser>().WithMany().HasForeignKey(userRole => userRole.UserId),
builder => builder.ToTable("AspNetUserRoles")
);

}
builder
.Entity<ApplicationRole>()
.HasMany(role => role.RoleClaims)
.WithOne()
.HasForeignKey(claim => claim.RoleId);
}
}
}
Binary file modified Data/app.db
Binary file not shown.
40 changes: 19 additions & 21 deletions Models/Authorization/PermissionAuthorizationHandler.cs
Original file line number Diff line number Diff line change
@@ -1,32 +1,30 @@
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using UserManagementUiDemo.Models.Enums;

namespace UserManagementUiDemo.Models.Authorization
namespace UserManagementUiDemo.Models.Authorization;

// Source: https://codewithmukesh.com/blog/permission-based-authorization-in-aspnet-core
public class PermissionAuthorizationHandler : AuthorizationHandler<PermissionRequirement>
{
// Source: https://codewithmukesh.com/blog/permission-based-authorization-in-aspnet-core
public class PermissionAuthorizationHandler : AuthorizationHandler<PermissionRequirement>
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
if (context.User?.Identity.IsAuthenticated != true)
{
if (context.User?.Identity.IsAuthenticated != true)
{
context.Fail();
return Task.CompletedTask;
}

var hasPermission = context.User.HasClaim(nameof(Permission), requirement.Permission);
if (hasPermission)
{
context.Succeed(requirement);
}
else
{
context.Fail();
}

context.Fail();
return Task.CompletedTask;
}

var hasPermission = context.User.HasClaim(nameof(Permission), requirement.Permission);
if (hasPermission)
{
context.Succeed(requirement);
}
else
{
context.Fail();
}

return Task.CompletedTask;
}
}
32 changes: 14 additions & 18 deletions Models/Authorization/PermissionPolicyProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,24 @@
using Microsoft.Extensions.Options;
using UserManagementUiDemo.Models.Enums;

namespace UserManagementUiDemo.Models.Authorization
namespace UserManagementUiDemo.Models.Authorization;
// Source: https://codewithmukesh.com/blog/permission-based-authorization-in-aspnet-core
public class PermissionPolicyProvider(IOptions<AuthorizationOptions> options) : IAuthorizationPolicyProvider
{
// Source: https://codewithmukesh.com/blog/permission-based-authorization-in-aspnet-core
public class PermissionPolicyProvider : IAuthorizationPolicyProvider
public DefaultAuthorizationPolicyProvider FallbackPolicyProvider { get; } = new DefaultAuthorizationPolicyProvider(options);

public Task<AuthorizationPolicy> GetDefaultPolicyAsync() => FallbackPolicyProvider.GetDefaultPolicyAsync();

public Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
{
public DefaultAuthorizationPolicyProvider FallbackPolicyProvider { get; }
public PermissionPolicyProvider(IOptions<AuthorizationOptions> options)
{
FallbackPolicyProvider = new DefaultAuthorizationPolicyProvider(options);
}
public Task<AuthorizationPolicy> GetDefaultPolicyAsync() => FallbackPolicyProvider.GetDefaultPolicyAsync();
public Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
if (Enum.GetNames<Permission>().Contains(policyName))
{
if (Enum.GetNames<Permission>().Contains(policyName))
{
AuthorizationPolicyBuilder policy = new();
policy.AddRequirements(new PermissionRequirement(policyName));
return Task.FromResult(policy.Build());
}
return FallbackPolicyProvider.GetPolicyAsync(policyName);
AuthorizationPolicyBuilder policy = new();
policy.AddRequirements(new PermissionRequirement(policyName));
return Task.FromResult(policy.Build());
}
public Task<AuthorizationPolicy> GetFallbackPolicyAsync() => FallbackPolicyProvider.GetDefaultPolicyAsync();
return FallbackPolicyProvider.GetPolicyAsync(policyName);
}

public Task<AuthorizationPolicy> GetFallbackPolicyAsync() => FallbackPolicyProvider.GetDefaultPolicyAsync();
}
16 changes: 5 additions & 11 deletions Models/Authorization/PermissionRequirement.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
using Microsoft.AspNetCore.Authorization;

namespace UserManagementUiDemo.Models.Authorization
namespace UserManagementUiDemo.Models.Authorization;

// Source: https://codewithmukesh.com/blog/permission-based-authorization-in-aspnet-core
public class PermissionRequirement(string permission) : IAuthorizationRequirement
{
// Source: https://codewithmukesh.com/blog/permission-based-authorization-in-aspnet-core
public class PermissionRequirement : IAuthorizationRequirement
{
public PermissionRequirement(string permission)
{
Permission = permission;
}

public string Permission { get; private set; }
}
public string Permission { get; private set; } = permission;
}
13 changes: 6 additions & 7 deletions Models/Entities/ApplicationRole.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
using System.Collections.Generic;
using Microsoft.AspNetCore.Identity;

namespace UserManagementUiDemo.Models.Entities
namespace UserManagementUiDemo.Models.Entities;

public class ApplicationRole : IdentityRole
{
public class ApplicationRole : IdentityRole
{
public string Description { get; set; }
public ICollection<IdentityRoleClaim<string>> RoleClaims { get; set; }
public ICollection<ApplicationUser> Users { get; set; }
}
public string Description { get; set; }
public ICollection<IdentityRoleClaim<string>> RoleClaims { get; set; }
public ICollection<ApplicationUser> Users { get; set; }
}
13 changes: 6 additions & 7 deletions Models/Entities/ApplicationUser.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
using System.Collections.Generic;
using Microsoft.AspNetCore.Identity;

namespace UserManagementUiDemo.Models.Entities
namespace UserManagementUiDemo.Models.Entities;

public class ApplicationUser : IdentityUser
{
public class ApplicationUser : IdentityUser
{
public string FullName { get; set; }
public ICollection<IdentityUserClaim<string>> UserClaims { get; set; }
public ICollection<ApplicationRole> Roles { get; set; }
}
public string FullName { get; set; }
public ICollection<IdentityUserClaim<string>> UserClaims { get; set; }
public ICollection<ApplicationRole> Roles { get; set; }
}
15 changes: 7 additions & 8 deletions Models/Enums/Permission.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
namespace UserManagementUiDemo.Models.Enums
namespace UserManagementUiDemo.Models.Enums;

public enum Permission
{
public enum Permission
{
Operation1,
Operation2,
Operation3,
UserManagement
}
Operation1,
Operation2,
Operation3,
UserManagement
}
31 changes: 14 additions & 17 deletions Models/InputModels/EditClaimInputModel.cs
Original file line number Diff line number Diff line change
@@ -1,25 +1,22 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.Security.Claims;
using UserManagementUiDemo.Models.Entities;

namespace UserManagementUiDemo.Models.InputModels
namespace UserManagementUiDemo.Models.InputModels;

public class EditClaimInputModel
{
public class EditClaimInputModel
{

[Required(ErrorMessage = "Il claim type è obbligatorio"),
MaxLength(255, ErrorMessage = "Il claim type è troppo lungo"),
Display(Name = "Claim Type")]
public string Type { get; set; }
[Required(ErrorMessage = "Il claim type è obbligatorio"),
MaxLength(255, ErrorMessage = "Il claim type è troppo lungo"),
Display(Name = "Claim Type")]
public string Type { get; set; }

[MaxLength(255, ErrorMessage = "Il claim value è troppo lungo"),
Display(Name = "Claim Value")]
public string Value { get; set; }
[MaxLength(255, ErrorMessage = "Il claim value è troppo lungo"),
Display(Name = "Claim Value")]
public string Value { get; set; }

public Claim ToClaim()
{
return new(Type, Value ?? string.Empty);
}
public Claim ToClaim()
{
return new(Type, Value ?? string.Empty);
}
}
}
17 changes: 8 additions & 9 deletions Models/InputModels/EditRoleInputModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@
using System.Security.Claims;
using UserManagementUiDemo.Models.Entities;

namespace UserManagementUiDemo.Models.InputModels
namespace UserManagementUiDemo.Models.InputModels;

public class EditRoleInputModel
{
public class EditRoleInputModel
{

[Required(ErrorMessage = "Il nome del ruolo è obbligatorio"),
MaxLength(255, ErrorMessage = "Il nome del ruolo è troppo lungo"),
Display(Name = "Nome del ruolo")]
public string Name { get; set; }
}
}
[Required(ErrorMessage = "Il nome del ruolo è obbligatorio"),
MaxLength(255, ErrorMessage = "Il nome del ruolo è troppo lungo"),
Display(Name = "Nome del ruolo")]
public string Name { get; set; }
}
27 changes: 13 additions & 14 deletions Models/InputModels/RoleCreateInputModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,21 @@
using Microsoft.AspNetCore.Identity;
using UserManagementUiDemo.Models.Entities;

namespace UserManagementUiDemo.Models.InputModels
namespace UserManagementUiDemo.Models.InputModels;

public class RoleCreateInputModel
{
public class RoleCreateInputModel
{
[Required(ErrorMessage = "Il nome è obbligatorio"),
MaxLength(255, ErrorMessage = "Il nome è troppo lungo"),
Display(Name = "Nome")]
public string Name { get; set; }
[Required(ErrorMessage = "Il nome è obbligatorio"),
MaxLength(255, ErrorMessage = "Il nome è troppo lungo"),
Display(Name = "Nome")]
public string Name { get; set; }

public ApplicationRole ToApplicationRole()
public ApplicationRole ToApplicationRole()
{
ApplicationRole role = new()
{
ApplicationRole role = new()
{
Name = Name
};
return role;
}
Name = Name
};
return role;
}
}
43 changes: 21 additions & 22 deletions Models/InputModels/RoleEditInfoInputModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,30 @@
using Microsoft.AspNetCore.Identity;
using UserManagementUiDemo.Models.Entities;

namespace UserManagementUiDemo.Models.InputModels
namespace UserManagementUiDemo.Models.InputModels;

public class RoleEditInfoInputModel
{
public class RoleEditInfoInputModel
{
[Required(ErrorMessage = "Il nome è obbligatorio"),
MaxLength(255, ErrorMessage = "Il nome è troppo lungo"),
Display(Name = "Nome completo")]
public string Name { get; set; }
[Required(ErrorMessage = "Il nome è obbligatorio"),
MaxLength(255, ErrorMessage = "Il nome è troppo lungo"),
Display(Name = "Nome completo")]
public string Name { get; set; }

[Display(Name = "Descrizione (opzionale)"),
MaxLength(255, ErrorMessage = "La descrizione è troppo lunga")]
public string Description { get; set; }
public void CopyToApplicationRole(ApplicationRole role)
{
role.Name = Name;
role.Description = Description;
}
[Display(Name = "Descrizione (opzionale)"),
MaxLength(255, ErrorMessage = "La descrizione è troppo lunga")]
public string Description { get; set; }
public void CopyToApplicationRole(ApplicationRole role)
{
role.Name = Name;
role.Description = Description;
}

public static RoleEditInfoInputModel FromApplicationRole(ApplicationRole role)
public static RoleEditInfoInputModel FromApplicationRole(ApplicationRole role)
{
return new RoleEditInfoInputModel
{
return new RoleEditInfoInputModel
{
Name = role.Name,
Description = role.Description
};
}
Name = role.Name,
Description = role.Description
};
}
}
Loading