From d385c50009d9d8814cf93eb14b61054b763628bd Mon Sep 17 00:00:00 2001 From: gradovenko Date: Tue, 8 Oct 2019 00:47:47 +0500 Subject: [PATCH 1/8] Add the required entities in the migration project --- .../Entities/Member.cs | 11 ++++++ .../Entities/MemberRole.cs | 10 +++++ .../Entities/Permission.cs | 10 +++++ .../Entities/Role.cs | 10 +++++ .../Entities/RolePermission.cs | 12 ++++++ .../ProjectManagementSystemDbContext.cs | 38 +++++++++++++------ .../Admin/Roles/IRoleRepository.cs | 7 ++++ .../Admin/Roles/Role.cs | 7 ++++ 8 files changed, 93 insertions(+), 12 deletions(-) create mode 100644 src/ProjectManagementSystem.DatabaseMigrations/Entities/Member.cs create mode 100644 src/ProjectManagementSystem.DatabaseMigrations/Entities/MemberRole.cs create mode 100644 src/ProjectManagementSystem.DatabaseMigrations/Entities/Permission.cs create mode 100644 src/ProjectManagementSystem.DatabaseMigrations/Entities/Role.cs create mode 100644 src/ProjectManagementSystem.DatabaseMigrations/Entities/RolePermission.cs create mode 100644 src/ProjectManagementSystem.Domain/Admin/Roles/IRoleRepository.cs create mode 100644 src/ProjectManagementSystem.Domain/Admin/Roles/Role.cs diff --git a/src/ProjectManagementSystem.DatabaseMigrations/Entities/Member.cs b/src/ProjectManagementSystem.DatabaseMigrations/Entities/Member.cs new file mode 100644 index 0000000..85a2a62 --- /dev/null +++ b/src/ProjectManagementSystem.DatabaseMigrations/Entities/Member.cs @@ -0,0 +1,11 @@ +using System; + +namespace ProjectManagementSystem.DatabaseMigrations.Entities +{ + public sealed class Member + { + public Guid Id { get; set; } + public Guid UserId { get; set; } + public Guid ProjectId { get; set; } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.DatabaseMigrations/Entities/MemberRole.cs b/src/ProjectManagementSystem.DatabaseMigrations/Entities/MemberRole.cs new file mode 100644 index 0000000..2851a30 --- /dev/null +++ b/src/ProjectManagementSystem.DatabaseMigrations/Entities/MemberRole.cs @@ -0,0 +1,10 @@ +using System; + +namespace ProjectManagementSystem.DatabaseMigrations.Entities +{ + public sealed class MemberRole + { + public Guid MemberId { get; set; } + public Guid RoleId { get; set; } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.DatabaseMigrations/Entities/Permission.cs b/src/ProjectManagementSystem.DatabaseMigrations/Entities/Permission.cs new file mode 100644 index 0000000..6645311 --- /dev/null +++ b/src/ProjectManagementSystem.DatabaseMigrations/Entities/Permission.cs @@ -0,0 +1,10 @@ +using System; + +namespace ProjectManagementSystem.DatabaseMigrations.Entities +{ + public sealed class Permission + { + public Guid Id { get; set; } + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.DatabaseMigrations/Entities/Role.cs b/src/ProjectManagementSystem.DatabaseMigrations/Entities/Role.cs new file mode 100644 index 0000000..ae7d37a --- /dev/null +++ b/src/ProjectManagementSystem.DatabaseMigrations/Entities/Role.cs @@ -0,0 +1,10 @@ +using System; + +namespace ProjectManagementSystem.DatabaseMigrations.Entities +{ + public sealed class Role + { + public Guid Id { get; set; } + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.DatabaseMigrations/Entities/RolePermission.cs b/src/ProjectManagementSystem.DatabaseMigrations/Entities/RolePermission.cs new file mode 100644 index 0000000..6aeaab9 --- /dev/null +++ b/src/ProjectManagementSystem.DatabaseMigrations/Entities/RolePermission.cs @@ -0,0 +1,12 @@ +using System; + +namespace ProjectManagementSystem.DatabaseMigrations.Entities +{ + public sealed class RolePermission + { + public Guid RoleId { get; set; } + public Role Role { get; set; } + public Guid PermissionId { get; set; } + public Permission Permission { get; set; } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.DatabaseMigrations/ProjectManagementSystemDbContext.cs b/src/ProjectManagementSystem.DatabaseMigrations/ProjectManagementSystemDbContext.cs index 61eb0e8..f380f86 100644 --- a/src/ProjectManagementSystem.DatabaseMigrations/ProjectManagementSystemDbContext.cs +++ b/src/ProjectManagementSystem.DatabaseMigrations/ProjectManagementSystemDbContext.cs @@ -5,8 +5,11 @@ namespace ProjectManagementSystem.DatabaseMigrations { public sealed class ProjectManagementSystemDbContext : DbContext - { - public ProjectManagementSystemDbContext(DbContextOptions options) : base(options) { } + { + public ProjectManagementSystemDbContext(DbContextOptions options) : + base(options) + { + } protected override void OnModelCreating(ModelBuilder modelBuilder) { @@ -66,13 +69,14 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) builder.HasIndex(u => u.Email) .HasName("EmailIndex") .IsUnique(); - + builder.HasData(new User { Id = new Guid("0ae12bbd-58ef-4c2e-87a6-2c2cb3f9592d"), Name = "Admin", Email = "admin@projectms.local", - PasswordHash = "AQAAAAEAACcQAAAAEDcxbCGbTbY1rUJBVafqc/qaL1rWXro6aoahEwrPF5zHb8DB11apWESUm5UyMRF3mA==", + PasswordHash = + "AQAAAAEAACcQAAAAEDcxbCGbTbY1rUJBVafqc/qaL1rWXro6aoahEwrPF5zHb8DB11apWESUm5UyMRF3mA==", FirstName = "Admin", LastName = "Admin", Role = UserRole.Admin, @@ -80,9 +84,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) Status = UserStatus.Active, ConcurrencyStamp = Guid.NewGuid() }); - }); - + modelBuilder.Entity(builder => { builder.ToTable("RefreshToken"); @@ -99,7 +102,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasColumnName("UserId") .IsRequired(); }); - + modelBuilder.Entity(builder => { builder.ToTable("IssuePriority"); @@ -116,7 +119,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasColumnName("IsActive") .IsRequired(); }); - + modelBuilder.Entity(builder => { builder.ToTable("IssueStatus"); @@ -133,7 +136,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasColumnName("IsActive") .IsRequired(); }); - + modelBuilder.Entity(builder => { builder.ToTable("Project"); @@ -165,7 +168,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasColumnName("ConcurrencyStamp") .IsConcurrencyToken(); }); - + modelBuilder.Entity(builder => { builder.ToTable("Tracker"); @@ -182,11 +185,11 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasColumnName("ConcurrencyStamp") .IsConcurrencyToken(); }); - + modelBuilder.Entity(builder => { builder.ToTable("ProjectTracker"); - builder.HasKey(pt => new { pt.ProjectId, pt.TrackerId }); + builder.HasKey(pt => new {pt.ProjectId, pt.TrackerId}); builder.HasOne(pt => pt.Project) .WithMany() @@ -197,6 +200,17 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasForeignKey(pt => pt.TrackerId) .HasPrincipalKey(t => t.Id); }); + + modelBuilder.Entity(builder => + { + builder.ToTable("Permission"); + builder.HasKey(p => p.Id); + + builder.Property(t => t.Id) + .ValueGeneratedNever(); + builder.Property(t => t.Name) + .IsRequired(); + }); } } } \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/Admin/Roles/IRoleRepository.cs b/src/ProjectManagementSystem.Domain/Admin/Roles/IRoleRepository.cs new file mode 100644 index 0000000..056a91e --- /dev/null +++ b/src/ProjectManagementSystem.Domain/Admin/Roles/IRoleRepository.cs @@ -0,0 +1,7 @@ +namespace ProjectManagementSystem.Domain.Admin.Roles +{ + public interface IRoleRepository + { + + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/Admin/Roles/Role.cs b/src/ProjectManagementSystem.Domain/Admin/Roles/Role.cs new file mode 100644 index 0000000..f624c36 --- /dev/null +++ b/src/ProjectManagementSystem.Domain/Admin/Roles/Role.cs @@ -0,0 +1,7 @@ +namespace ProjectManagementSystem.Domain.Admin.Roles +{ + public sealed class Role + { + + } +} \ No newline at end of file From 44a19700b195bd78cf6fb60cf713686a6d5908c6 Mon Sep 17 00:00:00 2001 From: gradovenko Date: Fri, 11 Oct 2019 00:22:03 +0500 Subject: [PATCH 2/8] Add repositories, handlers, and entities for role logic --- .../Entities/Permission.cs | 1 - .../Admin/CreateMembers/IMemberRepository.cs | 7 +++++++ .../Admin/CreateMembers/IProjectRepository.cs | 7 +++++++ .../Admin/CreateMembers/IUserRepository.cs | 7 +++++++ .../Admin/CreateMembers/Member.cs | 7 +++++++ .../Admin/CreateMembers/Project.cs | 7 +++++++ .../Admin/CreateMembers/User.cs | 7 +++++++ .../Admin/CreateRoles/Role.cs | 7 +++++++ .../Admin/Roles/Role.cs | 7 ------- .../PermissionAuthorizationHandler.cs | 14 ++++++++++++++ .../Authorization/PermissionRequirement.cs | 14 ++++++++++++++ 11 files changed, 77 insertions(+), 8 deletions(-) create mode 100644 src/ProjectManagementSystem.Domain/Admin/CreateMembers/IMemberRepository.cs create mode 100644 src/ProjectManagementSystem.Domain/Admin/CreateMembers/IProjectRepository.cs create mode 100644 src/ProjectManagementSystem.Domain/Admin/CreateMembers/IUserRepository.cs create mode 100644 src/ProjectManagementSystem.Domain/Admin/CreateMembers/Member.cs create mode 100644 src/ProjectManagementSystem.Domain/Admin/CreateMembers/Project.cs create mode 100644 src/ProjectManagementSystem.Domain/Admin/CreateMembers/User.cs create mode 100644 src/ProjectManagementSystem.Domain/Admin/CreateRoles/Role.cs delete mode 100644 src/ProjectManagementSystem.Domain/Admin/Roles/Role.cs create mode 100644 src/ProjectManagementSystem.WebApi/Authorization/PermissionAuthorizationHandler.cs create mode 100644 src/ProjectManagementSystem.WebApi/Authorization/PermissionRequirement.cs diff --git a/src/ProjectManagementSystem.DatabaseMigrations/Entities/Permission.cs b/src/ProjectManagementSystem.DatabaseMigrations/Entities/Permission.cs index 6645311..5aefd5d 100644 --- a/src/ProjectManagementSystem.DatabaseMigrations/Entities/Permission.cs +++ b/src/ProjectManagementSystem.DatabaseMigrations/Entities/Permission.cs @@ -4,7 +4,6 @@ namespace ProjectManagementSystem.DatabaseMigrations.Entities { public sealed class Permission { - public Guid Id { get; set; } public string Name { get; set; } } } \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/Admin/CreateMembers/IMemberRepository.cs b/src/ProjectManagementSystem.Domain/Admin/CreateMembers/IMemberRepository.cs new file mode 100644 index 0000000..21ccc1b --- /dev/null +++ b/src/ProjectManagementSystem.Domain/Admin/CreateMembers/IMemberRepository.cs @@ -0,0 +1,7 @@ +namespace ProjectManagementSystem.Domain.Admin.CreateMembers +{ + public interface IMemberRepository + { + + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/Admin/CreateMembers/IProjectRepository.cs b/src/ProjectManagementSystem.Domain/Admin/CreateMembers/IProjectRepository.cs new file mode 100644 index 0000000..feb6ada --- /dev/null +++ b/src/ProjectManagementSystem.Domain/Admin/CreateMembers/IProjectRepository.cs @@ -0,0 +1,7 @@ +namespace ProjectManagementSystem.Domain.Admin.CreateMembers +{ + public interface IProjectRepository + { + + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/Admin/CreateMembers/IUserRepository.cs b/src/ProjectManagementSystem.Domain/Admin/CreateMembers/IUserRepository.cs new file mode 100644 index 0000000..a7d5c80 --- /dev/null +++ b/src/ProjectManagementSystem.Domain/Admin/CreateMembers/IUserRepository.cs @@ -0,0 +1,7 @@ +namespace ProjectManagementSystem.Domain.Admin.CreateMembers +{ + public interface IUserRepository + { + + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/Admin/CreateMembers/Member.cs b/src/ProjectManagementSystem.Domain/Admin/CreateMembers/Member.cs new file mode 100644 index 0000000..c53cb8c --- /dev/null +++ b/src/ProjectManagementSystem.Domain/Admin/CreateMembers/Member.cs @@ -0,0 +1,7 @@ +namespace ProjectManagementSystem.Domain.Admin.CreateMembers +{ + public sealed class Member + { + + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/Admin/CreateMembers/Project.cs b/src/ProjectManagementSystem.Domain/Admin/CreateMembers/Project.cs new file mode 100644 index 0000000..aa4d2f2 --- /dev/null +++ b/src/ProjectManagementSystem.Domain/Admin/CreateMembers/Project.cs @@ -0,0 +1,7 @@ +namespace ProjectManagementSystem.Domain.Admin.CreateMembers +{ + public sealed class Project + { + + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/Admin/CreateMembers/User.cs b/src/ProjectManagementSystem.Domain/Admin/CreateMembers/User.cs new file mode 100644 index 0000000..ae30f80 --- /dev/null +++ b/src/ProjectManagementSystem.Domain/Admin/CreateMembers/User.cs @@ -0,0 +1,7 @@ +namespace ProjectManagementSystem.Domain.Admin.CreateMembers +{ + public sealed class User + { + + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/Admin/CreateRoles/Role.cs b/src/ProjectManagementSystem.Domain/Admin/CreateRoles/Role.cs new file mode 100644 index 0000000..fcae58f --- /dev/null +++ b/src/ProjectManagementSystem.Domain/Admin/CreateRoles/Role.cs @@ -0,0 +1,7 @@ +namespace ProjectManagementSystem.Domain.Admin.CreateRoles +{ + public sealed class Role + { + + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/Admin/Roles/Role.cs b/src/ProjectManagementSystem.Domain/Admin/Roles/Role.cs deleted file mode 100644 index f624c36..0000000 --- a/src/ProjectManagementSystem.Domain/Admin/Roles/Role.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace ProjectManagementSystem.Domain.Admin.Roles -{ - public sealed class Role - { - - } -} \ No newline at end of file diff --git a/src/ProjectManagementSystem.WebApi/Authorization/PermissionAuthorizationHandler.cs b/src/ProjectManagementSystem.WebApi/Authorization/PermissionAuthorizationHandler.cs new file mode 100644 index 0000000..e4fd22a --- /dev/null +++ b/src/ProjectManagementSystem.WebApi/Authorization/PermissionAuthorizationHandler.cs @@ -0,0 +1,14 @@ +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; + +namespace ProjectManagementSystem.WebApi.Authorization +{ + public class PermissionAuthorizationHandler : AuthorizationHandler + { + protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement) + { + + } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.WebApi/Authorization/PermissionRequirement.cs b/src/ProjectManagementSystem.WebApi/Authorization/PermissionRequirement.cs new file mode 100644 index 0000000..09d888b --- /dev/null +++ b/src/ProjectManagementSystem.WebApi/Authorization/PermissionRequirement.cs @@ -0,0 +1,14 @@ +using Microsoft.AspNetCore.Authorization; + +namespace ProjectManagementSystem.WebApi.Authorization +{ + public sealed class PermissionRequirement : IAuthorizationRequirement + { + public string Permission { get; } + + public PermissionRequirement(string permission) + { + Permission = permission; + } + } +} \ No newline at end of file From 127aa4abf2b5b9da81cd4a23dcd6338817d7f100 Mon Sep 17 00:00:00 2001 From: gradovenko Date: Fri, 11 Oct 2019 00:22:29 +0500 Subject: [PATCH 3/8] Add repository --- .../Admin/CreateRoles/IRoleRepository.cs | 13 +++++++++++++ .../Admin/Roles/IRoleRepository.cs | 7 ------- 2 files changed, 13 insertions(+), 7 deletions(-) create mode 100644 src/ProjectManagementSystem.Domain/Admin/CreateRoles/IRoleRepository.cs delete mode 100644 src/ProjectManagementSystem.Domain/Admin/Roles/IRoleRepository.cs diff --git a/src/ProjectManagementSystem.Domain/Admin/CreateRoles/IRoleRepository.cs b/src/ProjectManagementSystem.Domain/Admin/CreateRoles/IRoleRepository.cs new file mode 100644 index 0000000..3b3aad0 --- /dev/null +++ b/src/ProjectManagementSystem.Domain/Admin/CreateRoles/IRoleRepository.cs @@ -0,0 +1,13 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace ProjectManagementSystem.Domain.Admin.CreateRoles +{ + public interface IRoleRepository + { + Task Get(Guid id, CancellationToken cancellationToken); + + Task Save(Role role); + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/Admin/Roles/IRoleRepository.cs b/src/ProjectManagementSystem.Domain/Admin/Roles/IRoleRepository.cs deleted file mode 100644 index 056a91e..0000000 --- a/src/ProjectManagementSystem.Domain/Admin/Roles/IRoleRepository.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace ProjectManagementSystem.Domain.Admin.Roles -{ - public interface IRoleRepository - { - - } -} \ No newline at end of file From 945bd3c2e51564f7c640cc7bcc2b3c7c7979bad4 Mon Sep 17 00:00:00 2001 From: gradovenko Date: Sun, 24 Nov 2019 21:08:10 +0500 Subject: [PATCH 4/8] Add permission logic, add entities and relationships for database migrations for permissions and members --- .../Entities/Member.cs | 2 + .../Entities/MemberRole.cs | 2 + .../Entities/Permission.cs | 1 + .../ProjectManagementSystemDbContext.cs | 77 +++++++++++++++--- .../CreateRoles/IPermissionRepository.cs | 11 +++ .../Admin/CreateRoles/Permission.cs | 10 +++ .../Admin/CreateRoles/Role.cs | 20 +++++ .../Admin/CreateRoles/RolePermission.cs | 16 ++++ .../Admin/Permissions/PermissionView.cs | 7 ++ .../Admin/Roles/RoleListQuery.cs | 7 ++ .../Admin/Roles/RoleListViewModel.cs | 7 ++ .../Admin/Roles/RoleQuery.cs | 15 ++++ .../Admin/Roles/RoleViewModel.cs | 7 ++ .../PermissionAuthorizationHandler.cs | 1 + .../Admin/PermissionsController.cs | 10 +++ .../Controllers/Admin/RolesController.cs | 81 +++++++++++++++++++ .../Exceptions/ErrorCode.cs | 1 + .../Admin/Roles/AddPermissionBindModel.cs | 9 +++ .../Models/Admin/Roles/CreateRoleBindModel.cs | 12 +++ .../Models/Admin/Roles/QueryRoleBindModel.cs | 7 ++ src/ProjectManagementSystem.WebApi/Startup.cs | 1 + 21 files changed, 294 insertions(+), 10 deletions(-) create mode 100644 src/ProjectManagementSystem.Domain/Admin/CreateRoles/IPermissionRepository.cs create mode 100644 src/ProjectManagementSystem.Domain/Admin/CreateRoles/Permission.cs create mode 100644 src/ProjectManagementSystem.Domain/Admin/CreateRoles/RolePermission.cs create mode 100644 src/ProjectManagementSystem.Queries/Admin/Permissions/PermissionView.cs create mode 100644 src/ProjectManagementSystem.Queries/Admin/Roles/RoleListQuery.cs create mode 100644 src/ProjectManagementSystem.Queries/Admin/Roles/RoleListViewModel.cs create mode 100644 src/ProjectManagementSystem.Queries/Admin/Roles/RoleQuery.cs create mode 100644 src/ProjectManagementSystem.Queries/Admin/Roles/RoleViewModel.cs create mode 100644 src/ProjectManagementSystem.WebApi/Controllers/Admin/PermissionsController.cs create mode 100644 src/ProjectManagementSystem.WebApi/Controllers/Admin/RolesController.cs create mode 100644 src/ProjectManagementSystem.WebApi/Models/Admin/Roles/AddPermissionBindModel.cs create mode 100644 src/ProjectManagementSystem.WebApi/Models/Admin/Roles/CreateRoleBindModel.cs create mode 100644 src/ProjectManagementSystem.WebApi/Models/Admin/Roles/QueryRoleBindModel.cs diff --git a/src/ProjectManagementSystem.DatabaseMigrations/Entities/Member.cs b/src/ProjectManagementSystem.DatabaseMigrations/Entities/Member.cs index 85a2a62..40f01dd 100644 --- a/src/ProjectManagementSystem.DatabaseMigrations/Entities/Member.cs +++ b/src/ProjectManagementSystem.DatabaseMigrations/Entities/Member.cs @@ -6,6 +6,8 @@ public sealed class Member { public Guid Id { get; set; } public Guid UserId { get; set; } + public User User { get; set; } public Guid ProjectId { get; set; } + public Project Project { get; set; } } } \ No newline at end of file diff --git a/src/ProjectManagementSystem.DatabaseMigrations/Entities/MemberRole.cs b/src/ProjectManagementSystem.DatabaseMigrations/Entities/MemberRole.cs index 2851a30..2e6a3b8 100644 --- a/src/ProjectManagementSystem.DatabaseMigrations/Entities/MemberRole.cs +++ b/src/ProjectManagementSystem.DatabaseMigrations/Entities/MemberRole.cs @@ -5,6 +5,8 @@ namespace ProjectManagementSystem.DatabaseMigrations.Entities public sealed class MemberRole { public Guid MemberId { get; set; } + public Member Member { get; set; } public Guid RoleId { get; set; } + public Role Role { get; set; } } } \ No newline at end of file diff --git a/src/ProjectManagementSystem.DatabaseMigrations/Entities/Permission.cs b/src/ProjectManagementSystem.DatabaseMigrations/Entities/Permission.cs index 5aefd5d..6645311 100644 --- a/src/ProjectManagementSystem.DatabaseMigrations/Entities/Permission.cs +++ b/src/ProjectManagementSystem.DatabaseMigrations/Entities/Permission.cs @@ -4,6 +4,7 @@ namespace ProjectManagementSystem.DatabaseMigrations.Entities { public sealed class Permission { + public Guid Id { get; set; } public string Name { get; set; } } } \ No newline at end of file diff --git a/src/ProjectManagementSystem.DatabaseMigrations/ProjectManagementSystemDbContext.cs b/src/ProjectManagementSystem.DatabaseMigrations/ProjectManagementSystemDbContext.cs index ca2afa2..be6e2a6 100644 --- a/src/ProjectManagementSystem.DatabaseMigrations/ProjectManagementSystemDbContext.cs +++ b/src/ProjectManagementSystem.DatabaseMigrations/ProjectManagementSystemDbContext.cs @@ -5,8 +5,11 @@ namespace ProjectManagementSystem.DatabaseMigrations { public sealed class ProjectManagementSystemDbContext : DbContext - { - public ProjectManagementSystemDbContext(DbContextOptions options) : base(options) { } + { + public ProjectManagementSystemDbContext(DbContextOptions options) : + base(options) + { + } protected override void OnModelCreating(ModelBuilder modelBuilder) { @@ -72,7 +75,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) Id = new Guid("0ae12bbd-58ef-4c2e-87a6-2c2cb3f9592d"), Name = "Admin", Email = "admin@projectms.local", - PasswordHash = "AQAAAAEAACcQAAAAEDcxbCGbTbY1rUJBVafqc/qaL1rWXro6aoahEwrPF5zHb8DB11apWESUm5UyMRF3mA==", + PasswordHash = + "AQAAAAEAACcQAAAAEDcxbCGbTbY1rUJBVafqc/qaL1rWXro6aoahEwrPF5zHb8DB11apWESUm5UyMRF3mA==", FirstName = "Admin", LastName = "Admin", Role = UserRole.Admin, @@ -185,7 +189,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(builder => { builder.ToTable("ProjectTracker"); - builder.HasKey(pt => new { pt.ProjectId, pt.TrackerId }); + builder.HasKey(pt => new {pt.ProjectId, pt.TrackerId}); builder.HasOne(pt => pt.Project) .WithMany() @@ -196,7 +200,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasForeignKey(pt => pt.TrackerId) .HasPrincipalKey(t => t.Id); }); - + modelBuilder.Entity(builder => { builder.ToTable("Issue"); @@ -264,7 +268,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasForeignKey(i => i.PerformerId) .HasPrincipalKey(p => p.Id); }); - + modelBuilder.Entity(builder => { builder.ToTable("TimeEntryActivity"); @@ -282,7 +286,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasColumnName("ConcurrencyStamp") .IsConcurrencyToken(); }); - + modelBuilder.Entity(builder => { builder.ToTable("TimeEntry"); @@ -337,16 +341,69 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasPrincipalKey(p => p.Id); }); + modelBuilder.Entity(builder => + { + builder.ToTable("Role"); + builder.HasKey(r => r.Id); + builder.Property(r => r.Id) + .ValueGeneratedNever(); + builder.Property(r => r.Name) + .IsRequired(); + }); + modelBuilder.Entity(builder => { builder.ToTable("Permission"); builder.HasKey(p => p.Id); - - builder.Property(t => t.Id) + builder.Property(p => p.Id) .ValueGeneratedNever(); - builder.Property(t => t.Name) + builder.Property(p => p.Name) .IsRequired(); }); + + modelBuilder.Entity(builder => + { + builder.ToTable("RolePermission"); + builder.HasKey(rp => new {rp.RoleId, rp.PermissionId}); + builder.HasOne(rp => rp.Role) + .WithMany() + .HasForeignKey(rp => rp.RoleId) + .HasPrincipalKey(r => r.Id); + builder.HasOne(rp => rp.Permission) + .WithMany() + .HasForeignKey(rp => rp.PermissionId) + .HasPrincipalKey(p => p.Id); + }); + + modelBuilder.Entity(builder => + { + builder.ToTable("Member"); + builder.HasKey(m => m.Id); + builder.Property(m => m.Id) + .ValueGeneratedNever(); + builder.HasOne(m => m.User) + .WithMany() + .HasForeignKey(m => m.UserId) + .HasPrincipalKey(u => u.Id); + builder.HasOne(m => m.Project) + .WithMany() + .HasForeignKey(m => m.ProjectId) + .HasPrincipalKey(p => p.Id); + }); + + modelBuilder.Entity(builder => + { + builder.ToTable("MemberRole"); + builder.HasKey(mr => new {mr.MemberId, mr.RoleId}); + builder.HasOne(mr => mr.Member) + .WithMany() + .HasForeignKey(mr => mr.MemberId) + .HasPrincipalKey(m => m.Id); + builder.HasOne(mr => mr.Role) + .WithMany() + .HasForeignKey(mr => mr.RoleId) + .HasPrincipalKey(r => r.Id); + }); } } } \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/Admin/CreateRoles/IPermissionRepository.cs b/src/ProjectManagementSystem.Domain/Admin/CreateRoles/IPermissionRepository.cs new file mode 100644 index 0000000..90af72b --- /dev/null +++ b/src/ProjectManagementSystem.Domain/Admin/CreateRoles/IPermissionRepository.cs @@ -0,0 +1,11 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace ProjectManagementSystem.Domain.Admin.CreateRoles +{ + public interface IPermissionRepository + { + Task Get(Guid id, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/Admin/CreateRoles/Permission.cs b/src/ProjectManagementSystem.Domain/Admin/CreateRoles/Permission.cs new file mode 100644 index 0000000..19bcd2a --- /dev/null +++ b/src/ProjectManagementSystem.Domain/Admin/CreateRoles/Permission.cs @@ -0,0 +1,10 @@ +using System; + +namespace ProjectManagementSystem.Domain.Admin.CreateRoles +{ + public sealed class Permission + { + public Guid Id { get; private set; } + public string Name { get; private set; } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/Admin/CreateRoles/Role.cs b/src/ProjectManagementSystem.Domain/Admin/CreateRoles/Role.cs index fcae58f..a4bfc18 100644 --- a/src/ProjectManagementSystem.Domain/Admin/CreateRoles/Role.cs +++ b/src/ProjectManagementSystem.Domain/Admin/CreateRoles/Role.cs @@ -1,7 +1,27 @@ +using System; +using System.Collections.Generic; + namespace ProjectManagementSystem.Domain.Admin.CreateRoles { public sealed class Role { + public Guid Id { get; private set; } + public string Name { get; private set; } + private List _rolePermissions = new List(); + public IEnumerable RolePermissions => _rolePermissions; + private Guid _concurrencyStamp = Guid.NewGuid(); + + public Role(Guid id, string name) + { + Id = id; + Name = name; + } + public void AddRolePermission(RolePermission rolePermission) + { + _rolePermissions.Add(rolePermission); + + _concurrencyStamp = Guid.NewGuid(); + } } } \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/Admin/CreateRoles/RolePermission.cs b/src/ProjectManagementSystem.Domain/Admin/CreateRoles/RolePermission.cs new file mode 100644 index 0000000..f871d8b --- /dev/null +++ b/src/ProjectManagementSystem.Domain/Admin/CreateRoles/RolePermission.cs @@ -0,0 +1,16 @@ +using System; + +namespace ProjectManagementSystem.Domain.Admin.CreateRoles +{ + public class RolePermission + { + public Guid RoleId { get; set; } + public Guid PermissionId { get; set; } + + public RolePermission(Guid roleId, Guid permissionId) + { + RoleId = roleId; + PermissionId = permissionId; + } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Queries/Admin/Permissions/PermissionView.cs b/src/ProjectManagementSystem.Queries/Admin/Permissions/PermissionView.cs new file mode 100644 index 0000000..f5361ae --- /dev/null +++ b/src/ProjectManagementSystem.Queries/Admin/Permissions/PermissionView.cs @@ -0,0 +1,7 @@ +namespace ProjectManagementSystem.Queries.Admin.Permissions +{ + public sealed class PermissionView + { + + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Queries/Admin/Roles/RoleListQuery.cs b/src/ProjectManagementSystem.Queries/Admin/Roles/RoleListQuery.cs new file mode 100644 index 0000000..86ed17f --- /dev/null +++ b/src/ProjectManagementSystem.Queries/Admin/Roles/RoleListQuery.cs @@ -0,0 +1,7 @@ +namespace ProjectManagementSystem.Queries.Admin.Roles +{ + public sealed class RoleListQuery : PageQuery + { + public RoleListQuery(int offset, int limit) : base(offset, limit) { } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Queries/Admin/Roles/RoleListViewModel.cs b/src/ProjectManagementSystem.Queries/Admin/Roles/RoleListViewModel.cs new file mode 100644 index 0000000..2b3ff1f --- /dev/null +++ b/src/ProjectManagementSystem.Queries/Admin/Roles/RoleListViewModel.cs @@ -0,0 +1,7 @@ +namespace ProjectManagementSystem.Queries.Admin.Roles +{ + public sealed class RoleListViewModel + { + + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Queries/Admin/Roles/RoleQuery.cs b/src/ProjectManagementSystem.Queries/Admin/Roles/RoleQuery.cs new file mode 100644 index 0000000..53482b3 --- /dev/null +++ b/src/ProjectManagementSystem.Queries/Admin/Roles/RoleQuery.cs @@ -0,0 +1,15 @@ +using System; +using MediatR; + +namespace ProjectManagementSystem.Queries.Admin.Roles +{ + public sealed class RoleQuery : IRequest + { + public Guid Id { get; } + + public RoleQuery(Guid id) + { + Id = id; + } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Queries/Admin/Roles/RoleViewModel.cs b/src/ProjectManagementSystem.Queries/Admin/Roles/RoleViewModel.cs new file mode 100644 index 0000000..b8c4fc4 --- /dev/null +++ b/src/ProjectManagementSystem.Queries/Admin/Roles/RoleViewModel.cs @@ -0,0 +1,7 @@ +namespace ProjectManagementSystem.Queries.Admin.Roles +{ + public sealed class RoleViewModel + { + + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.WebApi/Authorization/PermissionAuthorizationHandler.cs b/src/ProjectManagementSystem.WebApi/Authorization/PermissionAuthorizationHandler.cs index e4fd22a..d2a492c 100644 --- a/src/ProjectManagementSystem.WebApi/Authorization/PermissionAuthorizationHandler.cs +++ b/src/ProjectManagementSystem.WebApi/Authorization/PermissionAuthorizationHandler.cs @@ -8,6 +8,7 @@ public class PermissionAuthorizationHandler : AuthorizationHandler Create( + CancellationToken cancellationToken, + [FromBody] CreateRoleBindModel model, + [FromServices] IRoleRepository roleRepository, + [FromServices] IPermissionRepository permissionRepository) + { + var role = await roleRepository.Get(model.Id, cancellationToken); + + if (role != null) + throw new ApiException(HttpStatusCode.Conflict, ErrorCode.RoleAlreadyExists, + "Role already exists with other parameters"); + + role = new Role(model.Id, model.Name); + + foreach (var permissionModel in model.Permissions) + { + var permission = await permissionRepository.Get(permissionModel.Id, cancellationToken); + + if (permission == null) + throw new ApiException(HttpStatusCode.NotFound, ErrorCode.TrackerNotFound, "Tracker not found"); + + var rolePermission = new RolePermission(model.Id, permission.Id); + + role.AddRolePermission(rolePermission); + } + + await roleRepository.Save(role); + + return CreatedAtRoute("GetRoleAdminRoute", new {id = role.Id}, null); + } + + [HttpGet("admin/roles")] + [ProducesResponseType(typeof(RoleListViewModel), 200)] + public async Task Find( + CancellationToken cancellationToken, + [FromQuery] QueryRoleBindModel model, + [FromServices] IMediator mediator) + { + return Ok(await mediator.Send(new RoleListQuery(model.Offset, model.Limit), cancellationToken)); + } + + [HttpGet("admin/roles/{id}", Name = "GetRoleAdminRoute")] + [ProducesResponseType(typeof(RoleViewModel), 200)] + [ProducesResponseType(typeof(ProblemDetails), 404)] + public async Task Get( + CancellationToken cancellationToken, + [FromRoute] Guid id, + [FromServices] IMediator mediator) + { + var role = await mediator.Send(new RoleQuery(id), cancellationToken); + + if (role == null) + throw new ApiException(HttpStatusCode.NotFound, ErrorCode.IssueStatusNotFound, + "Issue status not found"); + + return Ok(role); + } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.WebApi/Exceptions/ErrorCode.cs b/src/ProjectManagementSystem.WebApi/Exceptions/ErrorCode.cs index 50182d1..57a9ffc 100644 --- a/src/ProjectManagementSystem.WebApi/Exceptions/ErrorCode.cs +++ b/src/ProjectManagementSystem.WebApi/Exceptions/ErrorCode.cs @@ -32,5 +32,6 @@ public class ErrorCode public const string TimeEntryActivityAlreadyExists = "time_entry_activity_already_exists"; public const string TimeEntryNotFound = "time_entry_not_found"; public const string TimeEntryAlreadyExists = "time_entry_already_exists"; + public const string RoleAlreadyExists = "role_activity_already_exists"; } } \ No newline at end of file diff --git a/src/ProjectManagementSystem.WebApi/Models/Admin/Roles/AddPermissionBindModel.cs b/src/ProjectManagementSystem.WebApi/Models/Admin/Roles/AddPermissionBindModel.cs new file mode 100644 index 0000000..017ccc7 --- /dev/null +++ b/src/ProjectManagementSystem.WebApi/Models/Admin/Roles/AddPermissionBindModel.cs @@ -0,0 +1,9 @@ +using System; + +namespace ProjectManagementSystem.WebApi.Models.Admin.Roles +{ + public sealed class AddPermissionBindModel + { + public Guid Id { get; set; } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.WebApi/Models/Admin/Roles/CreateRoleBindModel.cs b/src/ProjectManagementSystem.WebApi/Models/Admin/Roles/CreateRoleBindModel.cs new file mode 100644 index 0000000..d98f122 --- /dev/null +++ b/src/ProjectManagementSystem.WebApi/Models/Admin/Roles/CreateRoleBindModel.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; + +namespace ProjectManagementSystem.WebApi.Models.Admin.Roles +{ + public sealed class CreateRoleBindModel + { + public Guid Id { get; private set; } + public string Name { get; private set; } + public IEnumerable Permissions { get; set; } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.WebApi/Models/Admin/Roles/QueryRoleBindModel.cs b/src/ProjectManagementSystem.WebApi/Models/Admin/Roles/QueryRoleBindModel.cs new file mode 100644 index 0000000..e7203f6 --- /dev/null +++ b/src/ProjectManagementSystem.WebApi/Models/Admin/Roles/QueryRoleBindModel.cs @@ -0,0 +1,7 @@ +namespace ProjectManagementSystem.WebApi.Models.Admin.Roles +{ + public sealed class QueryRoleBindModel : QueryPageBindModel + { + + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.WebApi/Startup.cs b/src/ProjectManagementSystem.WebApi/Startup.cs index 329ffc9..0e5bdc5 100644 --- a/src/ProjectManagementSystem.WebApi/Startup.cs +++ b/src/ProjectManagementSystem.WebApi/Startup.cs @@ -40,6 +40,7 @@ public void ConfigureServices(IServiceCollection services) services .AddMvc(options => { + options.Filters.Add(typeof(PermissionAuthorizationHandler)); options.Filters.Add(typeof(ErrorHandlingFilter)); options.EnableEndpointRouting = false; }) From 89ee6632b00ce7e61afb5ec4bf1ec8ada59cf036 Mon Sep 17 00:00:00 2001 From: gradovenko Date: Mon, 3 Feb 2020 01:23:19 +0500 Subject: [PATCH 5/8] Add role logic and add some membership logic --- .../Controllers/Admin/MembersController.cs | 70 ++++++++++++++ .../Controllers/Admin/RolesController.cs | 94 +++++++++++++++++++ .../Exceptions/ErrorCode.cs | 2 + .../Admin/Members/CreateMemberBinding.cs | 23 +++++ .../Admin/Members/CreateProjectRoleBinding.cs | 29 ++++++ .../Models/Admin/Roles/CreateRoleBinding.cs | 37 ++++++++ .../Models/Admin/Roles/FindProjectsBinding.cs | 29 ++++++ .../Entities/Member.cs | 2 +- .../Entities/Permission.cs | 2 +- .../Entities/Role.cs | 2 +- .../ProjectManagementSystemDbContext.cs | 20 ++-- .../Admin/CreateMembers/IMemberRepository.cs | 7 -- .../Admin/CreateMembers/IProjectRepository.cs | 7 -- .../Admin/CreateMembers/IUserRepository.cs | 7 -- .../Admin/CreateMembers/Member.cs | 7 -- .../Admin/CreateMembers/Project.cs | 7 -- .../Admin/CreateMembers/User.cs | 7 -- .../Admin/CreateRoles/Permission.cs | 10 -- .../Admin/Members/IMemberRepository.cs | 11 +++ .../Admin/Members/IProjectRepository.cs | 11 +++ .../Admin/Members/IUserRepository.cs | 11 +++ .../Admin/Members/Member.cs | 12 +++ .../Admin/Members/MemberRole.cs | 10 ++ .../Admin/Members/Project.cs | 9 ++ .../Admin/Members/Role.cs | 9 ++ .../Admin/Members/User.cs | 19 ++++ .../IPermissionRepository.cs | 2 +- .../{CreateRoles => Roles}/IRoleRepository.cs | 4 +- .../Admin/Roles/Permission.cs | 10 ++ .../Admin/{CreateRoles => Roles}/Role.cs | 8 +- .../Admin/Roles/RoleCreationService.cs | 7 ++ .../{CreateRoles => Roles}/RolePermission.cs | 8 +- .../Admin/Roles/RoleListItemView.cs | 9 ++ .../Admin/Roles/RoleListQuery.cs | 2 +- .../Admin/Roles/RoleListViewModel.cs | 7 -- .../Admin/Roles/RoleQuery.cs | 2 +- .../Admin/Roles/RoleView.cs | 9 ++ .../Admin/Roles/RoleViewModel.cs | 7 -- 38 files changed, 437 insertions(+), 92 deletions(-) create mode 100644 src/ProjectManagementSystem.Api/Controllers/Admin/MembersController.cs create mode 100644 src/ProjectManagementSystem.Api/Controllers/Admin/RolesController.cs create mode 100644 src/ProjectManagementSystem.Api/Models/Admin/Members/CreateMemberBinding.cs create mode 100644 src/ProjectManagementSystem.Api/Models/Admin/Members/CreateProjectRoleBinding.cs create mode 100644 src/ProjectManagementSystem.Api/Models/Admin/Roles/CreateRoleBinding.cs create mode 100644 src/ProjectManagementSystem.Api/Models/Admin/Roles/FindProjectsBinding.cs delete mode 100644 src/ProjectManagementSystem.Domain/Admin/CreateMembers/IMemberRepository.cs delete mode 100644 src/ProjectManagementSystem.Domain/Admin/CreateMembers/IProjectRepository.cs delete mode 100644 src/ProjectManagementSystem.Domain/Admin/CreateMembers/IUserRepository.cs delete mode 100644 src/ProjectManagementSystem.Domain/Admin/CreateMembers/Member.cs delete mode 100644 src/ProjectManagementSystem.Domain/Admin/CreateMembers/Project.cs delete mode 100644 src/ProjectManagementSystem.Domain/Admin/CreateMembers/User.cs delete mode 100644 src/ProjectManagementSystem.Domain/Admin/CreateRoles/Permission.cs create mode 100644 src/ProjectManagementSystem.Domain/Admin/Members/IMemberRepository.cs create mode 100644 src/ProjectManagementSystem.Domain/Admin/Members/IProjectRepository.cs create mode 100644 src/ProjectManagementSystem.Domain/Admin/Members/IUserRepository.cs create mode 100644 src/ProjectManagementSystem.Domain/Admin/Members/Member.cs create mode 100644 src/ProjectManagementSystem.Domain/Admin/Members/MemberRole.cs create mode 100644 src/ProjectManagementSystem.Domain/Admin/Members/Project.cs create mode 100644 src/ProjectManagementSystem.Domain/Admin/Members/Role.cs create mode 100644 src/ProjectManagementSystem.Domain/Admin/Members/User.cs rename src/ProjectManagementSystem.Domain/Admin/{CreateRoles => Roles}/IPermissionRepository.cs (77%) rename src/ProjectManagementSystem.Domain/Admin/{CreateRoles => Roles}/IRoleRepository.cs (63%) create mode 100644 src/ProjectManagementSystem.Domain/Admin/Roles/Permission.cs rename src/ProjectManagementSystem.Domain/Admin/{CreateRoles => Roles}/Role.cs (77%) create mode 100644 src/ProjectManagementSystem.Domain/Admin/Roles/RoleCreationService.cs rename src/ProjectManagementSystem.Domain/Admin/{CreateRoles => Roles}/RolePermission.cs (50%) create mode 100644 src/ProjectManagementSystem.Queries/Admin/Roles/RoleListItemView.cs delete mode 100644 src/ProjectManagementSystem.Queries/Admin/Roles/RoleListViewModel.cs create mode 100644 src/ProjectManagementSystem.Queries/Admin/Roles/RoleView.cs delete mode 100644 src/ProjectManagementSystem.Queries/Admin/Roles/RoleViewModel.cs diff --git a/src/ProjectManagementSystem.Api/Controllers/Admin/MembersController.cs b/src/ProjectManagementSystem.Api/Controllers/Admin/MembersController.cs new file mode 100644 index 0000000..fc2dd3e --- /dev/null +++ b/src/ProjectManagementSystem.Api/Controllers/Admin/MembersController.cs @@ -0,0 +1,70 @@ +using System; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using ProjectManagementSystem.Api.Exceptions; +using ProjectManagementSystem.Api.Extensions; +using ProjectManagementSystem.Api.Models.Admin.Members; +using ProjectManagementSystem.Domain.Admin.Members; + +namespace ProjectManagementSystem.Api.Controllers.Admin +{ + [Authorize(Roles = "Admin")] + [ApiController] + [ProducesResponseType(401)] + public sealed class MembersController : ControllerBase + { + /// + /// Create member + /// + /// Input model + [HttpPost("admin/users/{id}/membership")] + [ProducesResponseType(201)] + [ProducesResponseType(400)] + [ProducesResponseType(typeof(ProblemDetails), 409)] + public async Task Create( + CancellationToken cancellationToken, + [FromRoute] Guid id, + [FromBody] CreateMemberBinding binding, + //[FromServices] IUser roleRepository, + [FromServices] IUserRepository userRepository) + { + // + + var user = await userRepository.Get(binding.Id, cancellationToken); + + // if (role != null) + // if (!role.Name.Equals(binding.Name)) + // throw new ApiException(HttpStatusCode.Conflict, ErrorCode.RoleAlreadyExists, + // "Role already exists with other parameters"); + // + // role = new Role(binding.Id, binding.Name); + + foreach (var projectRole in binding.ProjectRoles) + { + user.AddMember(new Member(Guid.NewGuid(), id, projectRole.ProjectId)); + + foreach (var member in user.Members) + { + member. + } + + // + // var permission = await permissionRepository.Get(permissionId, cancellationToken); + // + // if (permission == null) + // throw new ApiException(HttpStatusCode.NotFound, ErrorCode.PermissionNotFound, "Permission not found"); + // + // var rolePermission = new RolePermission(binding.Id, permission.Id); + // + // role.AddRolePermission(rolePermission); + } + + await roleRepository.Save(role); + + return CreatedAtRoute("GetRoleAdminRoute", new {id = role.Id}, null); + } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Api/Controllers/Admin/RolesController.cs b/src/ProjectManagementSystem.Api/Controllers/Admin/RolesController.cs new file mode 100644 index 0000000..bb57ccf --- /dev/null +++ b/src/ProjectManagementSystem.Api/Controllers/Admin/RolesController.cs @@ -0,0 +1,94 @@ +using System; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using MediatR; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using ProjectManagementSystem.Api.Exceptions; +using ProjectManagementSystem.Api.Models.Admin.Roles; +using ProjectManagementSystem.Domain.Admin.Roles; +using ProjectManagementSystem.Queries.Admin.Roles; + +namespace ProjectManagementSystem.Api.Controllers.Admin +{ + [Authorize(Roles = "Admin")] + [ApiController] + [ProducesResponseType(401)] + public sealed class RolesController : ControllerBase + { + /// + /// Create project + /// + /// Input model + [HttpPost("admin/roles")] + [ProducesResponseType(201)] + [ProducesResponseType(400)] + [ProducesResponseType(typeof(ProblemDetails), 409)] + public async Task Create( + CancellationToken cancellationToken, + [FromBody] CreateRoleBinding binding, + [FromServices] IRoleRepository roleRepository, + [FromServices] IPermissionRepository permissionRepository) + { + var role = await roleRepository.Get(binding.Id, cancellationToken); + + if (role != null) + if (!role.Name.Equals(binding.Name)) + throw new ApiException(HttpStatusCode.Conflict, ErrorCode.RoleAlreadyExists, + "Role already exists with other parameters"); + + role = new Role(binding.Id, binding.Name); + + foreach (var permissionId in binding.Permissions) + { + var permission = await permissionRepository.Get(permissionId, cancellationToken); + + if (permission == null) + throw new ApiException(HttpStatusCode.NotFound, ErrorCode.PermissionNotFound, "Permission not found"); + + var rolePermission = new RolePermission(binding.Id, permission.Id); + + role.AddRolePermission(rolePermission); + } + + await roleRepository.Save(role); + + return CreatedAtRoute("GetRoleAdminRoute", new {id = role.Id}, null); + } + + /// + /// Find roles + /// + /// Input model + [HttpGet("admin/roles", Name = "FindRolesAdminRoute")] + [ProducesResponseType(typeof(RoleListItemView), 200)] + public async Task Find( + CancellationToken cancellationToken, + [FromQuery] FindRolesBinding binding, + [FromServices] IMediator mediator) + { + return Ok(await mediator.Send(new RoleListQuery(binding.Offset, binding.Limit), cancellationToken)); + } + + /// + /// Get the role + /// + /// Role identifier + [HttpGet("admin/roles/{id}", Name = "GetRoleAdminRoute")] + [ProducesResponseType(typeof(RoleView), 200)] + [ProducesResponseType(typeof(ProblemDetails), 404)] + public async Task Get( + CancellationToken cancellationToken, + [FromRoute] Guid id, + [FromServices] IMediator mediator) + { + var role = await mediator.Send(new RoleQuery(id), cancellationToken); + + if (role == null) + throw new ApiException(HttpStatusCode.NotFound, ErrorCode.RoleNotFound, "Role not found"); + + return Ok(role); + } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Api/Exceptions/ErrorCode.cs b/src/ProjectManagementSystem.Api/Exceptions/ErrorCode.cs index bd8f018..02b168f 100644 --- a/src/ProjectManagementSystem.Api/Exceptions/ErrorCode.cs +++ b/src/ProjectManagementSystem.Api/Exceptions/ErrorCode.cs @@ -28,6 +28,8 @@ public sealed class ErrorCode public const string TimeEntryActivityAlreadyExists = "time_entry_activity_already_exists"; public const string TimeEntryNotFound = "time_entry_not_found"; public const string TimeEntryAlreadyExists = "time_entry_already_exists"; + public const string PermissionNotFound = "permission_not_found"; + public const string RoleNotFound = "role_not_found"; public const string RoleAlreadyExists = "role_activity_already_exists"; } } \ No newline at end of file diff --git a/src/ProjectManagementSystem.Api/Models/Admin/Members/CreateMemberBinding.cs b/src/ProjectManagementSystem.Api/Models/Admin/Members/CreateMemberBinding.cs new file mode 100644 index 0000000..58f08b8 --- /dev/null +++ b/src/ProjectManagementSystem.Api/Models/Admin/Members/CreateMemberBinding.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using FluentValidation; + +namespace ProjectManagementSystem.Api.Models.Admin.Members +{ + public sealed class CreateMemberBinding + { + /// + /// + /// + public IEnumerable ProjectRoles { get; set; } + } + + public sealed class CreateMemberBindingValidator : AbstractValidator + { + public CreateMemberBindingValidator() + { + RuleForEach(b => b.ProjectRoles) + .NotEmpty(); + } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Api/Models/Admin/Members/CreateProjectRoleBinding.cs b/src/ProjectManagementSystem.Api/Models/Admin/Members/CreateProjectRoleBinding.cs new file mode 100644 index 0000000..ae8468c --- /dev/null +++ b/src/ProjectManagementSystem.Api/Models/Admin/Members/CreateProjectRoleBinding.cs @@ -0,0 +1,29 @@ +using System; +using FluentValidation; + +namespace ProjectManagementSystem.Api.Models.Admin.Members +{ + public sealed class CreateProjectRoleBinding + { + /// + /// + /// + public Guid ProjectId { get; set; } + + /// + /// + /// + public Guid RoleId { get; set; } + } + + public sealed class CreateProjectRoleBindingValidator : AbstractValidator + { + public CreateProjectRoleBindingValidator() + { + RuleFor(b => b.ProjectId) + .NotEmpty(); + RuleFor(b => b.RoleId) + .NotEmpty(); + } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Api/Models/Admin/Roles/CreateRoleBinding.cs b/src/ProjectManagementSystem.Api/Models/Admin/Roles/CreateRoleBinding.cs new file mode 100644 index 0000000..6355ac7 --- /dev/null +++ b/src/ProjectManagementSystem.Api/Models/Admin/Roles/CreateRoleBinding.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using FluentValidation; + +namespace ProjectManagementSystem.Api.Models.Admin.Roles +{ + public sealed class CreateRoleBinding + { + /// + /// + /// + public Guid Id { get; set; } + + /// + /// + /// + public string Name { get; set; } + + /// + /// + /// + public IEnumerable Permissions { get; set; } + } + + public sealed class CreateRoleBindingValidator : AbstractValidator + { + public CreateRoleBindingValidator() + { + RuleFor(b => b.Id) + .NotEmpty(); + RuleFor(b => b.Name) + .NotEmpty(); + RuleForEach(b => b.Permissions) + .NotEmpty(); + } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Api/Models/Admin/Roles/FindProjectsBinding.cs b/src/ProjectManagementSystem.Api/Models/Admin/Roles/FindProjectsBinding.cs new file mode 100644 index 0000000..420bf43 --- /dev/null +++ b/src/ProjectManagementSystem.Api/Models/Admin/Roles/FindProjectsBinding.cs @@ -0,0 +1,29 @@ +using FluentValidation; +using ProjectManagementSystem.Api.Models.Admin.Projects; + +namespace ProjectManagementSystem.Api.Models.Admin.Roles +{ + public sealed class FindRolesBinding + { + /// + /// Offset + /// + public int Offset { get; set; } = 0; + + /// + /// Limit + /// + public int Limit { get; set; } = 10; + } + + public sealed class FindRolesBindingValidator : AbstractValidator + { + public FindRolesBindingValidator() + { + RuleFor(b => b.Offset) + .GreaterThanOrEqualTo(0); + RuleFor(b => b.Limit) + .InclusiveBetween(2, 1000); + } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.DatabaseMigrations/Entities/Member.cs b/src/ProjectManagementSystem.DatabaseMigrations/Entities/Member.cs index 40f01dd..d7f78e7 100644 --- a/src/ProjectManagementSystem.DatabaseMigrations/Entities/Member.cs +++ b/src/ProjectManagementSystem.DatabaseMigrations/Entities/Member.cs @@ -4,7 +4,7 @@ namespace ProjectManagementSystem.DatabaseMigrations.Entities { public sealed class Member { - public Guid Id { get; set; } + public Guid MemberId { get; set; } public Guid UserId { get; set; } public User User { get; set; } public Guid ProjectId { get; set; } diff --git a/src/ProjectManagementSystem.DatabaseMigrations/Entities/Permission.cs b/src/ProjectManagementSystem.DatabaseMigrations/Entities/Permission.cs index 6645311..4e1f9db 100644 --- a/src/ProjectManagementSystem.DatabaseMigrations/Entities/Permission.cs +++ b/src/ProjectManagementSystem.DatabaseMigrations/Entities/Permission.cs @@ -4,7 +4,7 @@ namespace ProjectManagementSystem.DatabaseMigrations.Entities { public sealed class Permission { - public Guid Id { get; set; } + public Guid PermissionId { get; set; } public string Name { get; set; } } } \ No newline at end of file diff --git a/src/ProjectManagementSystem.DatabaseMigrations/Entities/Role.cs b/src/ProjectManagementSystem.DatabaseMigrations/Entities/Role.cs index ae7d37a..4f38ce0 100644 --- a/src/ProjectManagementSystem.DatabaseMigrations/Entities/Role.cs +++ b/src/ProjectManagementSystem.DatabaseMigrations/Entities/Role.cs @@ -4,7 +4,7 @@ namespace ProjectManagementSystem.DatabaseMigrations.Entities { public sealed class Role { - public Guid Id { get; set; } + public Guid RoleId { get; set; } public string Name { get; set; } } } \ No newline at end of file diff --git a/src/ProjectManagementSystem.DatabaseMigrations/ProjectManagementSystemDbContext.cs b/src/ProjectManagementSystem.DatabaseMigrations/ProjectManagementSystemDbContext.cs index 48aeb8d..993f77e 100644 --- a/src/ProjectManagementSystem.DatabaseMigrations/ProjectManagementSystemDbContext.cs +++ b/src/ProjectManagementSystem.DatabaseMigrations/ProjectManagementSystemDbContext.cs @@ -257,8 +257,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(builder => { builder.ToTable("Role"); - builder.HasKey(r => r.Id); - builder.Property(r => r.Id) + builder.HasKey(r => r.RoleId); + builder.Property(r => r.RoleId) .ValueGeneratedNever(); builder.Property(r => r.Name) .IsRequired(); @@ -267,8 +267,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(builder => { builder.ToTable("Permission"); - builder.HasKey(p => p.Id); - builder.Property(p => p.Id) + builder.HasKey(p => p.PermissionId); + builder.Property(p => p.PermissionId) .ValueGeneratedNever(); builder.Property(p => p.Name) .IsRequired(); @@ -281,18 +281,18 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) builder.HasOne(rp => rp.Role) .WithMany() .HasForeignKey(rp => rp.RoleId) - .HasPrincipalKey(r => r.Id); + .HasPrincipalKey(r => r.RoleId); builder.HasOne(rp => rp.Permission) .WithMany() .HasForeignKey(rp => rp.PermissionId) - .HasPrincipalKey(p => p.Id); + .HasPrincipalKey(p => p.PermissionId); }); modelBuilder.Entity(builder => { builder.ToTable("Member"); - builder.HasKey(m => m.Id); - builder.Property(m => m.Id) + builder.HasKey(m => m.MemberId); + builder.Property(m => m.MemberId) .ValueGeneratedNever(); builder.HasOne(m => m.User) .WithMany() @@ -311,11 +311,11 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) builder.HasOne(mr => mr.Member) .WithMany() .HasForeignKey(mr => mr.MemberId) - .HasPrincipalKey(m => m.Id); + .HasPrincipalKey(m => m.MemberId); builder.HasOne(mr => mr.Role) .WithMany() .HasForeignKey(mr => mr.RoleId) - .HasPrincipalKey(r => r.Id); + .HasPrincipalKey(r => r.RoleId); }); } } diff --git a/src/ProjectManagementSystem.Domain/Admin/CreateMembers/IMemberRepository.cs b/src/ProjectManagementSystem.Domain/Admin/CreateMembers/IMemberRepository.cs deleted file mode 100644 index 21ccc1b..0000000 --- a/src/ProjectManagementSystem.Domain/Admin/CreateMembers/IMemberRepository.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace ProjectManagementSystem.Domain.Admin.CreateMembers -{ - public interface IMemberRepository - { - - } -} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/Admin/CreateMembers/IProjectRepository.cs b/src/ProjectManagementSystem.Domain/Admin/CreateMembers/IProjectRepository.cs deleted file mode 100644 index feb6ada..0000000 --- a/src/ProjectManagementSystem.Domain/Admin/CreateMembers/IProjectRepository.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace ProjectManagementSystem.Domain.Admin.CreateMembers -{ - public interface IProjectRepository - { - - } -} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/Admin/CreateMembers/IUserRepository.cs b/src/ProjectManagementSystem.Domain/Admin/CreateMembers/IUserRepository.cs deleted file mode 100644 index a7d5c80..0000000 --- a/src/ProjectManagementSystem.Domain/Admin/CreateMembers/IUserRepository.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace ProjectManagementSystem.Domain.Admin.CreateMembers -{ - public interface IUserRepository - { - - } -} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/Admin/CreateMembers/Member.cs b/src/ProjectManagementSystem.Domain/Admin/CreateMembers/Member.cs deleted file mode 100644 index c53cb8c..0000000 --- a/src/ProjectManagementSystem.Domain/Admin/CreateMembers/Member.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace ProjectManagementSystem.Domain.Admin.CreateMembers -{ - public sealed class Member - { - - } -} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/Admin/CreateMembers/Project.cs b/src/ProjectManagementSystem.Domain/Admin/CreateMembers/Project.cs deleted file mode 100644 index aa4d2f2..0000000 --- a/src/ProjectManagementSystem.Domain/Admin/CreateMembers/Project.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace ProjectManagementSystem.Domain.Admin.CreateMembers -{ - public sealed class Project - { - - } -} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/Admin/CreateMembers/User.cs b/src/ProjectManagementSystem.Domain/Admin/CreateMembers/User.cs deleted file mode 100644 index ae30f80..0000000 --- a/src/ProjectManagementSystem.Domain/Admin/CreateMembers/User.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace ProjectManagementSystem.Domain.Admin.CreateMembers -{ - public sealed class User - { - - } -} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/Admin/CreateRoles/Permission.cs b/src/ProjectManagementSystem.Domain/Admin/CreateRoles/Permission.cs deleted file mode 100644 index 19bcd2a..0000000 --- a/src/ProjectManagementSystem.Domain/Admin/CreateRoles/Permission.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; - -namespace ProjectManagementSystem.Domain.Admin.CreateRoles -{ - public sealed class Permission - { - public Guid Id { get; private set; } - public string Name { get; private set; } - } -} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/Admin/Members/IMemberRepository.cs b/src/ProjectManagementSystem.Domain/Admin/Members/IMemberRepository.cs new file mode 100644 index 0000000..3200b3f --- /dev/null +++ b/src/ProjectManagementSystem.Domain/Admin/Members/IMemberRepository.cs @@ -0,0 +1,11 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace ProjectManagementSystem.Domain.Admin.Members +{ + public interface IMemberRepository + { + Task Get(Guid id, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/Admin/Members/IProjectRepository.cs b/src/ProjectManagementSystem.Domain/Admin/Members/IProjectRepository.cs new file mode 100644 index 0000000..076788a --- /dev/null +++ b/src/ProjectManagementSystem.Domain/Admin/Members/IProjectRepository.cs @@ -0,0 +1,11 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace ProjectManagementSystem.Domain.Admin.Members +{ + public interface IProjectRepository + { + Task Get(Guid id, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/Admin/Members/IUserRepository.cs b/src/ProjectManagementSystem.Domain/Admin/Members/IUserRepository.cs new file mode 100644 index 0000000..08984a9 --- /dev/null +++ b/src/ProjectManagementSystem.Domain/Admin/Members/IUserRepository.cs @@ -0,0 +1,11 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace ProjectManagementSystem.Domain.Admin.Members +{ + public interface IUserRepository + { + Task Get(Guid id, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/Admin/Members/Member.cs b/src/ProjectManagementSystem.Domain/Admin/Members/Member.cs new file mode 100644 index 0000000..2cc272f --- /dev/null +++ b/src/ProjectManagementSystem.Domain/Admin/Members/Member.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; + +namespace ProjectManagementSystem.Domain.Admin.Members +{ + public sealed class Member + { + public Guid MemberId { get; } + public Guid ProjectId { get; } + public Guid RoleId { get; } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/Admin/Members/MemberRole.cs b/src/ProjectManagementSystem.Domain/Admin/Members/MemberRole.cs new file mode 100644 index 0000000..ee22e0e --- /dev/null +++ b/src/ProjectManagementSystem.Domain/Admin/Members/MemberRole.cs @@ -0,0 +1,10 @@ +using System; + +namespace ProjectManagementSystem.Domain.Admin.Members +{ + public sealed class MemberRole + { + public Guid MemberId { get; } + public Guid UserId { get; } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/Admin/Members/Project.cs b/src/ProjectManagementSystem.Domain/Admin/Members/Project.cs new file mode 100644 index 0000000..a10fc8e --- /dev/null +++ b/src/ProjectManagementSystem.Domain/Admin/Members/Project.cs @@ -0,0 +1,9 @@ +using System; + +namespace ProjectManagementSystem.Domain.Admin.Members +{ + public sealed class Project + { + public Guid Id { get; } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/Admin/Members/Role.cs b/src/ProjectManagementSystem.Domain/Admin/Members/Role.cs new file mode 100644 index 0000000..56078a8 --- /dev/null +++ b/src/ProjectManagementSystem.Domain/Admin/Members/Role.cs @@ -0,0 +1,9 @@ +using System; + +namespace ProjectManagementSystem.Domain.Admin.Members +{ + public sealed class Role + { + public Guid Id { get; } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/Admin/Members/User.cs b/src/ProjectManagementSystem.Domain/Admin/Members/User.cs new file mode 100644 index 0000000..1cd5f89 --- /dev/null +++ b/src/ProjectManagementSystem.Domain/Admin/Members/User.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; + +namespace ProjectManagementSystem.Domain.Admin.Members +{ + public sealed class User + { + public Guid Id { get; } + private List _members = new List(); + public IEnumerable Members => _members; + private Guid _concurrencyStamp; + + public void AddMember(Member member) + { + _members.Add(member); + _concurrencyStamp = Guid.NewGuid(); + } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/Admin/CreateRoles/IPermissionRepository.cs b/src/ProjectManagementSystem.Domain/Admin/Roles/IPermissionRepository.cs similarity index 77% rename from src/ProjectManagementSystem.Domain/Admin/CreateRoles/IPermissionRepository.cs rename to src/ProjectManagementSystem.Domain/Admin/Roles/IPermissionRepository.cs index 90af72b..ac2b0c9 100644 --- a/src/ProjectManagementSystem.Domain/Admin/CreateRoles/IPermissionRepository.cs +++ b/src/ProjectManagementSystem.Domain/Admin/Roles/IPermissionRepository.cs @@ -2,7 +2,7 @@ using System.Threading; using System.Threading.Tasks; -namespace ProjectManagementSystem.Domain.Admin.CreateRoles +namespace ProjectManagementSystem.Domain.Admin.Roles { public interface IPermissionRepository { diff --git a/src/ProjectManagementSystem.Domain/Admin/CreateRoles/IRoleRepository.cs b/src/ProjectManagementSystem.Domain/Admin/Roles/IRoleRepository.cs similarity index 63% rename from src/ProjectManagementSystem.Domain/Admin/CreateRoles/IRoleRepository.cs rename to src/ProjectManagementSystem.Domain/Admin/Roles/IRoleRepository.cs index 3b3aad0..bb586ff 100644 --- a/src/ProjectManagementSystem.Domain/Admin/CreateRoles/IRoleRepository.cs +++ b/src/ProjectManagementSystem.Domain/Admin/Roles/IRoleRepository.cs @@ -2,12 +2,12 @@ using System.Threading; using System.Threading.Tasks; -namespace ProjectManagementSystem.Domain.Admin.CreateRoles +namespace ProjectManagementSystem.Domain.Admin.Roles { public interface IRoleRepository { Task Get(Guid id, CancellationToken cancellationToken); - + Task Get(string name, CancellationToken cancellationToken); Task Save(Role role); } } \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/Admin/Roles/Permission.cs b/src/ProjectManagementSystem.Domain/Admin/Roles/Permission.cs new file mode 100644 index 0000000..9a61998 --- /dev/null +++ b/src/ProjectManagementSystem.Domain/Admin/Roles/Permission.cs @@ -0,0 +1,10 @@ +using System; + +namespace ProjectManagementSystem.Domain.Admin.Roles +{ + public sealed class Permission + { + public Guid Id { get; } + //public string Name { get; } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/Admin/CreateRoles/Role.cs b/src/ProjectManagementSystem.Domain/Admin/Roles/Role.cs similarity index 77% rename from src/ProjectManagementSystem.Domain/Admin/CreateRoles/Role.cs rename to src/ProjectManagementSystem.Domain/Admin/Roles/Role.cs index a4bfc18..6e7d765 100644 --- a/src/ProjectManagementSystem.Domain/Admin/CreateRoles/Role.cs +++ b/src/ProjectManagementSystem.Domain/Admin/Roles/Role.cs @@ -1,26 +1,26 @@ using System; using System.Collections.Generic; -namespace ProjectManagementSystem.Domain.Admin.CreateRoles +namespace ProjectManagementSystem.Domain.Admin.Roles { public sealed class Role { - public Guid Id { get; private set; } + public Guid Id { get; } public string Name { get; private set; } private List _rolePermissions = new List(); public IEnumerable RolePermissions => _rolePermissions; - private Guid _concurrencyStamp = Guid.NewGuid(); + private Guid _concurrencyStamp; public Role(Guid id, string name) { Id = id; Name = name; + _concurrencyStamp = Guid.NewGuid(); } public void AddRolePermission(RolePermission rolePermission) { _rolePermissions.Add(rolePermission); - _concurrencyStamp = Guid.NewGuid(); } } diff --git a/src/ProjectManagementSystem.Domain/Admin/Roles/RoleCreationService.cs b/src/ProjectManagementSystem.Domain/Admin/Roles/RoleCreationService.cs new file mode 100644 index 0000000..0ae6a8a --- /dev/null +++ b/src/ProjectManagementSystem.Domain/Admin/Roles/RoleCreationService.cs @@ -0,0 +1,7 @@ +namespace ProjectManagementSystem.Domain.Admin.Roles +{ + public sealed class RoleCreationService + { + + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/Admin/CreateRoles/RolePermission.cs b/src/ProjectManagementSystem.Domain/Admin/Roles/RolePermission.cs similarity index 50% rename from src/ProjectManagementSystem.Domain/Admin/CreateRoles/RolePermission.cs rename to src/ProjectManagementSystem.Domain/Admin/Roles/RolePermission.cs index f871d8b..1a89902 100644 --- a/src/ProjectManagementSystem.Domain/Admin/CreateRoles/RolePermission.cs +++ b/src/ProjectManagementSystem.Domain/Admin/Roles/RolePermission.cs @@ -1,11 +1,11 @@ using System; -namespace ProjectManagementSystem.Domain.Admin.CreateRoles +namespace ProjectManagementSystem.Domain.Admin.Roles { - public class RolePermission + public sealed class RolePermission { - public Guid RoleId { get; set; } - public Guid PermissionId { get; set; } + public Guid RoleId { get; } + public Guid PermissionId { get; } public RolePermission(Guid roleId, Guid permissionId) { diff --git a/src/ProjectManagementSystem.Queries/Admin/Roles/RoleListItemView.cs b/src/ProjectManagementSystem.Queries/Admin/Roles/RoleListItemView.cs new file mode 100644 index 0000000..175ad30 --- /dev/null +++ b/src/ProjectManagementSystem.Queries/Admin/Roles/RoleListItemView.cs @@ -0,0 +1,9 @@ +using System; + +namespace ProjectManagementSystem.Queries.Admin.Roles +{ + public sealed class RoleListItemView + { + public Guid Id { get; set; } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Queries/Admin/Roles/RoleListQuery.cs b/src/ProjectManagementSystem.Queries/Admin/Roles/RoleListQuery.cs index 86ed17f..b934245 100644 --- a/src/ProjectManagementSystem.Queries/Admin/Roles/RoleListQuery.cs +++ b/src/ProjectManagementSystem.Queries/Admin/Roles/RoleListQuery.cs @@ -1,6 +1,6 @@ namespace ProjectManagementSystem.Queries.Admin.Roles { - public sealed class RoleListQuery : PageQuery + public sealed class RoleListQuery : PageQuery { public RoleListQuery(int offset, int limit) : base(offset, limit) { } } diff --git a/src/ProjectManagementSystem.Queries/Admin/Roles/RoleListViewModel.cs b/src/ProjectManagementSystem.Queries/Admin/Roles/RoleListViewModel.cs deleted file mode 100644 index 2b3ff1f..0000000 --- a/src/ProjectManagementSystem.Queries/Admin/Roles/RoleListViewModel.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace ProjectManagementSystem.Queries.Admin.Roles -{ - public sealed class RoleListViewModel - { - - } -} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Queries/Admin/Roles/RoleQuery.cs b/src/ProjectManagementSystem.Queries/Admin/Roles/RoleQuery.cs index 53482b3..672fc79 100644 --- a/src/ProjectManagementSystem.Queries/Admin/Roles/RoleQuery.cs +++ b/src/ProjectManagementSystem.Queries/Admin/Roles/RoleQuery.cs @@ -3,7 +3,7 @@ namespace ProjectManagementSystem.Queries.Admin.Roles { - public sealed class RoleQuery : IRequest + public sealed class RoleQuery : IRequest { public Guid Id { get; } diff --git a/src/ProjectManagementSystem.Queries/Admin/Roles/RoleView.cs b/src/ProjectManagementSystem.Queries/Admin/Roles/RoleView.cs new file mode 100644 index 0000000..e929240 --- /dev/null +++ b/src/ProjectManagementSystem.Queries/Admin/Roles/RoleView.cs @@ -0,0 +1,9 @@ +using System; + +namespace ProjectManagementSystem.Queries.Admin.Roles +{ + public sealed class RoleView + { + public Guid Id { get; set; } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Queries/Admin/Roles/RoleViewModel.cs b/src/ProjectManagementSystem.Queries/Admin/Roles/RoleViewModel.cs deleted file mode 100644 index b8c4fc4..0000000 --- a/src/ProjectManagementSystem.Queries/Admin/Roles/RoleViewModel.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace ProjectManagementSystem.Queries.Admin.Roles -{ - public sealed class RoleViewModel - { - - } -} \ No newline at end of file From 47ab9df18dd2f0bf4961504657271a5790692c3a Mon Sep 17 00:00:00 2001 From: gradovenko Date: Fri, 7 Feb 2020 16:37:59 +0500 Subject: [PATCH 6/8] Rework member logic --- .../Controllers/Admin/MembersController.cs | 38 ++++--------------- .../Controllers/User/IssuesController.cs | 1 + .../Admin/Members/CreateMemberBinding.cs | 10 ++--- .../Admin/Members/CreateProjectRoleBinding.cs | 15 +++++--- .../Admin/Members/IUserRepository.cs | 2 + .../Admin/Members/Member.cs | 10 ++++- .../Admin/Members/MemberRole.cs | 10 ----- 7 files changed, 34 insertions(+), 52 deletions(-) delete mode 100644 src/ProjectManagementSystem.Domain/Admin/Members/MemberRole.cs diff --git a/src/ProjectManagementSystem.Api/Controllers/Admin/MembersController.cs b/src/ProjectManagementSystem.Api/Controllers/Admin/MembersController.cs index fc2dd3e..bc3f898 100644 --- a/src/ProjectManagementSystem.Api/Controllers/Admin/MembersController.cs +++ b/src/ProjectManagementSystem.Api/Controllers/Admin/MembersController.cs @@ -27,44 +27,20 @@ public sealed class MembersController : ControllerBase public async Task Create( CancellationToken cancellationToken, [FromRoute] Guid id, - [FromBody] CreateMemberBinding binding, + [FromBody] CreateMembersBinding binding, //[FromServices] IUser roleRepository, [FromServices] IUserRepository userRepository) { - // - - var user = await userRepository.Get(binding.Id, cancellationToken); - - // if (role != null) - // if (!role.Name.Equals(binding.Name)) - // throw new ApiException(HttpStatusCode.Conflict, ErrorCode.RoleAlreadyExists, - // "Role already exists with other parameters"); - // - // role = new Role(binding.Id, binding.Name); - - foreach (var projectRole in binding.ProjectRoles) - { - user.AddMember(new Member(Guid.NewGuid(), id, projectRole.ProjectId)); + var user = await userRepository.Get(id, cancellationToken); - foreach (var member in user.Members) - { - member. - } - - // - // var permission = await permissionRepository.Get(permissionId, cancellationToken); - // - // if (permission == null) - // throw new ApiException(HttpStatusCode.NotFound, ErrorCode.PermissionNotFound, "Permission not found"); - // - // var rolePermission = new RolePermission(binding.Id, permission.Id); - // - // role.AddRolePermission(rolePermission); + foreach (var member in binding.Members) + { + user.AddMember(new Member(member.Id, id, member.ProjectId, member.RoleId)); } - await roleRepository.Save(role); + await userRepository.Save(user); - return CreatedAtRoute("GetRoleAdminRoute", new {id = role.Id}, null); + return CreatedAtRoute("GetMemberAdminRoute", new {id}, null); } } } \ No newline at end of file diff --git a/src/ProjectManagementSystem.Api/Controllers/User/IssuesController.cs b/src/ProjectManagementSystem.Api/Controllers/User/IssuesController.cs index 5e8fe1c..1c5462e 100644 --- a/src/ProjectManagementSystem.Api/Controllers/User/IssuesController.cs +++ b/src/ProjectManagementSystem.Api/Controllers/User/IssuesController.cs @@ -31,6 +31,7 @@ public sealed class IssuesController : ControllerBase [ProducesResponseType(400)] [ProducesResponseType(typeof(ProblemDetails), 409)] [ProducesResponseType(typeof(ProblemDetails), 422)] + [Permission("CreateIssue")] public async Task CreateIssue( CancellationToken cancellationToken, [FromBody] CreateIssueBinding binding, diff --git a/src/ProjectManagementSystem.Api/Models/Admin/Members/CreateMemberBinding.cs b/src/ProjectManagementSystem.Api/Models/Admin/Members/CreateMemberBinding.cs index 58f08b8..5286e6e 100644 --- a/src/ProjectManagementSystem.Api/Models/Admin/Members/CreateMemberBinding.cs +++ b/src/ProjectManagementSystem.Api/Models/Admin/Members/CreateMemberBinding.cs @@ -4,19 +4,19 @@ namespace ProjectManagementSystem.Api.Models.Admin.Members { - public sealed class CreateMemberBinding + public sealed class CreateMembersBinding { /// /// /// - public IEnumerable ProjectRoles { get; set; } + public IEnumerable Members { get; set; } } - public sealed class CreateMemberBindingValidator : AbstractValidator + public sealed class CreateMembersBindingValidator : AbstractValidator { - public CreateMemberBindingValidator() + public CreateMembersBindingValidator() { - RuleForEach(b => b.ProjectRoles) + RuleForEach(b => b.Members) .NotEmpty(); } } diff --git a/src/ProjectManagementSystem.Api/Models/Admin/Members/CreateProjectRoleBinding.cs b/src/ProjectManagementSystem.Api/Models/Admin/Members/CreateProjectRoleBinding.cs index ae8468c..2e27fec 100644 --- a/src/ProjectManagementSystem.Api/Models/Admin/Members/CreateProjectRoleBinding.cs +++ b/src/ProjectManagementSystem.Api/Models/Admin/Members/CreateProjectRoleBinding.cs @@ -3,22 +3,27 @@ namespace ProjectManagementSystem.Api.Models.Admin.Members { - public sealed class CreateProjectRoleBinding + public sealed class CreateMemberBinding { /// - /// + /// Member identifier + /// + public Guid Id { get; set; } + + /// + /// Project identifier /// public Guid ProjectId { get; set; } /// - /// + /// Role identifier /// public Guid RoleId { get; set; } } - public sealed class CreateProjectRoleBindingValidator : AbstractValidator + public sealed class CreateMemberBindingValidator : AbstractValidator { - public CreateProjectRoleBindingValidator() + public CreateMemberBindingValidator() { RuleFor(b => b.ProjectId) .NotEmpty(); diff --git a/src/ProjectManagementSystem.Domain/Admin/Members/IUserRepository.cs b/src/ProjectManagementSystem.Domain/Admin/Members/IUserRepository.cs index 08984a9..e48a7b1 100644 --- a/src/ProjectManagementSystem.Domain/Admin/Members/IUserRepository.cs +++ b/src/ProjectManagementSystem.Domain/Admin/Members/IUserRepository.cs @@ -7,5 +7,7 @@ namespace ProjectManagementSystem.Domain.Admin.Members public interface IUserRepository { Task Get(Guid id, CancellationToken cancellationToken); + + Task Save(User user); } } \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/Admin/Members/Member.cs b/src/ProjectManagementSystem.Domain/Admin/Members/Member.cs index 2cc272f..370a557 100644 --- a/src/ProjectManagementSystem.Domain/Admin/Members/Member.cs +++ b/src/ProjectManagementSystem.Domain/Admin/Members/Member.cs @@ -1,12 +1,20 @@ using System; -using System.Collections.Generic; namespace ProjectManagementSystem.Domain.Admin.Members { public sealed class Member { public Guid MemberId { get; } + public Guid UserId { get; } public Guid ProjectId { get; } public Guid RoleId { get; } + + public Member(Guid memberId, Guid userId, Guid projectId, Guid roleId) + { + MemberId = memberId; + UserId = userId; + ProjectId = projectId; + RoleId = roleId; + } } } \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/Admin/Members/MemberRole.cs b/src/ProjectManagementSystem.Domain/Admin/Members/MemberRole.cs deleted file mode 100644 index ee22e0e..0000000 --- a/src/ProjectManagementSystem.Domain/Admin/Members/MemberRole.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; - -namespace ProjectManagementSystem.Domain.Admin.Members -{ - public sealed class MemberRole - { - public Guid MemberId { get; } - public Guid UserId { get; } - } -} \ No newline at end of file From 2637309dbc398283972dc19a563c293b9d2b6b69 Mon Sep 17 00:00:00 2001 From: gradovenko Date: Sat, 15 Feb 2020 10:56:40 +0500 Subject: [PATCH 7/8] Add some improvements to the membership system and other improvements --- .../Controllers/Admin/MembersController.cs | 14 +- .../Controllers/User/IssuesController.cs | 1 - .../Filters/PermissionAuthorizationFilter.cs | 25 ++++ .../Admin/Members/CreateProjectRoleBinding.cs | 2 + .../Admin/Members/IUserRepository.cs | 1 - .../User/Members/IMemberRepository.cs | 11 ++ .../User/Members/Member.cs | 23 ++++ .../User/Members/RolePermission.cs | 16 +++ .../Admin/Members/UserDbContext.cs | 28 ++++ .../Admin/Members/UserRepository.cs | 32 +++++ .../User/Members/MemberDbContext.cs | 28 ++++ .../User/Members/MemberRepository.cs | 24 ++++ .../User/Issues/Issue.cs | 28 ++++ .../User/Issues/IssueDbContext.cs | 103 ++++++++++++++ .../User/Issues/IssueListQueryHandler.cs | 45 +++++++ .../User/Issues/IssuePriority.cs | 10 ++ .../User/Issues/IssueQueryHandler.cs | 49 +++++++ .../User/Issues/IssueStatus.cs | 10 ++ .../User/Issues/Project.cs | 9 ++ .../User/Issues/ProjectDbContext.cs | 126 ++++++++++++++++++ .../User/Issues/Tracker.cs | 10 ++ .../User/Issues/User.cs | 10 ++ 22 files changed, 594 insertions(+), 11 deletions(-) create mode 100644 src/ProjectManagementSystem.Api/Filters/PermissionAuthorizationFilter.cs create mode 100644 src/ProjectManagementSystem.Domain/User/Members/IMemberRepository.cs create mode 100644 src/ProjectManagementSystem.Domain/User/Members/Member.cs create mode 100644 src/ProjectManagementSystem.Domain/User/Members/RolePermission.cs create mode 100644 src/ProjectManagementSystem.Infrastructure/Admin/Members/UserDbContext.cs create mode 100644 src/ProjectManagementSystem.Infrastructure/Admin/Members/UserRepository.cs create mode 100644 src/ProjectManagementSystem.Infrastructure/User/Members/MemberDbContext.cs create mode 100644 src/ProjectManagementSystem.Infrastructure/User/Members/MemberRepository.cs create mode 100644 src/ProjectManagementSystem.Queries.Infrastructure/User/Issues/Issue.cs create mode 100644 src/ProjectManagementSystem.Queries.Infrastructure/User/Issues/IssueDbContext.cs create mode 100644 src/ProjectManagementSystem.Queries.Infrastructure/User/Issues/IssueListQueryHandler.cs create mode 100644 src/ProjectManagementSystem.Queries.Infrastructure/User/Issues/IssuePriority.cs create mode 100644 src/ProjectManagementSystem.Queries.Infrastructure/User/Issues/IssueQueryHandler.cs create mode 100644 src/ProjectManagementSystem.Queries.Infrastructure/User/Issues/IssueStatus.cs create mode 100644 src/ProjectManagementSystem.Queries.Infrastructure/User/Issues/Project.cs create mode 100644 src/ProjectManagementSystem.Queries.Infrastructure/User/Issues/ProjectDbContext.cs create mode 100644 src/ProjectManagementSystem.Queries.Infrastructure/User/Issues/Tracker.cs create mode 100644 src/ProjectManagementSystem.Queries.Infrastructure/User/Issues/User.cs diff --git a/src/ProjectManagementSystem.Api/Controllers/Admin/MembersController.cs b/src/ProjectManagementSystem.Api/Controllers/Admin/MembersController.cs index bc3f898..8b72277 100644 --- a/src/ProjectManagementSystem.Api/Controllers/Admin/MembersController.cs +++ b/src/ProjectManagementSystem.Api/Controllers/Admin/MembersController.cs @@ -19,28 +19,24 @@ public sealed class MembersController : ControllerBase /// /// Create member /// + /// /// Input model - [HttpPost("admin/users/{id}/membership")] - [ProducesResponseType(201)] + [HttpPost("admin/users/{id}/members")] [ProducesResponseType(400)] [ProducesResponseType(typeof(ProblemDetails), 409)] public async Task Create( CancellationToken cancellationToken, [FromRoute] Guid id, - [FromBody] CreateMembersBinding binding, - //[FromServices] IUser roleRepository, + [FromBody] CreateMemberBinding binding, [FromServices] IUserRepository userRepository) { var user = await userRepository.Get(id, cancellationToken); - foreach (var member in binding.Members) - { - user.AddMember(new Member(member.Id, id, member.ProjectId, member.RoleId)); - } + user.AddMember(new Member(binding.Id, id, binding.ProjectId, binding.RoleId)); await userRepository.Save(user); - return CreatedAtRoute("GetMemberAdminRoute", new {id}, null); + return CreatedAtRoute("GetMemberAdminRoute", new {id = binding.Id}, null); } } } \ No newline at end of file diff --git a/src/ProjectManagementSystem.Api/Controllers/User/IssuesController.cs b/src/ProjectManagementSystem.Api/Controllers/User/IssuesController.cs index 4a10074..1570ba6 100644 --- a/src/ProjectManagementSystem.Api/Controllers/User/IssuesController.cs +++ b/src/ProjectManagementSystem.Api/Controllers/User/IssuesController.cs @@ -32,7 +32,6 @@ public sealed class IssuesController : ControllerBase [ProducesResponseType(400)] [ProducesResponseType(typeof(ProblemDetails), 409)] [ProducesResponseType(typeof(ProblemDetails), 422)] - [Permission("CreateIssue")] public async Task CreateIssue( CancellationToken cancellationToken, [FromBody] CreateIssueBinding binding, diff --git a/src/ProjectManagementSystem.Api/Filters/PermissionAuthorizationFilter.cs b/src/ProjectManagementSystem.Api/Filters/PermissionAuthorizationFilter.cs new file mode 100644 index 0000000..21af93e --- /dev/null +++ b/src/ProjectManagementSystem.Api/Filters/PermissionAuthorizationFilter.cs @@ -0,0 +1,25 @@ +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.Filters; +using ProjectManagementSystem.Api.Extensions; +using ProjectManagementSystem.Domain.User.Members; + +namespace ProjectManagementSystem.Api.Filters +{ + public sealed class PermissionAuthorizationFilter : IAsyncAuthorizationFilter + { + private readonly IMemberRepository _userRepository; + + public PermissionAuthorizationFilter(IMemberRepository userRepository) + { + _userRepository = userRepository; + } + + public Task OnAuthorizationAsync(AuthorizationFilterContext context) + { + var user = _userRepository.Get(context.HttpContext.User.GetId(), CancellationToken.None); + + + } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Api/Models/Admin/Members/CreateProjectRoleBinding.cs b/src/ProjectManagementSystem.Api/Models/Admin/Members/CreateProjectRoleBinding.cs index 2e27fec..4d4fad5 100644 --- a/src/ProjectManagementSystem.Api/Models/Admin/Members/CreateProjectRoleBinding.cs +++ b/src/ProjectManagementSystem.Api/Models/Admin/Members/CreateProjectRoleBinding.cs @@ -25,6 +25,8 @@ public sealed class CreateMemberBindingValidator : AbstractValidator b.Id) + .NotEmpty(); RuleFor(b => b.ProjectId) .NotEmpty(); RuleFor(b => b.RoleId) diff --git a/src/ProjectManagementSystem.Domain/Admin/Members/IUserRepository.cs b/src/ProjectManagementSystem.Domain/Admin/Members/IUserRepository.cs index e48a7b1..ce46016 100644 --- a/src/ProjectManagementSystem.Domain/Admin/Members/IUserRepository.cs +++ b/src/ProjectManagementSystem.Domain/Admin/Members/IUserRepository.cs @@ -7,7 +7,6 @@ namespace ProjectManagementSystem.Domain.Admin.Members public interface IUserRepository { Task Get(Guid id, CancellationToken cancellationToken); - Task Save(User user); } } \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/User/Members/IMemberRepository.cs b/src/ProjectManagementSystem.Domain/User/Members/IMemberRepository.cs new file mode 100644 index 0000000..5dba2dc --- /dev/null +++ b/src/ProjectManagementSystem.Domain/User/Members/IMemberRepository.cs @@ -0,0 +1,11 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace ProjectManagementSystem.Domain.User.Members +{ + public interface IMemberRepository + { + Task Get(Guid id, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/User/Members/Member.cs b/src/ProjectManagementSystem.Domain/User/Members/Member.cs new file mode 100644 index 0000000..1311121 --- /dev/null +++ b/src/ProjectManagementSystem.Domain/User/Members/Member.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; + +namespace ProjectManagementSystem.Domain.User.Members +{ + public sealed class Member + { + public Guid MemberId { get; } + public Guid UserId { get; } + public Guid ProjectId { get; } + public Guid RoleId { get; } + private List _rolePermissions = new List(); + public IEnumerable RolePermissions => _rolePermissions; + + public Member(Guid memberId, Guid userId, Guid projectId, Guid roleId) + { + MemberId = memberId; + UserId = userId; + ProjectId = projectId; + RoleId = roleId; + } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/User/Members/RolePermission.cs b/src/ProjectManagementSystem.Domain/User/Members/RolePermission.cs new file mode 100644 index 0000000..4a7498f --- /dev/null +++ b/src/ProjectManagementSystem.Domain/User/Members/RolePermission.cs @@ -0,0 +1,16 @@ +using System; + +namespace ProjectManagementSystem.Domain.User.Members +{ + public sealed class RolePermission + { + public Guid RoleId { get; } + public Guid PermissionId { get; } + + public RolePermission(Guid roleId, Guid permissionId) + { + RoleId = roleId; + PermissionId = permissionId; + } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Infrastructure/Admin/Members/UserDbContext.cs b/src/ProjectManagementSystem.Infrastructure/Admin/Members/UserDbContext.cs new file mode 100644 index 0000000..ddc4e6b --- /dev/null +++ b/src/ProjectManagementSystem.Infrastructure/Admin/Members/UserDbContext.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore; + +namespace ProjectManagementSystem.Infrastructure.Admin.Members +{ + public sealed class UserDbContext : DbContext + { + public UserDbContext(DbContextOptions options) : base(options) { } + + internal DbSet Users { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + modelBuilder.Entity(builder => + { + builder.ToTable("User"); + builder.HasKey(u => u.Id); + builder.Property(u => u.Id) + .HasColumnName("UserId") + .ValueGeneratedNever(); + builder.Property("_concurrencyStamp") + .HasColumnName("ConcurrencyStamp") + .IsConcurrencyToken(); + }); + } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Infrastructure/Admin/Members/UserRepository.cs b/src/ProjectManagementSystem.Infrastructure/Admin/Members/UserRepository.cs new file mode 100644 index 0000000..4844189 --- /dev/null +++ b/src/ProjectManagementSystem.Infrastructure/Admin/Members/UserRepository.cs @@ -0,0 +1,32 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using ProjectManagementSystem.Domain.Admin.Members; + +namespace ProjectManagementSystem.Infrastructure.Admin.Members +{ + public sealed class UserRepository : IUserRepository + { + private readonly UserDbContext _context; + + public UserRepository(UserDbContext context) + { + _context = context; + } + + public async Task Get(Guid userId, CancellationToken cancellationToken) + { + return await _context.Users + .SingleOrDefaultAsync(u => u.Id == userId, cancellationToken); + } + + public async Task Save(Domain.Admin.Members.User user) + { + if (_context.Entry(user).State == EntityState.Detached) + await _context.Users.AddAsync(user); + + await _context.SaveChangesAsync(); + } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Infrastructure/User/Members/MemberDbContext.cs b/src/ProjectManagementSystem.Infrastructure/User/Members/MemberDbContext.cs new file mode 100644 index 0000000..f3628c1 --- /dev/null +++ b/src/ProjectManagementSystem.Infrastructure/User/Members/MemberDbContext.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore; + +namespace ProjectManagementSystem.Infrastructure.User.Members +{ + public sealed class MemberDbContext : DbContext + { + public MemberDbContext(DbContextOptions options) : base(options) { } + + internal DbSet Members { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + modelBuilder.Entity(builder => + { + builder.ToTable("User"); + builder.HasKey(u => u.Id); + builder.Property(u => u.Id) + .HasColumnName("UserId") + .ValueGeneratedNever(); + builder.Property("_concurrencyStamp") + .HasColumnName("ConcurrencyStamp") + .IsConcurrencyToken(); + }); + } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Infrastructure/User/Members/MemberRepository.cs b/src/ProjectManagementSystem.Infrastructure/User/Members/MemberRepository.cs new file mode 100644 index 0000000..c827257 --- /dev/null +++ b/src/ProjectManagementSystem.Infrastructure/User/Members/MemberRepository.cs @@ -0,0 +1,24 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using ProjectManagementSystem.Domain.User.Members; + +namespace ProjectManagementSystem.Infrastructure.User.Members +{ + public sealed class MemberRepository : IMemberRepository + { + private readonly MemberDbContext _context; + + public MemberRepository(MemberDbContext context) + { + _context = context; + } + + public async Task Get(Guid userId, CancellationToken cancellationToken) + { + return await _context.Members + .SingleOrDefaultAsync(u => u.UserId == userId, cancellationToken); + } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Queries.Infrastructure/User/Issues/Issue.cs b/src/ProjectManagementSystem.Queries.Infrastructure/User/Issues/Issue.cs new file mode 100644 index 0000000..b749646 --- /dev/null +++ b/src/ProjectManagementSystem.Queries.Infrastructure/User/Issues/Issue.cs @@ -0,0 +1,28 @@ +using System; + +namespace ProjectManagementSystem.Queries.Infrastructure.User.Issues +{ + internal sealed class Issue + { + public Guid Id { get; } + public long Number { get; } + public string Title { get; } + public string Description { get; } + public DateTime CreateDate { get; } + public DateTime? UpdateDate { get; } + public DateTime? StartDate { get; } + public DateTime? DueDate { get; } + public Guid ProjectId { get; } + public Project Project { get; } + public Guid TrackerId { get; } + public Tracker Tracker { get; } + public Guid StatusId { get; } + public IssueStatus Status { get; } + public Guid PriorityId { get; } + public IssuePriority Priority { get; } + public Guid AuthorId { get; } + public User Author { get; } + public Guid? AssigneeId { get; } + public User? Assignee { get; } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Queries.Infrastructure/User/Issues/IssueDbContext.cs b/src/ProjectManagementSystem.Queries.Infrastructure/User/Issues/IssueDbContext.cs new file mode 100644 index 0000000..36afde0 --- /dev/null +++ b/src/ProjectManagementSystem.Queries.Infrastructure/User/Issues/IssueDbContext.cs @@ -0,0 +1,103 @@ +using Microsoft.EntityFrameworkCore; + +namespace ProjectManagementSystem.Queries.Infrastructure.User.Issues +{ + public sealed class IssueDbContext : DbContext + { + public IssueDbContext(DbContextOptions options) : base(options) { } + + internal DbSet Issues { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + modelBuilder.Entity(builder => + { + builder.ToTable("Project"); + builder.HasKey(p => p.Id); + builder.Property(p => p.Id) + .HasColumnName("ProjectId"); + }); + + modelBuilder.Entity(builder => + { + builder.ToTable("Tracker"); + builder.HasKey(t => t.Id); + builder.Property(t => t.Id) + .HasColumnName("TrackerId"); + builder.Property(t => t.Name); + }); + + modelBuilder.Entity(builder => + { + builder.ToTable("IssueStatus"); + builder.HasKey(@is => @is.Id); + builder.Property(@is => @is.Id) + .HasColumnName("IssueStatusId"); + builder.Property(@is => @is.Name); + }); + + modelBuilder.Entity(builder => + { + builder.ToTable("IssuePriority"); + builder.HasKey(ip => ip.Id); + builder.Property(ip => ip.Id) + .HasColumnName("IssuePriorityId"); + builder.Property(ip => ip.Name); + }); + + modelBuilder.Entity(builder => + { + builder.ToTable("User"); + builder.HasKey(u => u.Id); + builder.Property(u => u.Id) + .HasColumnName("UserId"); + builder.Property(u => u.Name); + }); + + modelBuilder.Entity(builder => + { + builder.ToTable("Issue"); + builder.HasKey(i => i.Id); + builder.Property(i => i.Id) + .HasColumnName("IssueId"); + builder.Property(i => i.Number); + builder.Property(i => i.Title); + builder.Property(i => i.Description); + builder.Property(i => i.CreateDate); + builder.Property(i => i.StartDate); + builder.Property(i => i.DueDate); + builder.Property(i => i.TrackerId); + builder.Property(i => i.StatusId); + builder.Property(i => i.PriorityId); + builder.Property(i => i.AuthorId); + builder.Property(i => i.AssigneeId); + builder.HasOne(i => i.Project) + .WithMany() + .HasForeignKey(i => i.ProjectId) + .HasPrincipalKey(p => p.Id); + builder.HasOne(i => i.Tracker) + .WithMany() + .HasForeignKey(i => i.TrackerId) + .HasPrincipalKey(t => t.Id); + builder.HasOne(i => i.Status) + .WithMany() + .HasForeignKey(i => i.StatusId) + .HasPrincipalKey(@is => @is.Id); + builder.HasOne(i => i.Priority) + .WithMany() + .HasForeignKey(i => i.PriorityId) + .HasPrincipalKey(ip => ip.Id); + builder.HasOne(i => i.Author) + .WithMany() + .HasForeignKey(i => i.AuthorId) + .HasPrincipalKey(a => a.Id); + builder.HasOne(i => i.Assignee) + .WithMany() + .HasForeignKey(i => i.AssigneeId) + .HasPrincipalKey(p => p.Id); + }); + } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Queries.Infrastructure/User/Issues/IssueListQueryHandler.cs b/src/ProjectManagementSystem.Queries.Infrastructure/User/Issues/IssueListQueryHandler.cs new file mode 100644 index 0000000..4b982fc --- /dev/null +++ b/src/ProjectManagementSystem.Queries.Infrastructure/User/Issues/IssueListQueryHandler.cs @@ -0,0 +1,45 @@ +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediatR; +using Microsoft.EntityFrameworkCore; +using ProjectManagementSystem.Queries.User.Issues; + +namespace ProjectManagementSystem.Queries.Infrastructure.User.Issues +{ + public sealed class IssueListQueryHandler : IRequestHandler> + { + private readonly IssueDbContext _context; + + public IssueListQueryHandler(IssueDbContext context) + { + _context = context; + } + + public async Task> Handle(IssueListQuery query, CancellationToken cancellationToken) + { + var sql = _context.Issues.AsNoTracking() + .OrderBy(p => p.CreateDate) + .Select(i => new IssueListItemView + { + Id = i.Id, + Number = i.Number, + Title = i.Title, + TrackerName = i.Tracker.Name, + StatusName = i.Status.Name, + PriorityName = i.Priority.Name, + AssigneeName = i.Assignee.Name, + UpdateDate = i.UpdateDate ?? i.CreateDate, + }) + .AsQueryable(); + + return new Page + { + Limit = query.Limit, + Offset = query.Offset, + Total = await sql.CountAsync(cancellationToken), + Items = await sql.Skip(query.Offset).Take(query.Limit).ToListAsync(cancellationToken) + }; + } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Queries.Infrastructure/User/Issues/IssuePriority.cs b/src/ProjectManagementSystem.Queries.Infrastructure/User/Issues/IssuePriority.cs new file mode 100644 index 0000000..13cb65c --- /dev/null +++ b/src/ProjectManagementSystem.Queries.Infrastructure/User/Issues/IssuePriority.cs @@ -0,0 +1,10 @@ +using System; + +namespace ProjectManagementSystem.Queries.Infrastructure.User.Issues +{ + internal sealed class IssuePriority + { + public Guid Id { get; } + public string Name { get; } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Queries.Infrastructure/User/Issues/IssueQueryHandler.cs b/src/ProjectManagementSystem.Queries.Infrastructure/User/Issues/IssueQueryHandler.cs new file mode 100644 index 0000000..a4cfc2c --- /dev/null +++ b/src/ProjectManagementSystem.Queries.Infrastructure/User/Issues/IssueQueryHandler.cs @@ -0,0 +1,49 @@ +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using MediatR; +using Microsoft.EntityFrameworkCore; +using ProjectManagementSystem.Queries.User.Issues; + +namespace ProjectManagementSystem.Queries.Infrastructure.User.Issues +{ + public sealed class IssueQueryHandler : IRequestHandler + { + private readonly IssueDbContext _context; + + public IssueQueryHandler(IssueDbContext context) + { + _context = context; + } + + public async Task Handle(IssueQuery query, CancellationToken cancellationToken) + { + return await _context.Issues + .Include(i => i.Project) + .Include(i => i.Tracker) + .Include(i => i.Status) + .Include(i => i.Priority) + .Include(i => i.Author) + .Include(i => i.Assignee) + .AsNoTracking() + .Where(i => i.Id == query.Id) + .Select(i => new IssueView + { + Id = i.Id, + Number = i.Number, + Title = i.Title, + Description = i.Description, + CreateDate = i.CreateDate, + UpdateDate = i.UpdateDate, + StartDate = i.StartDate, + DueDate = i.DueDate, + TrackerName = i.Tracker.Name, + StatusName = i.Status.Name, + PriorityName = i.Priority.Name, + AuthorName = i.Author.Name, + AssigneeName = i.Assignee.Name + }) + .SingleOrDefaultAsync(cancellationToken); + } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Queries.Infrastructure/User/Issues/IssueStatus.cs b/src/ProjectManagementSystem.Queries.Infrastructure/User/Issues/IssueStatus.cs new file mode 100644 index 0000000..8136434 --- /dev/null +++ b/src/ProjectManagementSystem.Queries.Infrastructure/User/Issues/IssueStatus.cs @@ -0,0 +1,10 @@ +using System; + +namespace ProjectManagementSystem.Queries.Infrastructure.User.Issues +{ + internal sealed class IssueStatus + { + public Guid Id { get; } + public string Name { get; } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Queries.Infrastructure/User/Issues/Project.cs b/src/ProjectManagementSystem.Queries.Infrastructure/User/Issues/Project.cs new file mode 100644 index 0000000..0fbbfeb --- /dev/null +++ b/src/ProjectManagementSystem.Queries.Infrastructure/User/Issues/Project.cs @@ -0,0 +1,9 @@ +using System; + +namespace ProjectManagementSystem.Queries.Infrastructure.User.Issues +{ + internal sealed class Project + { + public Guid Id { get; } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Queries.Infrastructure/User/Issues/ProjectDbContext.cs b/src/ProjectManagementSystem.Queries.Infrastructure/User/Issues/ProjectDbContext.cs new file mode 100644 index 0000000..59272ee --- /dev/null +++ b/src/ProjectManagementSystem.Queries.Infrastructure/User/Issues/ProjectDbContext.cs @@ -0,0 +1,126 @@ +using Microsoft.EntityFrameworkCore; + +namespace ProjectManagementSystem.Queries.Infrastructure.User.Issues +{ + public sealed class ProjectDbContext : DbContext + { + public ProjectDbContext(DbContextOptions options) : base(options) { } + + internal DbSet Projects { get; set; } + internal DbSet Trackers { get; set; } + internal DbSet IssueStatuses { get; set; } + internal DbSet IssuePriorities { get; set; } + internal DbSet Users { get; set; } + internal DbSet Issues { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + modelBuilder.Entity(builder => + { + builder.ToTable("Project"); + builder.HasKey(p => p.Id); + builder.Property(p => p.Id) + .HasColumnName("Id") + .ValueGeneratedNever(); + }); + + modelBuilder.Entity(builder => + { + builder.ToTable("Tracker"); + builder.HasKey(t => t.Id); + builder.Property(t => t.Id) + .HasColumnName("Id") + .ValueGeneratedNever(); + }); + + modelBuilder.Entity(builder => + { + builder.ToTable("IssueStatus"); + builder.HasKey(@is => @is.Id); + builder.Property(@is => @is.Id) + .HasColumnName("Id") + .ValueGeneratedNever(); + }); + + modelBuilder.Entity(builder => + { + builder.ToTable("IssuePriority"); + builder.HasKey(ip => ip.Id); + builder.Property(ip => ip.Id) + .HasColumnName("Id") + .ValueGeneratedNever(); + }); + + modelBuilder.Entity(builder => + { + builder.ToTable("User"); + builder.HasKey(u => u.Id); + builder.Property(u => u.Id) + .HasColumnName("Id") + .ValueGeneratedNever(); + }); + + modelBuilder.Entity(builder => + { + builder.ToTable("Issue"); + builder.HasKey(i => i.Id); + builder.Property(i => i.Id) + .HasColumnName("Id") + .ValueGeneratedNever(); + builder.Property(i => i.Title) + .HasColumnName("Title") + .IsRequired(); + builder.Property(i => i.Description) + .HasColumnName("Description") + .IsRequired(); + builder.Property(i => i.CreateDate) + .HasColumnName("CreateDate") + .IsRequired(); + builder.Property(i => i.StartDate) + .HasColumnName("StartDate"); + builder.Property(i => i.DueDate) + .HasColumnName("DueDate"); + builder.Property(i => i.TrackerId) + .HasColumnName("TrackerId") + .IsRequired(); + builder.Property(i => i.StatusId) + .HasColumnName("StatusId") + .IsRequired(); + builder.Property(i => i.PriorityId) + .HasColumnName("PriorityId") + .IsRequired(); + builder.Property(i => i.AuthorId) + .HasColumnName("AuthorId") + .IsRequired(); + builder.Property(i => i.AssigneeId) + .HasColumnName("AssigneeId") + .IsRequired(); + builder.Property("_concurrencyStamp") + .HasColumnName("ConcurrencyStamp") + .IsConcurrencyToken(); + builder.HasOne(i => i.Tracker) + .WithMany() + .HasForeignKey(i => i.TrackerId) + .HasPrincipalKey(t => t.Id); + builder.HasOne(i => i.Status) + .WithMany() + .HasForeignKey(i => i.StatusId) + .HasPrincipalKey(@is => @is.Id); + builder.HasOne(i => i.Priority) + .WithMany() + .HasForeignKey(i => i.PriorityId) + .HasPrincipalKey(ip => ip.Id); + builder.HasOne(i => i.Author) + .WithMany() + .HasForeignKey(i => i.AuthorId) + .HasPrincipalKey(a => a.Id); + builder.HasOne(i => i.Assignee) + .WithMany() + .HasForeignKey(i => i.AssigneeId) + .HasPrincipalKey(p => p.Id); + }); + } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Queries.Infrastructure/User/Issues/Tracker.cs b/src/ProjectManagementSystem.Queries.Infrastructure/User/Issues/Tracker.cs new file mode 100644 index 0000000..e4c0cd2 --- /dev/null +++ b/src/ProjectManagementSystem.Queries.Infrastructure/User/Issues/Tracker.cs @@ -0,0 +1,10 @@ +using System; + +namespace ProjectManagementSystem.Queries.Infrastructure.User.Issues +{ + internal sealed class Tracker + { + public Guid Id { get; } + public string Name { get; } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Queries.Infrastructure/User/Issues/User.cs b/src/ProjectManagementSystem.Queries.Infrastructure/User/Issues/User.cs new file mode 100644 index 0000000..984508f --- /dev/null +++ b/src/ProjectManagementSystem.Queries.Infrastructure/User/Issues/User.cs @@ -0,0 +1,10 @@ +using System; + +namespace ProjectManagementSystem.Queries.Infrastructure.User.Issues +{ + internal sealed class User + { + public Guid Id { get; } + public string Name { get; } + } +} \ No newline at end of file From 03d4d7be163ca877ecb7d834fa85a79d6d2bed0f Mon Sep 17 00:00:00 2001 From: gradovenko Date: Sun, 22 Mar 2020 22:47:01 +0500 Subject: [PATCH 8/8] Add authentication service and add domain and infrastructure layer logic for membership --- .../Filters/PermissionAuthorizationFilter.cs | 25 --------- .../Models/Admin/Roles/CreateRoleBinding.cs | 2 +- .../Entities/Member.cs | 3 +- .../Entities/MemberRole.cs | 12 ----- .../Entities/Permission.cs | 3 +- .../ProjectManagementSystemDbContext.cs | 20 ++----- .../Admin/Members/IRoleRepository.cs | 11 ++++ .../Admin/Roles/IPermissionRepository.cs | 2 +- .../Admin/Roles/Permission.cs | 3 +- .../Admin/Roles/RolePermission.cs | 4 +- .../User/Members/IMemberRepository.cs | 2 +- .../User/Members/IPermissionRepository.cs | 10 ++++ .../User/Members/IProjectRepository.cs | 11 ++++ .../User/Members/IUserRepository.cs | 11 ++++ .../User/Members/Member.cs | 16 +++--- .../User/Members/Permission.cs | 11 ++++ .../User/Members/Project.cs | 9 ++++ .../User/Members/Role.cs | 9 ++++ .../User/Members/RolePermission.cs | 4 +- .../User/Members/User.cs | 9 ++++ .../User/Members/UserAuthorizationService.cs | 50 ++++++++++++++++++ .../User/Members/MemberDbContext.cs | 52 +++++++++++++++++-- .../User/Members/MemberRepository.cs | 10 ++-- .../User/Members/PermissionRepository.cs | 22 ++++++++ .../User/Members/ProjectRepository.cs | 23 ++++++++ .../User/Members/UserRepository.cs | 23 ++++++++ 26 files changed, 273 insertions(+), 84 deletions(-) delete mode 100644 src/ProjectManagementSystem.Api/Filters/PermissionAuthorizationFilter.cs delete mode 100644 src/ProjectManagementSystem.DatabaseMigrations/Entities/MemberRole.cs create mode 100644 src/ProjectManagementSystem.Domain/Admin/Members/IRoleRepository.cs create mode 100644 src/ProjectManagementSystem.Domain/User/Members/IPermissionRepository.cs create mode 100644 src/ProjectManagementSystem.Domain/User/Members/IProjectRepository.cs create mode 100644 src/ProjectManagementSystem.Domain/User/Members/IUserRepository.cs create mode 100644 src/ProjectManagementSystem.Domain/User/Members/Permission.cs create mode 100644 src/ProjectManagementSystem.Domain/User/Members/Project.cs create mode 100644 src/ProjectManagementSystem.Domain/User/Members/Role.cs create mode 100644 src/ProjectManagementSystem.Domain/User/Members/User.cs create mode 100644 src/ProjectManagementSystem.Domain/User/Members/UserAuthorizationService.cs create mode 100644 src/ProjectManagementSystem.Infrastructure/User/Members/PermissionRepository.cs create mode 100644 src/ProjectManagementSystem.Infrastructure/User/Members/ProjectRepository.cs create mode 100644 src/ProjectManagementSystem.Infrastructure/User/Members/UserRepository.cs diff --git a/src/ProjectManagementSystem.Api/Filters/PermissionAuthorizationFilter.cs b/src/ProjectManagementSystem.Api/Filters/PermissionAuthorizationFilter.cs deleted file mode 100644 index 21af93e..0000000 --- a/src/ProjectManagementSystem.Api/Filters/PermissionAuthorizationFilter.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc.Filters; -using ProjectManagementSystem.Api.Extensions; -using ProjectManagementSystem.Domain.User.Members; - -namespace ProjectManagementSystem.Api.Filters -{ - public sealed class PermissionAuthorizationFilter : IAsyncAuthorizationFilter - { - private readonly IMemberRepository _userRepository; - - public PermissionAuthorizationFilter(IMemberRepository userRepository) - { - _userRepository = userRepository; - } - - public Task OnAuthorizationAsync(AuthorizationFilterContext context) - { - var user = _userRepository.Get(context.HttpContext.User.GetId(), CancellationToken.None); - - - } - } -} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Api/Models/Admin/Roles/CreateRoleBinding.cs b/src/ProjectManagementSystem.Api/Models/Admin/Roles/CreateRoleBinding.cs index 6355ac7..f1106ed 100644 --- a/src/ProjectManagementSystem.Api/Models/Admin/Roles/CreateRoleBinding.cs +++ b/src/ProjectManagementSystem.Api/Models/Admin/Roles/CreateRoleBinding.cs @@ -19,7 +19,7 @@ public sealed class CreateRoleBinding /// /// /// - public IEnumerable Permissions { get; set; } + public IEnumerable Permissions { get; set; } } public sealed class CreateRoleBindingValidator : AbstractValidator diff --git a/src/ProjectManagementSystem.DatabaseMigrations/Entities/Member.cs b/src/ProjectManagementSystem.DatabaseMigrations/Entities/Member.cs index d7f78e7..0d09b05 100644 --- a/src/ProjectManagementSystem.DatabaseMigrations/Entities/Member.cs +++ b/src/ProjectManagementSystem.DatabaseMigrations/Entities/Member.cs @@ -4,10 +4,11 @@ namespace ProjectManagementSystem.DatabaseMigrations.Entities { public sealed class Member { - public Guid MemberId { get; set; } public Guid UserId { get; set; } public User User { get; set; } public Guid ProjectId { get; set; } public Project Project { get; set; } + public Guid RoleId { get; set; } + public Role Role { get; set; } } } \ No newline at end of file diff --git a/src/ProjectManagementSystem.DatabaseMigrations/Entities/MemberRole.cs b/src/ProjectManagementSystem.DatabaseMigrations/Entities/MemberRole.cs deleted file mode 100644 index 2e6a3b8..0000000 --- a/src/ProjectManagementSystem.DatabaseMigrations/Entities/MemberRole.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace ProjectManagementSystem.DatabaseMigrations.Entities -{ - public sealed class MemberRole - { - public Guid MemberId { get; set; } - public Member Member { get; set; } - public Guid RoleId { get; set; } - public Role Role { get; set; } - } -} \ No newline at end of file diff --git a/src/ProjectManagementSystem.DatabaseMigrations/Entities/Permission.cs b/src/ProjectManagementSystem.DatabaseMigrations/Entities/Permission.cs index 4e1f9db..107b50d 100644 --- a/src/ProjectManagementSystem.DatabaseMigrations/Entities/Permission.cs +++ b/src/ProjectManagementSystem.DatabaseMigrations/Entities/Permission.cs @@ -4,7 +4,6 @@ namespace ProjectManagementSystem.DatabaseMigrations.Entities { public sealed class Permission { - public Guid PermissionId { get; set; } - public string Name { get; set; } + public string PermissionId { get; set; } } } \ No newline at end of file diff --git a/src/ProjectManagementSystem.DatabaseMigrations/ProjectManagementSystemDbContext.cs b/src/ProjectManagementSystem.DatabaseMigrations/ProjectManagementSystemDbContext.cs index 993f77e..ebe508f 100644 --- a/src/ProjectManagementSystem.DatabaseMigrations/ProjectManagementSystemDbContext.cs +++ b/src/ProjectManagementSystem.DatabaseMigrations/ProjectManagementSystemDbContext.cs @@ -270,8 +270,6 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) builder.HasKey(p => p.PermissionId); builder.Property(p => p.PermissionId) .ValueGeneratedNever(); - builder.Property(p => p.Name) - .IsRequired(); }); modelBuilder.Entity(builder => @@ -291,9 +289,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(builder => { builder.ToTable("Member"); - builder.HasKey(m => m.MemberId); - builder.Property(m => m.MemberId) - .ValueGeneratedNever(); + builder.HasKey(m => new {m.UserId, m.ProjectId, m.RoleId}); builder.HasOne(m => m.User) .WithMany() .HasForeignKey(m => m.UserId) @@ -302,19 +298,9 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .WithMany() .HasForeignKey(m => m.ProjectId) .HasPrincipalKey(p => p.ProjectId); - }); - - modelBuilder.Entity(builder => - { - builder.ToTable("MemberRole"); - builder.HasKey(mr => new {mr.MemberId, mr.RoleId}); - builder.HasOne(mr => mr.Member) - .WithMany() - .HasForeignKey(mr => mr.MemberId) - .HasPrincipalKey(m => m.MemberId); - builder.HasOne(mr => mr.Role) + builder.HasOne(m => m.Role) .WithMany() - .HasForeignKey(mr => mr.RoleId) + .HasForeignKey(m => m.RoleId) .HasPrincipalKey(r => r.RoleId); }); } diff --git a/src/ProjectManagementSystem.Domain/Admin/Members/IRoleRepository.cs b/src/ProjectManagementSystem.Domain/Admin/Members/IRoleRepository.cs new file mode 100644 index 0000000..3758276 --- /dev/null +++ b/src/ProjectManagementSystem.Domain/Admin/Members/IRoleRepository.cs @@ -0,0 +1,11 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace ProjectManagementSystem.Domain.Admin.Members +{ + public interface IRoleRepository + { + Task Get(Guid id, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/Admin/Roles/IPermissionRepository.cs b/src/ProjectManagementSystem.Domain/Admin/Roles/IPermissionRepository.cs index ac2b0c9..d46a793 100644 --- a/src/ProjectManagementSystem.Domain/Admin/Roles/IPermissionRepository.cs +++ b/src/ProjectManagementSystem.Domain/Admin/Roles/IPermissionRepository.cs @@ -6,6 +6,6 @@ namespace ProjectManagementSystem.Domain.Admin.Roles { public interface IPermissionRepository { - Task Get(Guid id, CancellationToken cancellationToken); + Task Get(string id, CancellationToken cancellationToken); } } \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/Admin/Roles/Permission.cs b/src/ProjectManagementSystem.Domain/Admin/Roles/Permission.cs index 9a61998..b47b986 100644 --- a/src/ProjectManagementSystem.Domain/Admin/Roles/Permission.cs +++ b/src/ProjectManagementSystem.Domain/Admin/Roles/Permission.cs @@ -4,7 +4,6 @@ namespace ProjectManagementSystem.Domain.Admin.Roles { public sealed class Permission { - public Guid Id { get; } - //public string Name { get; } + public string Id { get; } } } \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/Admin/Roles/RolePermission.cs b/src/ProjectManagementSystem.Domain/Admin/Roles/RolePermission.cs index 1a89902..4ea5e9a 100644 --- a/src/ProjectManagementSystem.Domain/Admin/Roles/RolePermission.cs +++ b/src/ProjectManagementSystem.Domain/Admin/Roles/RolePermission.cs @@ -5,9 +5,9 @@ namespace ProjectManagementSystem.Domain.Admin.Roles public sealed class RolePermission { public Guid RoleId { get; } - public Guid PermissionId { get; } + public string PermissionId { get; } - public RolePermission(Guid roleId, Guid permissionId) + public RolePermission(Guid roleId, string permissionId) { RoleId = roleId; PermissionId = permissionId; diff --git a/src/ProjectManagementSystem.Domain/User/Members/IMemberRepository.cs b/src/ProjectManagementSystem.Domain/User/Members/IMemberRepository.cs index 5dba2dc..1007151 100644 --- a/src/ProjectManagementSystem.Domain/User/Members/IMemberRepository.cs +++ b/src/ProjectManagementSystem.Domain/User/Members/IMemberRepository.cs @@ -6,6 +6,6 @@ namespace ProjectManagementSystem.Domain.User.Members { public interface IMemberRepository { - Task Get(Guid id, CancellationToken cancellationToken); + Task Get(Guid userId, Guid projectId, Guid roleId, CancellationToken cancellationToken); } } \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/User/Members/IPermissionRepository.cs b/src/ProjectManagementSystem.Domain/User/Members/IPermissionRepository.cs new file mode 100644 index 0000000..e72aebc --- /dev/null +++ b/src/ProjectManagementSystem.Domain/User/Members/IPermissionRepository.cs @@ -0,0 +1,10 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace ProjectManagementSystem.Domain.User.Members +{ + public interface IPermissionRepository + { + Task Get(string id, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/User/Members/IProjectRepository.cs b/src/ProjectManagementSystem.Domain/User/Members/IProjectRepository.cs new file mode 100644 index 0000000..d2f5987 --- /dev/null +++ b/src/ProjectManagementSystem.Domain/User/Members/IProjectRepository.cs @@ -0,0 +1,11 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace ProjectManagementSystem.Domain.User.Members +{ + public interface IProjectRepository + { + Task Get(Guid id, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/User/Members/IUserRepository.cs b/src/ProjectManagementSystem.Domain/User/Members/IUserRepository.cs new file mode 100644 index 0000000..189bc25 --- /dev/null +++ b/src/ProjectManagementSystem.Domain/User/Members/IUserRepository.cs @@ -0,0 +1,11 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace ProjectManagementSystem.Domain.User.Members +{ + public interface IUserRepository + { + Task Get(Guid id, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/User/Members/Member.cs b/src/ProjectManagementSystem.Domain/User/Members/Member.cs index 1311121..6d98694 100644 --- a/src/ProjectManagementSystem.Domain/User/Members/Member.cs +++ b/src/ProjectManagementSystem.Domain/User/Members/Member.cs @@ -1,20 +1,18 @@ using System; -using System.Collections.Generic; namespace ProjectManagementSystem.Domain.User.Members { public sealed class Member { - public Guid MemberId { get; } - public Guid UserId { get; } - public Guid ProjectId { get; } - public Guid RoleId { get; } - private List _rolePermissions = new List(); - public IEnumerable RolePermissions => _rolePermissions; + public Guid UserId { get; set; } + public User User { get; set; } + public Guid ProjectId { get; set; } + public Project Project { get; set; } + public Guid RoleId { get; set; } + public Role Role { get; set; } - public Member(Guid memberId, Guid userId, Guid projectId, Guid roleId) + public Member(Guid userId, Guid projectId, Guid roleId) { - MemberId = memberId; UserId = userId; ProjectId = projectId; RoleId = roleId; diff --git a/src/ProjectManagementSystem.Domain/User/Members/Permission.cs b/src/ProjectManagementSystem.Domain/User/Members/Permission.cs new file mode 100644 index 0000000..2855b0c --- /dev/null +++ b/src/ProjectManagementSystem.Domain/User/Members/Permission.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace ProjectManagementSystem.Domain.User.Members +{ + public sealed class Permission + { + public string Id { get; } + private List _rolePermissions = new List(); + public IEnumerable RolePermissions => _rolePermissions; + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/User/Members/Project.cs b/src/ProjectManagementSystem.Domain/User/Members/Project.cs new file mode 100644 index 0000000..cbe96a9 --- /dev/null +++ b/src/ProjectManagementSystem.Domain/User/Members/Project.cs @@ -0,0 +1,9 @@ +using System; + +namespace ProjectManagementSystem.Domain.User.Members +{ + public sealed class Project + { + public Guid Id { get; } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/User/Members/Role.cs b/src/ProjectManagementSystem.Domain/User/Members/Role.cs new file mode 100644 index 0000000..9bc9126 --- /dev/null +++ b/src/ProjectManagementSystem.Domain/User/Members/Role.cs @@ -0,0 +1,9 @@ +using System; + +namespace ProjectManagementSystem.Domain.User.Members +{ + public sealed class Role + { + public Guid Id { get; } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/User/Members/RolePermission.cs b/src/ProjectManagementSystem.Domain/User/Members/RolePermission.cs index 4a7498f..65a8356 100644 --- a/src/ProjectManagementSystem.Domain/User/Members/RolePermission.cs +++ b/src/ProjectManagementSystem.Domain/User/Members/RolePermission.cs @@ -5,9 +5,9 @@ namespace ProjectManagementSystem.Domain.User.Members public sealed class RolePermission { public Guid RoleId { get; } - public Guid PermissionId { get; } + public string PermissionId { get; } - public RolePermission(Guid roleId, Guid permissionId) + public RolePermission(Guid roleId, string permissionId) { RoleId = roleId; PermissionId = permissionId; diff --git a/src/ProjectManagementSystem.Domain/User/Members/User.cs b/src/ProjectManagementSystem.Domain/User/Members/User.cs new file mode 100644 index 0000000..5ee0bbb --- /dev/null +++ b/src/ProjectManagementSystem.Domain/User/Members/User.cs @@ -0,0 +1,9 @@ +using System; + +namespace ProjectManagementSystem.Domain.User.Members +{ + public sealed class User + { + public Guid Id { get; } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Domain/User/Members/UserAuthorizationService.cs b/src/ProjectManagementSystem.Domain/User/Members/UserAuthorizationService.cs new file mode 100644 index 0000000..a20ae4f --- /dev/null +++ b/src/ProjectManagementSystem.Domain/User/Members/UserAuthorizationService.cs @@ -0,0 +1,50 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace ProjectManagementSystem.Domain.User.Members +{ + public class UserAuthorizationService + { + private readonly IUserRepository _userRepository; + private readonly IProjectRepository _projectRepository; + private readonly IPermissionRepository _permissionRepository; + private readonly IMemberRepository _memberRepository; + + public UserAuthorizationService(IUserRepository userRepository, IProjectRepository projectRepository, + IPermissionRepository permissionRepository, IMemberRepository memberRepository) + { + _userRepository = userRepository; + _projectRepository = projectRepository; + _permissionRepository = permissionRepository; + _memberRepository = memberRepository; + } + + public async Task Authorization(Guid userId, Guid projectId, string permissionName, + CancellationToken cancellationToken) + { + var user = await _userRepository.Get(userId, cancellationToken); + + if (user == null) + throw new Exception(); + + var project = await _projectRepository.Get(projectId, cancellationToken); + + if (project == null) + throw new Exception(); + + var permission = await _permissionRepository.Get(permissionName, cancellationToken); + + if (permission == null) + throw new Exception(); + + foreach (var rolePermission in permission.RolePermissions) + { + if (await _memberRepository.Get(userId, projectId, rolePermission.RoleId, cancellationToken) != null) + return true; + } + + return false; + } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Infrastructure/User/Members/MemberDbContext.cs b/src/ProjectManagementSystem.Infrastructure/User/Members/MemberDbContext.cs index f3628c1..f07988b 100644 --- a/src/ProjectManagementSystem.Infrastructure/User/Members/MemberDbContext.cs +++ b/src/ProjectManagementSystem.Infrastructure/User/Members/MemberDbContext.cs @@ -6,6 +6,9 @@ public sealed class MemberDbContext : DbContext { public MemberDbContext(DbContextOptions options) : base(options) { } + internal DbSet Users { get; set; } + internal DbSet Projects { get; set; } + internal DbSet Permissions { get; set; } internal DbSet Members { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) @@ -17,11 +20,50 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) builder.ToTable("User"); builder.HasKey(u => u.Id); builder.Property(u => u.Id) - .HasColumnName("UserId") - .ValueGeneratedNever(); - builder.Property("_concurrencyStamp") - .HasColumnName("ConcurrencyStamp") - .IsConcurrencyToken(); + .HasColumnName("UserId"); + }); + + modelBuilder.Entity(builder => + { + builder.ToTable("Project"); + builder.HasKey(p => p.Id); + builder.Property(p => p.Id) + .HasColumnName("ProjectId"); + }); + + modelBuilder.Entity(builder => + { + builder.ToTable("RolePermission"); + builder.HasKey(rp => new {rp.RoleId, rp.PermissionId}); + }); + + modelBuilder.Entity(builder => + { + builder.ToTable("Permission"); + builder.HasKey(p => p.Id); + builder.Property(p => p.Id) + .HasColumnName("PermissionId"); + builder.HasMany(p => p.RolePermissions) + .WithOne() + .HasForeignKey(rp => rp.PermissionId) + .HasPrincipalKey(p => p.Id); + }); + + modelBuilder.Entity(builder => + { + builder.ToTable("Member"); + builder.HasOne(m => m.User) + .WithMany() + .HasForeignKey(m => m.UserId) + .HasPrincipalKey(u => u.Id); + builder.HasOne(m => m.Project) + .WithMany() + .HasForeignKey(m => m.ProjectId) + .HasPrincipalKey(p => p.Id); + builder.HasOne(m => m.Role) + .WithMany() + .HasForeignKey(m => m.RoleId) + .HasPrincipalKey(r => r.Id); }); } } diff --git a/src/ProjectManagementSystem.Infrastructure/User/Members/MemberRepository.cs b/src/ProjectManagementSystem.Infrastructure/User/Members/MemberRepository.cs index c827257..eda9759 100644 --- a/src/ProjectManagementSystem.Infrastructure/User/Members/MemberRepository.cs +++ b/src/ProjectManagementSystem.Infrastructure/User/Members/MemberRepository.cs @@ -14,11 +14,13 @@ public MemberRepository(MemberDbContext context) { _context = context; } - - public async Task Get(Guid userId, CancellationToken cancellationToken) + + public async Task Get(Guid userId, Guid projectId, Guid roleId, CancellationToken cancellationToken) { - return await _context.Members - .SingleOrDefaultAsync(u => u.UserId == userId, cancellationToken); + return await _context.Members.SingleOrDefaultAsync(m => + m.UserId == userId && + m.ProjectId == projectId && + m.RoleId == roleId, cancellationToken); } } } \ No newline at end of file diff --git a/src/ProjectManagementSystem.Infrastructure/User/Members/PermissionRepository.cs b/src/ProjectManagementSystem.Infrastructure/User/Members/PermissionRepository.cs new file mode 100644 index 0000000..a1c5d1b --- /dev/null +++ b/src/ProjectManagementSystem.Infrastructure/User/Members/PermissionRepository.cs @@ -0,0 +1,22 @@ +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using ProjectManagementSystem.Domain.User.Members; + +namespace ProjectManagementSystem.Infrastructure.User.Members +{ + public sealed class PermissionRepository : IPermissionRepository + { + private readonly MemberDbContext _context; + + public PermissionRepository(MemberDbContext context) + { + _context = context; + } + + public async Task Get(string id, CancellationToken cancellationToken) + { + return await _context.Permissions.SingleOrDefaultAsync(p => p.Id == id, cancellationToken); + } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Infrastructure/User/Members/ProjectRepository.cs b/src/ProjectManagementSystem.Infrastructure/User/Members/ProjectRepository.cs new file mode 100644 index 0000000..f5cbea0 --- /dev/null +++ b/src/ProjectManagementSystem.Infrastructure/User/Members/ProjectRepository.cs @@ -0,0 +1,23 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using ProjectManagementSystem.Domain.User.Members; + +namespace ProjectManagementSystem.Infrastructure.User.Members +{ + public sealed class ProjectRepository : IProjectRepository + { + private readonly MemberDbContext _context; + + public ProjectRepository(MemberDbContext context) + { + _context = context; + } + + public async Task Get(Guid id, CancellationToken cancellationToken) + { + return await _context.Projects.SingleOrDefaultAsync(u => u.Id == id, cancellationToken); + } + } +} \ No newline at end of file diff --git a/src/ProjectManagementSystem.Infrastructure/User/Members/UserRepository.cs b/src/ProjectManagementSystem.Infrastructure/User/Members/UserRepository.cs new file mode 100644 index 0000000..12dbd27 --- /dev/null +++ b/src/ProjectManagementSystem.Infrastructure/User/Members/UserRepository.cs @@ -0,0 +1,23 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using ProjectManagementSystem.Domain.User.Members; + +namespace ProjectManagementSystem.Infrastructure.User.Members +{ + public sealed class UserRepository : IUserRepository + { + private readonly MemberDbContext _context; + + public UserRepository(MemberDbContext context) + { + _context = context; + } + + public async Task Get(Guid id, CancellationToken cancellationToken) + { + return await _context.Users.SingleOrDefaultAsync(u => u.Id == id, cancellationToken); + } + } +} \ No newline at end of file