diff --git a/Directory.Build.props b/Directory.Build.props
index b4b2f3d..f2b90fb 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -1,7 +1,7 @@
- 2.3.0
+ 2.4.0
Tudor Stanciu
STA
Tuitio
diff --git a/ReleaseNotes.xml b/ReleaseNotes.xml
index 2cf75b9..3122d04 100644
--- a/ReleaseNotes.xml
+++ b/ReleaseNotes.xml
@@ -86,4 +86,13 @@
◾ Published new versions of Tuitio's nuget packages
+
+ 2.4.0
+ 2023-04-03 01:14
+
+ Added user groups and roles
+ ◾ From this version, any user can be assigned to groups and can have roles.
+ ◾ Each user group can have roles that will be applied to all users who are part of the group.
+
+
\ No newline at end of file
diff --git a/src/Tuitio.Domain.Data/DbContexts/TuitioDbContext.cs b/src/Tuitio.Domain.Data/DbContexts/TuitioDbContext.cs
index 26b4da0..84d58df 100644
--- a/src/Tuitio.Domain.Data/DbContexts/TuitioDbContext.cs
+++ b/src/Tuitio.Domain.Data/DbContexts/TuitioDbContext.cs
@@ -29,6 +29,11 @@ namespace Tuitio.Domain.Data.DbContexts
modelBuilder.ApplyConfiguration(new UserTokenConfiguration());
modelBuilder.ApplyConfiguration(new ContactTypeConfiguration());
modelBuilder.ApplyConfiguration(new ContactOptionConfiguration());
+ modelBuilder.ApplyConfiguration(new UserGroupConfiguration());
+ modelBuilder.ApplyConfiguration(new UserRoleConfiguration());
+ modelBuilder.ApplyConfiguration(new UserXUserGroupConfiguration());
+ modelBuilder.ApplyConfiguration(new UserGroupXUserRoleConfiguration());
+ modelBuilder.ApplyConfiguration(new UserXUserRoleConfiguration());
}
}
}
diff --git a/src/Tuitio.Domain.Data/EntityTypeConfiguration/AppUserConfiguration.cs b/src/Tuitio.Domain.Data/EntityTypeConfiguration/AppUserConfiguration.cs
index 771d0d0..b6e5ea6 100644
--- a/src/Tuitio.Domain.Data/EntityTypeConfiguration/AppUserConfiguration.cs
+++ b/src/Tuitio.Domain.Data/EntityTypeConfiguration/AppUserConfiguration.cs
@@ -15,6 +15,8 @@ namespace Tuitio.Domain.Data.EntityTypeConfiguration
builder.HasOne(z => z.Status).WithMany().HasForeignKey(z => z.StatusId);
builder.HasMany(z => z.Claims).WithOne().HasForeignKey(z => z.UserId);
builder.HasMany(z => z.ContactOptions).WithOne().HasForeignKey(z => z.UserId);
+ builder.HasMany(z => z.UserGroups).WithOne().HasForeignKey(z => z.UserId);
+ builder.HasMany(z => z.UserRoles).WithOne().HasForeignKey(z => z.UserId);
}
}
}
diff --git a/src/Tuitio.Domain.Data/EntityTypeConfiguration/UserGroupConfiguration.cs b/src/Tuitio.Domain.Data/EntityTypeConfiguration/UserGroupConfiguration.cs
new file mode 100644
index 0000000..d0d8092
--- /dev/null
+++ b/src/Tuitio.Domain.Data/EntityTypeConfiguration/UserGroupConfiguration.cs
@@ -0,0 +1,18 @@
+// Copyright (c) 2020 Tudor Stanciu
+
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Metadata.Builders;
+using Tuitio.Domain.Entities;
+
+namespace Tuitio.Domain.Data.EntityTypeConfiguration
+{
+ class UserGroupConfiguration : IEntityTypeConfiguration
+ {
+ public void Configure(EntityTypeBuilder builder)
+ {
+ builder.ToTable("UserGroup").HasKey(z => z.UserGroupId);
+ builder.Property(z => z.UserGroupId).ValueGeneratedOnAdd();
+ builder.HasMany(z => z.GroupRoles).WithOne().HasForeignKey(z => z.UserGroupId);
+ }
+ }
+}
diff --git a/src/Tuitio.Domain.Data/EntityTypeConfiguration/UserGroupXUserRoleConfiguration.cs b/src/Tuitio.Domain.Data/EntityTypeConfiguration/UserGroupXUserRoleConfiguration.cs
new file mode 100644
index 0000000..a37d700
--- /dev/null
+++ b/src/Tuitio.Domain.Data/EntityTypeConfiguration/UserGroupXUserRoleConfiguration.cs
@@ -0,0 +1,17 @@
+// Copyright (c) 2020 Tudor Stanciu
+
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Metadata.Builders;
+using Tuitio.Domain.Entities;
+
+namespace Tuitio.Domain.Data.EntityTypeConfiguration
+{
+ class UserGroupXUserRoleConfiguration : IEntityTypeConfiguration
+ {
+ public void Configure(EntityTypeBuilder builder)
+ {
+ builder.ToTable("UserGroupXUserRole").HasKey(z => new { z.UserGroupId, z.UserRoleId });
+ builder.HasOne(z => z.UserRole).WithMany().HasForeignKey(z => z.UserRoleId);
+ }
+ }
+}
diff --git a/src/Tuitio.Domain.Data/EntityTypeConfiguration/UserRoleConfiguration.cs b/src/Tuitio.Domain.Data/EntityTypeConfiguration/UserRoleConfiguration.cs
new file mode 100644
index 0000000..db4ae95
--- /dev/null
+++ b/src/Tuitio.Domain.Data/EntityTypeConfiguration/UserRoleConfiguration.cs
@@ -0,0 +1,17 @@
+// Copyright (c) 2020 Tudor Stanciu
+
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Metadata.Builders;
+using Tuitio.Domain.Entities;
+
+namespace Tuitio.Domain.Data.EntityTypeConfiguration
+{
+ class UserRoleConfiguration : IEntityTypeConfiguration
+ {
+ public void Configure(EntityTypeBuilder builder)
+ {
+ builder.ToTable("UserRole").HasKey(z => z.UserRoleId);
+ builder.Property(z => z.UserRoleId).ValueGeneratedOnAdd();
+ }
+ }
+}
diff --git a/src/Tuitio.Domain.Data/EntityTypeConfiguration/UserXUserGroupConfiguration.cs b/src/Tuitio.Domain.Data/EntityTypeConfiguration/UserXUserGroupConfiguration.cs
new file mode 100644
index 0000000..b37bca8
--- /dev/null
+++ b/src/Tuitio.Domain.Data/EntityTypeConfiguration/UserXUserGroupConfiguration.cs
@@ -0,0 +1,17 @@
+// Copyright (c) 2020 Tudor Stanciu
+
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Metadata.Builders;
+using Tuitio.Domain.Entities;
+
+namespace Tuitio.Domain.Data.EntityTypeConfiguration
+{
+ class UserXUserGroupConfiguration : IEntityTypeConfiguration
+ {
+ public void Configure(EntityTypeBuilder builder)
+ {
+ builder.ToTable("UserXUserGroup").HasKey(z => new { z.UserId, z.UserGroupId });
+ builder.HasOne(z => z.UserGroup).WithMany().HasForeignKey(z => z.UserGroupId);
+ }
+ }
+}
diff --git a/src/Tuitio.Domain.Data/EntityTypeConfiguration/UserXUserRoleConfiguration.cs b/src/Tuitio.Domain.Data/EntityTypeConfiguration/UserXUserRoleConfiguration.cs
new file mode 100644
index 0000000..a3b8762
--- /dev/null
+++ b/src/Tuitio.Domain.Data/EntityTypeConfiguration/UserXUserRoleConfiguration.cs
@@ -0,0 +1,17 @@
+// Copyright (c) 2020 Tudor Stanciu
+
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Metadata.Builders;
+using Tuitio.Domain.Entities;
+
+namespace Tuitio.Domain.Data.EntityTypeConfiguration
+{
+ class UserXUserRoleConfiguration : IEntityTypeConfiguration
+ {
+ public void Configure(EntityTypeBuilder builder)
+ {
+ builder.ToTable("UserXUserRole").HasKey(z => new { z.UserId, z.UserRoleId });
+ builder.HasOne(z => z.UserRole).WithMany().HasForeignKey(z => z.UserRoleId);
+ }
+ }
+}
diff --git a/src/Tuitio.Domain.Data/Scripts/2.4.0/01.UserGroup table.sql b/src/Tuitio.Domain.Data/Scripts/2.4.0/01.UserGroup table.sql
new file mode 100644
index 0000000..3b63d01
--- /dev/null
+++ b/src/Tuitio.Domain.Data/Scripts/2.4.0/01.UserGroup table.sql
@@ -0,0 +1,19 @@
+if not exists (select top 1 1 from sys.objects where name = 'UserGroup' and type = 'U')
+begin
+ create table UserGroup
+ (
+ UserGroupId int identity(1, 1) constraint PK_UserGroup primary key,
+ UserGroupCode varchar(30) not null,
+ UserGroupName varchar(50) not null
+ )
+end
+
+if not exists (select top 1 1 from UserGroup)
+begin
+ insert into UserGroup(UserGroupCode, UserGroupName)
+ values ('ADMINISTRATORS', 'Administrators'),
+ ('USERS', 'Users'),
+ ('DEVELOPERS', 'Developers'),
+ ('VIEWERS', 'Viewers'),
+ ('GUESTS', 'Guests')
+end
\ No newline at end of file
diff --git a/src/Tuitio.Domain.Data/Scripts/2.4.0/02.UserRole table.sql b/src/Tuitio.Domain.Data/Scripts/2.4.0/02.UserRole table.sql
new file mode 100644
index 0000000..d179656
--- /dev/null
+++ b/src/Tuitio.Domain.Data/Scripts/2.4.0/02.UserRole table.sql
@@ -0,0 +1,22 @@
+if not exists (select top 1 1 from sys.objects where name = 'UserRole' and type = 'U')
+begin
+ create table UserRole
+ (
+ UserRoleId int identity(1, 1) constraint PK_UserRole primary key,
+ UserRoleCode varchar(30) not null,
+ UserRoleName varchar(50) not null
+ )
+end
+
+if not exists (select top 1 1 from UserRole)
+begin
+ insert into UserRole(UserRoleCode, UserRoleName)
+ values ('SYSTEM_ADMINISTRATOR', 'System administrator'),
+ ('FULLSTACK_DEVELOPER', 'Fullstack developer'),
+ ('POWER_USER', 'Power user'),
+ ('REGULAR_USER', 'Regular user'),
+ ('READONLY_USER', 'Readonly user'),
+ ('ANONYMOUS_USER', 'Anonymous user'),
+ ('DEMO_USER', 'Demo user')
+end
+
diff --git a/src/Tuitio.Domain.Data/Scripts/2.4.0/03.New tables UserXUserGroup UserGroupXUserRole UserXUserRole.sql b/src/Tuitio.Domain.Data/Scripts/2.4.0/03.New tables UserXUserGroup UserGroupXUserRole UserXUserRole.sql
new file mode 100644
index 0000000..0068d32
--- /dev/null
+++ b/src/Tuitio.Domain.Data/Scripts/2.4.0/03.New tables UserXUserGroup UserGroupXUserRole UserXUserRole.sql
@@ -0,0 +1,29 @@
+if not exists (select top 1 1 from sys.objects where name = 'UserXUserGroup' and type = 'U')
+begin
+ create table UserXUserGroup
+ (
+ UserId int not null constraint FK_UserXUserGroup_AppUser foreign key references AppUser(UserId),
+ UserGroupId int not null constraint FK_UserXUserGroup_UserGroup foreign key references UserGroup(UserGroupId),
+ constraint PK_UserXUserGroup primary key (UserId, UserGroupId)
+ )
+end
+
+if not exists (select top 1 1 from sys.objects where name = 'UserGroupXUserRole' and type = 'U')
+begin
+ create table UserGroupXUserRole
+ (
+ UserGroupId int not null constraint FK_UserGroupXUserRole_UserGroup references UserGroup(UserGroupId),
+ UserRoleId int not null constraint FK_UserGroupXUserRole_UserRole references UserRole(UserRoleId),
+ constraint PK_UserGroupXUserRole primary key (UserGroupId, UserRoleId)
+ )
+end
+
+if not exists (select top 1 1 from sys.objects where name = 'UserXUserRole' and type = 'U')
+begin
+ create table UserXUserRole
+ (
+ UserId int not null constraint FK_UserXUserRole_AppUser references AppUser(UserId),
+ UserRoleId int not null constraint FK_UserXUserRole_UserRole references UserRole(UserRoleId),
+ constraint PK_UserXUserRole primary key (UserId, UserRoleId)
+ )
+end
\ No newline at end of file
diff --git a/src/Tuitio.Domain.Data/Scripts/2.4.0/04.Link user groups and roles.sql b/src/Tuitio.Domain.Data/Scripts/2.4.0/04.Link user groups and roles.sql
new file mode 100644
index 0000000..e1a91c8
--- /dev/null
+++ b/src/Tuitio.Domain.Data/Scripts/2.4.0/04.Link user groups and roles.sql
@@ -0,0 +1,58 @@
+
+declare @admin_group_id int, @admin_role_id int
+
+select @admin_group_id = UserGroupId from UserGroup where UserGroupCode = 'ADMINISTRATORS'
+select @admin_role_id = UserRoleId from UserRole where UserRoleCode = 'SYSTEM_ADMINISTRATOR'
+
+if not exists (select top 1 1 from UserGroupXUserRole where UserGroupId = @admin_group_id and UserRoleId = @admin_role_id)
+begin
+ insert into UserGroupXUserRole (UserGroupId, UserRoleId)
+ values (@admin_group_id, @admin_role_id)
+end
+
+
+declare @developer_group_id int, @developer_role_id int
+
+select @developer_group_id = UserGroupId from UserGroup where UserGroupCode = 'DEVELOPERS'
+select @developer_role_id = UserRoleId from UserRole where UserRoleCode = 'FULLSTACK_DEVELOPER'
+
+if not exists (select top 1 1 from UserGroupXUserRole where UserGroupId = @developer_group_id and UserRoleId = @developer_role_id)
+begin
+ insert into UserGroupXUserRole (UserGroupId, UserRoleId)
+ values (@developer_group_id, @developer_role_id)
+end
+
+
+declare @user_group_id int, @user_role_id int
+
+select @user_group_id = UserGroupId from UserGroup where UserGroupCode = 'USERS'
+select @user_role_id = UserRoleId from UserRole where UserRoleCode = 'REGULAR_USER'
+
+if not exists (select top 1 1 from UserGroupXUserRole where UserGroupId = @user_group_id and UserRoleId = @user_role_id)
+begin
+ insert into UserGroupXUserRole (UserGroupId, UserRoleId)
+ values (@user_group_id, @user_role_id)
+end
+
+
+declare @viewer_group_id int, @viewer_role_id int
+
+select @viewer_group_id = UserGroupId from UserGroup where UserGroupCode = 'VIEWERS'
+select @viewer_role_id = UserRoleId from UserRole where UserRoleCode = 'READONLY_USER'
+
+if not exists (select top 1 1 from UserGroupXUserRole where UserGroupId = @viewer_group_id and UserRoleId = @viewer_role_id)
+begin
+ insert into UserGroupXUserRole (UserGroupId, UserRoleId)
+ values (@viewer_group_id, @viewer_role_id)
+end
+
+declare @guest_group_id int, @guest_role_id int
+
+select @guest_group_id = UserGroupId from UserGroup where UserGroupCode = 'GUESTS'
+select @guest_role_id = UserRoleId from UserRole where UserRoleCode = 'ANONYMOUS_USER'
+
+if not exists (select top 1 1 from UserGroupXUserRole where UserGroupId = @guest_group_id and UserRoleId = @guest_role_id)
+begin
+ insert into UserGroupXUserRole (UserGroupId, UserRoleId)
+ values (@guest_group_id, @guest_role_id)
+end
\ No newline at end of file
diff --git a/src/Tuitio.Domain.Data/Tuitio.Domain.Data.csproj b/src/Tuitio.Domain.Data/Tuitio.Domain.Data.csproj
index cd5c1bf..48067a6 100644
--- a/src/Tuitio.Domain.Data/Tuitio.Domain.Data.csproj
+++ b/src/Tuitio.Domain.Data/Tuitio.Domain.Data.csproj
@@ -34,6 +34,18 @@
PreserveNewest
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
diff --git a/src/Tuitio.Domain/Entities/AppUser.cs b/src/Tuitio.Domain/Entities/AppUser.cs
index f7933bf..49714eb 100644
--- a/src/Tuitio.Domain/Entities/AppUser.cs
+++ b/src/Tuitio.Domain/Entities/AppUser.cs
@@ -23,5 +23,7 @@ namespace Tuitio.Domain.Entities
public UserStatus Status { get; set; }
public ICollection Claims { get; set; }
public ICollection ContactOptions { get; set; }
+ public ICollection UserGroups { get; set; }
+ public ICollection UserRoles { get; set; }
}
}
diff --git a/src/Tuitio.Domain/Entities/UserGroup.cs b/src/Tuitio.Domain/Entities/UserGroup.cs
new file mode 100644
index 0000000..20f7da4
--- /dev/null
+++ b/src/Tuitio.Domain/Entities/UserGroup.cs
@@ -0,0 +1,14 @@
+// Copyright (c) 2020 Tudor Stanciu
+
+using System.Collections.Generic;
+
+namespace Tuitio.Domain.Entities
+{
+ public class UserGroup
+ {
+ public int UserGroupId { get; set; }
+ public string UserGroupCode { get; set; }
+ public string UserGroupName { get; set; }
+ public ICollection GroupRoles { get; set; }
+ }
+}
diff --git a/src/Tuitio.Domain/Entities/UserGroupXUserRole.cs b/src/Tuitio.Domain/Entities/UserGroupXUserRole.cs
new file mode 100644
index 0000000..7dc9d55
--- /dev/null
+++ b/src/Tuitio.Domain/Entities/UserGroupXUserRole.cs
@@ -0,0 +1,11 @@
+// Copyright (c) 2020 Tudor Stanciu
+
+namespace Tuitio.Domain.Entities
+{
+ public class UserGroupXUserRole
+ {
+ public int UserGroupId { get; set; }
+ public int UserRoleId { get; set; }
+ public UserRole UserRole { get; set; }
+ }
+}
diff --git a/src/Tuitio.Domain/Entities/UserRole.cs b/src/Tuitio.Domain/Entities/UserRole.cs
new file mode 100644
index 0000000..e81894f
--- /dev/null
+++ b/src/Tuitio.Domain/Entities/UserRole.cs
@@ -0,0 +1,11 @@
+// Copyright (c) 2020 Tudor Stanciu
+
+namespace Tuitio.Domain.Entities
+{
+ public class UserRole
+ {
+ public int UserRoleId { get; set; }
+ public string UserRoleCode { get; set; }
+ public string UserRoleName { get; set; }
+ }
+}
diff --git a/src/Tuitio.Domain/Entities/UserXUserGroup.cs b/src/Tuitio.Domain/Entities/UserXUserGroup.cs
new file mode 100644
index 0000000..e17a7ee
--- /dev/null
+++ b/src/Tuitio.Domain/Entities/UserXUserGroup.cs
@@ -0,0 +1,11 @@
+// Copyright (c) 2020 Tudor Stanciu
+
+namespace Tuitio.Domain.Entities
+{
+ public class UserXUserGroup
+ {
+ public int UserId { get; set; }
+ public int UserGroupId { get; set; }
+ public UserGroup UserGroup { get; set; }
+ }
+}
diff --git a/src/Tuitio.Domain/Entities/UserXUserRole.cs b/src/Tuitio.Domain/Entities/UserXUserRole.cs
new file mode 100644
index 0000000..a5274c6
--- /dev/null
+++ b/src/Tuitio.Domain/Entities/UserXUserRole.cs
@@ -0,0 +1,11 @@
+// Copyright (c) 2020 Tudor Stanciu
+
+namespace Tuitio.Domain.Entities
+{
+ public class UserXUserRole
+ {
+ public int UserId { get; set; }
+ public int UserRoleId { get; set; }
+ public UserRole UserRole { get; set; }
+ }
+}
\ No newline at end of file