From 6d7b5e846bc63e1bf6f2cbcac8615f0e8ed5975a Mon Sep 17 00:00:00 2001 From: ponselvamsakthivel-bc <87376328+ponselvamsakthivel-bc@users.noreply.github.com> Date: Mon, 27 Mar 2023 22:37:02 +0530 Subject: [PATCH 01/24] =?UTF-8?q?Revert=20"4890-Buyer-Both-roles-are-not-a?= =?UTF-8?q?ssigned-to-Org-admin-one-time-job-runs=E2=80=A6"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Jobs/OrganisationAutovalidationJob.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/api/CcsSso.Core.JobScheduler/Jobs/OrganisationAutovalidationJob.cs b/api/CcsSso.Core.JobScheduler/Jobs/OrganisationAutovalidationJob.cs index 88036e58..623ac1e6 100644 --- a/api/CcsSso.Core.JobScheduler/Jobs/OrganisationAutovalidationJob.cs +++ b/api/CcsSso.Core.JobScheduler/Jobs/OrganisationAutovalidationJob.cs @@ -152,8 +152,7 @@ public async Task> GetOrganisationsAsync() public async Task> GetOrganisationsForAutoValidationAsync() { var organisations = await _dataContext.Organisation.Where( - org => org.RightToBuy == false - && !org.IsDeleted && org.CreatedOnUtc >= TimeZoneInfo.ConvertTimeToUtc(startDate) && org.CreatedOnUtc <= TimeZoneInfo.ConvertTimeToUtc(endDate) + org => !org.IsDeleted && org.CreatedOnUtc >= TimeZoneInfo.ConvertTimeToUtc(startDate) && org.CreatedOnUtc <= TimeZoneInfo.ConvertTimeToUtc(endDate) && !_dataContext.OrganisationAudit.Any(orgAudit => orgAudit.OrganisationId == org.Id)) .Select(o => new OrganisationDetail() { From 8d4fba468a961038848a80fbacc068d1fabda892 Mon Sep 17 00:00:00 2001 From: brijrajsinh-bc <109584978+brijrajsinh-bc@users.noreply.github.com> Date: Tue, 4 Apr 2023 16:58:24 +0530 Subject: [PATCH 02/24] Feature/ppg 212 dev non associated email domain check via managed groups (#1599) * PPG-212 - Database Changes (#1582) * Change to create new request or auto approve it (#1589) * PPG-212 - Database Changes * Role Approval (Send fleet role request for approval) * Group with role approval for manage users (#1594) * Task 5020: PPG-212-Dev-Non-Associated Email Domain Check via Managed Groups (#1595) * PPG-212 - Database Changes * Role Approval (Send fleet role request for approval) * PPG 212 - Request role approval change * PPG 212 - Fix merge issue * PPG 212 - User role approval changes * PPG 212 - codeclimate fix (#1596) * PPG-212 - Database Changes * Role Approval (Send fleet role request for approval) * PPG 212 - Request role approval change * PPG 212 - Fix merge issue * PPG 212 - User role approval changes * PPG 212 - Code climate fix --------- Co-authored-by: brijeshpatel-bc <108462846+brijeshpatel-bc@users.noreply.github.com> --- ...ending_OrganisationUserGroupId.Designer.cs | 2834 +++++++++++++++++ ...cessRolePending_OrganisationUserGroupId.cs | 44 + .../Migrations/DataContextModelSnapshot.cs | 11 + ...essRolePending_OrganisationUserGroupId.sql | 13 + .../Entity/UserAccessRolePending.cs | 4 + api/CcsSso.Core.Domain/Constants/Constants.cs | 2 + .../IUserProfileRoleApprovalService.cs | 2 +- .../Dtos/External/UserProfileResponseInfo.cs | 2 + .../UserRolesApprovalController.cs | 9 +- .../External/ServiceRoleGroupMapperService.cs | 5 +- .../UserProfileRoleApprovalService.cs | 284 +- .../External/UserProfileService.cs | 138 +- api/CcsSso.Core.Service/UserService.cs | 195 +- api/CcsSso.Core.Tests/UserServiceTests.cs | 3 +- 14 files changed, 3359 insertions(+), 187 deletions(-) create mode 100644 api/CcsSso.Core.DbMigrations/Migrations/20230327094219_Add_UserAccessRolePending_OrganisationUserGroupId.Designer.cs create mode 100644 api/CcsSso.Core.DbMigrations/Migrations/20230327094219_Add_UserAccessRolePending_OrganisationUserGroupId.cs create mode 100644 api/CcsSso.Core.DbMigrations/Scripts/Phase3_Sprint8/1_Add_UserAccessRolePending_OrganisationUserGroupId.sql diff --git a/api/CcsSso.Core.DbMigrations/Migrations/20230327094219_Add_UserAccessRolePending_OrganisationUserGroupId.Designer.cs b/api/CcsSso.Core.DbMigrations/Migrations/20230327094219_Add_UserAccessRolePending_OrganisationUserGroupId.Designer.cs new file mode 100644 index 00000000..2247e693 --- /dev/null +++ b/api/CcsSso.Core.DbMigrations/Migrations/20230327094219_Add_UserAccessRolePending_OrganisationUserGroupId.Designer.cs @@ -0,0 +1,2834 @@ +// +using System; +using CcsSso.DbPersistence; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +namespace CcsSso.Core.DbMigrations.Migrations +{ + [DbContext(typeof(DataContext))] + [Migration("20230327094219_Add_UserAccessRolePending_OrganisationUserGroupId")] + partial class Add_UserAccessRolePending_OrganisationUserGroupId + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("Relational:MaxIdentifierLength", 63) + .HasAnnotation("ProductVersion", "5.0.10") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("Application") + .HasColumnType("text"); + + b.Property("Device") + .HasColumnType("text"); + + b.Property("Event") + .HasColumnType("text"); + + b.Property("EventTimeUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("IpAddress") + .HasColumnType("text"); + + b.Property("ReferenceData") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("AuditLog"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.AutoValidationRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("AssignToAdmin") + .HasColumnType("boolean"); + + b.Property("AssignToOrg") + .HasColumnType("boolean"); + + b.Property("CcsAccessRoleId") + .HasColumnType("integer"); + + b.Property("IsBothFailed") + .HasColumnType("boolean"); + + b.Property("IsBothSuccess") + .HasColumnType("boolean"); + + b.Property("IsBuyerFailed") + .HasColumnType("boolean"); + + b.Property("IsBuyerSuccess") + .HasColumnType("boolean"); + + b.Property("IsSupplier") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasIndex("CcsAccessRoleId"); + + b.ToTable("AutoValidationRole"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.BulkUploadDetail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("BulkUploadStatus") + .HasColumnType("integer"); + + b.Property("ConcurrencyKey") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea"); + + b.Property("CreatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedUserId") + .HasColumnType("integer"); + + b.Property("DocUploadId") + .HasColumnType("text"); + + b.Property("FailedUserCount") + .HasColumnType("integer"); + + b.Property("FileKey") + .HasColumnType("text"); + + b.Property("FileKeyId") + .HasColumnType("text"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastUpdatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("LastUpdatedUserId") + .HasColumnType("integer"); + + b.Property("MigrationEndedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("MigrationStartedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("MigrationStringContent") + .HasColumnType("text"); + + b.Property("OrganisationId") + .HasColumnType("text"); + + b.Property("ProcessedUserCount") + .HasColumnType("integer"); + + b.Property("TotalOrganisationCount") + .HasColumnType("integer"); + + b.Property("TotalUserCount") + .HasColumnType("integer"); + + b.Property("ValidationErrorDetails") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("FileKeyId") + .IsUnique(); + + b.ToTable("BulkUploadDetail"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.CcsService", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ActivateOrganisations") + .HasColumnType("boolean"); + + b.Property("ConcurrencyKey") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea"); + + b.Property("CreatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedUserId") + .HasColumnType("integer"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("GlobalLevelOrganisationAccess") + .HasColumnType("boolean"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastUpdatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("LastUpdatedUserId") + .HasColumnType("integer"); + + b.Property("ServiceClientId") + .HasColumnType("text"); + + b.Property("ServiceCode") + .HasColumnType("text"); + + b.Property("ServiceName") + .HasColumnType("text"); + + b.Property("ServiceUrl") + .HasColumnType("text"); + + b.Property("TimeOutLength") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("CcsService"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.CcsServiceLogin", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("CcsServiceId") + .HasColumnType("integer"); + + b.Property("ConcurrencyKey") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea"); + + b.Property("CreatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedUserId") + .HasColumnType("integer"); + + b.Property("IdamUserLoginId") + .HasColumnType("integer"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastUpdatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("LastUpdatedUserId") + .HasColumnType("integer"); + + b.Property("TimedOut") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasIndex("CcsServiceId"); + + b.HasIndex("IdamUserLoginId"); + + b.ToTable("CcsServiceLogin"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.CcsServiceRoleGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ApprovalRequired") + .HasColumnType("integer"); + + b.Property("ConcurrencyKey") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea"); + + b.Property("CreatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedUserId") + .HasColumnType("integer"); + + b.Property("DefaultEligibility") + .HasColumnType("text"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("DisplayOrder") + .HasColumnType("integer"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("LastUpdatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("LastUpdatedUserId") + .HasColumnType("integer"); + + b.Property("MfaEnabled") + .HasColumnType("boolean"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("OrgTypeEligibility") + .HasColumnType("integer"); + + b.Property("SubscriptionTypeEligibility") + .HasColumnType("integer"); + + b.Property("TradeEligibility") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("CcsServiceRoleGroup"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.CcsServiceRoleMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("CcsAccessRoleId") + .HasColumnType("integer"); + + b.Property("CcsServiceRoleGroupId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("CcsAccessRoleId"); + + b.HasIndex("CcsServiceRoleGroupId"); + + b.ToTable("CcsServiceRoleMapping"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.CountryDetails", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("Code") + .HasColumnType("text"); + + b.Property("ConcurrencyKey") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea"); + + b.Property("CreatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedUserId") + .HasColumnType("integer"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastUpdatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("LastUpdatedUserId") + .HasColumnType("integer"); + + b.Property("Name") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("CountryDetails"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.ExternalServiceRoleMapping", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("CcsServiceId") + .HasColumnType("integer"); + + b.Property("ConcurrencyKey") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea"); + + b.Property("CreatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedUserId") + .HasColumnType("integer"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastUpdatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("LastUpdatedUserId") + .HasColumnType("integer"); + + b.Property("OrganisationEligibleRoleId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("CcsServiceId"); + + b.HasIndex("OrganisationEligibleRoleId"); + + b.ToTable("ExternalServiceRoleMapping"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.IdamUserLogin", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("CcsLoginDateTime") + .HasColumnType("timestamp without time zone"); + + b.Property("CcsLogoutDateTime") + .HasColumnType("timestamp without time zone"); + + b.Property("ClientDevice") + .HasColumnType("text"); + + b.Property("ConcurrencyKey") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea"); + + b.Property("CreatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedUserId") + .HasColumnType("integer"); + + b.Property("DeviceType") + .HasColumnType("integer"); + + b.Property("IdentityProviderId") + .HasColumnType("integer"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastUpdatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("LastUpdatedUserId") + .HasColumnType("integer"); + + b.Property("Location") + .HasColumnType("text"); + + b.Property("LoginSuccessful") + .HasColumnType("boolean"); + + b.Property("UserId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("IdentityProviderId"); + + b.HasIndex("UserId"); + + b.ToTable("IdamUserLogin"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.IdamUserLoginRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("CcsAccessRoleId") + .HasColumnType("integer"); + + b.Property("ConcurrencyKey") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea"); + + b.Property("CreatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedUserId") + .HasColumnType("integer"); + + b.Property("IdamUserLoginId") + .HasColumnType("integer"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastUpdatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("LastUpdatedUserId") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("CcsAccessRoleId"); + + b.HasIndex("IdamUserLoginId"); + + b.HasIndex("UserId"); + + b.ToTable("IdamUserLoginRole"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.OrganisationAudit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("Actioned") + .HasColumnType("text"); + + b.Property("ActionedBy") + .HasColumnType("text"); + + b.Property("ActionedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("OrganisationId") + .HasColumnType("integer"); + + b.Property("SchemeIdentifier") + .HasColumnType("text"); + + b.Property("Status") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("OrganisationId"); + + b.ToTable("OrganisationAudit"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.OrganisationAuditEvent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("Actioned") + .HasColumnType("text"); + + b.Property("ActionedBy") + .HasColumnType("text"); + + b.Property("Date") + .HasColumnType("timestamp without time zone"); + + b.Property("Event") + .HasColumnType("text"); + + b.Property("FirstName") + .HasColumnType("text"); + + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("LastName") + .HasColumnType("text"); + + b.Property("OrganisationId") + .HasColumnType("integer"); + + b.Property("Roles") + .HasColumnType("text"); + + b.Property("SchemeIdentifier") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("OrganisationId"); + + b.ToTable("OrganisationAuditEvent"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.OrganisationEligibleIdentityProvider", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ConcurrencyKey") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea"); + + b.Property("CreatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedUserId") + .HasColumnType("integer"); + + b.Property("IdentityProviderId") + .HasColumnType("integer"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastUpdatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("LastUpdatedUserId") + .HasColumnType("integer"); + + b.Property("OrganisationId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("IdentityProviderId"); + + b.HasIndex("OrganisationId"); + + b.ToTable("OrganisationEligibleIdentityProvider"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.OrganisationEligibleRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("CcsAccessRoleId") + .HasColumnType("integer"); + + b.Property("ConcurrencyKey") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea"); + + b.Property("CreatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedUserId") + .HasColumnType("integer"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastUpdatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("LastUpdatedUserId") + .HasColumnType("integer"); + + b.Property("MfaEnabled") + .HasColumnType("boolean"); + + b.Property("OrganisationId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("CcsAccessRoleId"); + + b.HasIndex("OrganisationId"); + + b.ToTable("OrganisationEligibleRole"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.OrganisationEligibleRolePending", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("CcsAccessRoleId") + .HasColumnType("integer"); + + b.Property("ConcurrencyKey") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea"); + + b.Property("CreatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedUserId") + .HasColumnType("integer"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastUpdatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("LastUpdatedUserId") + .HasColumnType("integer"); + + b.Property("OrganisationId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("CcsAccessRoleId"); + + b.HasIndex("OrganisationId"); + + b.ToTable("OrganisationEligibleRolePending"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.OrganisationGroupEligibleRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ConcurrencyKey") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea"); + + b.Property("CreatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedUserId") + .HasColumnType("integer"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastUpdatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("LastUpdatedUserId") + .HasColumnType("integer"); + + b.Property("OrganisationEligibleRoleId") + .HasColumnType("integer"); + + b.Property("OrganisationUserGroupId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("OrganisationEligibleRoleId"); + + b.HasIndex("OrganisationUserGroupId"); + + b.ToTable("OrganisationGroupEligibleRole"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.ServicePermission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("CcsServiceId") + .HasColumnType("integer"); + + b.Property("ConcurrencyKey") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea"); + + b.Property("CreatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedUserId") + .HasColumnType("integer"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastUpdatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("LastUpdatedUserId") + .HasColumnType("integer"); + + b.Property("ServicePermissionName") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CcsServiceId"); + + b.ToTable("ServicePermission"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.ServiceRolePermission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("CcsAccessRoleId") + .HasColumnType("integer"); + + b.Property("ConcurrencyKey") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea"); + + b.Property("CreatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedUserId") + .HasColumnType("integer"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastUpdatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("LastUpdatedUserId") + .HasColumnType("integer"); + + b.Property("ServicePermissionId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("CcsAccessRoleId"); + + b.HasIndex("ServicePermissionId"); + + b.ToTable("ServiceRolePermission"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.SiteContact", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("AssignedContactType") + .HasColumnType("integer"); + + b.Property("ConcurrencyKey") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea"); + + b.Property("ContactId") + .HasColumnType("integer"); + + b.Property("ContactPointId") + .HasColumnType("integer"); + + b.Property("CreatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedUserId") + .HasColumnType("integer"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastUpdatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("LastUpdatedUserId") + .HasColumnType("integer"); + + b.Property("OriginalContactId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ContactPointId"); + + b.ToTable("SiteContact"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.UserIdentityProvider", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ConcurrencyKey") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea"); + + b.Property("CreatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedUserId") + .HasColumnType("integer"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastUpdatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("LastUpdatedUserId") + .HasColumnType("integer"); + + b.Property("OrganisationEligibleIdentityProviderId") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("OrganisationEligibleIdentityProviderId"); + + b.HasIndex("UserId"); + + b.ToTable("UserIdentityProvider"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.CcsAccessRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ApprovalRequired") + .HasColumnType("integer"); + + b.Property("CcsAccessRoleDescription") + .HasColumnType("text"); + + b.Property("CcsAccessRoleName") + .HasColumnType("text"); + + b.Property("CcsAccessRoleNameKey") + .HasColumnType("text"); + + b.Property("ConcurrencyKey") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea"); + + b.Property("CreatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedUserId") + .HasColumnType("integer"); + + b.Property("DefaultEligibility") + .HasColumnType("text"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastUpdatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("LastUpdatedUserId") + .HasColumnType("integer"); + + b.Property("MfaEnabled") + .HasColumnType("boolean"); + + b.Property("OrgTypeEligibility") + .HasColumnType("integer"); + + b.Property("SubscriptionTypeEligibility") + .HasColumnType("integer"); + + b.Property("TradeEligibility") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("CcsAccessRole"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.ContactDetail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ConcurrencyKey") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea"); + + b.Property("CreatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedUserId") + .HasColumnType("integer"); + + b.Property("EffectiveFrom") + .HasColumnType("timestamp without time zone"); + + b.Property("EffectiveTo") + .HasColumnType("timestamp without time zone"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastUpdatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("LastUpdatedUserId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("ContactDetail"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.ContactPoint", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("AssignedContactType") + .HasColumnType("integer"); + + b.Property("ConcurrencyKey") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea"); + + b.Property("ContactDetailId") + .HasColumnType("integer"); + + b.Property("ContactPointReasonId") + .HasColumnType("integer"); + + b.Property("CreatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedUserId") + .HasColumnType("integer"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("IsSite") + .HasColumnType("boolean"); + + b.Property("LastUpdatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("LastUpdatedUserId") + .HasColumnType("integer"); + + b.Property("OriginalContactPointId") + .HasColumnType("integer"); + + b.Property("PartyId") + .HasColumnType("integer"); + + b.Property("PartyTypeId") + .HasColumnType("integer"); + + b.Property("SiteName") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ContactDetailId"); + + b.HasIndex("ContactPointReasonId"); + + b.HasIndex("PartyId"); + + b.HasIndex("PartyTypeId"); + + b.ToTable("ContactPoint"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.ContactPointReason", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ConcurrencyKey") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea"); + + b.Property("CreatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedUserId") + .HasColumnType("integer"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastUpdatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("LastUpdatedUserId") + .HasColumnType("integer"); + + b.Property("Name") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("ContactPointReason"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.EnterpriseType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ConcurrencyKey") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea"); + + b.Property("CreatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedUserId") + .HasColumnType("integer"); + + b.Property("EnterpriseTypeName") + .HasColumnType("text"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastUpdatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("LastUpdatedUserId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("EnterpriseType"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.IdentityProvider", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ConcurrencyKey") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea"); + + b.Property("CreatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedUserId") + .HasColumnType("integer"); + + b.Property("DisplayOrder") + .HasColumnType("integer"); + + b.Property("ExternalIdpFlag") + .HasColumnType("boolean"); + + b.Property("IdpConnectionName") + .HasColumnType("text"); + + b.Property("IdpName") + .HasColumnType("text"); + + b.Property("IdpUri") + .HasColumnType("text"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastUpdatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("LastUpdatedUserId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("IdentityProvider"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.Organisation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("BusinessType") + .HasColumnType("text"); + + b.Property("CcsServiceId") + .HasColumnType("integer"); + + b.Property("CiiOrganisationId") + .HasColumnType("text"); + + b.Property("ConcurrencyKey") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea"); + + b.Property("CreatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedUserId") + .HasColumnType("integer"); + + b.Property("DomainName") + .HasColumnType("text"); + + b.Property("IsActivated") + .HasColumnType("boolean"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("IsSme") + .HasColumnType("boolean"); + + b.Property("IsVcse") + .HasColumnType("boolean"); + + b.Property("LastUpdatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("LastUpdatedUserId") + .HasColumnType("integer"); + + b.Property("LegalName") + .HasColumnType("text"); + + b.Property("OrganisationUri") + .HasColumnType("text"); + + b.Property("PartyId") + .HasColumnType("integer"); + + b.Property("RightToBuy") + .HasColumnType("boolean"); + + b.Property("SupplierBuyerType") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("CcsServiceId"); + + b.HasIndex("CiiOrganisationId"); + + b.HasIndex("PartyId") + .IsUnique(); + + b.ToTable("Organisation"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.OrganisationAccessRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ConcurrencyKey") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea"); + + b.Property("CreatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedUserId") + .HasColumnType("integer"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastUpdatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("LastUpdatedUserId") + .HasColumnType("integer"); + + b.Property("OrganisationAccessRoleDescription") + .HasColumnType("text"); + + b.Property("OrganisationAccessRoleName") + .HasColumnType("text"); + + b.Property("OrganisationId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("OrganisationId"); + + b.ToTable("OrganisationAccessRole"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.OrganisationEnterpriseType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ConcurrencyKey") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea"); + + b.Property("CreatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedUserId") + .HasColumnType("integer"); + + b.Property("EnterpriseTypeId") + .HasColumnType("integer"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastUpdatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("LastUpdatedUserId") + .HasColumnType("integer"); + + b.Property("OrganisationId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("EnterpriseTypeId"); + + b.HasIndex("OrganisationId"); + + b.ToTable("OrganisationEnterpriseType"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.OrganisationUserGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ConcurrencyKey") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea"); + + b.Property("CreatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedUserId") + .HasColumnType("integer"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastUpdatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("LastUpdatedUserId") + .HasColumnType("integer"); + + b.Property("MfaEnabled") + .HasColumnType("boolean"); + + b.Property("OrganisationId") + .HasColumnType("integer"); + + b.Property("UserGroupName") + .HasColumnType("text"); + + b.Property("UserGroupNameKey") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("OrganisationId"); + + b.ToTable("OrganisationUserGroup"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.Party", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ConcurrencyKey") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea"); + + b.Property("CreatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedUserId") + .HasColumnType("integer"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastUpdatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("LastUpdatedUserId") + .HasColumnType("integer"); + + b.Property("PartyTypeId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("PartyTypeId"); + + b.ToTable("Party"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.PartyType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ConcurrencyKey") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea"); + + b.Property("CreatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedUserId") + .HasColumnType("integer"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastUpdatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("LastUpdatedUserId") + .HasColumnType("integer"); + + b.Property("PartyTypeName") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("PartyType"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.Person", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ConcurrencyKey") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea"); + + b.Property("CreatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedUserId") + .HasColumnType("integer"); + + b.Property("FirstName") + .HasColumnType("text"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastName") + .HasColumnType("text"); + + b.Property("LastUpdatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("LastUpdatedUserId") + .HasColumnType("integer"); + + b.Property("OrganisationId") + .HasColumnType("integer"); + + b.Property("PartyId") + .HasColumnType("integer"); + + b.Property("Title") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("OrganisationId"); + + b.HasIndex("PartyId") + .IsUnique(); + + b.ToTable("Person"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.PhysicalAddress", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ConcurrencyKey") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea"); + + b.Property("ContactDetailId") + .HasColumnType("integer"); + + b.Property("CountryCode") + .HasColumnType("text"); + + b.Property("CreatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedUserId") + .HasColumnType("integer"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastUpdatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("LastUpdatedUserId") + .HasColumnType("integer"); + + b.Property("Locality") + .HasColumnType("text"); + + b.Property("PostalCode") + .HasColumnType("text"); + + b.Property("Region") + .HasColumnType("text"); + + b.Property("StreetAddress") + .HasColumnType("text"); + + b.Property("Uprn") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ContactDetailId") + .IsUnique(); + + b.ToTable("PhysicalAddress"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.ProcurementGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ConcurrencyKey") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea"); + + b.Property("CreatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedUserId") + .HasColumnType("integer"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastUpdatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("LastUpdatedUserId") + .HasColumnType("integer"); + + b.Property("OrganisationId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("OrganisationId"); + + b.ToTable("ProcurementGroup"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.RoleApprovalConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("CcsAccessRoleId") + .HasColumnType("integer"); + + b.Property("ConcurrencyKey") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea"); + + b.Property("CreatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedUserId") + .HasColumnType("integer"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastUpdatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("LastUpdatedUserId") + .HasColumnType("integer"); + + b.Property("LinkExpiryDurationInMinute") + .HasColumnType("integer"); + + b.Property("NotificationEmails") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CcsAccessRoleId"); + + b.ToTable("RoleApprovalConfiguration"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.TradingOrganisation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ConcurrencyKey") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea"); + + b.Property("CreatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedUserId") + .HasColumnType("integer"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastUpdatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("LastUpdatedUserId") + .HasColumnType("integer"); + + b.Property("OrganisationId") + .HasColumnType("integer"); + + b.Property("TradingName") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("OrganisationId"); + + b.ToTable("TradingOrganisation"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("AccountVerified") + .HasColumnType("boolean"); + + b.Property("CcsServiceId") + .HasColumnType("integer"); + + b.Property("ConcurrencyKey") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea"); + + b.Property("CreatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedUserId") + .HasColumnType("integer"); + + b.Property("DelegationAccepted") + .HasColumnType("boolean"); + + b.Property("DelegationEndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("DelegationStartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("JobTitle") + .HasColumnType("text"); + + b.Property("LastUpdatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("LastUpdatedUserId") + .HasColumnType("integer"); + + b.Property("MfaEnabled") + .HasColumnType("boolean"); + + b.Property("OriginOrganizationId") + .HasColumnType("integer"); + + b.Property("PartyId") + .HasColumnType("integer"); + + b.Property("UserName") + .HasColumnType("text"); + + b.Property("UserTitle") + .HasColumnType("integer"); + + b.Property("UserType") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("CcsServiceId"); + + b.HasIndex("OriginOrganizationId"); + + b.HasIndex("PartyId") + .IsUnique(); + + b.HasIndex("UserName"); + + b.ToTable("User"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.UserAccessRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ConcurrencyKey") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea"); + + b.Property("CreatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedUserId") + .HasColumnType("integer"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastUpdatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("LastUpdatedUserId") + .HasColumnType("integer"); + + b.Property("OrganisationEligibleRoleId") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("OrganisationEligibleRoleId"); + + b.HasIndex("UserId"); + + b.ToTable("UserAccessRole"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.UserAccessRolePending", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ConcurrencyKey") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea"); + + b.Property("CreatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedUserId") + .HasColumnType("integer"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastUpdatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("LastUpdatedUserId") + .HasColumnType("integer"); + + b.Property("OrganisationEligibleRoleId") + .HasColumnType("integer"); + + b.Property("OrganisationUserGroupId") + .HasColumnType("integer"); + + b.Property("SendEmailNotification") + .HasColumnType("boolean"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("OrganisationEligibleRoleId"); + + b.HasIndex("OrganisationUserGroupId"); + + b.HasIndex("UserId"); + + b.ToTable("UserAccessRolePending"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.UserGroupMembership", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ConcurrencyKey") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea"); + + b.Property("CreatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedUserId") + .HasColumnType("integer"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastUpdatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("LastUpdatedUserId") + .HasColumnType("integer"); + + b.Property("MembershipEndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("MembershipStartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("OrganisationUserGroupId") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("OrganisationUserGroupId"); + + b.HasIndex("UserId"); + + b.ToTable("UserGroupMembership"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.UserSetting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ConcurrencyKey") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea"); + + b.Property("CreatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedUserId") + .HasColumnType("integer"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastUpdatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("LastUpdatedUserId") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnType("integer"); + + b.Property("UserSettingTypeId") + .HasColumnType("integer"); + + b.Property("UserSettingValue") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("UserSettingTypeId"); + + b.ToTable("UserSetting"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.UserSettingType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ConcurrencyKey") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea"); + + b.Property("CreatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedUserId") + .HasColumnType("integer"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastUpdatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("LastUpdatedUserId") + .HasColumnType("integer"); + + b.Property("UserSettingName") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("UserSettingType"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.VirtualAddress", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ConcurrencyKey") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea"); + + b.Property("ContactDetailId") + .HasColumnType("integer"); + + b.Property("CreatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedUserId") + .HasColumnType("integer"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastUpdatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("LastUpdatedUserId") + .HasColumnType("integer"); + + b.Property("VirtualAddressTypeId") + .HasColumnType("integer"); + + b.Property("VirtualAddressValue") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ContactDetailId"); + + b.HasIndex("VirtualAddressTypeId"); + + b.ToTable("VirtualAddress"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.VirtualAddressType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ConcurrencyKey") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("bytea"); + + b.Property("CreatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatedUserId") + .HasColumnType("integer"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastUpdatedOnUtc") + .HasColumnType("timestamp without time zone"); + + b.Property("LastUpdatedUserId") + .HasColumnType("integer"); + + b.Property("Name") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("VirtualAddressType"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.AutoValidationRole", b => + { + b.HasOne("CcsSso.DbModel.Entity.CcsAccessRole", "CcsAccessRole") + .WithMany() + .HasForeignKey("CcsAccessRoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CcsAccessRole"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.CcsServiceLogin", b => + { + b.HasOne("CcsSso.Core.DbModel.Entity.CcsService", "CcsService") + .WithMany("CcsServiceLogins") + .HasForeignKey("CcsServiceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CcsSso.Core.DbModel.Entity.IdamUserLogin", "IdamUserLogin") + .WithMany("CcsServiceLogins") + .HasForeignKey("IdamUserLoginId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CcsService"); + + b.Navigation("IdamUserLogin"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.CcsServiceRoleMapping", b => + { + b.HasOne("CcsSso.DbModel.Entity.CcsAccessRole", "CcsAccessRole") + .WithMany() + .HasForeignKey("CcsAccessRoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CcsSso.Core.DbModel.Entity.CcsServiceRoleGroup", "CcsServiceRoleGroup") + .WithMany("CcsServiceRoleMappings") + .HasForeignKey("CcsServiceRoleGroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CcsAccessRole"); + + b.Navigation("CcsServiceRoleGroup"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.ExternalServiceRoleMapping", b => + { + b.HasOne("CcsSso.Core.DbModel.Entity.CcsService", "CcsService") + .WithMany("ExternalServiceRoleMappings") + .HasForeignKey("CcsServiceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CcsSso.Core.DbModel.Entity.OrganisationEligibleRole", "OrganisationEligibleRole") + .WithMany("ExternalServiceRoleMappings") + .HasForeignKey("OrganisationEligibleRoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CcsService"); + + b.Navigation("OrganisationEligibleRole"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.IdamUserLogin", b => + { + b.HasOne("CcsSso.DbModel.Entity.IdentityProvider", "IdentityProvider") + .WithMany("IdamUserLogins") + .HasForeignKey("IdentityProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CcsSso.DbModel.Entity.User", "User") + .WithMany("IdamUserLogins") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityProvider"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.IdamUserLoginRole", b => + { + b.HasOne("CcsSso.DbModel.Entity.CcsAccessRole", "CcsAccessRole") + .WithMany("IdamUserLoginRoles") + .HasForeignKey("CcsAccessRoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CcsSso.Core.DbModel.Entity.IdamUserLogin", null) + .WithMany("IdamUserLoginRoles") + .HasForeignKey("IdamUserLoginId"); + + b.HasOne("CcsSso.DbModel.Entity.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CcsAccessRole"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.OrganisationAudit", b => + { + b.HasOne("CcsSso.DbModel.Entity.Organisation", "Organisation") + .WithMany("OrganisationAudits") + .HasForeignKey("OrganisationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organisation"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.OrganisationAuditEvent", b => + { + b.HasOne("CcsSso.DbModel.Entity.Organisation", "Organisation") + .WithMany() + .HasForeignKey("OrganisationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organisation"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.OrganisationEligibleIdentityProvider", b => + { + b.HasOne("CcsSso.DbModel.Entity.IdentityProvider", "IdentityProvider") + .WithMany() + .HasForeignKey("IdentityProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CcsSso.DbModel.Entity.Organisation", "Organisation") + .WithMany("OrganisationEligibleIdentityProviders") + .HasForeignKey("OrganisationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityProvider"); + + b.Navigation("Organisation"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.OrganisationEligibleRole", b => + { + b.HasOne("CcsSso.DbModel.Entity.CcsAccessRole", "CcsAccessRole") + .WithMany("OrganisationEligibleRoles") + .HasForeignKey("CcsAccessRoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CcsSso.DbModel.Entity.Organisation", "Organisation") + .WithMany("OrganisationEligibleRoles") + .HasForeignKey("OrganisationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CcsAccessRole"); + + b.Navigation("Organisation"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.OrganisationEligibleRolePending", b => + { + b.HasOne("CcsSso.DbModel.Entity.CcsAccessRole", "CcsAccessRole") + .WithMany() + .HasForeignKey("CcsAccessRoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CcsSso.DbModel.Entity.Organisation", "Organisation") + .WithMany() + .HasForeignKey("OrganisationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CcsAccessRole"); + + b.Navigation("Organisation"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.OrganisationGroupEligibleRole", b => + { + b.HasOne("CcsSso.Core.DbModel.Entity.OrganisationEligibleRole", "OrganisationEligibleRole") + .WithMany() + .HasForeignKey("OrganisationEligibleRoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CcsSso.DbModel.Entity.OrganisationUserGroup", "OrganisationUserGroup") + .WithMany("GroupEligibleRoles") + .HasForeignKey("OrganisationUserGroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("OrganisationEligibleRole"); + + b.Navigation("OrganisationUserGroup"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.ServicePermission", b => + { + b.HasOne("CcsSso.Core.DbModel.Entity.CcsService", "CcsService") + .WithMany("ServicePermissions") + .HasForeignKey("CcsServiceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CcsService"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.ServiceRolePermission", b => + { + b.HasOne("CcsSso.DbModel.Entity.CcsAccessRole", "CcsAccessRole") + .WithMany("ServiceRolePermissions") + .HasForeignKey("CcsAccessRoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CcsSso.Core.DbModel.Entity.ServicePermission", "ServicePermission") + .WithMany("ServiceRolePermissions") + .HasForeignKey("ServicePermissionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CcsAccessRole"); + + b.Navigation("ServicePermission"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.SiteContact", b => + { + b.HasOne("CcsSso.DbModel.Entity.ContactPoint", "ContactPoint") + .WithMany("SiteContacts") + .HasForeignKey("ContactPointId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ContactPoint"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.UserIdentityProvider", b => + { + b.HasOne("CcsSso.Core.DbModel.Entity.OrganisationEligibleIdentityProvider", "OrganisationEligibleIdentityProvider") + .WithMany("UserIdentityProviders") + .HasForeignKey("OrganisationEligibleIdentityProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CcsSso.DbModel.Entity.User", "User") + .WithMany("UserIdentityProviders") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("OrganisationEligibleIdentityProvider"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.ContactPoint", b => + { + b.HasOne("CcsSso.DbModel.Entity.ContactDetail", "ContactDetail") + .WithMany("ContactPoints") + .HasForeignKey("ContactDetailId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CcsSso.DbModel.Entity.ContactPointReason", "ContactPointReason") + .WithMany("ContactPoints") + .HasForeignKey("ContactPointReasonId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CcsSso.DbModel.Entity.Party", "Party") + .WithMany("ContactPoints") + .HasForeignKey("PartyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CcsSso.DbModel.Entity.PartyType", "PartyType") + .WithMany() + .HasForeignKey("PartyTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ContactDetail"); + + b.Navigation("ContactPointReason"); + + b.Navigation("Party"); + + b.Navigation("PartyType"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.Organisation", b => + { + b.HasOne("CcsSso.Core.DbModel.Entity.CcsService", "CcsService") + .WithMany("CreatedOrganisations") + .HasForeignKey("CcsServiceId"); + + b.HasOne("CcsSso.DbModel.Entity.Party", "Party") + .WithOne("Organisation") + .HasForeignKey("CcsSso.DbModel.Entity.Organisation", "PartyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CcsService"); + + b.Navigation("Party"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.OrganisationAccessRole", b => + { + b.HasOne("CcsSso.DbModel.Entity.Organisation", "Organisation") + .WithMany("OrganisationAccessRoles") + .HasForeignKey("OrganisationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organisation"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.OrganisationEnterpriseType", b => + { + b.HasOne("CcsSso.DbModel.Entity.EnterpriseType", "EnterpriseType") + .WithMany("OrganisationEnterpriseTypes") + .HasForeignKey("EnterpriseTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CcsSso.DbModel.Entity.Organisation", "Organisation") + .WithMany("OrganisationEnterpriseTypes") + .HasForeignKey("OrganisationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("EnterpriseType"); + + b.Navigation("Organisation"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.OrganisationUserGroup", b => + { + b.HasOne("CcsSso.DbModel.Entity.Organisation", "Organisation") + .WithMany("UserGroups") + .HasForeignKey("OrganisationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organisation"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.Party", b => + { + b.HasOne("CcsSso.DbModel.Entity.PartyType", "PartyType") + .WithMany("Party") + .HasForeignKey("PartyTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PartyType"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.Person", b => + { + b.HasOne("CcsSso.DbModel.Entity.Organisation", "Organisation") + .WithMany("People") + .HasForeignKey("OrganisationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CcsSso.DbModel.Entity.Party", "Party") + .WithOne("Person") + .HasForeignKey("CcsSso.DbModel.Entity.Person", "PartyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organisation"); + + b.Navigation("Party"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.PhysicalAddress", b => + { + b.HasOne("CcsSso.DbModel.Entity.ContactDetail", "ContactDetail") + .WithOne("PhysicalAddress") + .HasForeignKey("CcsSso.DbModel.Entity.PhysicalAddress", "ContactDetailId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ContactDetail"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.ProcurementGroup", b => + { + b.HasOne("CcsSso.DbModel.Entity.Organisation", "Organisation") + .WithMany() + .HasForeignKey("OrganisationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organisation"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.RoleApprovalConfiguration", b => + { + b.HasOne("CcsSso.DbModel.Entity.CcsAccessRole", "CcsAccessRole") + .WithMany() + .HasForeignKey("CcsAccessRoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CcsAccessRole"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.TradingOrganisation", b => + { + b.HasOne("CcsSso.DbModel.Entity.Organisation", "Organisation") + .WithMany("TradingOrganisations") + .HasForeignKey("OrganisationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organisation"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.User", b => + { + b.HasOne("CcsSso.Core.DbModel.Entity.CcsService", "CcsService") + .WithMany("CreatedUsers") + .HasForeignKey("CcsServiceId"); + + b.HasOne("CcsSso.DbModel.Entity.Organisation", "OriginOrganization") + .WithMany() + .HasForeignKey("OriginOrganizationId"); + + b.HasOne("CcsSso.DbModel.Entity.Party", "Party") + .WithOne("User") + .HasForeignKey("CcsSso.DbModel.Entity.User", "PartyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CcsService"); + + b.Navigation("OriginOrganization"); + + b.Navigation("Party"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.UserAccessRole", b => + { + b.HasOne("CcsSso.Core.DbModel.Entity.OrganisationEligibleRole", "OrganisationEligibleRole") + .WithMany() + .HasForeignKey("OrganisationEligibleRoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CcsSso.DbModel.Entity.User", "User") + .WithMany("UserAccessRoles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("OrganisationEligibleRole"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.UserAccessRolePending", b => + { + b.HasOne("CcsSso.Core.DbModel.Entity.OrganisationEligibleRole", "OrganisationEligibleRole") + .WithMany() + .HasForeignKey("OrganisationEligibleRoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CcsSso.DbModel.Entity.OrganisationUserGroup", "OrganisationUserGroup") + .WithMany() + .HasForeignKey("OrganisationUserGroupId"); + + b.HasOne("CcsSso.DbModel.Entity.User", "User") + .WithMany("UserAccessRolePending") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("OrganisationEligibleRole"); + + b.Navigation("OrganisationUserGroup"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.UserGroupMembership", b => + { + b.HasOne("CcsSso.DbModel.Entity.OrganisationUserGroup", "OrganisationUserGroup") + .WithMany("UserGroupMemberships") + .HasForeignKey("OrganisationUserGroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CcsSso.DbModel.Entity.User", "User") + .WithMany("UserGroupMemberships") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("OrganisationUserGroup"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.UserSetting", b => + { + b.HasOne("CcsSso.DbModel.Entity.User", "User") + .WithMany("UserSettings") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CcsSso.DbModel.Entity.UserSettingType", "UserSettingType") + .WithMany("UserSettings") + .HasForeignKey("UserSettingTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + + b.Navigation("UserSettingType"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.VirtualAddress", b => + { + b.HasOne("CcsSso.DbModel.Entity.ContactDetail", "ContactDetail") + .WithMany("VirtualAddresses") + .HasForeignKey("ContactDetailId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("CcsSso.DbModel.Entity.VirtualAddressType", "VirtualAddressType") + .WithMany("VirtualAddresses") + .HasForeignKey("VirtualAddressTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ContactDetail"); + + b.Navigation("VirtualAddressType"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.CcsService", b => + { + b.Navigation("CcsServiceLogins"); + + b.Navigation("CreatedOrganisations"); + + b.Navigation("CreatedUsers"); + + b.Navigation("ExternalServiceRoleMappings"); + + b.Navigation("ServicePermissions"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.CcsServiceRoleGroup", b => + { + b.Navigation("CcsServiceRoleMappings"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.IdamUserLogin", b => + { + b.Navigation("CcsServiceLogins"); + + b.Navigation("IdamUserLoginRoles"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.OrganisationEligibleIdentityProvider", b => + { + b.Navigation("UserIdentityProviders"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.OrganisationEligibleRole", b => + { + b.Navigation("ExternalServiceRoleMappings"); + }); + + modelBuilder.Entity("CcsSso.Core.DbModel.Entity.ServicePermission", b => + { + b.Navigation("ServiceRolePermissions"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.CcsAccessRole", b => + { + b.Navigation("IdamUserLoginRoles"); + + b.Navigation("OrganisationEligibleRoles"); + + b.Navigation("ServiceRolePermissions"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.ContactDetail", b => + { + b.Navigation("ContactPoints"); + + b.Navigation("PhysicalAddress"); + + b.Navigation("VirtualAddresses"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.ContactPoint", b => + { + b.Navigation("SiteContacts"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.ContactPointReason", b => + { + b.Navigation("ContactPoints"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.EnterpriseType", b => + { + b.Navigation("OrganisationEnterpriseTypes"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.IdentityProvider", b => + { + b.Navigation("IdamUserLogins"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.Organisation", b => + { + b.Navigation("OrganisationAccessRoles"); + + b.Navigation("OrganisationAudits"); + + b.Navigation("OrganisationEligibleIdentityProviders"); + + b.Navigation("OrganisationEligibleRoles"); + + b.Navigation("OrganisationEnterpriseTypes"); + + b.Navigation("People"); + + b.Navigation("TradingOrganisations"); + + b.Navigation("UserGroups"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.OrganisationUserGroup", b => + { + b.Navigation("GroupEligibleRoles"); + + b.Navigation("UserGroupMemberships"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.Party", b => + { + b.Navigation("ContactPoints"); + + b.Navigation("Organisation"); + + b.Navigation("Person"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.PartyType", b => + { + b.Navigation("Party"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.User", b => + { + b.Navigation("IdamUserLogins"); + + b.Navigation("UserAccessRolePending"); + + b.Navigation("UserAccessRoles"); + + b.Navigation("UserGroupMemberships"); + + b.Navigation("UserIdentityProviders"); + + b.Navigation("UserSettings"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.UserSettingType", b => + { + b.Navigation("UserSettings"); + }); + + modelBuilder.Entity("CcsSso.DbModel.Entity.VirtualAddressType", b => + { + b.Navigation("VirtualAddresses"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/api/CcsSso.Core.DbMigrations/Migrations/20230327094219_Add_UserAccessRolePending_OrganisationUserGroupId.cs b/api/CcsSso.Core.DbMigrations/Migrations/20230327094219_Add_UserAccessRolePending_OrganisationUserGroupId.cs new file mode 100644 index 00000000..91c25c68 --- /dev/null +++ b/api/CcsSso.Core.DbMigrations/Migrations/20230327094219_Add_UserAccessRolePending_OrganisationUserGroupId.cs @@ -0,0 +1,44 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace CcsSso.Core.DbMigrations.Migrations +{ + public partial class Add_UserAccessRolePending_OrganisationUserGroupId : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "OrganisationUserGroupId", + table: "UserAccessRolePending", + type: "integer", + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_UserAccessRolePending_OrganisationUserGroupId", + table: "UserAccessRolePending", + column: "OrganisationUserGroupId"); + + migrationBuilder.AddForeignKey( + name: "FK_UserAccessRolePending_OrganisationUserGroup_OrganisationUse~", + table: "UserAccessRolePending", + column: "OrganisationUserGroupId", + principalTable: "OrganisationUserGroup", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_UserAccessRolePending_OrganisationUserGroup_OrganisationUse~", + table: "UserAccessRolePending"); + + migrationBuilder.DropIndex( + name: "IX_UserAccessRolePending_OrganisationUserGroupId", + table: "UserAccessRolePending"); + + migrationBuilder.DropColumn( + name: "OrganisationUserGroupId", + table: "UserAccessRolePending"); + } + } +} diff --git a/api/CcsSso.Core.DbMigrations/Migrations/DataContextModelSnapshot.cs b/api/CcsSso.Core.DbMigrations/Migrations/DataContextModelSnapshot.cs index d6081ae2..387d52d0 100644 --- a/api/CcsSso.Core.DbMigrations/Migrations/DataContextModelSnapshot.cs +++ b/api/CcsSso.Core.DbMigrations/Migrations/DataContextModelSnapshot.cs @@ -1889,6 +1889,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("OrganisationEligibleRoleId") .HasColumnType("integer"); + b.Property("OrganisationUserGroupId") + .HasColumnType("integer"); + b.Property("SendEmailNotification") .HasColumnType("boolean"); @@ -1902,6 +1905,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("OrganisationEligibleRoleId"); + b.HasIndex("OrganisationUserGroupId"); + b.HasIndex("UserId"); b.ToTable("UserAccessRolePending"); @@ -2603,6 +2608,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) .OnDelete(DeleteBehavior.Cascade) .IsRequired(); + b.HasOne("CcsSso.DbModel.Entity.OrganisationUserGroup", "OrganisationUserGroup") + .WithMany() + .HasForeignKey("OrganisationUserGroupId"); + b.HasOne("CcsSso.DbModel.Entity.User", "User") .WithMany("UserAccessRolePending") .HasForeignKey("UserId") @@ -2611,6 +2620,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("OrganisationEligibleRole"); + b.Navigation("OrganisationUserGroup"); + b.Navigation("User"); }); diff --git a/api/CcsSso.Core.DbMigrations/Scripts/Phase3_Sprint8/1_Add_UserAccessRolePending_OrganisationUserGroupId.sql b/api/CcsSso.Core.DbMigrations/Scripts/Phase3_Sprint8/1_Add_UserAccessRolePending_OrganisationUserGroupId.sql new file mode 100644 index 00000000..049fab63 --- /dev/null +++ b/api/CcsSso.Core.DbMigrations/Scripts/Phase3_Sprint8/1_Add_UserAccessRolePending_OrganisationUserGroupId.sql @@ -0,0 +1,13 @@ +START TRANSACTION; + +ALTER TABLE "UserAccessRolePending" ADD "OrganisationUserGroupId" integer NULL; + +CREATE INDEX "IX_UserAccessRolePending_OrganisationUserGroupId" ON "UserAccessRolePending" ("OrganisationUserGroupId"); + +ALTER TABLE "UserAccessRolePending" ADD CONSTRAINT "FK_UserAccessRolePending_OrganisationUserGroup_OrganisationUse~" FOREIGN KEY ("OrganisationUserGroupId") REFERENCES "OrganisationUserGroup" ("Id") ON DELETE RESTRICT; + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20230327094219_Add_UserAccessRolePending_OrganisationUserGroupId', '5.0.10'); + +COMMIT; + diff --git a/api/CcsSso.Core.DbModel/Entity/UserAccessRolePending.cs b/api/CcsSso.Core.DbModel/Entity/UserAccessRolePending.cs index 45b7356b..0d115b60 100644 --- a/api/CcsSso.Core.DbModel/Entity/UserAccessRolePending.cs +++ b/api/CcsSso.Core.DbModel/Entity/UserAccessRolePending.cs @@ -21,5 +21,9 @@ public class UserAccessRolePending : BaseEntity public int Status { get; set; } public bool SendEmailNotification { get; set; } = true; + + public OrganisationUserGroup OrganisationUserGroup { get; set; } + + public int? OrganisationUserGroupId { get; set; } } } diff --git a/api/CcsSso.Core.Domain/Constants/Constants.cs b/api/CcsSso.Core.Domain/Constants/Constants.cs index 9a7129a9..a7c53fd7 100644 --- a/api/CcsSso.Core.Domain/Constants/Constants.cs +++ b/api/CcsSso.Core.Domain/Constants/Constants.cs @@ -83,6 +83,8 @@ public static class ErrorConstant public const string ErrorLinkExpired = "ERROR_LINK_EXPIRED"; public const string ErrorUserAlreadyExists = "ERROR_USER_ALREADY_EXISTS"; + public const string ErrorUserRoleAlreadyExists = "ERROR_USER_ROLE_ALREADY_EXISTS"; + } public static class VirtualContactTypeName diff --git a/api/CcsSso.Core.Domain/Contracts/External/IUserProfileRoleApprovalService.cs b/api/CcsSso.Core.Domain/Contracts/External/IUserProfileRoleApprovalService.cs index 10e6f231..0680372e 100644 --- a/api/CcsSso.Core.Domain/Contracts/External/IUserProfileRoleApprovalService.cs +++ b/api/CcsSso.Core.Domain/Contracts/External/IUserProfileRoleApprovalService.cs @@ -12,7 +12,7 @@ public interface IUserProfileRoleApprovalService Task VerifyAndReturnRoleApprovalTokenDetailsAsync(string token); - Task RemoveApprovalPendingRolesAsync(string userName, string roles); + Task RemoveApprovalPendingRolesAsync(string userName, string roles, int? groupId = null); Task CreateUserRolesPendingForApprovalAsync(UserProfileEditRequestInfo userProfileRequestInfo, bool sendEmailNotification = true); diff --git a/api/CcsSso.Core.Domain/Dtos/External/UserProfileResponseInfo.cs b/api/CcsSso.Core.Domain/Dtos/External/UserProfileResponseInfo.cs index 75841fa8..d44f00ca 100644 --- a/api/CcsSso.Core.Domain/Dtos/External/UserProfileResponseInfo.cs +++ b/api/CcsSso.Core.Domain/Dtos/External/UserProfileResponseInfo.cs @@ -44,6 +44,8 @@ public class UserRequestMain public class UserRequestDetail : UserRequestMain { + public int? GroupId { get; set; } + public List RoleIds { get; set; } } diff --git a/api/CcsSso.Core.ExternalApi/Controllers/UserRolesApprovalController.cs b/api/CcsSso.Core.ExternalApi/Controllers/UserRolesApprovalController.cs index e9b90063..d0449711 100644 --- a/api/CcsSso.Core.ExternalApi/Controllers/UserRolesApprovalController.cs +++ b/api/CcsSso.Core.ExternalApi/Controllers/UserRolesApprovalController.cs @@ -63,7 +63,7 @@ public async Task> GetUserRolesPendingForAppr /// /// Sample request: /// - /// DELETE /approve/role?user-id=user@mail.com&roles=1,2 + /// DELETE /approve/role?user-id=user@mail.com&roles=1,2&groupId=1 /// /// /// @@ -72,9 +72,9 @@ public async Task> GetUserRolesPendingForAppr [OrganisationAuthorise("USER")] [SwaggerOperation(Tags = new[] { "User" })] [ProducesResponseType(typeof(void), 200)] - public async Task RemoveApprovalPendingRoles([FromQuery(Name = "user-id")] string userId, [FromQuery(Name = "roles")] string roleIds) + public async Task RemoveApprovalPendingRoles([FromQuery(Name = "user-id")] string userId, [FromQuery(Name = "roles")] string roleIds, [FromQuery(Name = "groupId")] int? groupId) { - await _userProfileRoleApprovalService.RemoveApprovalPendingRolesAsync(userId, roleIds); + await _userProfileRoleApprovalService.RemoveApprovalPendingRolesAsync(userId, roleIds, groupId); } /// @@ -115,7 +115,8 @@ public async Task VerifyRoleApprovalToken([Fr /// { /// "userName": "user@mail.com", /// "detail": { - /// "roleIds": { 1, 2 } + /// "roleIds": { 1, 2 }, + /// "groupId": null /// } /// } /// diff --git a/api/CcsSso.Core.Service/External/ServiceRoleGroupMapperService.cs b/api/CcsSso.Core.Service/External/ServiceRoleGroupMapperService.cs index 3643c225..255fc872 100644 --- a/api/CcsSso.Core.Service/External/ServiceRoleGroupMapperService.cs +++ b/api/CcsSso.Core.Service/External/ServiceRoleGroupMapperService.cs @@ -1,4 +1,5 @@ -using CcsSso.Core.DbModel.Entity; +using CcsSso.Core.DbModel.Constants; +using CcsSso.Core.DbModel.Entity; using CcsSso.Core.Domain.Contracts.External; using CcsSso.DbModel.Entity; using CcsSso.Domain.Constants; @@ -116,7 +117,7 @@ public async Task> ServiceRoleGroupsWithApprovalRequir { var serviceRoleGroups = await _dataContext.CcsServiceRoleGroup .Include(g => g.CcsServiceRoleMappings).ThenInclude(g => g.CcsAccessRole) - .Where(x => !x.IsDeleted && x.CcsServiceRoleMappings.Any(y => y.CcsAccessRole.ApprovalRequired == 1)).ToListAsync(); + .Where(x => !x.IsDeleted && x.CcsServiceRoleMappings.Any(y => y.CcsAccessRole.ApprovalRequired == (int)RoleApprovalRequiredStatus.ApprovalRequired)).ToListAsync(); return serviceRoleGroups; } diff --git a/api/CcsSso.Core.Service/External/UserProfileRoleApprovalService.cs b/api/CcsSso.Core.Service/External/UserProfileRoleApprovalService.cs index 404e3b5a..e7abd8a3 100644 --- a/api/CcsSso.Core.Service/External/UserProfileRoleApprovalService.cs +++ b/api/CcsSso.Core.Service/External/UserProfileRoleApprovalService.cs @@ -84,54 +84,7 @@ public async Task UpdateUserRoleStatusAsync(UserRoleApprovalEditRequest us throw new ResourceNotFoundException(); } - if (status == UserPendingRoleStaus.Rejected) - { - pendingUserRole.Status = (int)UserPendingRoleStaus.Rejected; - pendingUserRole.IsDeleted = true; - - } - else - { - pendingUserRole.Status = (int)UserPendingRoleStaus.Approved; - pendingUserRole.IsDeleted = true; - - var roleAlreadyExists = await _dataContext.UserAccessRole.FirstOrDefaultAsync(x => x.UserId == pendingUserRole.UserId && !x.IsDeleted && x.OrganisationEligibleRoleId == pendingUserRole.OrganisationEligibleRoleId); - - if (roleAlreadyExists == null) - { - user.UserAccessRoles.Add(new UserAccessRole - { - UserId = user.Id, - OrganisationEligibleRoleId = pendingUserRole.OrganisationEligibleRoleId - }); - - // On role approval assign normal roles of service as well - if (_appConfigInfo.ServiceRoleGroupSettings.Enable) - { - var serviceGroup = serviceRoleGroupsWithApprovalRequiredRole.FirstOrDefault(x => x.CcsServiceRoleMappings.Any(r => r.CcsAccessRoleId == pendingUserRole.OrganisationEligibleRole.CcsAccessRoleId)); - var serviceMappingCcsRoleIds = serviceGroup.CcsServiceRoleMappings.Where(y => y.CcsAccessRole.ApprovalRequired == 0).Select(x => x.CcsAccessRoleId).ToList(); - - var allOrgEligibleRoles = await _dataContext.OrganisationEligibleRole.Include(or => or.CcsAccessRole) - .Where(x => !x.IsDeleted && x.OrganisationId == pendingUserRole.OrganisationEligibleRole.OrganisationId && - serviceMappingCcsRoleIds.Contains(x.CcsAccessRoleId)).ToListAsync(); - foreach (var orgRole in allOrgEligibleRoles) - { - if (!user.UserAccessRoles.Any(x => x.OrganisationEligibleRoleId == orgRole.Id && !x.IsDeleted)) - { - user.UserAccessRoles.Add(new UserAccessRole - { - UserId = user.Id, - OrganisationEligibleRoleId = orgRole.Id - }); - } - } - - } - } - - } - - await _dataContext.SaveChangesAsync(); + await ApproveRejectRoleRequest(status, serviceRoleGroupsWithApprovalRequiredRole, pendingUserRole, user); var orgEligibleRole = await _dataContext.OrganisationEligibleRole.Include(or => or.CcsAccessRole) .ThenInclude(or => or.ServiceRolePermissions).ThenInclude(sr => sr.ServicePermission).ThenInclude(sr => sr.CcsService) @@ -170,9 +123,89 @@ public async Task UpdateUserRoleStatusAsync(UserRoleApprovalEditRequest us await _ccsSsoEmailService.SendRoleRejectedEmailAsync(email, user.UserName, serviceName); } } + + await UpdateRemaningRequestsOfUser(status, serviceRoleGroupsWithApprovalRequiredRole, pendingUserRole, user); } + return await Task.FromResult(true); + } + + private async Task UpdateRemaningRequestsOfUser(UserPendingRoleStaus status, List serviceRoleGroupsWithApprovalRequiredRole, UserAccessRolePending pendingUserRole, User user) + { + var userAccessRolePendingRequests = await _dataContext.UserAccessRolePending + .Include(x => x.OrganisationEligibleRole).ThenInclude(r => r.CcsAccessRole) + .Where(x => !x.IsDeleted && x.UserId == pendingUserRole.UserId && x.Status == (int)UserPendingRoleStaus.Pending + && x.OrganisationEligibleRoleId == pendingUserRole.OrganisationEligibleRoleId && x.Id != pendingUserRole.Id) + .ToListAsync(); + + foreach (var userAccessRolePendingRequest in userAccessRolePendingRequests) + { + await ApproveRejectRoleRequest(status, serviceRoleGroupsWithApprovalRequiredRole, userAccessRolePendingRequest, user); + } + } + + private async Task ApproveRejectRoleRequest(UserPendingRoleStaus status, List serviceRoleGroupsWithApprovalRequiredRole, UserAccessRolePending pendingUserRole, User user) + { + if (status == UserPendingRoleStaus.Rejected) + { + UpdateRoleRequestStatus(pendingUserRole, UserPendingRoleStaus.Rejected); + } + else + { + UpdateRoleRequestStatus(pendingUserRole, UserPendingRoleStaus.Approved); + await AssignRequestedRoleToUser(serviceRoleGroupsWithApprovalRequiredRole, pendingUserRole, user); + } + await _dataContext.SaveChangesAsync(); + } + + private async Task AssignRequestedRoleToUser(List serviceRoleGroupsWithApprovalRequiredRole, UserAccessRolePending pendingUserRole, User user) + { + if (pendingUserRole.OrganisationUserGroupId == null) + { + var roleAlreadyExists = await _dataContext.UserAccessRole.FirstOrDefaultAsync(x => x.UserId == pendingUserRole.UserId && !x.IsDeleted && x.OrganisationEligibleRoleId == pendingUserRole.OrganisationEligibleRoleId); + + if (roleAlreadyExists == null) + { + user.UserAccessRoles.Add(new UserAccessRole + { + UserId = user.Id, + OrganisationEligibleRoleId = pendingUserRole.OrganisationEligibleRoleId + }); + + // On role approval assign normal roles of service as well + await AssignOtherServiceGroupRoleToUser(serviceRoleGroupsWithApprovalRequiredRole, pendingUserRole, user); + } + } + } + + private async Task AssignOtherServiceGroupRoleToUser(List serviceRoleGroupsWithApprovalRequiredRole, UserAccessRolePending pendingUserRole, User user) + { + if (_appConfigInfo.ServiceRoleGroupSettings.Enable) + { + var serviceGroup = serviceRoleGroupsWithApprovalRequiredRole.FirstOrDefault(x => x.CcsServiceRoleMappings.Any(r => r.CcsAccessRoleId == pendingUserRole.OrganisationEligibleRole.CcsAccessRoleId)); + var serviceMappingCcsRoleIds = serviceGroup.CcsServiceRoleMappings.Where(y => y.CcsAccessRole.ApprovalRequired == 0).Select(x => x.CcsAccessRoleId).ToList(); + + var allOrgEligibleRoles = await _dataContext.OrganisationEligibleRole.Include(or => or.CcsAccessRole) + .Where(x => !x.IsDeleted && x.OrganisationId == pendingUserRole.OrganisationEligibleRole.OrganisationId && + serviceMappingCcsRoleIds.Contains(x.CcsAccessRoleId)).ToListAsync(); + foreach (var orgRole in allOrgEligibleRoles) + { + if (!user.UserAccessRoles.Any(x => x.OrganisationEligibleRoleId == orgRole.Id && !x.IsDeleted)) + { + user.UserAccessRoles.Add(new UserAccessRole + { + UserId = user.Id, + OrganisationEligibleRoleId = orgRole.Id + }); + } + } + } + } + private static void UpdateRoleRequestStatus(UserAccessRolePending pendingUserRole, UserPendingRoleStaus status) + { + pendingUserRole.Status = (int)status; + pendingUserRole.IsDeleted = true; } public async Task> GetUserRolesPendingForApprovalAsync(string userName) @@ -194,7 +227,8 @@ public async Task> GetUserRolesPendingForAppr var userAccessRolePendingAllList = await _dataContext.UserAccessRolePending .Include(u => u.OrganisationEligibleRole).ThenInclude(or => or.CcsAccessRole) - .Where(u => !u.IsDeleted && u.Status == (int)UserPendingRoleStaus.Pending && u.UserId == userId) + .Where(u => !u.IsDeleted && u.Status == (int)UserPendingRoleStaus.Pending + && u.OrganisationUserGroupId == null && u.UserId == userId) .ToListAsync(); var approvalRoleConfig = await _dataContext.RoleApprovalConfiguration.Where(x => !x.IsDeleted).ToListAsync(); @@ -212,7 +246,7 @@ public async Task> GetUserRolesPendingForAppr return userAccessRolePendingListResponse; } - public async Task RemoveApprovalPendingRolesAsync(string userName, string roleIds) + public async Task RemoveApprovalPendingRolesAsync(string userName, string roleIds, int? groupId = null) { if (!_appConfigInfo.UserRoleApproval.Enable) { @@ -236,8 +270,8 @@ public async Task RemoveApprovalPendingRolesAsync(string userName, string roleId throw new ResourceNotFoundException(); } - var userAccessRolePendingList = await _dataContext.UserAccessRolePending.Where(u => !u.IsDeleted && u.UserId == userId && - roles.Contains(u.OrganisationEligibleRoleId.ToString())).ToListAsync(); + var userAccessRolePendingList = await _dataContext.UserAccessRolePending.Where(u => !u.IsDeleted && u.UserId == userId + && u.OrganisationUserGroupId == groupId && roles.Contains(u.OrganisationEligibleRoleId.ToString())).ToListAsync(); if (userAccessRolePendingList.Any()) { @@ -342,6 +376,7 @@ public async Task CreateUserRolesPendingForApprovalAsync(UserProfileEditRequestI var user = await _dataContext.User .Include(u => u.Party).ThenInclude(p => p.Person).ThenInclude(o => o.Organisation) + .Include(u => u.UserAccessRoles) .Include(u => u.UserAccessRolePending) .FirstOrDefaultAsync(u => !u.IsDeleted && u.UserName.ToLower() == userName.ToLower() && u.UserType == UserType.Primary); @@ -373,14 +408,6 @@ public async Task CreateUserRolesPendingForApprovalAsync(UserProfileEditRequestI throw new CcsSsoException(ErrorConstant.ErrorInvalidRoleInfo); } - var userAccessRoles = await _dataContext.UserAccessRole - .FirstOrDefaultAsync(uar => !uar.IsDeleted && uar.UserId == user.Id && roles.Contains(uar.OrganisationEligibleRoleId)); - - if (userAccessRoles != null) - { - throw new ResourceAlreadyExistsException("User Role already exists"); - } - var roleRequiredApprovalIds = await _dataContext.CcsAccessRole .Where(x => !x.IsDeleted && x.ApprovalRequired == (int)RoleApprovalRequiredStatus.ApprovalRequired) .Select(x => x.Id) @@ -395,51 +422,155 @@ public async Task CreateUserRolesPendingForApprovalAsync(UserProfileEditRequestI throw new CcsSsoException(ErrorConstant.ErrorInvalidRoleInfo); } - List userAccessRolePendingList = new List(); + var groupId = userProfileRequestInfo.Detail.GroupId; + + if (groupId != null) + { + var group = await _dataContext.OrganisationUserGroup + .Include(g => g.GroupEligibleRoles) + .Include(g => g.UserGroupMemberships).ThenInclude(ugm => ugm.User) + .FirstOrDefaultAsync(g => !g.IsDeleted && g.Id == groupId && g.Organisation.CiiOrganisationId == organisation.CiiOrganisationId); + + if (group == null) + { + throw new ResourceAlreadyExistsException(ErrorConstant.ErrorInvalidUserGroup); + } + + var groupEligibleRoleIds = group.GroupEligibleRoles.Where(x => !x.IsDeleted).Select(x => x.OrganisationEligibleRoleId); + if (!roles.All(roleId => groupEligibleRoleIds.Any(x => x == roleId))) + { + throw new CcsSsoException(ErrorConstant.ErrorInvalidUserGroupRole); + } + + var isUserMemberOfGroup = group.UserGroupMemberships.Where(x => !x.IsDeleted).Any(x => x.UserId == user.Id); + if (!isUserMemberOfGroup) + { + throw new CcsSsoException(ErrorConstant.ErrorInvalidUserInfo); + } + } + + // If GroupId is null then request is for user profile so we need check for user role exists + if (groupId == null) + { + var userAccessRoles = await _dataContext.UserAccessRole + .FirstOrDefaultAsync(uar => !uar.IsDeleted && uar.UserId == user.Id && roles.Contains(uar.OrganisationEligibleRoleId)); + + if (userAccessRoles != null) + { + throw new ResourceAlreadyExistsException(ErrorConstant.ErrorUserRoleAlreadyExists); + } + } + + List userAccessRolePendingToSendEmail = new List(); + List userAccessRolePendingAutoApproved = new List(); - List rolesToSendEmail = new List(); + var userAccessRoleIds = await _dataContext.UserAccessRole.Where(x => !x.IsDeleted && x.UserId == user.Id).Select(x => x.OrganisationEligibleRoleId).ToListAsync(); + var userGroupApprovedRoleIds = await GetUserGroupApprovedRoleIds(user); roles.ForEach((roleId) => { + var isUserAccessRoleRequestPending = user.UserAccessRolePending.Any(x => !x.IsDeleted + && x.OrganisationEligibleRoleId == Convert.ToInt32(roleId) + && x.Status == (int)UserPendingRoleStaus.Pending); + var isUserAccessRolePendingExist = user.UserAccessRolePending.Any(x => !x.IsDeleted && x.OrganisationEligibleRoleId == Convert.ToInt32(roleId) + && x.OrganisationUserGroupId == groupId && x.Status == (int)UserPendingRoleStaus.Pending); if (!isUserAccessRolePendingExist) { - user.UserAccessRolePending.Add(new UserAccessRolePending + var isRoleAssignedToUser = userAccessRoleIds.Any(x => x == roleId); + var isRoleAssignedToUserFromGroup = userGroupApprovedRoleIds.Any(x => x == roleId); + + var userAccessRolePending = new UserAccessRolePending { OrganisationEligibleRoleId = roleId, Status = (int)UserPendingRoleStaus.Pending, + OrganisationUserGroupId = groupId, SendEmailNotification = sendEmailNotification - }); - rolesToSendEmail.Add(roleId); + }; + + if (isRoleAssignedToUser || isRoleAssignedToUserFromGroup) + { + userAccessRolePending.Status = (int)UserPendingRoleStaus.Approved; + userAccessRolePending.IsDeleted = true; + userAccessRolePendingAutoApproved.Add(userAccessRolePending); + } + + user.UserAccessRolePending.Add(userAccessRolePending); + + if (!isUserAccessRoleRequestPending && userAccessRolePending.Status == (int)UserPendingRoleStaus.Pending) + { + userAccessRolePendingToSendEmail.Add(userAccessRolePending); + } } }); await _dataContext.SaveChangesAsync(); - if (rolesToSendEmail.Count > 0) + if (userAccessRolePendingToSendEmail.Count > 0) + { + await SendEmailForApprovalPendingRolesAsync(user, userAccessRolePendingToSendEmail); + } + + if (userAccessRolePendingAutoApproved.Count > 0) { - await SendEmailForApprovalPendingRolesAsync(user, rolesToSendEmail); + await AssignRoleToUserForAutoApproved(user, userAccessRolePendingAutoApproved); } } - private async Task SendEmailForApprovalPendingRolesAsync(User user, List roles) + private async Task AssignRoleToUserForAutoApproved(User user, List userAccessRolePendingAutoApproved) { - var userAccessRolePendingList = await _dataContext.UserAccessRolePending - .Include(gr => gr.OrganisationEligibleRole).ThenInclude(or => or.CcsAccessRole) - .ThenInclude(or => or.ServiceRolePermissions).ThenInclude(sr => sr.ServicePermission).ThenInclude(sr => sr.CcsService) - .Where(u => !u.IsDeleted && u.UserId == user.Id && u.Status == (int)UserPendingRoleStaus.Pending && roles.Contains(u.OrganisationEligibleRoleId)) - .ToListAsync(); + var serviceRoleGroupsWithApprovalRequiredRole = await _serviceRoleGroupMapperService.ServiceRoleGroupsWithApprovalRequiredRoleAsync(); + + foreach (UserAccessRolePending userAccessRolePending in userAccessRolePendingAutoApproved) + { + await AssignRequestedRoleToUser(serviceRoleGroupsWithApprovalRequiredRole, userAccessRolePending, user); + } + + await _dataContext.SaveChangesAsync(); + } + + private async Task> GetUserGroupApprovedRoleIds(User user) + { + List userGroupApprovedRoleIds = new List(); + + var userGroupIds = await _dataContext.UserGroupMembership.Where(x => !x.IsDeleted && x.UserId == user.Id).Select(x => x.OrganisationUserGroupId).ToListAsync(); + var userGroupRoleIds = await _dataContext.OrganisationGroupEligibleRole.Where(x => !x.IsDeleted && userGroupIds.Contains(x.OrganisationUserGroupId)).Select(x => x.OrganisationEligibleRoleId).ToListAsync(); + + userGroupRoleIds.ForEach((userGroupRoleId) => + { + var lastUserAccessRolePedningRequest = user.UserAccessRolePending + .OrderByDescending(o => o.CreatedOnUtc) + .FirstOrDefault(x => x.IsDeleted && x.OrganisationUserGroupId != null && x.OrganisationEligibleRoleId == userGroupRoleId); + + if (lastUserAccessRolePedningRequest?.Status == (int)UserPendingRoleStaus.Approved) + { + userGroupApprovedRoleIds.Add(userGroupRoleId); + } + }); + + return userGroupApprovedRoleIds; + } + private async Task SendEmailForApprovalPendingRolesAsync(User user, List userAccessRolePendingRequests) + { string orgName = user.Party.Person.Organisation.LegalName; - if (userAccessRolePendingList.Any()) + var roleApprovalConfigurations = await _dataContext.RoleApprovalConfiguration.ToListAsync(); + + foreach (var userAccessRolePendingRequest in userAccessRolePendingRequests) { - var roleApprovalConfigurations = await _dataContext.RoleApprovalConfiguration.ToListAsync(); + var userAccessRolePending = await _dataContext.UserAccessRolePending + .Include(gr => gr.OrganisationEligibleRole).ThenInclude(or => or.CcsAccessRole) + .ThenInclude(or => or.ServiceRolePermissions).ThenInclude(sr => sr.ServicePermission).ThenInclude(sr => sr.CcsService) + .FirstOrDefaultAsync(u => !u.IsDeleted && u.UserId == userAccessRolePendingRequest.UserId + && u.Status == userAccessRolePendingRequest.Status + && u.OrganisationUserGroupId == userAccessRolePendingRequest.OrganisationUserGroupId + && u.OrganisationEligibleRoleId == userAccessRolePendingRequest.OrganisationEligibleRoleId); - foreach (var userAccessRolePending in userAccessRolePendingList) + if (userAccessRolePending != null) { var roleApprovalConfiguration = roleApprovalConfigurations.FirstOrDefault(x => x.CcsAccessRoleId == userAccessRolePending.OrganisationEligibleRole.CcsAccessRoleId); @@ -465,7 +596,6 @@ private async Task SendEmailForApprovalPendingRolesAsync(User user, List ro } } } - public async Task> GetUserServiceRoleGroupsPendingForApprovalAsync(string userName) { if (!_appConfigInfo.ServiceRoleGroupSettings.Enable) diff --git a/api/CcsSso.Core.Service/External/UserProfileService.cs b/api/CcsSso.Core.Service/External/UserProfileService.cs index df7c57e4..9c5c9134 100644 --- a/api/CcsSso.Core.Service/External/UserProfileService.cs +++ b/api/CcsSso.Core.Service/External/UserProfileService.cs @@ -18,6 +18,7 @@ using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; +using System.Data; using System.Linq; using System.Threading.Tasks; @@ -82,11 +83,14 @@ public async Task CreateUserAsync(UserProfileEditRequestIn .Include(o => o.OrganisationEligibleRoles).ThenInclude(or => or.CcsAccessRole) .Include(o => o.OrganisationEligibleIdentityProviders) .FirstOrDefaultAsync(o => !o.IsDeleted && o.CiiOrganisationId == userProfileRequestInfo.OrganisationId); + if (organisation == null) { throw new ResourceNotFoundException(); } + var isUserDomainValid = userName?.ToLower().Split('@')?[1] == organisation.DomainName?.ToLower(); + var user = await _dataContext.User .FirstOrDefaultAsync(u => !u.IsDeleted && u.UserName == userName); @@ -130,8 +134,13 @@ public async Task CreateUserAsync(UserProfileEditRequestIn } } - // Set user groups + var groupsWithRoleRequiredApproval = new List>>(); + if (_appConfigInfo.UserRoleApproval.Enable && !isUserDomainValid && userProfileRequestInfo.Detail.GroupIds.Any()) + { + groupsWithRoleRequiredApproval = GetGroupsWithApprovalOrgRole(organisation.UserGroups, userProfileRequestInfo.Detail.GroupIds); + } + var userGroupMemberships = new List(); userProfileRequestInfo.Detail.GroupIds?.ForEach((groupId) => { @@ -154,7 +163,6 @@ public async Task CreateUserAsync(UserProfileEditRequestIn { var ccsAccessRoleId = organisation.OrganisationEligibleRoles.FirstOrDefault(x => x.Id == roleId)?.CcsAccessRoleId; var isRoleRequiredApproval = ccsAccessRoleId != null && ccsAccessRoleRequiredApproval != null && ccsAccessRoleRequiredApproval.Any(x => x.Id == ccsAccessRoleId); - var isUserDomainValid = userName?.ToLower().Split('@')?[1] == organisation.DomainName?.ToLower(); if (_appConfigInfo.UserRoleApproval.Enable && !isUserDomainValid && isRoleRequiredApproval) { @@ -224,6 +232,23 @@ await _userProfileRoleApprovalService.CreateUserRolesPendingForApprovalAsync(new }); } + if (groupsWithRoleRequiredApproval.Any()) + { + foreach (var group in groupsWithRoleRequiredApproval) + { + await _userProfileRoleApprovalService.CreateUserRolesPendingForApprovalAsync(new UserProfileEditRequestInfo + { + UserName = userName, + OrganisationId = organisation.CiiOrganisationId, + Detail = new UserRequestDetail + { + GroupId = group.Key, + RoleIds = group.Value + } + }); + } + } + if (isConclaveConnectionIncluded) { SecurityApiUserInfo securityApiUserInfo = new SecurityApiUserInfo @@ -423,6 +448,9 @@ public async Task GetUserAsync(string userName, bool is if (user.UserGroupMemberships != null) { + var userGroupsApprovalRequest = await _dataContext.UserAccessRolePending.Where(x => !x.IsDeleted && x.UserId == user.Id + && x.OrganisationUserGroupId != null && x.Status == (int)UserPendingRoleStaus.Pending).ToListAsync(); + foreach (var userGroupMembership in user.UserGroupMemberships) { if (!userGroupMembership.IsDeleted && userGroupMembership.OrganisationUserGroup.GroupEligibleRoles != null) @@ -433,6 +461,11 @@ public async Task GetUserAsync(string userName, bool is // For every role in the group populate the group role info foreach (var groupAccess in userGroupMembership.OrganisationUserGroup.GroupEligibleRoles.Where(x => !x.IsDeleted)) { + if (_appConfigInfo.UserRoleApproval.Enable && userGroupsApprovalRequest.Any(x => x.OrganisationUserGroupId == groupAccess.OrganisationUserGroupId && x.OrganisationEligibleRoleId == groupAccess.OrganisationEligibleRoleId)) + { + continue; + } + var groupAccessRole = new GroupAccessRole { GroupId = userGroupMembership.OrganisationUserGroup.Id, @@ -850,7 +883,7 @@ public async Task UpdateUserAsync(string userName, UserPro { throw new ResourceNotFoundException(); } - + var isUserDomainValid = userName?.ToLower().Split('@')?[1] == organisation.DomainName?.ToLower(); var user = await _dataContext.User .Include(u => u.Party).ThenInclude(p => p.Person) .Include(u => u.UserGroupMemberships) @@ -920,6 +953,7 @@ join cr in _dataContext.CcsAccessRole on er.CcsAccessRoleId equals cr.Id List requestRoles = new(); List previousIdentityProviderIds = new(); var userAccessRoleRequiredApproval = new List(); + var groupsWithRoleRequiredApproval = new List>>(); if (!isMyProfile || isAdminUser == true) { @@ -981,6 +1015,11 @@ join cr in _dataContext.CcsAccessRole on er.CcsAccessRoleId equals cr.Id } // Set groups + if (_appConfigInfo.UserRoleApproval.Enable && !isUserDomainValid && userProfileRequestInfo.Detail.GroupIds != null && userProfileRequestInfo.Detail.GroupIds.Any()) + { + groupsWithRoleRequiredApproval = GetGroupsWithApprovalOrgRole(organisation.UserGroups, userProfileRequestInfo.Detail.GroupIds); + } + var userGroupMemberships = new List(); userProfileRequestInfo.Detail.GroupIds?.ForEach((groupId) => { @@ -1000,7 +1039,6 @@ join cr in _dataContext.CcsAccessRole on er.CcsAccessRoleId equals cr.Id { var ccsAccessRoleId = organisation.OrganisationEligibleRoles.FirstOrDefault(x => x.Id == roleId)?.CcsAccessRoleId; var isRoleRequiredApproval = ccsAccessRoleId != null && ccsAccessRoleRequiredApproval != null && ccsAccessRoleRequiredApproval.Any(x => x.Id == ccsAccessRoleId); - var isUserDomainValid = userName?.ToLower().Split('@')?[1] == organisation.DomainName?.ToLower(); if (_appConfigInfo.UserRoleApproval.Enable && !isUserDomainValid && isRoleRequiredApproval && !previousRoles.Any(x => x == roleId)) { @@ -1108,6 +1146,7 @@ join cr in _dataContext.CcsAccessRole on er.CcsAccessRoleId equals cr.Id if (_appConfigInfo.UserRoleApproval.Enable) { await CreatePendingRoleRequest(userAccessRoleRequiredApproval, user.Id, userName, organisation.CiiOrganisationId); + await CreatePendingRoleRequestForGroup(groupsWithRoleRequiredApproval, user.Id, userName, organisation.CiiOrganisationId); } // Log @@ -1435,7 +1474,7 @@ private void Validate(UserProfileEditRequestInfo userProfileReqestInfo, bool isM if (userProfileReqestInfo.Detail.RoleIds != null && userProfileReqestInfo.Detail.RoleIds.Any(gId => !orgRoleIds.Contains(gId))) { throw new CcsSsoException(ErrorConstant.ErrorInvalidUserRole); - } + } if (userProfileReqestInfo.Detail.IdentityProviderIds == null || !userProfileReqestInfo.Detail.IdentityProviderIds.Any() || userProfileReqestInfo.Detail.IdentityProviderIds.Any(id => !orgIdpIds.Contains(id))) @@ -1859,11 +1898,11 @@ private async Task CreatePendingRoleRequest(List userAccessRoleRequiredAppr { // remove roles that were pending for approval but now no longer required var userAccessRoleRequiredToRemoveFromApproval = await _dataContext.UserAccessRolePending.Where(x => !x.IsDeleted && x.UserId == userId - && !userAccessRoleRequiredApproval.Contains(x.OrganisationEligibleRoleId) && x.Status == (int)UserPendingRoleStaus.Pending).ToListAsync(); + && !userAccessRoleRequiredApproval.Contains(x.OrganisationEligibleRoleId) && x.Status == (int)UserPendingRoleStaus.Pending && x.OrganisationUserGroupId == null).ToListAsync(); // get roles that are pending for approval var existingPendingRequestRole = await _dataContext.UserAccessRolePending.Where(x => !x.IsDeleted && x.UserId == userId - && userAccessRoleRequiredApproval.Contains(x.OrganisationEligibleRoleId) && x.Status == (int)UserPendingRoleStaus.Pending).ToListAsync(); + && userAccessRoleRequiredApproval.Contains(x.OrganisationEligibleRoleId) && x.Status == (int)UserPendingRoleStaus.Pending && x.OrganisationUserGroupId == null).ToListAsync(); // ignore roles which are still pending for approval foreach (var existingPendingRequestToIgnore in existingPendingRequestRole) @@ -1872,12 +1911,7 @@ private async Task CreatePendingRoleRequest(List userAccessRoleRequiredAppr } // Remove pending for approval role that are no longer required (not passed in request) - if (userAccessRoleRequiredToRemoveFromApproval != null && userAccessRoleRequiredToRemoveFromApproval.Any()) - { - var roleIds = userAccessRoleRequiredToRemoveFromApproval.Select(x => x.OrganisationEligibleRoleId).ToList(); - - await _userProfileRoleApprovalService.RemoveApprovalPendingRolesAsync(userName, string.Join(",", roleIds)); - } + userAccessRoleRequiredToRemoveFromApproval.ForEach(l => { l.IsDeleted = true; l.Status = (int)UserPendingRoleStaus.Removed; }); if (userAccessRoleRequiredApproval.Any()) { @@ -1932,6 +1966,10 @@ public async Task GetUserV1Async(string List groupAccessServiceRoleGroups = new List(); + var userGroupsApprovalRequestRoleIds = await _dataContext.UserAccessRolePending.Include(u => u.User).Where(x => !x.IsDeleted && x.User.UserName == userName.ToLower() + && x.OrganisationUserGroupId != null && x.Status == (int)UserPendingRoleStaus.Pending).Select(x => x.OrganisationEligibleRoleId).ToListAsync(); + var userGroupsApprovalServiceRoleGroups = await _serviceRoleGroupMapperService.OrgRolesToServiceRoleGroupsAsync(userGroupsApprovalRequestRoleIds); + foreach (var groupId in groupIds) { var groupInfo = await _organisationGroupService.GetServiceRoleGroupAsync(userProfileResponseInfo.OrganisationId, groupId); @@ -1940,6 +1978,10 @@ public async Task GetUserV1Async(string { foreach (var serviceRoleGroup in groupInfo.ServiceRoleGroups) { + if (_appConfigInfo.UserRoleApproval.Enable && userGroupsApprovalServiceRoleGroups.Any(x => x.Id == serviceRoleGroup.Id)) + { + continue; + } groupAccessServiceRoleGroups.Add(new GroupAccessServiceRoleGroup() { GroupId = groupInfo.GroupId, @@ -2002,6 +2044,10 @@ public async Task UpdateUserV1Async(string userName, UserP var serviceRoleGroupIds = userProfileServiceRoleGroupEditRequestInfo?.Detail?.ServiceRoleGroupIds; var organisationId = userProfileServiceRoleGroupEditRequestInfo?.OrganisationId; + + userName = userName?.ToLower(); + userProfileServiceRoleGroupEditRequestInfo.UserName = userProfileServiceRoleGroupEditRequestInfo.UserName?.ToLower(); + _userHelper.ValidateUserName(userName); if (userName != userProfileServiceRoleGroupEditRequestInfo.UserName) @@ -2207,5 +2253,71 @@ private async Task ValidateJoiningRequestAsync(Dictionary>> GetGroupsWithApprovalOrgRole(List userGroups, List groupIds) + { + var grpWithPendingOrgEligibleRole = new List>>(); + var allSelectedGroups = userGroups.Where(oug => groupIds.Contains(oug.Id) && !oug.IsDeleted).ToList(); + + foreach (var group in allSelectedGroups) + { + List approvalRequiredRoles = new(); + foreach (var role in group.GroupEligibleRoles) + { + if (role.OrganisationEligibleRole.CcsAccessRole.ApprovalRequired == (int)RoleApprovalRequiredStatus.ApprovalRequired) + { + approvalRequiredRoles.Add(role.OrganisationEligibleRoleId); + } + } + if (approvalRequiredRoles.Any()) + { + grpWithPendingOrgEligibleRole.Add(new KeyValuePair>(group.Id, approvalRequiredRoles)); + } + } + + return grpWithPendingOrgEligibleRole; + } + + private async Task CreatePendingRoleRequestForGroup(List>> groupsWithRoleRequiredApproval, int userId, string userName, string ciiOrganisationId) + { + var userPendingGroups = await _dataContext.UserAccessRolePending.Where(x => !x.IsDeleted && x.UserId == userId && x.Status == (int)UserPendingRoleStaus.Pending && + x.OrganisationUserGroupId != null).ToListAsync(); + + foreach (var existRequest in userPendingGroups) + { + if (!groupsWithRoleRequiredApproval.Any(x => x.Key == existRequest.OrganisationUserGroupId && x.Value.Contains(existRequest.OrganisationEligibleRoleId))) + { + existRequest.IsDeleted = true; + existRequest.Status = (int)UserPendingRoleStaus.Removed; + } + } + + foreach (var group in groupsWithRoleRequiredApproval) + { + // get roles that are pending for approval + var existingPendingRequestRole = userPendingGroups.Where(x => !x.IsDeleted && group.Value.Contains(x.OrganisationEligibleRoleId)).ToList(); + + // ignore roles which are still pending for approval + foreach (var existingPendingRequestToIgnore in existingPendingRequestRole) + { + group.Value.Remove(existingPendingRequestToIgnore.OrganisationEligibleRoleId); + } + + if (group.Value.Any()) + { + await _userProfileRoleApprovalService.CreateUserRolesPendingForApprovalAsync(new UserProfileEditRequestInfo + { + UserName = userName, + OrganisationId = ciiOrganisationId, + Detail = new UserRequestDetail + { + GroupId = group.Key, + RoleIds = group.Value + } + }); + } + } + + } } } \ No newline at end of file diff --git a/api/CcsSso.Core.Service/UserService.cs b/api/CcsSso.Core.Service/UserService.cs index d2b968ef..dc54351b 100644 --- a/api/CcsSso.Core.Service/UserService.cs +++ b/api/CcsSso.Core.Service/UserService.cs @@ -1,3 +1,4 @@ +using CcsSso.Core.DbModel.Constants; using CcsSso.Core.Domain.Contracts; using CcsSso.Core.Domain.Contracts.External; using CcsSso.DbModel.Entity; @@ -12,105 +13,121 @@ namespace CcsSso.Service { - public class UserService : IUserService + public class UserService : IUserService + { + private readonly IDataContext _dataContext; + private readonly IHttpClientFactory _httpClientFactory; + private readonly ApplicationConfigurationInfo _applicationConfigurationInfo; + private readonly ICcsSsoEmailService _ccsSsoEmailService; + private readonly IUserProfileHelperService _userHelper; + private readonly IServiceRoleGroupMapperService _serviceRoleGroupMapperService; + + public UserService(IDataContext dataContext, IHttpClientFactory httpClientFactory, + ApplicationConfigurationInfo applicationConfigurationInfo, ICcsSsoEmailService ccsSsoEmailService, IUserProfileHelperService userHelper, IServiceRoleGroupMapperService serviceRoleGroupMapperService) { - private readonly IDataContext _dataContext; - private readonly IHttpClientFactory _httpClientFactory; - private readonly ApplicationConfigurationInfo _applicationConfigurationInfo; - private readonly ICcsSsoEmailService _ccsSsoEmailService; - private readonly IUserProfileHelperService _userHelper; - - public UserService(IDataContext dataContext, IHttpClientFactory httpClientFactory, - ApplicationConfigurationInfo applicationConfigurationInfo, ICcsSsoEmailService ccsSsoEmailService, IUserProfileHelperService userHelper) - { - _dataContext = dataContext; - _httpClientFactory = httpClientFactory; - _applicationConfigurationInfo = applicationConfigurationInfo; - _ccsSsoEmailService = ccsSsoEmailService; - _userHelper = userHelper; - } + _dataContext = dataContext; + _httpClientFactory = httpClientFactory; + _applicationConfigurationInfo = applicationConfigurationInfo; + _ccsSsoEmailService = ccsSsoEmailService; + _userHelper = userHelper; + _serviceRoleGroupMapperService = serviceRoleGroupMapperService; + } - public async Task> GetPermissions(string userName, string serviceClientId, string organisationId) - { - var users = await _dataContext.User.Include(p => p.Party).ThenInclude(p => p.Person).ThenInclude(o => o.Organisation) - .Include(u => u.UserGroupMemberships).ThenInclude(ugm => ugm.OrganisationUserGroup) - .ThenInclude(oug => oug.GroupEligibleRoles).ThenInclude(gr => gr.OrganisationEligibleRole).ThenInclude(or => or.CcsAccessRole) - .ThenInclude(or => or.ServiceRolePermissions).ThenInclude(sr => sr.ServicePermission).ThenInclude(sr => sr.CcsService) - .Include(u => u.UserAccessRoles).ThenInclude(gr => gr.OrganisationEligibleRole).ThenInclude(or => or.CcsAccessRole) - .ThenInclude(or => or.ServiceRolePermissions).ThenInclude(sr => sr.ServicePermission).ThenInclude(sr => sr.CcsService) - .Where(u => !u.IsDeleted && u.UserName == userName).ToListAsync(); - - User user; - if (!string.IsNullOrWhiteSpace(organisationId)) - { - user = users.FirstOrDefault(u => u.Party.Person.Organisation.CiiOrganisationId == organisationId); - } - else - { - user = users.SingleOrDefault(u => u.UserType == Core.DbModel.Constants.UserType.Primary); - } + public async Task> GetPermissions(string userName, string serviceClientId, string organisationId) + { + var users = await _dataContext.User.Include(p => p.Party).ThenInclude(p => p.Person).ThenInclude(o => o.Organisation) + .Include(u => u.UserGroupMemberships).ThenInclude(ugm => ugm.OrganisationUserGroup) + .ThenInclude(oug => oug.GroupEligibleRoles).ThenInclude(gr => gr.OrganisationEligibleRole).ThenInclude(or => or.CcsAccessRole) + .ThenInclude(or => or.ServiceRolePermissions).ThenInclude(sr => sr.ServicePermission).ThenInclude(sr => sr.CcsService) + .Include(u => u.UserAccessRoles).ThenInclude(gr => gr.OrganisationEligibleRole).ThenInclude(or => or.CcsAccessRole) + .ThenInclude(or => or.ServiceRolePermissions).ThenInclude(sr => sr.ServicePermission).ThenInclude(sr => sr.CcsService) + .Where(u => !u.IsDeleted && u.UserName == userName).ToListAsync(); - var rolePermissions = user.UserAccessRoles.Where(uar => !uar.IsDeleted).Select(uar => new UserRolePermissionInfo - { - RoleKey = uar.OrganisationEligibleRole.CcsAccessRole.CcsAccessRoleNameKey, - RoleName = uar.OrganisationEligibleRole.CcsAccessRole.CcsAccessRoleName, - PermissionList = uar.OrganisationEligibleRole.CcsAccessRole.ServiceRolePermissions.Where(sp => sp.ServicePermission.CcsService.ServiceClientId == serviceClientId).Select(srp => srp.ServicePermission.ServicePermissionName).ToList() - }).ToList(); + User user; + if (!string.IsNullOrWhiteSpace(organisationId)) + { + user = users.FirstOrDefault(u => u.Party.Person.Organisation.CiiOrganisationId == organisationId); + } + else + { + user = users.SingleOrDefault(u => u.UserType == Core.DbModel.Constants.UserType.Primary); + } - if (user.UserGroupMemberships != null) - { - foreach (var userGroupMembership in user.UserGroupMemberships) - { - if (!userGroupMembership.IsDeleted && userGroupMembership.OrganisationUserGroup.GroupEligibleRoles != null) - { - - if (userGroupMembership.OrganisationUserGroup.GroupEligibleRoles.Any()) - { - foreach (var groupAccess in userGroupMembership.OrganisationUserGroup.GroupEligibleRoles.Where(x => !x.IsDeleted)) - { - var groupAccessRole = new UserRolePermissionInfo - { - RoleName = groupAccess.OrganisationEligibleRole.CcsAccessRole.CcsAccessRoleName, - RoleKey = groupAccess.OrganisationEligibleRole.CcsAccessRole.CcsAccessRoleNameKey, - PermissionList = groupAccess.OrganisationEligibleRole.CcsAccessRole.ServiceRolePermissions.Where(sp => sp.ServicePermission.CcsService.ServiceClientId == serviceClientId).Select(srp => srp.ServicePermission.ServicePermissionName).ToList() - }; - rolePermissions.Add(groupAccessRole); - } - } - } - } - } - - var permissions = rolePermissions.SelectMany(rp => rp.PermissionList, (r, p) => new ServicePermissionDto() - { - RoleKey = r.RoleKey, - RoleName = r.RoleName, - PermissionName = p - }).Distinct().ToList(); + var rolePermissions = user.UserAccessRoles.Where(uar => !uar.IsDeleted).Select(uar => new UserRolePermissionInfo + { + RoleKey = uar.OrganisationEligibleRole.CcsAccessRole.CcsAccessRoleNameKey, + RoleName = uar.OrganisationEligibleRole.CcsAccessRole.CcsAccessRoleName, + PermissionList = uar.OrganisationEligibleRole.CcsAccessRole.ServiceRolePermissions.Where(sp => sp.ServicePermission.CcsService.ServiceClientId == serviceClientId).Select(srp => srp.ServicePermission.ServicePermissionName).ToList() + }).ToList(); - return permissions; - } + if (user.UserGroupMemberships != null) + { + await GetGroupPermissions(serviceClientId, user, rolePermissions); + } - public async Task SendUserActivationEmailAsync(string email, bool isExpired = false) - { - _userHelper.ValidateUserName(email); + var permissions = rolePermissions.SelectMany(rp => rp.PermissionList, (r, p) => new ServicePermissionDto() + { + RoleKey = r.RoleKey, + RoleName = r.RoleName, + PermissionName = p + }).Distinct().ToList(); - var client = _httpClientFactory.CreateClient("default"); - client.BaseAddress = new Uri(_applicationConfigurationInfo.SecurityApiDetails.Url); - var url = $"security/users/activation-emails?is-expired={isExpired}"; - client.DefaultRequestHeaders.Add("X-API-Key", _applicationConfigurationInfo.SecurityApiDetails.ApiKey); + return permissions; + } - var list = new List>(); - list.Add(new KeyValuePair("email", email)); - HttpContent codeContent = new FormUrlEncodedContent(list); - await client.PostAsync(url, codeContent); - } + public async Task SendUserActivationEmailAsync(string email, bool isExpired = false) + { + _userHelper.ValidateUserName(email); + + var client = _httpClientFactory.CreateClient("default"); + client.BaseAddress = new Uri(_applicationConfigurationInfo.SecurityApiDetails.Url); + var url = $"security/users/activation-emails?is-expired={isExpired}"; + client.DefaultRequestHeaders.Add("X-API-Key", _applicationConfigurationInfo.SecurityApiDetails.ApiKey); + + var list = new List>(); + list.Add(new KeyValuePair("email", email)); + HttpContent codeContent = new FormUrlEncodedContent(list); + await client.PostAsync(url, codeContent); + } + + public async Task NominateUserAsync(string email) + { + _userHelper.ValidateUserName(email); + var url = _applicationConfigurationInfo.ConclaveSettings.BaseUrl + _applicationConfigurationInfo.ConclaveSettings.OrgRegistrationRoute; + await _ccsSsoEmailService.SendNominateEmailAsync(email, url); + } - public async Task NominateUserAsync(string email) + private async Task GetGroupPermissions(string serviceClientId, User user, List rolePermissions) + { + var userGroupsApprovalRequest = await _dataContext.UserAccessRolePending.Where(x => !x.IsDeleted && x.UserId == user.Id + && x.OrganisationUserGroupId != null && x.Status == (int)UserPendingRoleStaus.Pending).ToListAsync(); + + var serviceRoleGroups = await _serviceRoleGroupMapperService.OrgRolesToServiceRoleGroupsAsync(userGroupsApprovalRequest.Select(x => x.OrganisationEligibleRoleId).ToList()); + + foreach (var userGroupMembership in user.UserGroupMemberships) + { + if (!userGroupMembership.IsDeleted && userGroupMembership.OrganisationUserGroup.GroupEligibleRoles != null && userGroupMembership.OrganisationUserGroup.GroupEligibleRoles.Any()) { - _userHelper.ValidateUserName(email); - var url = _applicationConfigurationInfo.ConclaveSettings.BaseUrl + _applicationConfigurationInfo.ConclaveSettings.OrgRegistrationRoute; - await _ccsSsoEmailService.SendNominateEmailAsync(email, url); + foreach (var groupAccess in userGroupMembership.OrganisationUserGroup.GroupEligibleRoles.Where(x => !x.IsDeleted)) + { + if (_applicationConfigurationInfo.UserRoleApproval.Enable && userGroupsApprovalRequest.Any(x => x.OrganisationUserGroupId == groupAccess.OrganisationUserGroupId && serviceRoleGroups.Any(g => g.CcsServiceRoleMappings.Any(m => m.CcsAccessRoleId == groupAccess.OrganisationEligibleRole.CcsAccessRoleId)))) + { + continue; + } + rolePermissions.Add(GetGroupRolePermissions(serviceClientId, groupAccess)); + } } + } + } + + private static UserRolePermissionInfo GetGroupRolePermissions(string serviceClientId, Core.DbModel.Entity.OrganisationGroupEligibleRole groupAccess) + { + return new UserRolePermissionInfo + { + RoleName = groupAccess.OrganisationEligibleRole.CcsAccessRole.CcsAccessRoleName, + RoleKey = groupAccess.OrganisationEligibleRole.CcsAccessRole.CcsAccessRoleNameKey, + PermissionList = groupAccess.OrganisationEligibleRole.CcsAccessRole.ServiceRolePermissions.Where(sp => sp.ServicePermission.CcsService.ServiceClientId == serviceClientId).Select(srp => srp.ServicePermission.ServicePermissionName).ToList() + }; } + } } diff --git a/api/CcsSso.Core.Tests/UserServiceTests.cs b/api/CcsSso.Core.Tests/UserServiceTests.cs index 969571fa..c9ca3672 100644 --- a/api/CcsSso.Core.Tests/UserServiceTests.cs +++ b/api/CcsSso.Core.Tests/UserServiceTests.cs @@ -56,6 +56,7 @@ static async Task GetUserService(IDataContext dataContext) var mockHttpClientFactory = new Mock(); var mockCcsSsoEmailService = new Mock(); var mockUserHelperService = new Mock(); + var mockServiceRoleGroupMapperService = new Mock(); var applicationConfigurationInfo = new ApplicationConfigurationInfo { EnableAdapterNotifications = false, @@ -65,7 +66,7 @@ static async Task GetUserService(IDataContext dataContext) } }; await SetupTestDataAsync(dataContext); - return new UserService(dataContext, mockHttpClientFactory.Object, applicationConfigurationInfo, mockCcsSsoEmailService.Object, mockUserHelperService.Object); + return new UserService(dataContext, mockHttpClientFactory.Object, applicationConfigurationInfo, mockCcsSsoEmailService.Object, mockUserHelperService.Object, mockServiceRoleGroupMapperService.Object); } static async Task SetupTestDataAsync(IDataContext dataContext) From f9f402989c9b8fa57fedf483bf83769c0b693e63 Mon Sep 17 00:00:00 2001 From: brijrajsinh-bc <109584978+brijrajsinh-bc@users.noreply.github.com> Date: Thu, 6 Apr 2023 14:58:06 +0530 Subject: [PATCH 03/24] Feature/ppg 212 dev non associated email domain check via managed groups (#1604) * PPG-212 - Database Changes (#1582) * Change to create new request or auto approve it (#1589) * PPG-212 - Database Changes * Role Approval (Send fleet role request for approval) * Group with role approval for manage users (#1594) * Task 5020: PPG-212-Dev-Non-Associated Email Domain Check via Managed Groups (#1595) * PPG-212 - Database Changes * Role Approval (Send fleet role request for approval) * PPG 212 - Request role approval change * PPG 212 - Fix merge issue * PPG 212 - User role approval changes * PPG 212 - codeclimate fix (#1596) * PPG-212 - Database Changes * Role Approval (Send fleet role request for approval) * PPG 212 - Request role approval change * PPG 212 - Fix merge issue * PPG 212 - User role approval changes * PPG 212 - Code climate fix * PR-PPG-212-non-associated-email-domain-check-via-managed-groups (#1600) * Group related changes role approval changes * Invalid user added/removed case has been handled * paging has been added * Minor changes to the property case * Security api changes to remove pending role approval for group and bugs for manage user solved (#1603) --------- Co-authored-by: brijeshpatel-bc <108462846+brijeshpatel-bc@users.noreply.github.com> Co-authored-by: VijayHirudayasamy-bc <103512567+VijayHirudayasamy-bc@users.noreply.github.com> --- .../External/IOrganisationGroupService.cs | 3 + .../IServiceRoleGroupMapperService.cs | 2 +- .../Dtos/External/OrganisationGroupInfo.cs | 9 + .../OrganisationProfileController.cs | 36 +++- .../External/OrganisationGroupService.cs | 159 +++++++++++++++++- .../External/ServiceRoleGroupMapperService.cs | 16 +- .../UserProfileRoleApprovalService.cs | 10 +- .../External/UserProfileService.cs | 22 ++- .../External/OrganisationGroupServiceTest.cs | 3 +- 9 files changed, 234 insertions(+), 26 deletions(-) diff --git a/api/CcsSso.Core.Domain/Contracts/External/IOrganisationGroupService.cs b/api/CcsSso.Core.Domain/Contracts/External/IOrganisationGroupService.cs index a4f7ed35..87023c0a 100644 --- a/api/CcsSso.Core.Domain/Contracts/External/IOrganisationGroupService.cs +++ b/api/CcsSso.Core.Domain/Contracts/External/IOrganisationGroupService.cs @@ -21,6 +21,9 @@ public interface IOrganisationGroupService Task GetServiceRoleGroupAsync(string ciiOrganisationId, int groupId); + Task GetGroupUsersPendingRequestSummary(int groupId, string ciiOrgId, ResultSetCriteria resultSetCriteria, bool isPendingApproval); + + Task UpdateServiceRoleGroupAsync(string ciiOrganisationId, int groupId, OrganisationServiceRoleGroupRequestInfo organisationServiceRoleGroupRequestInfo); } } diff --git a/api/CcsSso.Core.Domain/Contracts/External/IServiceRoleGroupMapperService.cs b/api/CcsSso.Core.Domain/Contracts/External/IServiceRoleGroupMapperService.cs index 9f235097..001efff5 100644 --- a/api/CcsSso.Core.Domain/Contracts/External/IServiceRoleGroupMapperService.cs +++ b/api/CcsSso.Core.Domain/Contracts/External/IServiceRoleGroupMapperService.cs @@ -17,6 +17,6 @@ public interface IServiceRoleGroupMapperService Task> ServiceRoleGroupsWithApprovalRequiredRoleAsync(); - Task RemoveApprovalRequiredRoleGroupOtherRolesAsync(List organisationEligibleRoles); + Task RemoveApprovalRequiredRoleGroupOtherRolesAsync(List organisationEligibleRoles, string userName); } } diff --git a/api/CcsSso.Core.Domain/Dtos/External/OrganisationGroupInfo.cs b/api/CcsSso.Core.Domain/Dtos/External/OrganisationGroupInfo.cs index 5f24220c..df460f4a 100644 --- a/api/CcsSso.Core.Domain/Dtos/External/OrganisationGroupInfo.cs +++ b/api/CcsSso.Core.Domain/Dtos/External/OrganisationGroupInfo.cs @@ -74,6 +74,14 @@ public class GroupServiceRoleGroup public string Description { get; set; } } + public class GroupUserListResponse:PaginationInfo + { + public int groupId { get; set; } + + public List GroupUser { get; set; } + + } + public class GroupUser { public string UserId { get; set; } @@ -81,6 +89,7 @@ public class GroupUser public string Name { get; set; } public bool IsAdmin { get; set; } = false; + public bool IsPendingApproval { get; set; } = false; } public class OrganisationGroupRolePatchInfo diff --git a/api/CcsSso.Core.ExternalApi/Controllers/OrganisationProfileController.cs b/api/CcsSso.Core.ExternalApi/Controllers/OrganisationProfileController.cs index 92e00ad5..e330acf8 100644 --- a/api/CcsSso.Core.ExternalApi/Controllers/OrganisationProfileController.cs +++ b/api/CcsSso.Core.ExternalApi/Controllers/OrganisationProfileController.cs @@ -1100,6 +1100,40 @@ public async Task UpdateOrganisationServiceRoleGroup(string organisationId, int { await _organisationGroupService.UpdateServiceRoleGroupAsync(organisationId, groupId, organisationServiceRoleGroupRequestInfo); } + + /// + /// Get organisation group users and their role approval status + /// + /// Ok + /// Unauthorised + /// Forbidden + /// Resource not found + /// + /// Sample request: + /// + /// GET /organisations/1/groups/1/groupusers + /// + /// + [HttpGet("{organisationId}/groups/{groupId}/groupusers")] + [ClaimAuthorise("ORG_ADMINISTRATOR")] + [OrganisationAuthorise("ORGANISATION")] + [SwaggerOperation(Tags = new[] { "Organisation Group" })] + [ProducesResponseType(typeof(GroupUserListResponse), 200)] + public async Task GetGroupUsersPendingRequestSummary(string organisationId, int groupId, + [FromQuery] ResultSetCriteria resultSetCriteria, + [FromQuery(Name = "is-pending-approval")] bool isPendingApproval = false) + { + resultSetCriteria ??= new ResultSetCriteria + { + CurrentPage = 1, + PageSize = 10 + }; + resultSetCriteria.CurrentPage = resultSetCriteria.CurrentPage <= 0 ? 1 : resultSetCriteria.CurrentPage; + resultSetCriteria.PageSize = resultSetCriteria.PageSize <= 0 ? 10 : resultSetCriteria.PageSize; + + return await _organisationGroupService.GetGroupUsersPendingRequestSummary(groupId, organisationId, resultSetCriteria,isPendingApproval); + } + #endregion #region Organisation IdentityProviders @@ -1431,7 +1465,7 @@ public async Task AutoValidateOrganisationTypeswitch(string organisationId, Orga [HttpPost("{ciiOrganisationId}/autovalidationjob")] [SwaggerOperation(Tags = new[] { "AutoValidation" })] [ProducesResponseType(typeof(string), 200)] - public async Task> AutovalidationJob(string ciiOrganisationId) + public async Task> AutovalidationJob(string ciiOrganisationId) { return await _organisationService.AutoValidateOrganisationJob(ciiOrganisationId); } diff --git a/api/CcsSso.Core.Service/External/OrganisationGroupService.cs b/api/CcsSso.Core.Service/External/OrganisationGroupService.cs index 4ab8b0ba..791fda89 100644 --- a/api/CcsSso.Core.Service/External/OrganisationGroupService.cs +++ b/api/CcsSso.Core.Service/External/OrganisationGroupService.cs @@ -1,3 +1,4 @@ +using CcsSso.Core.DbModel.Constants; using CcsSso.Core.DbModel.Entity; using CcsSso.Core.Domain.Contracts; using CcsSso.Core.Domain.Contracts.External; @@ -11,10 +12,12 @@ using CcsSso.Shared.Domain.Constants; using CcsSso.Shared.Domain.Helpers; using Microsoft.EntityFrameworkCore; +using StackExchange.Redis; using System; using System.Collections.Generic; using System.Linq; using System.Text; +using System.Text.RegularExpressions; using System.Threading.Tasks; namespace CcsSso.Core.Service.External @@ -29,11 +32,13 @@ public class OrganisationGroupService : IOrganisationGroupService private readonly ApplicationConfigurationInfo _appConfigInfo; private readonly IServiceRoleGroupMapperService _serviceRoleGroupMapperService; private readonly IOrganisationProfileService _organisationService; + private readonly IUserProfileRoleApprovalService _userProfileRoleApprovalService; public OrganisationGroupService(IDataContext dataContext, IUserProfileHelperService userProfileHelperService, IAuditLoginService auditLoginService, ICcsSsoEmailService ccsSsoEmailService, IWrapperCacheService wrapperCacheService, ApplicationConfigurationInfo appConfigInfo, IServiceRoleGroupMapperService serviceRoleGroupMapperService, - IOrganisationProfileService organisationService) + IOrganisationProfileService organisationService, + IUserProfileRoleApprovalService userProfileRoleApprovalService) { _dataContext = dataContext; _userProfileHelperService = userProfileHelperService; @@ -43,6 +48,7 @@ public OrganisationGroupService(IDataContext dataContext, IUserProfileHelperServ _appConfigInfo = appConfigInfo; _serviceRoleGroupMapperService = serviceRoleGroupMapperService; _organisationService = organisationService; + _userProfileRoleApprovalService = userProfileRoleApprovalService; } public async Task CreateGroupAsync(string ciiOrganisationId, OrganisationGroupNameInfo organisationGroupNameInfo) @@ -116,6 +122,8 @@ public async Task DeleteGroupAsync(string ciiOrganisationId, int groupId) await _dataContext.SaveChangesAsync(); + await RemoveGroupRolePendingRequest(group); + // Log await _auditLoginService.CreateLogAsync(AuditLogEvent.GroupeDelete, AuditLogApplication.ManageGroup, $"GroupId:{group.Id}, GroupName:{group.UserGroupName}, OrganisationId:{ciiOrganisationId}"); @@ -149,18 +157,27 @@ public async Task GetGroupAsync(string ciiOrganis { Id = gr.OrganisationEligibleRole.Id, Name = gr.OrganisationEligibleRole.CcsAccessRole.CcsAccessRoleName - }).ToList(), - Users = group.UserGroupMemberships.Where(ugm => !ugm.IsDeleted).Select(ugm => new GroupUser - { - UserId = ugm.User.UserName, - Name = $"{ugm.User.Party.Person.FirstName} {ugm.User.Party.Person.LastName}", - IsAdmin = ugm.User.UserAccessRoles.Any(r => !r.IsDeleted && r.OrganisationEligibleRole.CcsAccessRole.CcsAccessRoleNameKey == Contstant.OrgAdminRoleNameKey && !r.OrganisationEligibleRole.IsDeleted) }).ToList() + }; + var isApprovalRequired = group.GroupEligibleRoles.Any(x => !x.OrganisationEligibleRole.IsDeleted && x.OrganisationEligibleRole.CcsAccessRole.ApprovalRequired == 1); + + organisationGroupResponseInfo.Users = group.UserGroupMemberships.Where(ugm => !ugm.IsDeleted).Select(ugm => new GroupUser + { + UserId = ugm.User.UserName, + Name = $"{ugm.User.Party.Person.FirstName} {ugm.User.Party.Person.LastName}", + IsAdmin = ugm.User.UserAccessRoles.Any(r => !r.IsDeleted && r.OrganisationEligibleRole.CcsAccessRole.CcsAccessRoleNameKey == Contstant.OrgAdminRoleNameKey && !r.OrganisationEligibleRole.IsDeleted), + IsPendingApproval = !isApprovalRequired ? false : getUserRolePendingStatus(ugm), + }).ToList(); return organisationGroupResponseInfo; } + private bool getUserRolePendingStatus(UserGroupMembership ugm) + { + return _dataContext.UserAccessRolePending.OrderByDescending(y => y.Id).FirstOrDefault(x => !x.IsDeleted && x.UserId == ugm.User.Id && x.Status == (int)UserPendingRoleStaus.Pending) != null; + } + public async Task GetGroupsAsync(string ciiOrganisationId, string searchString = null) { var organisation = await _dataContext.Organisation @@ -191,7 +208,7 @@ public async Task GetGroupsAsync(string ciiOrganisationId public async Task UpdateGroupAsync(string ciiOrganisationId, int groupId, OrganisationGroupRequestInfo organisationGroupRequestInfo) { var group = await _dataContext.OrganisationUserGroup - .Include(g => g.GroupEligibleRoles).ThenInclude(r => r.OrganisationEligibleRole) + .Include(g => g.GroupEligibleRoles).ThenInclude(r => r.OrganisationEligibleRole).ThenInclude(x => x.CcsAccessRole) .Include(g => g.UserGroupMemberships).ThenInclude(ugm => ugm.User) .FirstOrDefaultAsync(g => !g.IsDeleted && g.Id == groupId && g.Organisation.CiiOrganisationId == ciiOrganisationId); @@ -372,6 +389,8 @@ public async Task UpdateGroupAsync(string ciiOrganisationId, int groupId, Organi group.MfaEnabled = mfaEnableRoleExists; await _dataContext.SaveChangesAsync(); + await VerifyAndCreateGroupRolePendingRequest(group, ciiOrganisationId); + //Log if (hasNameChanged) { @@ -416,6 +435,130 @@ await _auditLoginService.CreateLogAsync(AuditLogEvent.GroupeUserRemove, AuditLog await _wrapperCacheService.RemoveCacheAsync(invalidatingCacheKeys.ToArray()); } + private async Task VerifyAndCreateGroupRolePendingRequest(OrganisationUserGroup group, string ciiOrganisationId) + { + if (_appConfigInfo.UserRoleApproval.Enable) + { + var org = await _dataContext.Organisation.FirstOrDefaultAsync(x => x.CiiOrganisationId == ciiOrganisationId); + var orgDomain = org?.DomainName?.ToLower(); + + var latestExistingUserNames = group.UserGroupMemberships.Where(x => !x.IsDeleted).Select(ugm => ugm.User).ToList(); + var userHasInValidDomain = latestExistingUserNames.Where(user => user.UserName.ToLower().Split('@')?[1] != orgDomain).ToList(); + + if (userHasInValidDomain.Any()) + { + await RemoveGroupRolePendingRequest(group, userHasInValidDomain); + + List approvalRequiredRoles = new(); + foreach (var role in group.GroupEligibleRoles) + { + if (role.OrganisationEligibleRole.CcsAccessRole.ApprovalRequired == (int)RoleApprovalRequiredStatus.ApprovalRequired) + { + approvalRequiredRoles.Add(role.OrganisationEligibleRoleId); + } + } + + if (approvalRequiredRoles.Any()) + { + foreach (var user in userHasInValidDomain) + { + await _userProfileRoleApprovalService.CreateUserRolesPendingForApprovalAsync(new UserProfileEditRequestInfo + { + UserName = user.UserName, + OrganisationId = group.Organisation.CiiOrganisationId, + Detail = new UserRequestDetail + { + GroupId = group.Id, + RoleIds = approvalRequiredRoles + } + }); + } + } + else + { + // remove any pending request exists for this group + await RemoveGroupRolePendingRequest(group); + } + } + else + { + // remove any pending request exists for this group + await RemoveGroupRolePendingRequest(group); + } + } + } + + private async Task RemoveGroupRolePendingRequest(OrganisationUserGroup group) + { + var pendingGroupRequest = await _dataContext.UserAccessRolePending.Where(x => !x.IsDeleted + && x.OrganisationUserGroupId == group.Id + && x.Status == (int)UserPendingRoleStaus.Pending).ToListAsync(); + + foreach (var pendingRequest in pendingGroupRequest) + { + pendingRequest.IsDeleted = true; + pendingRequest.Status = (int)UserPendingRoleStaus.Removed; + } + await _dataContext.SaveChangesAsync(); + } + + private async Task RemoveGroupRolePendingRequest(OrganisationUserGroup group, List users) + { + var pendingGroupRequest = await _dataContext.UserAccessRolePending.Where(x => !x.IsDeleted + && x.OrganisationUserGroupId == group.Id + && !users.Select(user => user.Id).Contains(x.UserId) + && x.Status == (int)UserPendingRoleStaus.Pending).ToListAsync(); + + foreach (var pendingRequest in pendingGroupRequest) + { + pendingRequest.IsDeleted = true; + pendingRequest.Status = (int)UserPendingRoleStaus.Removed; + } + await _dataContext.SaveChangesAsync(); + } + + public async Task GetGroupUsersPendingRequestSummary(int groupId, string ciiOrgId, ResultSetCriteria resultSetCriteria, bool isPendingApproval) + { + var group = await _dataContext.OrganisationUserGroup + .Include(g => g.UserGroupMemberships).ThenInclude(ugm => ugm.User) + .FirstOrDefaultAsync(g => !g.IsDeleted && g.Id == groupId && g.Organisation.CiiOrganisationId == ciiOrgId); + + if (group == null) + { + throw new ResourceNotFoundException(); + } + + var existingUserIds = group.UserGroupMemberships.Where(x => !x.IsDeleted).Select(ugm => ugm.UserId); + + var pendingRequests = await _dataContext.UserAccessRolePending + .Where(x => !x.IsDeleted && existingUserIds.Contains(x.UserId) && x.Status == (int)UserPendingRoleStaus.Pending) + .ToListAsync(); + + var filteredUserIds = isPendingApproval ? existingUserIds.Where(x => !pendingRequests.Any(y => y.UserId == x)) : existingUserIds.Where(x => pendingRequests.Any(y => y.UserId == x)); + + var usersQuery = _dataContext.User.Include(u => u.Party).ThenInclude(p => p.Person).Where(user => !user.IsDeleted && filteredUserIds.Contains(user.Id)).OrderBy(u => u.UserName); + + var pagedResult = await _dataContext.GetPagedResultAsync(usersQuery, resultSetCriteria); + + var groupUserListResponse = new GroupUserListResponse + { + groupId = groupId, + CurrentPage = pagedResult.CurrentPage, + PageCount = pagedResult.PageCount, + RowCount = pagedResult.RowCount, + GroupUser = pagedResult.Results?.Select(up => new GroupUser + { + UserId = up.UserName, + IsPendingApproval = isPendingApproval, + Name = $"{up.Party.Person.FirstName} {up.Party.Person.LastName}", + }).ToList() ?? new List() + }; + + return groupUserListResponse; + } + + + public async Task GetServiceRoleGroupAsync(string ciiOrganisationId, int groupId) { if (!_appConfigInfo.ServiceRoleGroupSettings.Enable) diff --git a/api/CcsSso.Core.Service/External/ServiceRoleGroupMapperService.cs b/api/CcsSso.Core.Service/External/ServiceRoleGroupMapperService.cs index 255fc872..dd769289 100644 --- a/api/CcsSso.Core.Service/External/ServiceRoleGroupMapperService.cs +++ b/api/CcsSso.Core.Service/External/ServiceRoleGroupMapperService.cs @@ -124,16 +124,26 @@ public async Task> ServiceRoleGroupsWithApprovalRequir // This method will remove roles that are part of approval required Service Role Group but it self not required approval // This normal roles will be assigned together with approval required role, once it is approved. - public async Task RemoveApprovalRequiredRoleGroupOtherRolesAsync(List organisationEligibleRoles) + public async Task RemoveApprovalRequiredRoleGroupOtherRolesAsync(List organisationEligibleRoles, string userName) { var servicesWithApprovalRequiredRole = await ServiceRoleGroupsWithApprovalRequiredRoleAsync(); + var userExistingRoles = await _dataContext.User + .Include(u => u.UserAccessRoles).ThenInclude(gr => gr.OrganisationEligibleRole).ThenInclude(or => or.CcsAccessRole) + .Where(u => u.UserName == userName && !u.IsDeleted && u.UserType == UserType.Primary).ToListAsync(); foreach (var approvalRoleService in servicesWithApprovalRequiredRole) { // Remove all the roles of approval required service except approval required role. // All roles of approval required service will be assigned once approval required role is approved. - var removeRoles = approvalRoleService.CcsServiceRoleMappings.Where(x => x.CcsAccessRole.ApprovalRequired != 1).Select(x => x.CcsAccessRoleId).ToList(); - organisationEligibleRoles.RemoveAll(x => removeRoles.Contains(x.CcsAccessRoleId)); + var removeRoles = approvalRoleService.CcsServiceRoleMappings.Where(x => x.CcsAccessRole.ApprovalRequired == (int)RoleApprovalRequiredStatus.ApprovalNotRequired).Select(x => x.CcsAccessRoleId).ToList(); + + foreach (var removeRole in removeRoles) + { + if (!userExistingRoles.Any(u => u.UserAccessRoles.Any(a => !a.IsDeleted && a.OrganisationEligibleRole.CcsAccessRoleId == removeRole))) + { + organisationEligibleRoles.RemoveAll(x => x.CcsAccessRoleId == removeRole); + } + } } } diff --git a/api/CcsSso.Core.Service/External/UserProfileRoleApprovalService.cs b/api/CcsSso.Core.Service/External/UserProfileRoleApprovalService.cs index e7abd8a3..f00b84fb 100644 --- a/api/CcsSso.Core.Service/External/UserProfileRoleApprovalService.cs +++ b/api/CcsSso.Core.Service/External/UserProfileRoleApprovalService.cs @@ -465,7 +465,7 @@ public async Task CreateUserRolesPendingForApprovalAsync(UserProfileEditRequestI List userAccessRolePendingAutoApproved = new List(); var userAccessRoleIds = await _dataContext.UserAccessRole.Where(x => !x.IsDeleted && x.UserId == user.Id).Select(x => x.OrganisationEligibleRoleId).ToListAsync(); - var userGroupApprovedRoleIds = await GetUserGroupApprovedRoleIds(user); + var userGroupApprovedRoleIds = await GetUserGroupApprovedRoleIds(user, groupId); roles.ForEach((roleId) => { @@ -532,18 +532,22 @@ private async Task AssignRoleToUserForAutoApproved(User user, List> GetUserGroupApprovedRoleIds(User user) + private async Task> GetUserGroupApprovedRoleIds(User user, int? groupId) { List userGroupApprovedRoleIds = new List(); var userGroupIds = await _dataContext.UserGroupMembership.Where(x => !x.IsDeleted && x.UserId == user.Id).Select(x => x.OrganisationUserGroupId).ToListAsync(); + if (groupId != null) + { + userGroupIds = userGroupIds.Where(x => x != groupId).ToList(); + } var userGroupRoleIds = await _dataContext.OrganisationGroupEligibleRole.Where(x => !x.IsDeleted && userGroupIds.Contains(x.OrganisationUserGroupId)).Select(x => x.OrganisationEligibleRoleId).ToListAsync(); userGroupRoleIds.ForEach((userGroupRoleId) => { var lastUserAccessRolePedningRequest = user.UserAccessRolePending .OrderByDescending(o => o.CreatedOnUtc) - .FirstOrDefault(x => x.IsDeleted && x.OrganisationUserGroupId != null && x.OrganisationEligibleRoleId == userGroupRoleId); + .FirstOrDefault(x => x.OrganisationUserGroupId != null && x.OrganisationEligibleRoleId == userGroupRoleId); if (lastUserAccessRolePedningRequest?.Status == (int)UserPendingRoleStaus.Approved) { diff --git a/api/CcsSso.Core.Service/External/UserProfileService.cs b/api/CcsSso.Core.Service/External/UserProfileService.cs index 9c5c9134..7fe5762c 100644 --- a/api/CcsSso.Core.Service/External/UserProfileService.cs +++ b/api/CcsSso.Core.Service/External/UserProfileService.cs @@ -244,8 +244,8 @@ await _userProfileRoleApprovalService.CreateUserRolesPendingForApprovalAsync(new { GroupId = group.Key, RoleIds = group.Value - } - }); + }, + }, sendEmailNotification: false); } } @@ -450,7 +450,8 @@ public async Task GetUserAsync(string userName, bool is { var userGroupsApprovalRequest = await _dataContext.UserAccessRolePending.Where(x => !x.IsDeleted && x.UserId == user.Id && x.OrganisationUserGroupId != null && x.Status == (int)UserPendingRoleStaus.Pending).ToListAsync(); - + var userGroupsApprovalServiceRoleGroups = await _serviceRoleGroupMapperService.OrgRolesToServiceRoleGroupsAsync(userGroupsApprovalRequest.Select(x => x.OrganisationEligibleRoleId).ToList()); + foreach (var userGroupMembership in user.UserGroupMemberships) { if (!userGroupMembership.IsDeleted && userGroupMembership.OrganisationUserGroup.GroupEligibleRoles != null) @@ -461,7 +462,7 @@ public async Task GetUserAsync(string userName, bool is // For every role in the group populate the group role info foreach (var groupAccess in userGroupMembership.OrganisationUserGroup.GroupEligibleRoles.Where(x => !x.IsDeleted)) { - if (_appConfigInfo.UserRoleApproval.Enable && userGroupsApprovalRequest.Any(x => x.OrganisationUserGroupId == groupAccess.OrganisationUserGroupId && x.OrganisationEligibleRoleId == groupAccess.OrganisationEligibleRoleId)) + if (_appConfigInfo.UserRoleApproval.Enable && userGroupsApprovalServiceRoleGroups.Any(x => x.CcsServiceRoleMappings.Any(m => m.CcsAccessRoleId == groupAccess.OrganisationEligibleRole.CcsAccessRoleId))) { continue; } @@ -1014,12 +1015,14 @@ join cr in _dataContext.CcsAccessRole on er.CcsAccessRoleId equals cr.Id hasProfileInfoChanged = true; } - // Set groups - if (_appConfigInfo.UserRoleApproval.Enable && !isUserDomainValid && userProfileRequestInfo.Detail.GroupIds != null && userProfileRequestInfo.Detail.GroupIds.Any()) + // list of new groups to check for approval required role + var newlyAddedGroupIds = userProfileRequestInfo.Detail.GroupIds.Where(x => !previousGroups.Contains(x)).ToList(); + if (_appConfigInfo.UserRoleApproval.Enable && !isUserDomainValid && userProfileRequestInfo.Detail.GroupIds != null && newlyAddedGroupIds.Any()) { - groupsWithRoleRequiredApproval = GetGroupsWithApprovalOrgRole(organisation.UserGroups, userProfileRequestInfo.Detail.GroupIds); + groupsWithRoleRequiredApproval = GetGroupsWithApprovalOrgRole(organisation.UserGroups, newlyAddedGroupIds); } + // Set groups var userGroupMemberships = new List(); userProfileRequestInfo.Detail.GroupIds?.ForEach((groupId) => { @@ -2097,7 +2100,7 @@ private async Task ConvertServiceRoleGroupToUserRole if (userDomain?.Trim() != orgDoamin?.Trim()) { - await _serviceRoleGroupMapperService.RemoveApprovalRequiredRoleGroupOtherRolesAsync(organisationEligibleRoles); + await _serviceRoleGroupMapperService.RemoveApprovalRequiredRoleGroupOtherRolesAsync(organisationEligibleRoles, userProfileServiceRoleGroupEditRequestInfo?.UserName); } roleIds = organisationEligibleRoles.Select(x => x.Id).ToList(); @@ -2283,6 +2286,7 @@ private async Task CreatePendingRoleRequestForGroup(List !x.IsDeleted && x.UserId == userId && x.Status == (int)UserPendingRoleStaus.Pending && x.OrganisationUserGroupId != null).ToListAsync(); + // remove request that no longer required foreach (var existRequest in userPendingGroups) { if (!groupsWithRoleRequiredApproval.Any(x => x.Key == existRequest.OrganisationUserGroupId && x.Value.Contains(existRequest.OrganisationEligibleRoleId))) @@ -2314,7 +2318,7 @@ await _userProfileRoleApprovalService.CreateUserRolesPendingForApprovalAsync(new GroupId = group.Key, RoleIds = group.Value } - }); + }, sendEmailNotification: false); } } diff --git a/api/CcsSso.Core.Tests/External/OrganisationGroupServiceTest.cs b/api/CcsSso.Core.Tests/External/OrganisationGroupServiceTest.cs index f67d1e73..e4e43557 100644 --- a/api/CcsSso.Core.Tests/External/OrganisationGroupServiceTest.cs +++ b/api/CcsSso.Core.Tests/External/OrganisationGroupServiceTest.cs @@ -972,9 +972,10 @@ public static OrganisationGroupService UserService(IDataContext dataContext) ApplicationConfigurationInfo applicationConfigurationInfo = new(); var mockRolesToServiceRoleGroupMapperService = new Mock(); var mockOrganisationProfileService = new Mock(); + var mockUserProfileRoleApprovalService = new Mock(); var service = new OrganisationGroupService(dataContext, userProfileHelperService, mockAuditLoginService.Object, mockEmailService.Object, - mockCacheService.Object, applicationConfigurationInfo, mockRolesToServiceRoleGroupMapperService.Object, mockOrganisationProfileService.Object); + mockCacheService.Object, applicationConfigurationInfo, mockRolesToServiceRoleGroupMapperService.Object, mockOrganisationProfileService.Object, mockUserProfileRoleApprovalService.Object); return service; } From 33a24e3699b5aad1c1a889f6a3f18e55885299cc Mon Sep 17 00:00:00 2001 From: brijeshpatel-bc <108462846+brijeshpatel-bc@users.noreply.github.com> Date: Thu, 6 Apr 2023 15:51:11 +0530 Subject: [PATCH 04/24] Task 5084: PPG 212- Dev -Non-Associated Email Domain Check via Managed Groups (#1606) * PPG-212 - Database Changes (#1582) * Change to create new request or auto approve it (#1589) * PPG-212 - Database Changes * Role Approval (Send fleet role request for approval) * Group with role approval for manage users (#1594) * Task 5020: PPG-212-Dev-Non-Associated Email Domain Check via Managed Groups (#1595) * PPG-212 - Database Changes * Role Approval (Send fleet role request for approval) * PPG 212 - Request role approval change * PPG 212 - Fix merge issue * PPG 212 - User role approval changes * PPG 212 - codeclimate fix (#1596) * PPG-212 - Database Changes * Role Approval (Send fleet role request for approval) * PPG 212 - Request role approval change * PPG 212 - Fix merge issue * PPG 212 - User role approval changes * PPG 212 - Code climate fix * PR-PPG-212-non-associated-email-domain-check-via-managed-groups (#1600) * Group related changes role approval changes * Invalid user added/removed case has been handled * paging has been added * Minor changes to the property case * Security api changes to remove pending role approval for group and bugs for manage user solved (#1603) * Task 5084: PPG 212- Dev -Non-Associated Email Domain Check via Managed Groups (#1605) * PPG-212 - Database Changes * Role Approval (Send fleet role request for approval) * PPG 212 - Request role approval change * PPG 212 - Fix merge issue * PPG 212 - User role approval changes * PPG 212 - Code climate fix * Task 5084: PPG 212- Dev -Non-Associated Email Domain Check via Managed Groups --------- Co-authored-by: brijrajsinh-bc <109584978+brijrajsinh-bc@users.noreply.github.com> Co-authored-by: chudasama brijrajsinh Co-authored-by: VijayHirudayasamy-bc <103512567+VijayHirudayasamy-bc@users.noreply.github.com> --- .../Jobs/RoleApprovalLinkExpiredJob.cs | 1 + .../RoleApprovalLinkExpiredService.cs | 34 +++++++++++++++---- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/api/CcsSso.Core.JobScheduler/Jobs/RoleApprovalLinkExpiredJob.cs b/api/CcsSso.Core.JobScheduler/Jobs/RoleApprovalLinkExpiredJob.cs index 405638c2..2bdfba66 100644 --- a/api/CcsSso.Core.JobScheduler/Jobs/RoleApprovalLinkExpiredJob.cs +++ b/api/CcsSso.Core.JobScheduler/Jobs/RoleApprovalLinkExpiredJob.cs @@ -83,6 +83,7 @@ public async Task> GetPendingRoleApproval() var userAccessRolePendingAllList = await _dataContext.UserAccessRolePending .Include(u => u.OrganisationEligibleRole).ThenInclude(or => or.CcsAccessRole) .Where(u => !u.IsDeleted && u.Status == (int)UserPendingRoleStaus.Pending) + .OrderBy(u => u.CreatedOnUtc) .ToListAsync(); return userAccessRolePendingAllList; diff --git a/api/CcsSso.Core.JobScheduler/Services/RoleApprovalLinkExpiredService.cs b/api/CcsSso.Core.JobScheduler/Services/RoleApprovalLinkExpiredService.cs index 0d9d2106..6613bba4 100644 --- a/api/CcsSso.Core.JobScheduler/Services/RoleApprovalLinkExpiredService.cs +++ b/api/CcsSso.Core.JobScheduler/Services/RoleApprovalLinkExpiredService.cs @@ -41,10 +41,10 @@ public RoleApprovalLinkExpiredService(IServiceScopeFactory factory, public async Task PerformJobAsync(List pendingRoles) { - var approvalRoleConfig = await _dataContext.RoleApprovalConfiguration.Where(x => !x.IsDeleted).ToListAsync(); List expiredUserAccessRolePendingList = new(); + List relatedExpiredUserAccessRolePendingList = new(); foreach (var role in pendingRoles) { @@ -53,10 +53,35 @@ public async Task PerformJobAsync(List pendingRoles) if (roleExpireTime < DateTime.UtcNow) { - expiredUserAccessRolePendingList.Add(role); + var isExistInRelatedList = relatedExpiredUserAccessRolePendingList.Any(x => x.Id == role.Id); + + if (!isExistInRelatedList) + { + expiredUserAccessRolePendingList.Add(role); + + var relatedPendingRoles = pendingRoles.Where(x => x.Id != role.Id && x.UserId == role.UserId).ToList(); + relatedExpiredUserAccessRolePendingList.AddRange(relatedPendingRoles); + } } } + await ProcessRelatedExpiredUserAccessRolePending(relatedExpiredUserAccessRolePendingList); + await ProcessExpiredUserAccessRolePending(expiredUserAccessRolePendingList); + } + + private async Task ProcessRelatedExpiredUserAccessRolePending(List relatedExpiredUserAccessRolePendingList) + { + _logger.LogInformation($"Total number of related expired Roles: {relatedExpiredUserAccessRolePendingList.Count()}"); + + if (relatedExpiredUserAccessRolePendingList.Any()) + { + await RemoveExpiredApprovalPendingRolesAsync(relatedExpiredUserAccessRolePendingList); + _logger.LogInformation($"Successfully updated the related expired roles"); + } + } + + private async Task ProcessExpiredUserAccessRolePending(List expiredUserAccessRolePendingList) + { _logger.LogInformation($"Total number of expired Roles: {expiredUserAccessRolePendingList.Count()}"); if (expiredUserAccessRolePendingList.Any()) @@ -67,7 +92,6 @@ public async Task PerformJobAsync(List pendingRoles) _logger.LogInformation($"Sending email if it is eligible"); await SendEmail(expiredUserAccessRolePendingList); _logger.LogInformation($"Finished sending email"); - } } @@ -78,9 +102,7 @@ private async Task RemoveExpiredApprovalPendingRolesAsync(List { l.IsDeleted = true; l.Status = (int)UserPendingRoleStaus.Expired; }); - await _dataContext.SaveChangesAsync(); - - + await _dataContext.SaveChangesAsync(); } } From 67e6b104676b2205b3586d0b108d214641955259 Mon Sep 17 00:00:00 2001 From: brijrajsinh-bc <109584978+brijrajsinh-bc@users.noreply.github.com> Date: Thu, 6 Apr 2023 17:43:17 +0530 Subject: [PATCH 05/24] Feature/ppg 212 dev non associated email domain check via managed groups (#1608) * PPG-212 - Database Changes (#1582) * Change to create new request or auto approve it (#1589) * PPG-212 - Database Changes * Role Approval (Send fleet role request for approval) * Group with role approval for manage users (#1594) * Task 5020: PPG-212-Dev-Non-Associated Email Domain Check via Managed Groups (#1595) * PPG-212 - Database Changes * Role Approval (Send fleet role request for approval) * PPG 212 - Request role approval change * PPG 212 - Fix merge issue * PPG 212 - User role approval changes * PPG 212 - codeclimate fix (#1596) * PPG-212 - Database Changes * Role Approval (Send fleet role request for approval) * PPG 212 - Request role approval change * PPG 212 - Fix merge issue * PPG 212 - User role approval changes * PPG 212 - Code climate fix * PR-PPG-212-non-associated-email-domain-check-via-managed-groups (#1600) * Group related changes role approval changes * Invalid user added/removed case has been handled * paging has been added * Minor changes to the property case * Security api changes to remove pending role approval for group and bugs for manage user solved (#1603) * Task 5084: PPG 212- Dev -Non-Associated Email Domain Check via Managed Groups (#1605) * PPG-212 - Database Changes * Role Approval (Send fleet role request for approval) * PPG 212 - Request role approval change * PPG 212 - Fix merge issue * PPG 212 - User role approval changes * PPG 212 - Code climate fix * Task 5084: PPG 212- Dev -Non-Associated Email Domain Check via Managed Groups * Bug/raj ppg 212 dev non associated email domain check via managed groups (#1607) * Security api changes to remove pending role approval for group and bugs for manage user solved * Permission api group role approval improvement --------- Co-authored-by: brijeshpatel-bc <108462846+brijeshpatel-bc@users.noreply.github.com> Co-authored-by: VijayHirudayasamy-bc <103512567+VijayHirudayasamy-bc@users.noreply.github.com> Co-authored-by: Brijesh Patel --- api/CcsSso.Core.Service/UserService.cs | 27 ++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/api/CcsSso.Core.Service/UserService.cs b/api/CcsSso.Core.Service/UserService.cs index dc54351b..10fd383c 100644 --- a/api/CcsSso.Core.Service/UserService.cs +++ b/api/CcsSso.Core.Service/UserService.cs @@ -99,24 +99,35 @@ public async Task NominateUserAsync(string email) private async Task GetGroupPermissions(string serviceClientId, User user, List rolePermissions) { - var userGroupsApprovalRequest = await _dataContext.UserAccessRolePending.Where(x => !x.IsDeleted && x.UserId == user.Id - && x.OrganisationUserGroupId != null && x.Status == (int)UserPendingRoleStaus.Pending).ToListAsync(); - - var serviceRoleGroups = await _serviceRoleGroupMapperService.OrgRolesToServiceRoleGroupsAsync(userGroupsApprovalRequest.Select(x => x.OrganisationEligibleRoleId).ToList()); + var userGroupsApprovalRequest = await _dataContext.UserAccessRolePending.Where(x => x.UserId == user.Id && x.OrganisationUserGroupId != null).ToListAsync(); foreach (var userGroupMembership in user.UserGroupMemberships) { if (!userGroupMembership.IsDeleted && userGroupMembership.OrganisationUserGroup.GroupEligibleRoles != null && userGroupMembership.OrganisationUserGroup.GroupEligibleRoles.Any()) { - foreach (var groupAccess in userGroupMembership.OrganisationUserGroup.GroupEligibleRoles.Where(x => !x.IsDeleted)) + await AddRolePermissionsFromGroup(serviceClientId, rolePermissions, userGroupsApprovalRequest, userGroupMembership); + } + } + } + + private async Task AddRolePermissionsFromGroup(string serviceClientId, List rolePermissions, List userGroupsApprovalRequest, UserGroupMembership userGroupMembership) + { + foreach (var groupAccess in userGroupMembership.OrganisationUserGroup.GroupEligibleRoles.Where(x => !x.IsDeleted)) + { + if (userGroupsApprovalRequest.Any()) + { + var lastGroupRequest = userGroupsApprovalRequest.OrderByDescending(x => x.Id).FirstOrDefault(x => x.OrganisationUserGroupId == groupAccess.OrganisationUserGroupId); + if (lastGroupRequest != null && lastGroupRequest.Status != (int)UserPendingRoleStaus.Approved) { - if (_applicationConfigurationInfo.UserRoleApproval.Enable && userGroupsApprovalRequest.Any(x => x.OrganisationUserGroupId == groupAccess.OrganisationUserGroupId && serviceRoleGroups.Any(g => g.CcsServiceRoleMappings.Any(m => m.CcsAccessRoleId == groupAccess.OrganisationEligibleRole.CcsAccessRoleId)))) + var serviceRoleGroups = await _serviceRoleGroupMapperService.OrgRolesToServiceRoleGroupsAsync(new List() { lastGroupRequest.OrganisationEligibleRoleId }); + + if (_applicationConfigurationInfo.UserRoleApproval.Enable && serviceRoleGroups.Any(g => g.CcsServiceRoleMappings.Any(m => m.CcsAccessRoleId == groupAccess.OrganisationEligibleRole.CcsAccessRoleId))) { continue; - } - rolePermissions.Add(GetGroupRolePermissions(serviceClientId, groupAccess)); + } } } + rolePermissions.Add(GetGroupRolePermissions(serviceClientId, groupAccess)); } } From 09e6a77a183dbd5bdb041058e31d0c3705f585ce Mon Sep 17 00:00:00 2001 From: brijrajsinh-bc <109584978+brijrajsinh-bc@users.noreply.github.com> Date: Thu, 6 Apr 2023 18:25:48 +0530 Subject: [PATCH 06/24] Feature/ppg 212 dev non associated email domain check via managed groups (#1610) * PPG-212 - Database Changes (#1582) * Change to create new request or auto approve it (#1589) * PPG-212 - Database Changes * Role Approval (Send fleet role request for approval) * Group with role approval for manage users (#1594) * Task 5020: PPG-212-Dev-Non-Associated Email Domain Check via Managed Groups (#1595) * PPG-212 - Database Changes * Role Approval (Send fleet role request for approval) * PPG 212 - Request role approval change * PPG 212 - Fix merge issue * PPG 212 - User role approval changes * PPG 212 - codeclimate fix (#1596) * PPG-212 - Database Changes * Role Approval (Send fleet role request for approval) * PPG 212 - Request role approval change * PPG 212 - Fix merge issue * PPG 212 - User role approval changes * PPG 212 - Code climate fix * PR-PPG-212-non-associated-email-domain-check-via-managed-groups (#1600) * Group related changes role approval changes * Invalid user added/removed case has been handled * paging has been added * Minor changes to the property case * Security api changes to remove pending role approval for group and bugs for manage user solved (#1603) * Task 5084: PPG 212- Dev -Non-Associated Email Domain Check via Managed Groups (#1605) * PPG-212 - Database Changes * Role Approval (Send fleet role request for approval) * PPG 212 - Request role approval change * PPG 212 - Fix merge issue * PPG 212 - User role approval changes * PPG 212 - Code climate fix * Task 5084: PPG 212- Dev -Non-Associated Email Domain Check via Managed Groups * Bug/raj ppg 212 dev non associated email domain check via managed groups (#1607) * Security api changes to remove pending role approval for group and bugs for manage user solved * Permission api group role approval improvement * Bug/raj ppg 212 dev non associated email domain check via managed groups (#1609) * Security api changes to remove pending role approval for group and bugs for manage user solved * Permission api group role approval improvement * Group bug fix for manage my account --------- Co-authored-by: brijeshpatel-bc <108462846+brijeshpatel-bc@users.noreply.github.com> Co-authored-by: VijayHirudayasamy-bc <103512567+VijayHirudayasamy-bc@users.noreply.github.com> Co-authored-by: Brijesh Patel --- .../External/UserProfileService.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/api/CcsSso.Core.Service/External/UserProfileService.cs b/api/CcsSso.Core.Service/External/UserProfileService.cs index 7fe5762c..cb852bf2 100644 --- a/api/CcsSso.Core.Service/External/UserProfileService.cs +++ b/api/CcsSso.Core.Service/External/UserProfileService.cs @@ -1016,10 +1016,13 @@ join cr in _dataContext.CcsAccessRole on er.CcsAccessRoleId equals cr.Id } // list of new groups to check for approval required role - var newlyAddedGroupIds = userProfileRequestInfo.Detail.GroupIds.Where(x => !previousGroups.Contains(x)).ToList(); - if (_appConfigInfo.UserRoleApproval.Enable && !isUserDomainValid && userProfileRequestInfo.Detail.GroupIds != null && newlyAddedGroupIds.Any()) - { - groupsWithRoleRequiredApproval = GetGroupsWithApprovalOrgRole(organisation.UserGroups, newlyAddedGroupIds); + if (userProfileRequestInfo.Detail.GroupIds != null) + { + var newlyAddedGroupIds = userProfileRequestInfo.Detail.GroupIds.Where(x => !previousGroups.Contains(x)).ToList(); + if (_appConfigInfo.UserRoleApproval.Enable && !isUserDomainValid && userProfileRequestInfo.Detail.GroupIds != null && newlyAddedGroupIds.Any()) + { + groupsWithRoleRequiredApproval = GetGroupsWithApprovalOrgRole(organisation.UserGroups, newlyAddedGroupIds); + } } // Set groups From 23921a15825d79d111ba868aed627dcf0266b249 Mon Sep 17 00:00:00 2001 From: brijeshpatel-bc <108462846+brijeshpatel-bc@users.noreply.github.com> Date: Thu, 6 Apr 2023 20:06:25 +0530 Subject: [PATCH 07/24] Bug 5112: UAT- Manage my account - fails to remove 'Pending Approval' tag after is approved. (#1613) --- .../2_Add_FP_USER_Roles/Add_Role_FP_USER_Dashboard_Service.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/CcsSso.Core.DbMigrations/Scripts/Phase3_Sprint6/2_Add_FP_USER_Roles/Add_Role_FP_USER_Dashboard_Service.sql b/api/CcsSso.Core.DbMigrations/Scripts/Phase3_Sprint6/2_Add_FP_USER_Roles/Add_Role_FP_USER_Dashboard_Service.sql index cc3233a1..0fd8c12d 100644 --- a/api/CcsSso.Core.DbMigrations/Scripts/Phase3_Sprint6/2_Add_FP_USER_Roles/Add_Role_FP_USER_Dashboard_Service.sql +++ b/api/CcsSso.Core.DbMigrations/Scripts/Phase3_Sprint6/2_Add_FP_USER_Roles/Add_Role_FP_USER_Dashboard_Service.sql @@ -28,7 +28,7 @@ INSERT INTO public."CcsAccessRole"( "CcsAccessRoleNameKey", "CcsAccessRoleName", "CcsAccessRoleDescription", "OrgTypeEligibility", "SubscriptionTypeEligibility", "TradeEligibility", "ApprovalRequired","CreatedUserId", "LastUpdatedUserId", "CreatedOnUtc", "LastUpdatedOnUtc", "IsDeleted", "MfaEnabled") - VALUES ('FP_USER', 'Fleet Portal Tile', 'Fleet Portal Tile', 2, 0, 1,1, 0, 0, now(), now(), + VALUES ('FP_USER', 'Fleet Portal Tile', 'Fleet Portal Tile', 2, 0, 1,0, 0, 0, now(), now(), false, false); SELECT "Id" into RoleId From public."CcsAccessRole" WHERE "CcsAccessRoleNameKey" = 'FP_USER' AND "CcsAccessRoleName" = 'Fleet Portal Tile' LIMIT 1; From 1f03df9206a0a87043b3aded641e8f40a87e9975 Mon Sep 17 00:00:00 2001 From: brijrajsinh-bc <109584978+brijrajsinh-bc@users.noreply.github.com> Date: Fri, 7 Apr 2023 10:57:38 +0530 Subject: [PATCH 08/24] Feature/ppg 212 dev non associated email domain check via managed groups (#1617) * PPG-212 - Database Changes (#1582) * Change to create new request or auto approve it (#1589) * PPG-212 - Database Changes * Role Approval (Send fleet role request for approval) * Group with role approval for manage users (#1594) * Task 5020: PPG-212-Dev-Non-Associated Email Domain Check via Managed Groups (#1595) * PPG-212 - Database Changes * Role Approval (Send fleet role request for approval) * PPG 212 - Request role approval change * PPG 212 - Fix merge issue * PPG 212 - User role approval changes * PPG 212 - codeclimate fix (#1596) * PPG-212 - Database Changes * Role Approval (Send fleet role request for approval) * PPG 212 - Request role approval change * PPG 212 - Fix merge issue * PPG 212 - User role approval changes * PPG 212 - Code climate fix * PR-PPG-212-non-associated-email-domain-check-via-managed-groups (#1600) * Group related changes role approval changes * Invalid user added/removed case has been handled * paging has been added * Minor changes to the property case * Security api changes to remove pending role approval for group and bugs for manage user solved (#1603) * Task 5084: PPG 212- Dev -Non-Associated Email Domain Check via Managed Groups (#1605) * PPG-212 - Database Changes * Role Approval (Send fleet role request for approval) * PPG 212 - Request role approval change * PPG 212 - Fix merge issue * PPG 212 - User role approval changes * PPG 212 - Code climate fix * Task 5084: PPG 212- Dev -Non-Associated Email Domain Check via Managed Groups * Bug/raj ppg 212 dev non associated email domain check via managed groups (#1607) * Security api changes to remove pending role approval for group and bugs for manage user solved * Permission api group role approval improvement * Bug/raj ppg 212 dev non associated email domain check via managed groups (#1609) * Security api changes to remove pending role approval for group and bugs for manage user solved * Permission api group role approval improvement * Group bug fix for manage my account * Get user details return group details for expired approval roles (#1616) * PR-ppg 212 vijay non associated email domain check via managed groups (#1618) * Group related changes role approval changes * Invalid user added/removed case has been handled * paging has been added * Minor changes to the property case * Changes done on the IsPending flag which has been replaced with UserPendingRoleStatus Enum * few issues after buddy check has been resolved --------- Co-authored-by: brijeshpatel-bc <108462846+brijeshpatel-bc@users.noreply.github.com> Co-authored-by: VijayHirudayasamy-bc <103512567+VijayHirudayasamy-bc@users.noreply.github.com> Co-authored-by: Brijesh Patel --- .../Dtos/External/OrganisationGroupInfo.cs | 5 +- .../External/OrganisationGroupService.cs | 49 +++++++++++++------ .../External/OrganisationProfileService.cs | 12 ++++- .../External/UserProfileService.cs | 24 ++++++--- 4 files changed, 67 insertions(+), 23 deletions(-) diff --git a/api/CcsSso.Core.Domain/Dtos/External/OrganisationGroupInfo.cs b/api/CcsSso.Core.Domain/Dtos/External/OrganisationGroupInfo.cs index df460f4a..bb08ea5b 100644 --- a/api/CcsSso.Core.Domain/Dtos/External/OrganisationGroupInfo.cs +++ b/api/CcsSso.Core.Domain/Dtos/External/OrganisationGroupInfo.cs @@ -1,3 +1,4 @@ +using CcsSso.Core.DbModel.Constants; using System; using System.Collections.Generic; @@ -89,7 +90,9 @@ public class GroupUser public string Name { get; set; } public bool IsAdmin { get; set; } = false; - public bool IsPendingApproval { get; set; } = false; + + public UserPendingRoleStaus? UserPendingRoleStaus { get; set; } + } public class OrganisationGroupRolePatchInfo diff --git a/api/CcsSso.Core.Service/External/OrganisationGroupService.cs b/api/CcsSso.Core.Service/External/OrganisationGroupService.cs index 791fda89..e0349e73 100644 --- a/api/CcsSso.Core.Service/External/OrganisationGroupService.cs +++ b/api/CcsSso.Core.Service/External/OrganisationGroupService.cs @@ -167,15 +167,26 @@ public async Task GetGroupAsync(string ciiOrganis UserId = ugm.User.UserName, Name = $"{ugm.User.Party.Person.FirstName} {ugm.User.Party.Person.LastName}", IsAdmin = ugm.User.UserAccessRoles.Any(r => !r.IsDeleted && r.OrganisationEligibleRole.CcsAccessRole.CcsAccessRoleNameKey == Contstant.OrgAdminRoleNameKey && !r.OrganisationEligibleRole.IsDeleted), - IsPendingApproval = !isApprovalRequired ? false : getUserRolePendingStatus(ugm), + + UserPendingRoleStaus = !isApprovalRequired ? null : getUserRolePendingStatus(ugm), }).ToList(); return organisationGroupResponseInfo; } - private bool getUserRolePendingStatus(UserGroupMembership ugm) + private UserPendingRoleStaus? getUserRolePendingStatus(UserGroupMembership ugm) { - return _dataContext.UserAccessRolePending.OrderByDescending(y => y.Id).FirstOrDefault(x => !x.IsDeleted && x.UserId == ugm.User.Id && x.Status == (int)UserPendingRoleStaus.Pending) != null; + // return _dataContext.UserAccessRolePending.OrderByDescending(y => y.Id).FirstOrDefault(x => !x.IsDeleted && x.UserId == ugm.User.Id && x.Status == (int)UserPendingRoleStaus.Pending) != null; + var pendingRole = _dataContext.UserAccessRolePending + .OrderByDescending(y => y.Id) + .FirstOrDefault(x => x.UserId == ugm.User.Id && x.OrganisationUserGroupId == ugm.OrganisationUserGroupId + && (x.Status == (int)UserPendingRoleStaus.Pending || + x.Status == (int)UserPendingRoleStaus.Rejected || + x.Status == (int)UserPendingRoleStaus.Removed || + x.Status == (int)UserPendingRoleStaus.Expired)); + + + return (UserPendingRoleStaus?)(pendingRole?.Status); } public async Task GetGroupsAsync(string ciiOrganisationId, string searchString = null) @@ -450,7 +461,7 @@ private async Task VerifyAndCreateGroupRolePendingRequest(OrganisationUserGroup await RemoveGroupRolePendingRequest(group, userHasInValidDomain); List approvalRequiredRoles = new(); - foreach (var role in group.GroupEligibleRoles) + foreach (var role in group.GroupEligibleRoles.Where(x => !x.IsDeleted)) { if (role.OrganisationEligibleRole.CcsAccessRole.ApprovalRequired == (int)RoleApprovalRequiredStatus.ApprovalRequired) { @@ -460,7 +471,14 @@ private async Task VerifyAndCreateGroupRolePendingRequest(OrganisationUserGroup if (approvalRequiredRoles.Any()) { - foreach (var user in userHasInValidDomain) + // Check any approved and pending request are there for the user + var existingApprovedUsers = await _dataContext.UserAccessRolePending.Where(x => approvalRequiredRoles.Contains(x.OrganisationEligibleRoleId) + && x.OrganisationUserGroupId == group.Id + && (x.Status == (int)UserPendingRoleStaus.Approved || x.Status == (int)UserPendingRoleStaus.Rejected || x.Status == (int)UserPendingRoleStaus.Expired)).ToListAsync(); + + var usersRequiredPendingRequest = userHasInValidDomain.Where(x => !existingApprovedUsers.Any(y => y.UserId == x.Id)); + + foreach (var user in usersRequiredPendingRequest) { await _userProfileRoleApprovalService.CreateUserRolesPendingForApprovalAsync(new UserProfileEditRequestInfo { @@ -490,9 +508,10 @@ await _userProfileRoleApprovalService.CreateUserRolesPendingForApprovalAsync(new private async Task RemoveGroupRolePendingRequest(OrganisationUserGroup group) { - var pendingGroupRequest = await _dataContext.UserAccessRolePending.Where(x => !x.IsDeleted - && x.OrganisationUserGroupId == group.Id - && x.Status == (int)UserPendingRoleStaus.Pending).ToListAsync(); + var pendingGroupRequest = await _dataContext.UserAccessRolePending.Where(x => + (x.Status == (int)UserPendingRoleStaus.Pending || + x.Status == (int)UserPendingRoleStaus.Approved) && + x.OrganisationUserGroupId == group.Id).ToListAsync(); foreach (var pendingRequest in pendingGroupRequest) { @@ -504,10 +523,11 @@ private async Task RemoveGroupRolePendingRequest(OrganisationUserGroup group) private async Task RemoveGroupRolePendingRequest(OrganisationUserGroup group, List users) { - var pendingGroupRequest = await _dataContext.UserAccessRolePending.Where(x => !x.IsDeleted - && x.OrganisationUserGroupId == group.Id + var pendingGroupRequest = await _dataContext.UserAccessRolePending.Where(x => + x.OrganisationUserGroupId == group.Id && !users.Select(user => user.Id).Contains(x.UserId) - && x.Status == (int)UserPendingRoleStaus.Pending).ToListAsync(); + && (x.Status == (int)UserPendingRoleStaus.Pending + || x.Status == (int)UserPendingRoleStaus.Approved)).ToListAsync(); foreach (var pendingRequest in pendingGroupRequest) { @@ -531,10 +551,11 @@ public async Task GetGroupUsersPendingRequestSummary(int var existingUserIds = group.UserGroupMemberships.Where(x => !x.IsDeleted).Select(ugm => ugm.UserId); var pendingRequests = await _dataContext.UserAccessRolePending - .Where(x => !x.IsDeleted && existingUserIds.Contains(x.UserId) && x.Status == (int)UserPendingRoleStaus.Pending) + .Where(x => !x.IsDeleted && existingUserIds.Contains(x.UserId) && x.OrganisationUserGroupId == groupId + && (x.Status == (int)UserPendingRoleStaus.Pending || x.Status == (int)UserPendingRoleStaus.Rejected)) .ToListAsync(); - var filteredUserIds = isPendingApproval ? existingUserIds.Where(x => !pendingRequests.Any(y => y.UserId == x)) : existingUserIds.Where(x => pendingRequests.Any(y => y.UserId == x)); + var filteredUserIds = isPendingApproval ? existingUserIds.Where(x => pendingRequests.Any(y => y.UserId == x)) : existingUserIds.Where(x => !pendingRequests.Any(y => y.UserId == x)); var usersQuery = _dataContext.User.Include(u => u.Party).ThenInclude(p => p.Person).Where(user => !user.IsDeleted && filteredUserIds.Contains(user.Id)).OrderBy(u => u.UserName); @@ -549,7 +570,7 @@ public async Task GetGroupUsersPendingRequestSummary(int GroupUser = pagedResult.Results?.Select(up => new GroupUser { UserId = up.UserName, - IsPendingApproval = isPendingApproval, + UserPendingRoleStaus = isPendingApproval? UserPendingRoleStaus.Pending: null, // pending and rejected will be shown as users doesn't have the role. Name = $"{up.Party.Person.FirstName} {up.Party.Person.LastName}", }).ToList() ?? new List() }; diff --git a/api/CcsSso.Core.Service/External/OrganisationProfileService.cs b/api/CcsSso.Core.Service/External/OrganisationProfileService.cs index 184758aa..59cd285c 100644 --- a/api/CcsSso.Core.Service/External/OrganisationProfileService.cs +++ b/api/CcsSso.Core.Service/External/OrganisationProfileService.cs @@ -1611,7 +1611,7 @@ private async Task RemoveOrgRoles(List rolesToDelete, { orgGroupRolesWithDeletedRole.IsDeleted = true; }); - + userAccessRolesWithDeletedRoles.ForEach((userAccessRolesWithDeletedRole) => { userAccessRolesWithDeletedRole.IsDeleted = true; @@ -1695,6 +1695,16 @@ private async Task AdminRoleAssignment(Organisation organisation, RoleEligibleTr } else { + // Check any approved and pending request are there for the user + var anyExistingRoleRequest = await _dataContext.UserAccessRolePending.AnyAsync(x => x.OrganisationEligibleRoleId== organisationEligibleRoleId + && x.OrganisationUserGroupId == null + && x.UserId == adminDetails.Id + && (x.Status == (int)UserPendingRoleStaus.Approved || x.Status == (int)UserPendingRoleStaus.Rejected || x.Status == (int)UserPendingRoleStaus.Expired)); + + if (anyExistingRoleRequest) + { + continue; + } await _userProfileRoleApprovalService.CreateUserRolesPendingForApprovalAsync(new UserProfileEditRequestInfo { UserName = adminDetails.UserName, diff --git a/api/CcsSso.Core.Service/External/UserProfileService.cs b/api/CcsSso.Core.Service/External/UserProfileService.cs index cb852bf2..38a0219b 100644 --- a/api/CcsSso.Core.Service/External/UserProfileService.cs +++ b/api/CcsSso.Core.Service/External/UserProfileService.cs @@ -448,10 +448,15 @@ public async Task GetUserAsync(string userName, bool is if (user.UserGroupMemberships != null) { - var userGroupsApprovalRequest = await _dataContext.UserAccessRolePending.Where(x => !x.IsDeleted && x.UserId == user.Id - && x.OrganisationUserGroupId != null && x.Status == (int)UserPendingRoleStaus.Pending).ToListAsync(); - var userGroupsApprovalServiceRoleGroups = await _serviceRoleGroupMapperService.OrgRolesToServiceRoleGroupsAsync(userGroupsApprovalRequest.Select(x => x.OrganisationEligibleRoleId).ToList()); - + var lastGroupRequest = await _dataContext.UserAccessRolePending.Include(u => u.User).OrderByDescending(x => x.Id).FirstOrDefaultAsync(x => x.User.UserName == userName.ToLower() + && x.OrganisationUserGroupId != null); + + List userGroupsApprovalServiceRoleGroups = new(); + if (lastGroupRequest != null && lastGroupRequest.Status != (int)UserPendingRoleStaus.Approved) + { + userGroupsApprovalServiceRoleGroups = await _serviceRoleGroupMapperService.OrgRolesToServiceRoleGroupsAsync(new List() { lastGroupRequest.OrganisationEligibleRoleId }); + } + foreach (var userGroupMembership in user.UserGroupMemberships) { if (!userGroupMembership.IsDeleted && userGroupMembership.OrganisationUserGroup.GroupEligibleRoles != null) @@ -1972,9 +1977,14 @@ public async Task GetUserV1Async(string List groupAccessServiceRoleGroups = new List(); - var userGroupsApprovalRequestRoleIds = await _dataContext.UserAccessRolePending.Include(u => u.User).Where(x => !x.IsDeleted && x.User.UserName == userName.ToLower() - && x.OrganisationUserGroupId != null && x.Status == (int)UserPendingRoleStaus.Pending).Select(x => x.OrganisationEligibleRoleId).ToListAsync(); - var userGroupsApprovalServiceRoleGroups = await _serviceRoleGroupMapperService.OrgRolesToServiceRoleGroupsAsync(userGroupsApprovalRequestRoleIds); + var lastGroupRequest = await _dataContext.UserAccessRolePending.Include(u => u.User).OrderByDescending(x => x.Id).FirstOrDefaultAsync(x => x.User.UserName == userName.ToLower() + && x.OrganisationUserGroupId != null); + + List userGroupsApprovalServiceRoleGroups = new(); + if (lastGroupRequest != null && lastGroupRequest.Status != (int)UserPendingRoleStaus.Approved) + { + userGroupsApprovalServiceRoleGroups = await _serviceRoleGroupMapperService.OrgRolesToServiceRoleGroupsAsync(new List() { lastGroupRequest.OrganisationEligibleRoleId }); + } foreach (var groupId in groupIds) { From 55d205e2e62f0cbed068bf92d4d29b35c50f4424 Mon Sep 17 00:00:00 2001 From: brijeshpatel-bc <108462846+brijeshpatel-bc@users.noreply.github.com> Date: Mon, 10 Apr 2023 20:47:32 +0530 Subject: [PATCH 09/24] PPG-212 bug fix (#1619) --- .../External/OrganisationProfileService.cs | 72 ++++++++-------- .../External/UserProfileService.cs | 82 +++++++++++++------ 2 files changed, 95 insertions(+), 59 deletions(-) diff --git a/api/CcsSso.Core.Service/External/OrganisationProfileService.cs b/api/CcsSso.Core.Service/External/OrganisationProfileService.cs index 59cd285c..537e35f3 100644 --- a/api/CcsSso.Core.Service/External/OrganisationProfileService.cs +++ b/api/CcsSso.Core.Service/External/OrganisationProfileService.cs @@ -1138,7 +1138,7 @@ private async Task AutoValidateForValidDomain(Organisation organisation, U //for admin roles if (isFromBackgroundJob) { - await AutoValidationAdminRolesForBackgroundJob(organisation, schemeIdentifier, groupId, auditEventLogs, adminUserDetails,true); + await AutoValidationAdminRolesForBackgroundJob(organisation, schemeIdentifier, groupId, auditEventLogs, adminUserDetails, true); } else { @@ -1219,7 +1219,7 @@ private async Task AutoValidateForInValidDomain(Organisation organisation, //for admin roles if (isFromBackgroundJob) { - await AutoValidationAdminRolesForBackgroundJob(organisation, schemeIdentifier, groupId, auditEventLogs, adminUserDetails,false); + await AutoValidationAdminRolesForBackgroundJob(organisation, schemeIdentifier, groupId, auditEventLogs, adminUserDetails, false); } else { @@ -1324,7 +1324,7 @@ private async Task AutoValidationAdminRoleAssignmentAsync(User adminDeta roleIds = roleIds.Union(successAdminRoleIds); } - var defaultRoles = await _dataContext.OrganisationEligibleRole + var defaultRoles = await _dataContext.OrganisationEligibleRole .Where(r => r.Organisation.CiiOrganisationId == ciiOrganisation && !r.IsDeleted && roleIds.Contains(r.CcsAccessRoleId)) .ToListAsync(); @@ -1589,33 +1589,33 @@ private async Task RemoveOrgRoles(List rolesToDelete, var userAccessRolesWithDeletedRoles = userAccessRolesForOrgUsers .Where(uar => deletingRoleIds.Contains(uar.OrganisationEligibleRole.CcsAccessRoleId)).ToList(); - var allAccessRolePending = await _dataContext.UserAccessRolePending.Where(u => !u.IsDeleted && u.Status == (int)UserPendingRoleStaus.Pending).ToListAsync(); deletingOrgEligibleRoles.ForEach((deletingOrgEligibleRole) => { - if (_applicationConfigurationInfo.UserRoleApproval.Enable) - { - var pendingRequests = allAccessRolePending.Where(x => x.OrganisationEligibleRoleId == deletingOrgEligibleRole.Id).ToList(); - foreach (var pendingRequest in pendingRequests) - { - pendingRequest.IsDeleted = true; - pendingRequest.Status = (int)UserPendingRoleStaus.Removed; - } - } - deletingOrgEligibleRole.IsDeleted = true; rolesRemoved.Append(rolesRemoved.Length > 0 ? "," + deletingOrgEligibleRole.CcsAccessRole.CcsAccessRoleName : deletingOrgEligibleRole.CcsAccessRole.CcsAccessRoleName); - }); orgGroupRolesWithDeletedRoles.ForEach((orgGroupRolesWithDeletedRole) => { orgGroupRolesWithDeletedRole.IsDeleted = true; }); - + userAccessRolesWithDeletedRoles.ForEach((userAccessRolesWithDeletedRole) => { userAccessRolesWithDeletedRole.IsDeleted = true; }); + + if (_applicationConfigurationInfo.UserRoleApproval.Enable) + { + var deletingOrgEligibleRoleIds = deletingOrgEligibleRoles.Select(x => x.Id); + var allAccessRolePending = await _dataContext.UserAccessRolePending.Where(u => deletingOrgEligibleRoleIds.Contains(u.OrganisationEligibleRoleId)).ToListAsync(); + + allAccessRolePending.ForEach((pendingRequest) => + { + pendingRequest.IsDeleted = true; + pendingRequest.Status = (int)UserPendingRoleStaus.Removed; + }); + } } return rolesRemoved.ToString(); @@ -1696,7 +1696,7 @@ private async Task AdminRoleAssignment(Organisation organisation, RoleEligibleTr else { // Check any approved and pending request are there for the user - var anyExistingRoleRequest = await _dataContext.UserAccessRolePending.AnyAsync(x => x.OrganisationEligibleRoleId== organisationEligibleRoleId + var anyExistingRoleRequest = await _dataContext.UserAccessRolePending.AnyAsync(x => x.OrganisationEligibleRoleId == organisationEligibleRoleId && x.OrganisationUserGroupId == null && x.UserId == adminDetails.Id && (x.Status == (int)UserPendingRoleStaus.Approved || x.Status == (int)UserPendingRoleStaus.Rejected || x.Status == (int)UserPendingRoleStaus.Expired)); @@ -2104,30 +2104,30 @@ private async Task ManualValidateAdminRoleAssignmentAsync(Organisation organisat await _dataContext.SaveChangesAsync(); } - private async Task AssignRoleToAllOrgAdmins(OrganisationEligibleRole role, List allAdminsOfOrg, Organisation organisation, List servicesWithApprovalRequiredRole) + private async Task AssignRoleToAllOrgAdmins(OrganisationEligibleRole role, List allAdminsOfOrg, Organisation organisation, List servicesWithApprovalRequiredRole) { foreach (var adminDetails in allAdminsOfOrg) { if (!adminDetails.UserAccessRoles.Any(x => x.OrganisationEligibleRoleId == role.Id && !x.IsDeleted)) { var isAdminDomainSameAsOrg = adminDetails.UserName.ToLower().Split('@')?[1] == organisation.DomainName?.ToLower(); - + // Remove normals roles which are part of service which required role approval // They will be assigned together with role approval. if (_applicationConfigurationInfo.UserRoleApproval.Enable && _applicationConfigurationInfo.ServiceRoleGroupSettings.Enable && - !isAdminDomainSameAsOrg && RoleBelongToApprovalRequiredService(role, servicesWithApprovalRequiredRole)) + !isAdminDomainSameAsOrg && RoleBelongToApprovalRequiredService(role, servicesWithApprovalRequiredRole)) { continue; } - if (!_applicationConfigurationInfo.UserRoleApproval.Enable || role.CcsAccessRole.ApprovalRequired == (int)RoleApprovalRequiredStatus.ApprovalNotRequired || + if (!_applicationConfigurationInfo.UserRoleApproval.Enable || role.CcsAccessRole.ApprovalRequired == (int)RoleApprovalRequiredStatus.ApprovalNotRequired || isAdminDomainSameAsOrg) { var defaultUserRole = new UserAccessRole { OrganisationEligibleRoleId = role.Id }; - adminDetails.UserAccessRoles.Add(defaultUserRole); + adminDetails.UserAccessRoles.Add(defaultUserRole); } else { @@ -2165,16 +2165,16 @@ public async Task> GetOrganisationServiceRoleGroupsAsync( var orgRoles = await GetOrganisationRolesAsync(ciiOrganisationId); var serviceRoleGroupsEntity = await _rolesToServiceRoleGroupMapperService.OrgRolesToServiceRoleGroupsAsync(orgRoles.Select(x => x.RoleId).ToList()); var serviceRoleGroups = serviceRoleGroupsEntity.Select(x => new ServiceRoleGroup - { - Id = x.Id, - Key = x.Key, - Name = x.Name, - OrgTypeEligibility = x.OrgTypeEligibility, - SubscriptionTypeEligibility = x.SubscriptionTypeEligibility, - TradeEligibility = x.TradeEligibility, - DisplayOrder = x.DisplayOrder, - Description = x.Description - }).ToList(); + { + Id = x.Id, + Key = x.Key, + Name = x.Name, + OrgTypeEligibility = x.OrgTypeEligibility, + SubscriptionTypeEligibility = x.SubscriptionTypeEligibility, + TradeEligibility = x.TradeEligibility, + DisplayOrder = x.DisplayOrder, + Description = x.Description + }).ToList(); return serviceRoleGroups; } @@ -2185,7 +2185,7 @@ public async Task UpdateOrganisationEligibleServiceRoleGroupsAsync(string ciiOrg throw new InvalidOperationException(); } - if (!ValidateCiiOrganisationID(ciiOrganisationId) || serviceRoleGroupsToAdd == null || serviceRoleGroupsToDelete == null) + if (!ValidateCiiOrganisationID(ciiOrganisationId) || serviceRoleGroupsToAdd == null || serviceRoleGroupsToDelete == null) { throw new CcsSsoException(ErrorConstant.ErrorInvalidDetails); } @@ -2199,7 +2199,7 @@ public async Task UpdateOrganisationEligibleServiceRoleGroupsAsync(string ciiOrg await UpdateOrganisationEligibleRolesAsync(ciiOrganisationId, isBuyer, addRoles, deleteRoles); } - public async Task UpdateOrgAutoValidServiceRoleGroupsAsync(string ciiOrganisationId, RoleEligibleTradeType newOrgType, List serviceRoleGroupsToAdd, List serviceRoleGroupsToDelete, List serviceRoleGroupsToAutoValid, string? companyHouseId) + public async Task UpdateOrgAutoValidServiceRoleGroupsAsync(string ciiOrganisationId, RoleEligibleTradeType newOrgType, List serviceRoleGroupsToAdd, List serviceRoleGroupsToDelete, List serviceRoleGroupsToAutoValid, string? companyHouseId) { if (!_applicationConfigurationInfo.ServiceRoleGroupSettings.Enable) { @@ -2222,13 +2222,13 @@ public async Task UpdateOrgAutoValidServiceRoleGroupsAsync(string ciiOrganisatio await UpdateOrgAutoValidationEligibleRolesAsync(ciiOrganisationId, newOrgType, addRoles, deleteRoles, autoValidRoles, companyHouseId); } - private static bool RoleBelongToApprovalRequiredService(OrganisationEligibleRole role, List servicesWithApprovalRequiredRole) + private static bool RoleBelongToApprovalRequiredService(OrganisationEligibleRole role, List servicesWithApprovalRequiredRole) { foreach (var approvalRoleService in servicesWithApprovalRequiredRole) { var removeRoles = approvalRoleService.CcsServiceRoleMappings.Where(x => x.CcsAccessRole.ApprovalRequired != 1).Select(x => x.CcsAccessRoleId).ToList(); // Return true for normal role that belongs to approval required service - if (removeRoles.Any(x => x == role.CcsAccessRoleId)) + if (removeRoles.Any(x => x == role.CcsAccessRoleId)) { return true; } diff --git a/api/CcsSso.Core.Service/External/UserProfileService.cs b/api/CcsSso.Core.Service/External/UserProfileService.cs index 38a0219b..2ed2c57a 100644 --- a/api/CcsSso.Core.Service/External/UserProfileService.cs +++ b/api/CcsSso.Core.Service/External/UserProfileService.cs @@ -461,14 +461,14 @@ public async Task GetUserAsync(string userName, bool is { if (!userGroupMembership.IsDeleted && userGroupMembership.OrganisationUserGroup.GroupEligibleRoles != null) { - - if (userGroupMembership.OrganisationUserGroup.GroupEligibleRoles.Any()) + if (userGroupMembership.OrganisationUserGroup.GroupEligibleRoles.Any(x => !x.IsDeleted)) { // For every role in the group populate the group role info foreach (var groupAccess in userGroupMembership.OrganisationUserGroup.GroupEligibleRoles.Where(x => !x.IsDeleted)) { if (_appConfigInfo.UserRoleApproval.Enable && userGroupsApprovalServiceRoleGroups.Any(x => x.CcsServiceRoleMappings.Any(m => m.CcsAccessRoleId == groupAccess.OrganisationEligibleRole.CcsAccessRoleId))) { + PopulateUserGroupWithoutRole(userProfileInfo, userGroupMembership); continue; } @@ -487,15 +487,7 @@ public async Task GetUserAsync(string userName, bool is } else // If group doesnt have a role then just send the group with empty role { - var groupAccessRole = new GroupAccessRole - { - GroupId = userGroupMembership.OrganisationUserGroup.Id, - Group = userGroupMembership.OrganisationUserGroup.UserGroupName, - AccessRoleName = string.Empty, - AccessRole = string.Empty, - }; - - userProfileInfo.Detail.UserGroups.Add(groupAccessRole); + PopulateUserGroupWithoutRole(userProfileInfo, userGroupMembership); } } } @@ -506,6 +498,20 @@ public async Task GetUserAsync(string userName, bool is throw new ResourceNotFoundException(); } + + private static void PopulateUserGroupWithoutRole(UserProfileResponseInfo userProfileInfo, UserGroupMembership userGroupMembership) + { + var groupAccessRole = new GroupAccessRole + { + GroupId = userGroupMembership.OrganisationUserGroup.Id, + Group = userGroupMembership.OrganisationUserGroup.UserGroupName, + AccessRoleName = string.Empty, + AccessRole = string.Empty, + }; + + userProfileInfo.Detail.UserGroups.Add(groupAccessRole); + } + // #Delegated public async Task GetUsersV1Async(string organisationId, ResultSetCriteria resultSetCriteria, UserFilterCriteria userFilterCriteria) @@ -960,6 +966,8 @@ join cr in _dataContext.CcsAccessRole on er.CcsAccessRoleId equals cr.Id List previousIdentityProviderIds = new(); var userAccessRoleRequiredApproval = new List(); var groupsWithRoleRequiredApproval = new List>>(); + var removedGroupIds = new List(); + var removedRoleIds = new List(); if (!isMyProfile || isAdminUser == true) { @@ -1030,6 +1038,8 @@ join cr in _dataContext.CcsAccessRole on er.CcsAccessRoleId equals cr.Id } } + removedGroupIds = previousGroups.Where(x => !userProfileRequestInfo.Detail.GroupIds.Contains(x)).ToList(); + // Set groups var userGroupMemberships = new List(); userProfileRequestInfo.Detail.GroupIds?.ForEach((groupId) => @@ -1065,6 +1075,8 @@ join cr in _dataContext.CcsAccessRole on er.CcsAccessRoleId equals cr.Id }); user.UserAccessRoles = userAccessRoles; + removedRoleIds = previousRoles.Where(x => !userProfileRequestInfo.Detail.RoleIds.Contains(x)).ToList(); + // Check the admin group availability in request var noAdminRoleGroupInRequest = userProfileRequestInfo.Detail.GroupIds == null || !userProfileRequestInfo.Detail.GroupIds.Any() || !organisation.UserGroups.Any(g => !g.IsDeleted @@ -1156,6 +1168,9 @@ join cr in _dataContext.CcsAccessRole on er.CcsAccessRoleId equals cr.Id if (_appConfigInfo.UserRoleApproval.Enable) { + await RemoveRoleApproveRequest(removedRoleIds, user.Id); + await RemoveGroupRoleApproveRequest(removedGroupIds, user.Id); + await CreatePendingRoleRequest(userAccessRoleRequiredApproval, user.Id, userName, organisation.CiiOrganisationId); await CreatePendingRoleRequestForGroup(groupsWithRoleRequiredApproval, user.Id, userName, organisation.CiiOrganisationId); } @@ -1936,7 +1951,32 @@ await _userProfileRoleApprovalService.CreateUserRolesPendingForApprovalAsync(new } }); } + } + + private async Task RemoveGroupRoleApproveRequest(List groupIds, int userId) + { + if (groupIds != null && groupIds.Any()) + { + var userAccessRolePending = await _dataContext.UserAccessRolePending.Where(x => x.UserId == userId + && x.OrganisationUserGroupId != null && groupIds.Contains(x.OrganisationUserGroupId.Value)).ToListAsync(); + + userAccessRolePending.ForEach(l => { l.IsDeleted = true; l.Status = (int)UserPendingRoleStaus.Removed; }); + + await _dataContext.SaveChangesAsync(); + } + } + + private async Task RemoveRoleApproveRequest(List roleIds, int userId) + { + if (roleIds != null && roleIds.Any()) + { + var userAccessRolePending = await _dataContext.UserAccessRolePending.Where(x => x.UserId == userId + && roleIds.Contains(x.OrganisationEligibleRoleId) && x.OrganisationUserGroupId == null).ToListAsync(); + + userAccessRolePending.ForEach(l => { l.IsDeleted = true; l.Status = (int)UserPendingRoleStaus.Removed; }); + await _dataContext.SaveChangesAsync(); + } } #region User Profile Version 1 @@ -1996,6 +2036,11 @@ public async Task GetUserV1Async(string { if (_appConfigInfo.UserRoleApproval.Enable && userGroupsApprovalServiceRoleGroups.Any(x => x.Id == serviceRoleGroup.Id)) { + groupAccessServiceRoleGroups.Add(new GroupAccessServiceRoleGroup() + { + GroupId = groupInfo.GroupId, + Group = groupInfo.GroupName, + }); continue; } groupAccessServiceRoleGroups.Add(new GroupAccessServiceRoleGroup() @@ -2278,7 +2323,7 @@ private static List>> GetGroupsWithApprovalOrgRole(L foreach (var group in allSelectedGroups) { List approvalRequiredRoles = new(); - foreach (var role in group.GroupEligibleRoles) + foreach (var role in group.GroupEligibleRoles.Where(x => !x.IsDeleted)) { if (role.OrganisationEligibleRole.CcsAccessRole.ApprovalRequired == (int)RoleApprovalRequiredStatus.ApprovalRequired) { @@ -2299,20 +2344,11 @@ private async Task CreatePendingRoleRequestForGroup(List !x.IsDeleted && x.UserId == userId && x.Status == (int)UserPendingRoleStaus.Pending && x.OrganisationUserGroupId != null).ToListAsync(); - // remove request that no longer required - foreach (var existRequest in userPendingGroups) - { - if (!groupsWithRoleRequiredApproval.Any(x => x.Key == existRequest.OrganisationUserGroupId && x.Value.Contains(existRequest.OrganisationEligibleRoleId))) - { - existRequest.IsDeleted = true; - existRequest.Status = (int)UserPendingRoleStaus.Removed; - } - } - foreach (var group in groupsWithRoleRequiredApproval) { // get roles that are pending for approval - var existingPendingRequestRole = userPendingGroups.Where(x => !x.IsDeleted && group.Value.Contains(x.OrganisationEligibleRoleId)).ToList(); + var existingPendingRequestRole = userPendingGroups.Where(x => !x.IsDeleted + && x.OrganisationUserGroupId == group.Key && group.Value.Contains(x.OrganisationEligibleRoleId)).ToList(); // ignore roles which are still pending for approval foreach (var existingPendingRequestToIgnore in existingPendingRequestRole) From 4127ac92da84c5df3204ac3a7cce400135b8c6c6 Mon Sep 17 00:00:00 2001 From: brijrajsinh-bc <109584978+brijrajsinh-bc@users.noreply.github.com> Date: Tue, 11 Apr 2023 15:30:05 +0530 Subject: [PATCH 10/24] Bug 5125: Manage Groups :Access denied for Fleet Portal message is displayed even though Fleet Portal Request approved, (#1620) Bug 5117: Fails to create new request with status '0' for the user(who is already denied against the group), when Fleet service removed and assigned agian/ Denied user removed and added again, Bug 5139: Approve/Denied notifications are generated when the request sent via group --- .../External/OrganisationGroupService.cs | 100 +++++++++--------- 1 file changed, 52 insertions(+), 48 deletions(-) diff --git a/api/CcsSso.Core.Service/External/OrganisationGroupService.cs b/api/CcsSso.Core.Service/External/OrganisationGroupService.cs index e0349e73..efee899e 100644 --- a/api/CcsSso.Core.Service/External/OrganisationGroupService.cs +++ b/api/CcsSso.Core.Service/External/OrganisationGroupService.cs @@ -179,11 +179,7 @@ public async Task GetGroupAsync(string ciiOrganis // return _dataContext.UserAccessRolePending.OrderByDescending(y => y.Id).FirstOrDefault(x => !x.IsDeleted && x.UserId == ugm.User.Id && x.Status == (int)UserPendingRoleStaus.Pending) != null; var pendingRole = _dataContext.UserAccessRolePending .OrderByDescending(y => y.Id) - .FirstOrDefault(x => x.UserId == ugm.User.Id && x.OrganisationUserGroupId == ugm.OrganisationUserGroupId - && (x.Status == (int)UserPendingRoleStaus.Pending || - x.Status == (int)UserPendingRoleStaus.Rejected || - x.Status == (int)UserPendingRoleStaus.Removed || - x.Status == (int)UserPendingRoleStaus.Expired)); + .FirstOrDefault(x => x.UserId == ugm.User.Id && x.OrganisationUserGroupId == ugm.OrganisationUserGroupId); return (UserPendingRoleStaus?)(pendingRole?.Status); @@ -448,62 +444,44 @@ await _auditLoginService.CreateLogAsync(AuditLogEvent.GroupeUserRemove, AuditLog private async Task VerifyAndCreateGroupRolePendingRequest(OrganisationUserGroup group, string ciiOrganisationId) { - if (_appConfigInfo.UserRoleApproval.Enable) + if (!_appConfigInfo.UserRoleApproval.Enable) { - var org = await _dataContext.Organisation.FirstOrDefaultAsync(x => x.CiiOrganisationId == ciiOrganisationId); - var orgDomain = org?.DomainName?.ToLower(); + return; + } + var org = await _dataContext.Organisation.FirstOrDefaultAsync(x => x.CiiOrganisationId == ciiOrganisationId); + var orgDomain = org?.DomainName?.ToLower(); - var latestExistingUserNames = group.UserGroupMemberships.Where(x => !x.IsDeleted).Select(ugm => ugm.User).ToList(); - var userHasInValidDomain = latestExistingUserNames.Where(user => user.UserName.ToLower().Split('@')?[1] != orgDomain).ToList(); + var latestExistingUserNames = group.UserGroupMemberships.Where(x => !x.IsDeleted).Select(ugm => ugm.User).ToList(); + var userHasInValidDomain = latestExistingUserNames.Where(user => user.UserName.ToLower().Split('@')?[1] != orgDomain).ToList(); - if (userHasInValidDomain.Any()) - { - await RemoveGroupRolePendingRequest(group, userHasInValidDomain); + if (userHasInValidDomain.Any()) + { + await RemoveGroupRolePendingRequest(group, userHasInValidDomain); - List approvalRequiredRoles = new(); - foreach (var role in group.GroupEligibleRoles.Where(x => !x.IsDeleted)) + List approvalRequiredRoles = new(); + foreach (var role in group.GroupEligibleRoles.Where(x => !x.IsDeleted)) + { + if (role.OrganisationEligibleRole.CcsAccessRole.ApprovalRequired == (int)RoleApprovalRequiredStatus.ApprovalRequired) { - if (role.OrganisationEligibleRole.CcsAccessRole.ApprovalRequired == (int)RoleApprovalRequiredStatus.ApprovalRequired) - { - approvalRequiredRoles.Add(role.OrganisationEligibleRoleId); - } + approvalRequiredRoles.Add(role.OrganisationEligibleRoleId); } + } - if (approvalRequiredRoles.Any()) - { - // Check any approved and pending request are there for the user - var existingApprovedUsers = await _dataContext.UserAccessRolePending.Where(x => approvalRequiredRoles.Contains(x.OrganisationEligibleRoleId) - && x.OrganisationUserGroupId == group.Id - && (x.Status == (int)UserPendingRoleStaus.Approved || x.Status == (int)UserPendingRoleStaus.Rejected || x.Status == (int)UserPendingRoleStaus.Expired)).ToListAsync(); - - var usersRequiredPendingRequest = userHasInValidDomain.Where(x => !existingApprovedUsers.Any(y => y.UserId == x.Id)); - - foreach (var user in usersRequiredPendingRequest) - { - await _userProfileRoleApprovalService.CreateUserRolesPendingForApprovalAsync(new UserProfileEditRequestInfo - { - UserName = user.UserName, - OrganisationId = group.Organisation.CiiOrganisationId, - Detail = new UserRequestDetail - { - GroupId = group.Id, - RoleIds = approvalRequiredRoles - } - }); - } - } - else - { - // remove any pending request exists for this group - await RemoveGroupRolePendingRequest(group); - } + if (approvalRequiredRoles.Any()) + { + await GeneratePendingRequests(group, userHasInValidDomain, approvalRequiredRoles); } else { - // remove any pending request exists for this group + // remove any pending request exists for this group await RemoveGroupRolePendingRequest(group); } } + else + { + // remove any pending request exists for this group + await RemoveGroupRolePendingRequest(group); + } } private async Task RemoveGroupRolePendingRequest(OrganisationUserGroup group) @@ -691,5 +669,31 @@ private async Task> ConvertServiceRoleGroupsToOrganisationRoleIds(stri return roleIds; } + + private async Task GeneratePendingRequests(OrganisationUserGroup group, List userHasInValidDomain, List approvalRequiredRoles) + { + var existingUsersRequests = await _dataContext.UserAccessRolePending.Where(x => approvalRequiredRoles.Contains(x.OrganisationEligibleRoleId) + && x.OrganisationUserGroupId == group.Id + && userHasInValidDomain.Select(u => u.Id).Contains(x.UserId)).ToListAsync(); + + foreach (var user in userHasInValidDomain) + { + var latestRequestOfUser = existingUsersRequests.OrderByDescending(x => x.Id).FirstOrDefault(x => x.UserId == user.Id); + if (latestRequestOfUser == null || (latestRequestOfUser.Status != (int)UserPendingRoleStaus.Pending && latestRequestOfUser.Status != (int)UserPendingRoleStaus.Approved)) + { + await _userProfileRoleApprovalService.CreateUserRolesPendingForApprovalAsync(new UserProfileEditRequestInfo + { + UserName = user.UserName, + OrganisationId = group.Organisation.CiiOrganisationId, + Detail = new UserRequestDetail + { + GroupId = group.Id, + RoleIds = approvalRequiredRoles + } + }, sendEmailNotification: false); + } + } + } + } } From 758c8e5add9bd29a077f8d94b1e4e1c0e33b3261 Mon Sep 17 00:00:00 2001 From: brijrajsinh-bc <109584978+brijrajsinh-bc@users.noreply.github.com> Date: Tue, 11 Apr 2023 19:35:12 +0530 Subject: [PATCH 11/24] Typo fixed (#1622) * Typo fixed * Code climate issue resolved --- .../Dtos/External/OrganisationGroupInfo.cs | 2 +- .../External/OrganisationGroupService.cs | 4 ++-- api/CcsSso.Core.Service/External/UserProfileService.cs | 7 ++----- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/api/CcsSso.Core.Domain/Dtos/External/OrganisationGroupInfo.cs b/api/CcsSso.Core.Domain/Dtos/External/OrganisationGroupInfo.cs index bb08ea5b..e653b5a0 100644 --- a/api/CcsSso.Core.Domain/Dtos/External/OrganisationGroupInfo.cs +++ b/api/CcsSso.Core.Domain/Dtos/External/OrganisationGroupInfo.cs @@ -91,7 +91,7 @@ public class GroupUser public bool IsAdmin { get; set; } = false; - public UserPendingRoleStaus? UserPendingRoleStaus { get; set; } + public UserPendingRoleStaus? UserPendingRoleStatus { get; set; } } diff --git a/api/CcsSso.Core.Service/External/OrganisationGroupService.cs b/api/CcsSso.Core.Service/External/OrganisationGroupService.cs index efee899e..ec2c6ca2 100644 --- a/api/CcsSso.Core.Service/External/OrganisationGroupService.cs +++ b/api/CcsSso.Core.Service/External/OrganisationGroupService.cs @@ -168,7 +168,7 @@ public async Task GetGroupAsync(string ciiOrganis Name = $"{ugm.User.Party.Person.FirstName} {ugm.User.Party.Person.LastName}", IsAdmin = ugm.User.UserAccessRoles.Any(r => !r.IsDeleted && r.OrganisationEligibleRole.CcsAccessRole.CcsAccessRoleNameKey == Contstant.OrgAdminRoleNameKey && !r.OrganisationEligibleRole.IsDeleted), - UserPendingRoleStaus = !isApprovalRequired ? null : getUserRolePendingStatus(ugm), + UserPendingRoleStatus = !isApprovalRequired ? null : getUserRolePendingStatus(ugm), }).ToList(); return organisationGroupResponseInfo; @@ -548,7 +548,7 @@ public async Task GetGroupUsersPendingRequestSummary(int GroupUser = pagedResult.Results?.Select(up => new GroupUser { UserId = up.UserName, - UserPendingRoleStaus = isPendingApproval? UserPendingRoleStaus.Pending: null, // pending and rejected will be shown as users doesn't have the role. + UserPendingRoleStatus = isPendingApproval? UserPendingRoleStaus.Pending: null, // pending and rejected will be shown as users doesn't have the role. Name = $"{up.Party.Person.FirstName} {up.Party.Person.LastName}", }).ToList() ?? new List() }; diff --git a/api/CcsSso.Core.Service/External/UserProfileService.cs b/api/CcsSso.Core.Service/External/UserProfileService.cs index 2ed2c57a..19084891 100644 --- a/api/CcsSso.Core.Service/External/UserProfileService.cs +++ b/api/CcsSso.Core.Service/External/UserProfileService.cs @@ -2341,14 +2341,11 @@ private static List>> GetGroupsWithApprovalOrgRole(L private async Task CreatePendingRoleRequestForGroup(List>> groupsWithRoleRequiredApproval, int userId, string userName, string ciiOrganisationId) { - var userPendingGroups = await _dataContext.UserAccessRolePending.Where(x => !x.IsDeleted && x.UserId == userId && x.Status == (int)UserPendingRoleStaus.Pending && - x.OrganisationUserGroupId != null).ToListAsync(); + var userPendingGroups = await _dataContext.UserAccessRolePending.Where(x => !x.IsDeleted && x.UserId == userId && x.Status == (int)UserPendingRoleStaus.Pending && x.OrganisationUserGroupId != null).ToListAsync(); foreach (var group in groupsWithRoleRequiredApproval) { - // get roles that are pending for approval - var existingPendingRequestRole = userPendingGroups.Where(x => !x.IsDeleted - && x.OrganisationUserGroupId == group.Key && group.Value.Contains(x.OrganisationEligibleRoleId)).ToList(); + var existingPendingRequestRole = userPendingGroups.Where(x => !x.IsDeleted && x.OrganisationUserGroupId == group.Key && group.Value.Contains(x.OrganisationEligibleRoleId)).ToList(); // ignore roles which are still pending for approval foreach (var existingPendingRequestToIgnore in existingPendingRequestRole) From 05f2e6df713af54b0a90bf6f527665d8b8f4de0f Mon Sep 17 00:00:00 2001 From: brijeshpatel-bc <108462846+brijeshpatel-bc@users.noreply.github.com> Date: Tue, 11 Apr 2023 19:53:23 +0530 Subject: [PATCH 12/24] Bug 5141: Manage User API : email link shows removed message if fleet request created user or group role updated (#1623) * PPG-212 bug fix * Bug 5141: Manage User API : email link shows removed message if fleet request created user or group role updated --- .../UserProfileRoleApprovalService.cs | 112 ++++++++++++------ 1 file changed, 75 insertions(+), 37 deletions(-) diff --git a/api/CcsSso.Core.Service/External/UserProfileRoleApprovalService.cs b/api/CcsSso.Core.Service/External/UserProfileRoleApprovalService.cs index f00b84fb..52b2507e 100644 --- a/api/CcsSso.Core.Service/External/UserProfileRoleApprovalService.cs +++ b/api/CcsSso.Core.Service/External/UserProfileRoleApprovalService.cs @@ -47,15 +47,14 @@ public async Task UpdateUserRoleStatusAsync(UserRoleApprovalEditRequest us var pendingRoleIds = userApprovalRequest.PendingRoleIds; var status = userApprovalRequest.Status; - var serviceName = String.Empty; - + if (status != UserPendingRoleStaus.Approved && status != UserPendingRoleStaus.Rejected) { throw new CcsSsoException(ErrorConstant.ErrorInvalidStatusInfo); } var pendingRole = await _dataContext.UserAccessRolePending - .Where(x => pendingRoleIds.Contains(x.Id) && !x.IsDeleted && x.Status == (int)UserPendingRoleStaus.Pending).ToListAsync(); + .Where(x => pendingRoleIds.Contains(x.Id)).ToListAsync(); if (pendingRole != null && pendingRole.Count() < pendingRoleIds.Length) { @@ -68,13 +67,24 @@ public async Task UpdateUserRoleStatusAsync(UserRoleApprovalEditRequest us foreach (var pendingRoleId in pendingRoleIds) { var pendingUserRole = await _dataContext.UserAccessRolePending.Include(x => x.OrganisationEligibleRole).ThenInclude(r => r.CcsAccessRole) - .FirstOrDefaultAsync(x => x.Id == pendingRoleId && !x.IsDeleted && x.Status == (int)UserPendingRoleStaus.Pending); + .FirstOrDefaultAsync(x => x.Id == pendingRoleId); if (pendingUserRole == null) { throw new CcsSsoException(ErrorConstant.ErrorInvalidRoleInfo); } + var isPendingRequest = !pendingUserRole.IsDeleted && pendingUserRole.Status == (int)UserPendingRoleStaus.Pending; + + var isOtherPendingRequest = await _dataContext.UserAccessRolePending + .AnyAsync(x => !x.IsDeleted && x.UserId == pendingUserRole.UserId && x.Status == (int)UserPendingRoleStaus.Pending + && x.OrganisationEligibleRoleId == pendingUserRole.OrganisationEligibleRoleId && x.Id != pendingUserRole.Id); + + if (!isPendingRequest && !isOtherPendingRequest) + { + throw new CcsSsoException(ErrorConstant.ErrorInvalidRoleInfo); + } + var user = await _dataContext.User .Include(u => u.UserAccessRoles).ThenInclude(u => u.OrganisationEligibleRole) .FirstOrDefaultAsync(x => x.Id == pendingUserRole.UserId && !x.IsDeleted && x.UserType == UserType.Primary); @@ -84,50 +94,66 @@ public async Task UpdateUserRoleStatusAsync(UserRoleApprovalEditRequest us throw new ResourceNotFoundException(); } - await ApproveRejectRoleRequest(status, serviceRoleGroupsWithApprovalRequiredRole, pendingUserRole, user); - - var orgEligibleRole = await _dataContext.OrganisationEligibleRole.Include(or => or.CcsAccessRole) - .ThenInclude(or => or.ServiceRolePermissions).ThenInclude(sr => sr.ServicePermission).ThenInclude(sr => sr.CcsService) - .FirstOrDefaultAsync(u => u.Id == pendingUserRole.OrganisationEligibleRoleId! && !u.IsDeleted); - - if (orgEligibleRole != null) + if (isPendingRequest) { - serviceName = orgEligibleRole.CcsAccessRole.ServiceRolePermissions.FirstOrDefault()?.ServicePermission.CcsService.ServiceName; - if (_appConfigInfo.ServiceRoleGroupSettings.Enable) - { - var roleServiceInfo = await _serviceRoleGroupMapperService.CcsRolesToServiceRoleGroupsAsync(new List() { orgEligibleRole.CcsAccessRoleId }); - serviceName = roleServiceInfo?.FirstOrDefault()?.Name; - } + await ApproveRejectRoleRequest(status, serviceRoleGroupsWithApprovalRequiredRole, pendingUserRole, user); + await SendApproveRejectRoleRequestEmail(status, pendingUserRole, user); } - var emailList = new List() { user.UserName }; + await UpdateRemaningRequestsOfUser(status, serviceRoleGroupsWithApprovalRequiredRole, pendingUserRole, user); + } - if (pendingUserRole.UserId != pendingUserRole.CreatedUserId) - { - var roleRequester = await _dataContext.User - .FirstOrDefaultAsync(x => x.Id == pendingUserRole.CreatedUserId && !x.IsDeleted && x.UserType == UserType.Primary); + return await Task.FromResult(true); + } - if (roleRequester != null) - { - emailList.Add(roleRequester.UserName); - } + private async Task SendApproveRejectRoleRequestEmail(UserPendingRoleStaus status, UserAccessRolePending pendingUserRole, User user) + { + string serviceName = await GetServiceNameForEmail(pendingUserRole); + + var emailList = new List() { user.UserName }; + + if (pendingUserRole.UserId != pendingUserRole.CreatedUserId) + { + var roleRequester = await _dataContext.User + .FirstOrDefaultAsync(x => x.Id == pendingUserRole.CreatedUserId && !x.IsDeleted && x.UserType == UserType.Primary); + + if (roleRequester != null) + { + emailList.Add(roleRequester.UserName); } + } - if (pendingUserRole.SendEmailNotification) + if (pendingUserRole.SendEmailNotification) + { + foreach (var email in emailList) { - foreach (var email in emailList) - { - if (status == UserPendingRoleStaus.Approved) - await _ccsSsoEmailService.SendRoleApprovedEmailAsync(email, user.UserName, serviceName, _appConfigInfo.ConclaveLoginUrl); - else - await _ccsSsoEmailService.SendRoleRejectedEmailAsync(email, user.UserName, serviceName); - } + if (status == UserPendingRoleStaus.Approved) + await _ccsSsoEmailService.SendRoleApprovedEmailAsync(email, user.UserName, serviceName, _appConfigInfo.ConclaveLoginUrl); + else + await _ccsSsoEmailService.SendRoleRejectedEmailAsync(email, user.UserName, serviceName); } + } + } - await UpdateRemaningRequestsOfUser(status, serviceRoleGroupsWithApprovalRequiredRole, pendingUserRole, user); + private async Task GetServiceNameForEmail(UserAccessRolePending pendingUserRole) + { + var serviceName = String.Empty; + + var orgEligibleRole = await _dataContext.OrganisationEligibleRole.Include(or => or.CcsAccessRole) + .ThenInclude(or => or.ServiceRolePermissions).ThenInclude(sr => sr.ServicePermission).ThenInclude(sr => sr.CcsService) + .FirstOrDefaultAsync(u => u.Id == pendingUserRole.OrganisationEligibleRoleId! && !u.IsDeleted); + + if (orgEligibleRole != null) + { + serviceName = orgEligibleRole.CcsAccessRole.ServiceRolePermissions.FirstOrDefault()?.ServicePermission.CcsService.ServiceName; + if (_appConfigInfo.ServiceRoleGroupSettings.Enable) + { + var roleServiceInfo = await _serviceRoleGroupMapperService.CcsRolesToServiceRoleGroupsAsync(new List() { orgEligibleRole.CcsAccessRoleId }); + serviceName = roleServiceInfo?.FirstOrDefault()?.Name; + } } - return await Task.FromResult(true); + return serviceName; } private async Task UpdateRemaningRequestsOfUser(UserPendingRoleStaus status, List serviceRoleGroupsWithApprovalRequiredRole, UserAccessRolePending pendingUserRole, User user) @@ -336,6 +362,18 @@ public async Task VerifyAndReturnRoleApproval } else { + var status = isLinkExpired ? (int)UserPendingRoleStaus.Expired : userAccessRolePendingRoleDetails.Status; + + if (status != (int)UserPendingRoleStaus.Pending) + { + var isPendingRequest = await _dataContext.UserAccessRolePending + .AnyAsync(u => !u.IsDeleted && u.Status == (int)UserPendingRoleStaus.Pending + && u.UserId == userAccessRolePendingRoleDetails.UserId + && u.OrganisationEligibleRoleId == userAccessRolePendingRoleDetails.OrganisationEligibleRoleId); + + status = isPendingRequest ? (int)UserPendingRoleStaus.Pending : status; + } + return new UserAccessRolePendingTokenDetails { Id = userAccessRolePendingRoleDetails.Id, @@ -343,7 +381,7 @@ public async Task VerifyAndReturnRoleApproval RoleId = userAccessRolePendingRoleDetails.OrganisationEligibleRole.CcsAccessRoleId, RoleName = userAccessRolePendingRoleDetails.OrganisationEligibleRole.CcsAccessRole.CcsAccessRoleName, RoleKey = userAccessRolePendingRoleDetails.OrganisationEligibleRole.CcsAccessRole.CcsAccessRoleNameKey, - Status = isLinkExpired ? (int)UserPendingRoleStaus.Expired : userAccessRolePendingRoleDetails.Status + Status = status }; } } From c040e8ee1588137b8bd308207fcb1ce23a8dad97 Mon Sep 17 00:00:00 2001 From: VijayHirudayasamy-bc <103512567+VijayHirudayasamy-bc@users.noreply.github.com> Date: Thu, 13 Apr 2023 08:14:48 +0100 Subject: [PATCH 13/24] bugs 5147, 5124, 5116 are fixed (#1625) --- .../Services/RoleApprovalLinkExpiredService.cs | 9 +++++++-- .../External/OrganisationGroupService.cs | 18 +++++++++++++----- .../External/OrganisationProfileService.cs | 2 +- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/api/CcsSso.Core.JobScheduler/Services/RoleApprovalLinkExpiredService.cs b/api/CcsSso.Core.JobScheduler/Services/RoleApprovalLinkExpiredService.cs index 6613bba4..8fa5715a 100644 --- a/api/CcsSso.Core.JobScheduler/Services/RoleApprovalLinkExpiredService.cs +++ b/api/CcsSso.Core.JobScheduler/Services/RoleApprovalLinkExpiredService.cs @@ -48,8 +48,13 @@ public async Task PerformJobAsync(List pendingRoles) foreach (var role in pendingRoles) { - var roleExpireTime = role.LastUpdatedOnUtc.AddMinutes(approvalRoleConfig.FirstOrDefault(x => x.CcsAccessRoleId == - role.OrganisationEligibleRole.CcsAccessRole.Id).LinkExpiryDurationInMinute); + var roleConfig = approvalRoleConfig.FirstOrDefault(x => x.CcsAccessRoleId == + role.OrganisationEligibleRole.CcsAccessRole.Id); + + if(roleConfig == null) + continue; + + var roleExpireTime = role.LastUpdatedOnUtc.AddMinutes(roleConfig.LinkExpiryDurationInMinute); if (roleExpireTime < DateTime.UtcNow) { diff --git a/api/CcsSso.Core.Service/External/OrganisationGroupService.cs b/api/CcsSso.Core.Service/External/OrganisationGroupService.cs index ec2c6ca2..cce26727 100644 --- a/api/CcsSso.Core.Service/External/OrganisationGroupService.cs +++ b/api/CcsSso.Core.Service/External/OrganisationGroupService.cs @@ -505,7 +505,8 @@ private async Task RemoveGroupRolePendingRequest(OrganisationUserGroup group, Li x.OrganisationUserGroupId == group.Id && !users.Select(user => user.Id).Contains(x.UserId) && (x.Status == (int)UserPendingRoleStaus.Pending - || x.Status == (int)UserPendingRoleStaus.Approved)).ToListAsync(); + || x.Status == (int)UserPendingRoleStaus.Approved + || x.Status == (int)UserPendingRoleStaus.Rejected)).ToListAsync(); foreach (var pendingRequest in pendingGroupRequest) { @@ -528,12 +529,15 @@ public async Task GetGroupUsersPendingRequestSummary(int var existingUserIds = group.UserGroupMemberships.Where(x => !x.IsDeleted).Select(ugm => ugm.UserId); - var pendingRequests = await _dataContext.UserAccessRolePending - .Where(x => !x.IsDeleted && existingUserIds.Contains(x.UserId) && x.OrganisationUserGroupId == groupId + var pendingAndRejectedRequests = await _dataContext.UserAccessRolePending + .Where(x => existingUserIds.Contains(x.UserId) && x.OrganisationUserGroupId == groupId && (x.Status == (int)UserPendingRoleStaus.Pending || x.Status == (int)UserPendingRoleStaus.Rejected)) .ToListAsync(); - var filteredUserIds = isPendingApproval ? existingUserIds.Where(x => pendingRequests.Any(y => y.UserId == x)) : existingUserIds.Where(x => !pendingRequests.Any(y => y.UserId == x)); + var pendingRequests = pendingAndRejectedRequests.Where(x => x.Status ==(int) UserPendingRoleStaus.Pending && !x.IsDeleted); + + + var filteredUserIds = isPendingApproval ? existingUserIds.Where(x => pendingRequests.Any(y => y.UserId == x)) : existingUserIds.Where(x => !pendingAndRejectedRequests.Any(y => y.UserId == x)); var usersQuery = _dataContext.User.Include(u => u.Party).ThenInclude(p => p.Person).Where(user => !user.IsDeleted && filteredUserIds.Contains(user.Id)).OrderBy(u => u.UserName); @@ -679,7 +683,11 @@ private async Task GeneratePendingRequests(OrganisationUserGroup group, List x.Id).FirstOrDefault(x => x.UserId == user.Id); - if (latestRequestOfUser == null || (latestRequestOfUser.Status != (int)UserPendingRoleStaus.Pending && latestRequestOfUser.Status != (int)UserPendingRoleStaus.Approved)) + + if (latestRequestOfUser == null || + (latestRequestOfUser.Status != (int)UserPendingRoleStaus.Pending + && latestRequestOfUser.Status != (int)UserPendingRoleStaus.Approved + && latestRequestOfUser.Status != (int)UserPendingRoleStaus.Rejected)) { await _userProfileRoleApprovalService.CreateUserRolesPendingForApprovalAsync(new UserProfileEditRequestInfo { diff --git a/api/CcsSso.Core.Service/External/OrganisationProfileService.cs b/api/CcsSso.Core.Service/External/OrganisationProfileService.cs index 537e35f3..49141a7e 100644 --- a/api/CcsSso.Core.Service/External/OrganisationProfileService.cs +++ b/api/CcsSso.Core.Service/External/OrganisationProfileService.cs @@ -1699,7 +1699,7 @@ private async Task AdminRoleAssignment(Organisation organisation, RoleEligibleTr var anyExistingRoleRequest = await _dataContext.UserAccessRolePending.AnyAsync(x => x.OrganisationEligibleRoleId == organisationEligibleRoleId && x.OrganisationUserGroupId == null && x.UserId == adminDetails.Id - && (x.Status == (int)UserPendingRoleStaus.Approved || x.Status == (int)UserPendingRoleStaus.Rejected || x.Status == (int)UserPendingRoleStaus.Expired)); + && (x.Status == (int)UserPendingRoleStaus.Approved)); if (anyExistingRoleRequest) { From 298fb502d5d7ce27cc08a99b9229dda76a9b05e4 Mon Sep 17 00:00:00 2001 From: VijayHirudayasamy-bc <103512567+VijayHirudayasamy-bc@users.noreply.github.com> Date: Thu, 13 Apr 2023 14:20:08 +0100 Subject: [PATCH 14/24] Feature/5116 group user api bug (#1626) * bugs 5147, 5124, 5116 are fixed * bug 5156 has been fixed --- api/CcsSso.Core.Service/External/OrganisationGroupService.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/api/CcsSso.Core.Service/External/OrganisationGroupService.cs b/api/CcsSso.Core.Service/External/OrganisationGroupService.cs index cce26727..f89fc882 100644 --- a/api/CcsSso.Core.Service/External/OrganisationGroupService.cs +++ b/api/CcsSso.Core.Service/External/OrganisationGroupService.cs @@ -487,8 +487,9 @@ private async Task VerifyAndCreateGroupRolePendingRequest(OrganisationUserGroup private async Task RemoveGroupRolePendingRequest(OrganisationUserGroup group) { var pendingGroupRequest = await _dataContext.UserAccessRolePending.Where(x => - (x.Status == (int)UserPendingRoleStaus.Pending || - x.Status == (int)UserPendingRoleStaus.Approved) && + (x.Status == (int)UserPendingRoleStaus.Pending + || x.Status == (int)UserPendingRoleStaus.Approved + || x.Status == (int)UserPendingRoleStaus.Rejected) && x.OrganisationUserGroupId == group.Id).ToListAsync(); foreach (var pendingRequest in pendingGroupRequest) From 23aa5944dbc10ef2c85698ca14cdcfa4a617f516 Mon Sep 17 00:00:00 2001 From: VijayHirudayasamy-bc <103512567+VijayHirudayasamy-bc@users.noreply.github.com> Date: Mon, 17 Apr 2023 12:06:03 +0100 Subject: [PATCH 15/24] PR-5157-Approved fleet users are getting new requests for approval when a group deleted and added. (#1629) * bugs 5147, 5124, 5116 are fixed * bug 5156 has been fixed * Issue 5157- Approved fleet users are getting new requests for approval when a group deleted and added. has been fixed --- .../External/UserProfileRoleApprovalService.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/api/CcsSso.Core.Service/External/UserProfileRoleApprovalService.cs b/api/CcsSso.Core.Service/External/UserProfileRoleApprovalService.cs index 52b2507e..5dc0b6df 100644 --- a/api/CcsSso.Core.Service/External/UserProfileRoleApprovalService.cs +++ b/api/CcsSso.Core.Service/External/UserProfileRoleApprovalService.cs @@ -47,7 +47,7 @@ public async Task UpdateUserRoleStatusAsync(UserRoleApprovalEditRequest us var pendingRoleIds = userApprovalRequest.PendingRoleIds; var status = userApprovalRequest.Status; - + if (status != UserPendingRoleStaus.Approved && status != UserPendingRoleStaus.Rejected) { throw new CcsSsoException(ErrorConstant.ErrorInvalidStatusInfo); @@ -82,7 +82,7 @@ public async Task UpdateUserRoleStatusAsync(UserRoleApprovalEditRequest us if (!isPendingRequest && !isOtherPendingRequest) { - throw new CcsSsoException(ErrorConstant.ErrorInvalidRoleInfo); + throw new CcsSsoException(ErrorConstant.ErrorInvalidRoleInfo); } var user = await _dataContext.User @@ -368,7 +368,7 @@ public async Task VerifyAndReturnRoleApproval { var isPendingRequest = await _dataContext.UserAccessRolePending .AnyAsync(u => !u.IsDeleted && u.Status == (int)UserPendingRoleStaus.Pending - && u.UserId == userAccessRolePendingRoleDetails.UserId + && u.UserId == userAccessRolePendingRoleDetails.UserId && u.OrganisationEligibleRoleId == userAccessRolePendingRoleDetails.OrganisationEligibleRoleId); status = isPendingRequest ? (int)UserPendingRoleStaus.Pending : status; @@ -583,11 +583,11 @@ private async Task> GetUserGroupApprovedRoleIds(User user, int? groupI userGroupRoleIds.ForEach((userGroupRoleId) => { - var lastUserAccessRolePedningRequest = user.UserAccessRolePending - .OrderByDescending(o => o.CreatedOnUtc) - .FirstOrDefault(x => x.OrganisationUserGroupId != null && x.OrganisationEligibleRoleId == userGroupRoleId); + // latest approved with in the group + var anyApprovedInTheGroup = user.UserAccessRolePending + .FirstOrDefault(x => x.OrganisationUserGroupId != null && x.OrganisationEligibleRoleId == userGroupRoleId && x.Status == (int)UserPendingRoleStaus.Approved); - if (lastUserAccessRolePedningRequest?.Status == (int)UserPendingRoleStaus.Approved) + if (anyApprovedInTheGroup != null) { userGroupApprovedRoleIds.Add(userGroupRoleId); } From ccfd67e0a9ed31f01f7166bcadbcdb6651153fa5 Mon Sep 17 00:00:00 2001 From: brijeshpatel-bc <108462846+brijeshpatel-bc@users.noreply.github.com> Date: Tue, 18 Apr 2023 14:43:59 +0530 Subject: [PATCH 16/24] Bug 5177: Fleet link is not expiring after the time set up in the RoleApprovalConfiguration table (#1631) --- .../External/UserProfileRoleApprovalService.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/api/CcsSso.Core.Service/External/UserProfileRoleApprovalService.cs b/api/CcsSso.Core.Service/External/UserProfileRoleApprovalService.cs index 5dc0b6df..f0adf3d2 100644 --- a/api/CcsSso.Core.Service/External/UserProfileRoleApprovalService.cs +++ b/api/CcsSso.Core.Service/External/UserProfileRoleApprovalService.cs @@ -368,6 +368,7 @@ public async Task VerifyAndReturnRoleApproval { var isPendingRequest = await _dataContext.UserAccessRolePending .AnyAsync(u => !u.IsDeleted && u.Status == (int)UserPendingRoleStaus.Pending + && u.Id != userAccessRolePendingRoleDetails.Id && u.UserId == userAccessRolePendingRoleDetails.UserId && u.OrganisationEligibleRoleId == userAccessRolePendingRoleDetails.OrganisationEligibleRoleId); From 235fb10ba500312b8d8ba7a97dab7503effb8cfd Mon Sep 17 00:00:00 2001 From: brijrajsinh-bc <109584978+brijrajsinh-bc@users.noreply.github.com> Date: Wed, 19 Apr 2023 17:19:52 +0530 Subject: [PATCH 17/24] RMI key and name changes. (#1636) --- .../Scripts/Phase3_Sprint7/Update_RMI_Key_Name.sql | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 api/CcsSso.Core.DbMigrations/Scripts/Phase3_Sprint7/Update_RMI_Key_Name.sql diff --git a/api/CcsSso.Core.DbMigrations/Scripts/Phase3_Sprint7/Update_RMI_Key_Name.sql b/api/CcsSso.Core.DbMigrations/Scripts/Phase3_Sprint7/Update_RMI_Key_Name.sql new file mode 100644 index 00000000..1ff2c32b --- /dev/null +++ b/api/CcsSso.Core.DbMigrations/Scripts/Phase3_Sprint7/Update_RMI_Key_Name.sql @@ -0,0 +1,7 @@ +Update "CcsServiceRoleGroup" +SET "Name" = 'Report Management Information', "Key" = 'RMI_USER' +WHERE "Key" = 'RMI' AND "IsDeleted" = false; + +UPDATE "CcsService" +SET "ServiceName" = 'Report Management Information' +WHERE "ServiceCode" = 'RMI_USER_DS' AND "IsDeleted" = false; \ No newline at end of file From 624d33dfe04af7d4edd64c7a3f215b4abd2db3b3 Mon Sep 17 00:00:00 2001 From: brijrajsinh-bc <109584978+brijrajsinh-bc@users.noreply.github.com> Date: Thu, 20 Apr 2023 15:09:05 +0530 Subject: [PATCH 18/24] RMI description (#1643) --- .../Scripts/Phase3_Sprint7/Update_RMI_Key_Name.sql | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/api/CcsSso.Core.DbMigrations/Scripts/Phase3_Sprint7/Update_RMI_Key_Name.sql b/api/CcsSso.Core.DbMigrations/Scripts/Phase3_Sprint7/Update_RMI_Key_Name.sql index 1ff2c32b..4f5e78c6 100644 --- a/api/CcsSso.Core.DbMigrations/Scripts/Phase3_Sprint7/Update_RMI_Key_Name.sql +++ b/api/CcsSso.Core.DbMigrations/Scripts/Phase3_Sprint7/Update_RMI_Key_Name.sql @@ -1,7 +1,15 @@ Update "CcsServiceRoleGroup" -SET "Name" = 'Report Management Information', "Key" = 'RMI_USER' +SET "Name" = 'Report Management Information', "Key" = 'RMI_USER', "LastUpdatedOnUtc" = now() WHERE "Key" = 'RMI' AND "IsDeleted" = false; UPDATE "CcsService" -SET "ServiceName" = 'Report Management Information' -WHERE "ServiceCode" = 'RMI_USER_DS' AND "IsDeleted" = false; \ No newline at end of file +SET "ServiceName" = 'Report Management Information', "LastUpdatedOnUtc" = now() +WHERE "ServiceCode" = 'RMI_USER_DS' AND "IsDeleted" = false; + +Update "CcsServiceRoleGroup" +SET "Description" = 'Use this service to obtain agreement templates, submit management information to CCS or report no business to CCS.', "LastUpdatedOnUtc" = now() +WHERE "Key" = 'RMI_USER' AND "IsDeleted" = false; + +UPDATE "CcsService" +SET "Description" = 'Use this service to obtain agreement templates, submit management information to CCS or report no business to CCS.', "LastUpdatedOnUtc" = now() +WHERE "ServiceCode" = 'RMI_USER_DS' AND "IsDeleted" = false; From bd645ac8220840d0537e26b33a22e9eacb34d30c Mon Sep 17 00:00:00 2001 From: brijrajsinh-bc <109584978+brijrajsinh-bc@users.noreply.github.com> Date: Mon, 24 Apr 2023 17:32:48 +0530 Subject: [PATCH 19/24] Data migration service description change (#1648) --- .../Scripts/Phase3_Sprint7/Update_DM_Service_description.sql | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 api/CcsSso.Core.DbMigrations/Scripts/Phase3_Sprint7/Update_DM_Service_description.sql diff --git a/api/CcsSso.Core.DbMigrations/Scripts/Phase3_Sprint7/Update_DM_Service_description.sql b/api/CcsSso.Core.DbMigrations/Scripts/Phase3_Sprint7/Update_DM_Service_description.sql new file mode 100644 index 00000000..cb684221 --- /dev/null +++ b/api/CcsSso.Core.DbMigrations/Scripts/Phase3_Sprint7/Update_DM_Service_description.sql @@ -0,0 +1,3 @@ +Update "CcsServiceRoleGroup" +SET "Description" = 'Migrate Users and Organisations', "LastUpdatedOnUtc" = now() +WHERE "Key" = 'DATA_MIGRATION' AND "IsDeleted" = false; \ No newline at end of file From c1db84b22a680b44bed691c7113ba97cc38fce95 Mon Sep 17 00:00:00 2001 From: brijeshpatel-bc <108462846+brijeshpatel-bc@users.noreply.github.com> Date: Thu, 27 Apr 2023 17:36:44 +0530 Subject: [PATCH 20/24] PPON Job changes for s8 (#1655) --- .../CcsSso.Core.PPONScheduler.csproj | 19 ++ .../Jobs/OneTimePPONJob.cs | 109 +++++++++ api/CcsSso.Core.PPONScheduler/Jobs/PPONJob.cs | 51 +++++ .../Model/Organisation.cs | 23 ++ .../Model/PPONAppSettings.cs | 51 +++++ .../Model/PPONDetails.cs | 24 ++ api/CcsSso.Core.PPONScheduler/Program.cs | 140 ++++++++++++ .../Properties/launchSettings.json | 11 + .../Service/Contracts/IPPONService.cs | 14 ++ .../Service/PPONService.cs | 215 ++++++++++++++++++ api/CcsSso.Core.PPONScheduler/appsecrets.json | 23 ++ .../appsettings.Development.json | 9 + .../appsettings.json | 15 ++ api/CcsSso.Core.Service/CiiService.cs | 5 +- api/CcsSso.sln | 10 +- 15 files changed, 716 insertions(+), 3 deletions(-) create mode 100644 api/CcsSso.Core.PPONScheduler/CcsSso.Core.PPONScheduler.csproj create mode 100644 api/CcsSso.Core.PPONScheduler/Jobs/OneTimePPONJob.cs create mode 100644 api/CcsSso.Core.PPONScheduler/Jobs/PPONJob.cs create mode 100644 api/CcsSso.Core.PPONScheduler/Model/Organisation.cs create mode 100644 api/CcsSso.Core.PPONScheduler/Model/PPONAppSettings.cs create mode 100644 api/CcsSso.Core.PPONScheduler/Model/PPONDetails.cs create mode 100644 api/CcsSso.Core.PPONScheduler/Program.cs create mode 100644 api/CcsSso.Core.PPONScheduler/Properties/launchSettings.json create mode 100644 api/CcsSso.Core.PPONScheduler/Service/Contracts/IPPONService.cs create mode 100644 api/CcsSso.Core.PPONScheduler/Service/PPONService.cs create mode 100644 api/CcsSso.Core.PPONScheduler/appsecrets.json create mode 100644 api/CcsSso.Core.PPONScheduler/appsettings.Development.json create mode 100644 api/CcsSso.Core.PPONScheduler/appsettings.json diff --git a/api/CcsSso.Core.PPONScheduler/CcsSso.Core.PPONScheduler.csproj b/api/CcsSso.Core.PPONScheduler/CcsSso.Core.PPONScheduler.csproj new file mode 100644 index 00000000..9a333ea9 --- /dev/null +++ b/api/CcsSso.Core.PPONScheduler/CcsSso.Core.PPONScheduler.csproj @@ -0,0 +1,19 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + + + diff --git a/api/CcsSso.Core.PPONScheduler/Jobs/OneTimePPONJob.cs b/api/CcsSso.Core.PPONScheduler/Jobs/OneTimePPONJob.cs new file mode 100644 index 00000000..c8469fcb --- /dev/null +++ b/api/CcsSso.Core.PPONScheduler/Jobs/OneTimePPONJob.cs @@ -0,0 +1,109 @@ +using CcsSso.Core.PPONScheduler.Model; +using CcsSso.Core.PPONScheduler.Service.Contracts; +using CcsSso.Domain.Contracts; +using CcsSso.Shared.Contracts; +using System.Globalization; + +namespace CcsSso.Core.PPONScheduler.Jobs +{ + public class OneTimePPONJob : BackgroundService + { + private readonly PPONAppSettings _appSettings; + private readonly IDataContext _dataContext; + private readonly IPPONService _pPONService; + private readonly ILogger _logger; + private bool ranOnce; + private DateTime startDate; + private DateTime endDate; + + + public OneTimePPONJob(ILogger logger, PPONAppSettings appSettings, + IServiceScopeFactory factory) + { + _logger = logger; + _appSettings = appSettings; + ranOnce = false; + _pPONService = factory.CreateScope().ServiceProvider.GetRequiredService(); + _dataContext = factory.CreateScope().ServiceProvider.GetRequiredService(); + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + while (!stoppingToken.IsCancellationRequested) + { + int interval = _appSettings.ScheduleJobSettings.ScheduleInMinutes * 60000; + var oneTimeValidationSwitch = _appSettings.OneTimeJobSettings.Switch; + if (oneTimeValidationSwitch && ranOnce) + { + _logger.LogInformation("One time validation ran already. Skipping this iteration."); + } + else if (oneTimeValidationSwitch) + { + bool isDateValid = ValidateDate(); + if (isDateValid) + { + await PerformJob(oneTimeValidationSwitch); + } + } + await Task.Delay(interval, stoppingToken); + } + } + + private bool ValidateDate() + { + var isDateValid = true; + var startDateString = _appSettings.OneTimeJobSettings.StartDate; + var endDateString = _appSettings.OneTimeJobSettings.EndDate; + + if (startDateString == null || endDateString == null) + { + _logger.LogError("One time validation needs start and end date. Skipping this iteration."); + isDateValid = false; + } + + if (isDateValid) + { + isDateValid = ConvertDate(startDateString, endDateString); + } + + return isDateValid; + } + + private bool ConvertDate(string? startDateString, string? endDateString) + { + var isDateValid = true; + + try + { + startDate = DateTime.ParseExact(startDateString, "yyyy-MM-dd HH:mm", CultureInfo.InvariantCulture); + endDate = DateTime.ParseExact(endDateString, "yyyy-MM-dd HH:mm", CultureInfo.InvariantCulture); + } + catch (FormatException) + { + _logger.LogError("{0} or {1} is not in the correct format. Date format should be as follows 'yyyy-MM-dd HH:mm' Skipping this iteration.", startDateString, endDateString); + isDateValid = false; + } + catch (Exception) + { + _logger.LogError("Error while reading the start or end date {0}, {1}. Skipping this iteration.", startDateString, endDateString); + isDateValid = false; + } + + return isDateValid; + } + + private async Task PerformJob(bool oneTimeValidationSwitch) + { + _logger.LogInformation(""); + _logger.LogInformation("One time validation job switched on. So it runs once to process all the organisation between dates"); + + _logger.LogInformation("PPON one time job started at: {time}", DateTimeOffset.Now); + + await _pPONService.PerformJob(oneTimeValidationSwitch, startDate, endDate); + ranOnce = true; + + _logger.LogInformation("PPON one time job Finsied at: {time}", DateTimeOffset.Now); + _logger.LogInformation(""); + } + } +} diff --git a/api/CcsSso.Core.PPONScheduler/Jobs/PPONJob.cs b/api/CcsSso.Core.PPONScheduler/Jobs/PPONJob.cs new file mode 100644 index 00000000..b6dc88f8 --- /dev/null +++ b/api/CcsSso.Core.PPONScheduler/Jobs/PPONJob.cs @@ -0,0 +1,51 @@ +using CcsSso.Core.PPONScheduler.Model; +using CcsSso.Core.PPONScheduler.Service; +using CcsSso.Core.PPONScheduler.Service.Contracts; +using CcsSso.Domain.Contracts; +using CcsSso.Shared.Contracts; + +namespace CcsSso.Core.PPONScheduler.Jobs +{ + public class PPONJob : BackgroundService + { + private readonly PPONAppSettings _appSettings; + private readonly IDataContext _dataContext; + private readonly IPPONService _pPONService; + private readonly ILogger _logger; + private DateTime startDate; + private DateTime endDate; + + + public PPONJob(ILogger logger, PPONAppSettings appSettings, + IServiceScopeFactory factory) + { + _logger = logger; + _appSettings = appSettings; + _pPONService = factory.CreateScope().ServiceProvider.GetRequiredService(); + _dataContext = factory.CreateScope().ServiceProvider.GetRequiredService(); + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + while (!stoppingToken.IsCancellationRequested) + { + int interval = _appSettings.ScheduleJobSettings.ScheduleInMinutes * 60000; + + var oneTimeValidationSwitch = _appSettings.OneTimeJobSettings.Switch; + + _logger.LogInformation(""); + _logger.LogInformation("PPON Scheduled job started at: {time}", DateTimeOffset.Now); + + if (!oneTimeValidationSwitch) + { + await _pPONService.PerformJob(oneTimeValidationSwitch, startDate, endDate); + } + + _logger.LogInformation("PPON Scheduled job Finsied at: {time}", DateTimeOffset.Now); + _logger.LogInformation(""); + + await Task.Delay(interval, stoppingToken); + } + } + } +} diff --git a/api/CcsSso.Core.PPONScheduler/Model/Organisation.cs b/api/CcsSso.Core.PPONScheduler/Model/Organisation.cs new file mode 100644 index 00000000..6f27bafd --- /dev/null +++ b/api/CcsSso.Core.PPONScheduler/Model/Organisation.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CcsSso.Core.PPONScheduler.Model +{ + public class Organisation + { + public int Id { get; set; } + + public string CiiOrganisationId { get; set; } + + public string LegalName { get; set; } + + public bool? RightToBuy { get; set; } + + public DateTime CreatedOnUtc { get; set; } + + public int? SupplierBuyerType { get; set; } + } +} diff --git a/api/CcsSso.Core.PPONScheduler/Model/PPONAppSettings.cs b/api/CcsSso.Core.PPONScheduler/Model/PPONAppSettings.cs new file mode 100644 index 00000000..4804f688 --- /dev/null +++ b/api/CcsSso.Core.PPONScheduler/Model/PPONAppSettings.cs @@ -0,0 +1,51 @@ +using CcsSso.Core.Domain.Jobs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CcsSso.Core.PPONScheduler.Model +{ + public class PPONAppSettings + { + public string DbConnection { get; set; } + + public ApiSettings? PPONApiSettings { get; set; } + + public CiiSettings? CiiSettings { get; set; } + + public ScheduleJob? ScheduleJobSettings { get; set; } + + public OneTimeJob? OneTimeJobSettings { get; set; } + + } + public class ApiSettings + { + public string? Key { get; set; } + + public string? Url { get; set; } + } + + public class CiiSettings + { + public string? Url { get; set; } + + public string? Token { get; set; } + } + + public class ScheduleJob + { + public int ScheduleInMinutes { get; set; } + public int DataDurationInMinutes { get; set; } + } + + public class OneTimeJob + { + public bool Switch { get; set; } + + public string StartDate { get; set; } + + public string EndDate { get; set; } + } +} diff --git a/api/CcsSso.Core.PPONScheduler/Model/PPONDetails.cs b/api/CcsSso.Core.PPONScheduler/Model/PPONDetails.cs new file mode 100644 index 00000000..4f679c92 --- /dev/null +++ b/api/CcsSso.Core.PPONScheduler/Model/PPONDetails.cs @@ -0,0 +1,24 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CcsSso.Core.PPONScheduler.Model +{ + public class Identifier + { + public string Id { get; set; } + + [JsonProperty("id-type")] + public string IdType { get; set; } + + public bool Persisted { get; set; } + } + + public class PPONDetails + { + public List Identifiers { get; set; } + } +} diff --git a/api/CcsSso.Core.PPONScheduler/Program.cs b/api/CcsSso.Core.PPONScheduler/Program.cs new file mode 100644 index 00000000..f047c3a1 --- /dev/null +++ b/api/CcsSso.Core.PPONScheduler/Program.cs @@ -0,0 +1,140 @@ +using CcsSso.Core.PPONScheduler.Jobs; +using CcsSso.Core.PPONScheduler.Model; +using CcsSso.DbPersistence; +using CcsSso.Domain.Contracts; +using CcsSso.Shared.Contracts; +using CcsSso.Shared.Services; +using Microsoft.EntityFrameworkCore; +using CcsSso.Shared.Domain.Contexts; +using CcsSso.Core.PPONScheduler.Service.Contracts; +using CcsSso.Core.PPONScheduler.Service; +using CcsSso.Service; +using CcsSso.Core.Service; +using CcsSso.Core.Domain.Contracts; + +namespace CcsSso.Core.PPONScheduler +{ + public class Program + { + private static bool vaultEnabled; + + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) + { + return Host.CreateDefaultBuilder(args) + .ConfigureAppConfiguration((hostingContext, config) => + { + PopulateAppConfiguration(config); + }) + + .ConfigureServices((hostContext, services) => + { + PPONAppSettings appSettings = GetConfigurationDetails(hostContext); + ConfigureHttpClients(services, appSettings); + ConfigureModels(services, appSettings); + ConfigureServices(services, appSettings); + ConfigureContexts(services, appSettings); + ConfigureJobs(services); + }); + } + + private static void PopulateAppConfiguration(IConfigurationBuilder config) + { + var configBuilder = new ConfigurationBuilder() + .AddJsonFile("appsettings.json", optional: false) + .Build(); + + var builtConfig = config.Build(); + vaultEnabled = configBuilder.GetValue("VaultEnabled"); + + if (!vaultEnabled) + { + config.AddJsonFile("appsecrets.json", optional: false, reloadOnChange: true); + } + } + + private static void ConfigureModels(IServiceCollection services, PPONAppSettings appSettings) + { + services.AddSingleton(s => + { + Dtos.Domain.Models.CiiConfig ciiConfigInfo = new Dtos.Domain.Models.CiiConfig() + { + url = appSettings.CiiSettings.Url, + token = appSettings.CiiSettings.Token + }; + return ciiConfigInfo; + }); + } + + private static void ConfigureJobs(IServiceCollection services) + { + services.AddHostedService(); + services.AddHostedService(); + } + + private static void ConfigureContexts(IServiceCollection services, PPONAppSettings appSettings) + { + services.AddScoped(s => new RequestContext { UserId = -1 }); // Set context user id to -1 to identify the updates done by the job + services.AddDbContext(options => options.UseNpgsql(appSettings.DbConnection)); + } + + private static void ConfigureServices(IServiceCollection services, PPONAppSettings appSettings) + { + services.AddSingleton(s => appSettings); + + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + } + + private static void ConfigureHttpClients(IServiceCollection services, PPONAppSettings appSettings) + { + services.AddHttpClient("PPONApi", c => + { + c.BaseAddress = new Uri(appSettings.PPONApiSettings.Url); + c.DefaultRequestHeaders.Add("x-api-key", appSettings.PPONApiSettings.Key); + }); + services.AddHttpClient("CiiApi", c => + { + c.BaseAddress = new Uri(appSettings.CiiSettings.Url); + c.DefaultRequestHeaders.Add("x-api-key", appSettings.CiiSettings.Token); + }); + } + + private static PPONAppSettings GetConfigurationDetails(HostBuilderContext hostContext) + { + ScheduleJob scheduleJob; + OneTimeJob oneTimeJob; + CiiSettings ciiSettings; + ApiSettings pPONApiSettings; + + string dbConnection; + + var config = hostContext.Configuration; + dbConnection = config["DbConnection"]; + + ciiSettings = config.GetSection("CIIApi").Get(); + pPONApiSettings = config.GetSection("PPONApi").Get(); + + scheduleJob = config.GetSection("ScheduleJob").Get(); + oneTimeJob = config.GetSection("OneTimeJob").Get(); + + var appSettings = new PPONAppSettings() + { + DbConnection = dbConnection, + CiiSettings = ciiSettings, + PPONApiSettings = pPONApiSettings, + ScheduleJobSettings = scheduleJob, + OneTimeJobSettings = oneTimeJob, + }; + + return appSettings; + } + } +} + diff --git a/api/CcsSso.Core.PPONScheduler/Properties/launchSettings.json b/api/CcsSso.Core.PPONScheduler/Properties/launchSettings.json new file mode 100644 index 00000000..78ab093e --- /dev/null +++ b/api/CcsSso.Core.PPONScheduler/Properties/launchSettings.json @@ -0,0 +1,11 @@ +{ + "profiles": { + "CcsSso.Core.ServiceOnboardingScheduler": { + "commandName": "Project", + "dotnetRunMessages": true, + "environmentVariables": { + "DOTNET_ENVIRONMENT": "Development" + } + } + } +} diff --git a/api/CcsSso.Core.PPONScheduler/Service/Contracts/IPPONService.cs b/api/CcsSso.Core.PPONScheduler/Service/Contracts/IPPONService.cs new file mode 100644 index 00000000..b9336cd4 --- /dev/null +++ b/api/CcsSso.Core.PPONScheduler/Service/Contracts/IPPONService.cs @@ -0,0 +1,14 @@ +using CcsSso.Core.PPONScheduler.Model; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CcsSso.Core.PPONScheduler.Service.Contracts +{ + public interface IPPONService + { + Task PerformJob(bool oneTimeValidationSwitch, DateTime startDate, DateTime endDate); + } +} diff --git a/api/CcsSso.Core.PPONScheduler/Service/PPONService.cs b/api/CcsSso.Core.PPONScheduler/Service/PPONService.cs new file mode 100644 index 00000000..e21625b1 --- /dev/null +++ b/api/CcsSso.Core.PPONScheduler/Service/PPONService.cs @@ -0,0 +1,215 @@ +using CcsSso.Core.PPONScheduler.Model; +using CcsSso.Core.PPONScheduler.Service.Contracts; +using CcsSso.Domain.Constants; +using CcsSso.Domain.Contracts; +using CcsSso.Dtos.Domain.Models; +using CcsSso.Shared.Contracts; +using Microsoft.EntityFrameworkCore; +using Newtonsoft.Json; + +namespace CcsSso.Core.PPONScheduler.Service +{ + public class PPONService : IPPONService + { + private readonly IDataContext _dataContext; + private readonly IHttpClientFactory _httpClientFactory; + private readonly PPONAppSettings _appSettings; + private readonly IDateTimeService _dataTimeService; + private readonly ICiiService _ciiService; + private readonly ILogger _logger; + + public PPONService(ILogger logger, IServiceScopeFactory factory, + PPONAppSettings appSettings, IDateTimeService dataTimeService, + IHttpClientFactory httpClientFactory) + { + _logger = logger; + _appSettings = appSettings; + _dataTimeService = dataTimeService; + _httpClientFactory = httpClientFactory; + _ciiService = factory.CreateScope().ServiceProvider.GetRequiredService(); + _dataContext = factory.CreateScope().ServiceProvider.GetRequiredService(); + } + + public async Task PerformJob(bool oneTimeValidationSwitch, DateTime startDate, DateTime endDate) + { + try + { + var listOfRegisteredOrgs = await GetRegisteredOrgsAsync(oneTimeValidationSwitch, startDate, endDate); + + if (listOfRegisteredOrgs == null || listOfRegisteredOrgs.Count() == 0) + { + _logger.LogInformation("No Organisation found"); + return; + } + + _logger.LogInformation($"Number of organisation {listOfRegisteredOrgs.Count()}"); + + foreach (var orgDetails in listOfRegisteredOrgs) + { + _logger.LogInformation("------------------------------------------------------------------------------------"); + await ProcessOrg(orgDetails); + _logger.LogInformation("------------------------------------------------------------------------------------"); + } + } + catch (Exception ex) + { + _logger.LogError($"*****Outer Exception during this schedule, exception message = {ex.Message}"); + } + } + + private async Task ProcessOrg(Organisation orgDetails) + { + var ciiOrgId = orgDetails.CiiOrganisationId; + var orgLegalName = orgDetails.LegalName; + + try + { + _logger.LogInformation($"Org: {ciiOrgId + " - " + orgLegalName}"); + + await LinkPPONWithOrgAsync(ciiOrgId); + } + catch (Exception ex) + { + _logger.LogError($"*****Inner Exception while processing the org: {ciiOrgId}, exception message = {ex.Message}"); + } + } + + private async Task> GetRegisteredOrgsAsync(bool oneTimeValidationSwitch, DateTime startDate, DateTime endDate) + { + var dataDuration = _appSettings.ScheduleJobSettings.DataDurationInMinutes; + var untilDateTime = _dataTimeService.GetUTCNow().AddMinutes(-dataDuration); + + try + { + if (oneTimeValidationSwitch) + { + return await GetRegisteredOrgsByDateAsync(startDate, endDate); + } + else + { + return await GetRegisteredOrgsByDateAsync(untilDateTime); + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Error"); + throw; + } + } + + private async Task> GetRegisteredOrgsByDateAsync(DateTime untilDateTime) + { + var result = await _dataContext.Organisation.Where( + org => !org.IsDeleted).ToListAsync(); + + return result.Where(org => org.CreatedOnUtc > untilDateTime) + .Select(o => + new Organisation + { + Id = o.Id, + CiiOrganisationId = o.CiiOrganisationId, + LegalName = o.LegalName, + CreatedOnUtc = o.CreatedOnUtc, + RightToBuy = o.RightToBuy, + SupplierBuyerType = o.SupplierBuyerType + }).ToList(); + } + + private async Task> GetRegisteredOrgsByDateAsync(DateTime startDate, DateTime endDate) + { + return await _dataContext.Organisation.Where( + org => !org.IsDeleted + && org.CreatedOnUtc >= TimeZoneInfo.ConvertTimeToUtc(startDate) && org.CreatedOnUtc <= TimeZoneInfo.ConvertTimeToUtc(endDate)) + .Select(o => new Organisation + { + Id = o.Id, + CiiOrganisationId = o.CiiOrganisationId, + LegalName = o.LegalName, + CreatedOnUtc = o.CreatedOnUtc, + RightToBuy = o.RightToBuy, + SupplierBuyerType = o.SupplierBuyerType + }).ToListAsync(); + } + + private async Task LinkPPONWithOrgAsync(string ciiOrganisationId) + { + try + { + _logger.LogInformation("Try to get organisation details from CII"); + + var isExist = await IsPPONExistsAsync(ciiOrganisationId); + + if (isExist) + { + _logger.LogInformation("PPON Id already exists"); + } + else + { + var pPONDetails = await GetPPONDetailsAsync(ciiOrganisationId); + await UpdatePPONAsync(ciiOrganisationId, pPONDetails); + + _logger.LogInformation("PPON Id updated successfully"); + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Error"); + throw; + } + } + + private async Task UpdatePPONAsync(string ciiOrganisationId, PPONDetails pPONDetails) + { + if (pPONDetails != null && pPONDetails.Identifiers != null && pPONDetails.Identifiers.Count > 0) + { + var id = pPONDetails.Identifiers.FirstOrDefault().Id; + _logger.LogInformation("PPON Id: {0}", id); + await _ciiService.AddSchemeAsync(ciiOrganisationId, "GB-PPG", id, null); + } + else + { + _logger.LogInformation("PPON Id not found"); + } + } + + private async Task IsPPONExistsAsync(string ciiOrganisationId) + { + var isExist = false; + + _logger.LogInformation("Try to get organisation details from CII"); + + CiiDto organisationInfo = await _ciiService.GetOrgDetailsAsync(ciiOrganisationId); + + if (organisationInfo != null) + { + _logger.LogInformation("Organisation found on CII database"); + + isExist = organisationInfo?.AdditionalIdentifiers?.Any(x => x.Scheme == "GB-PPG") ?? false; + } + else + { + _logger.LogInformation("Organisation not found on CII database"); + } + + return isExist; + } + + private async Task GetPPONDetailsAsync(string ciiOrganisationId) + { + var client = _httpClientFactory.CreateClient("PPONApi"); + var response = await client.PostAsync("identifiers/id/ppon", null); + + if (response.IsSuccessStatusCode) + { + var content = await response.Content.ReadAsStringAsync(); + return JsonConvert.DeserializeObject(content); + } + else + { + _logger.LogError("Error while getting PPON details: {0}", response.StatusCode); + throw new Exception("ERROR_RETRIEVING_PPON_DETAILS"); + } + } + } +} + diff --git a/api/CcsSso.Core.PPONScheduler/appsecrets.json b/api/CcsSso.Core.PPONScheduler/appsecrets.json new file mode 100644 index 00000000..1ebb2336 --- /dev/null +++ b/api/CcsSso.Core.PPONScheduler/appsecrets.json @@ -0,0 +1,23 @@ +{ + "DbConnection": "", + "PPONApi": { + "Key": "", + "Url": "" + }, + "CIIApi": { + "Token": "", + "Url": "" + }, + "ScheduleJob": { + "ScheduleInMinutes": 60, + "DataDurationInMinutes": 60 + }, + "OneTimeJob": { + "Switch": true, + "_Comments_Switch": "scheduled job will not run if it is true.", + "StartDate": "2000-01-01 00:00", + "_Comments_StartDate": "yyyy-MM-dd HH:mm", + "EndDate": "2000-01-01 23:59", + "_Comments_EndDate": "yyyy-MM-dd HH:mm" + } +} \ No newline at end of file diff --git a/api/CcsSso.Core.PPONScheduler/appsettings.Development.json b/api/CcsSso.Core.PPONScheduler/appsettings.Development.json new file mode 100644 index 00000000..b0e35d1c --- /dev/null +++ b/api/CcsSso.Core.PPONScheduler/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Error", + "Microsoft.Hosting.Lifetime": "Error" + } + } +} diff --git a/api/CcsSso.Core.PPONScheduler/appsettings.json b/api/CcsSso.Core.PPONScheduler/appsettings.json new file mode 100644 index 00000000..849703e4 --- /dev/null +++ b/api/CcsSso.Core.PPONScheduler/appsettings.json @@ -0,0 +1,15 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Error", + "Microsoft.Hosting.Lifetime": "Error" + } + }, + "VaultEnabled": false, + "Source": "AWS", + "_Comments_Source": "AWS or Hashicorp", + "Vault": { + "Address": "" + } +} diff --git a/api/CcsSso.Core.Service/CiiService.cs b/api/CcsSso.Core.Service/CiiService.cs index 5178b787..06373d3b 100644 --- a/api/CcsSso.Core.Service/CiiService.cs +++ b/api/CcsSso.Core.Service/CiiService.cs @@ -43,7 +43,10 @@ public CiiService(CiiConfig config, IAuditLoginService auditLoginService, IHttpC public async Task AddSchemeAsync(string ciiOrganisationId, string scheme, string identifier, string token) { var client = _httpClientFactory.CreateClient("CiiApi"); - client.DefaultRequestHeaders.Add("Authorization", token); + if (!string.IsNullOrEmpty(token)) + { + client.DefaultRequestHeaders.Add("Authorization", token); + } //var body = JsonConvert.SerializeObject(model); var response = await client.PutAsync($"identities/organisations/{ciiOrganisationId}/schemes/{scheme}/identifiers/{identifier}", new StringContent("", System.Text.Encoding.UTF8, "application/json")); if (response.IsSuccessStatusCode) diff --git a/api/CcsSso.sln b/api/CcsSso.sln index 132137bd..3d89c8eb 100644 --- a/api/CcsSso.sln +++ b/api/CcsSso.sln @@ -1,4 +1,3 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.32014.148 @@ -80,6 +79,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CcsSso.Core.ServiceOnboardi EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CcsSso.Core.BSIRolesRemovalOneTimeJob", "BSIRolesRemovalOneTimeJob\CcsSso.Core.BSIRolesRemovalOneTimeJob.csproj", "{219802C2-39AF-4890-964A-D9FE976FDD97}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CcsSso.Core.PPONScheduler", "CcsSso.Core.PPONScheduler\CcsSso.Core.PPONScheduler.csproj", "{B09C54AB-9462-40D0-8F54-6F0EBD719B7E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -206,6 +207,10 @@ Global {219802C2-39AF-4890-964A-D9FE976FDD97}.Debug|Any CPU.Build.0 = Debug|Any CPU {219802C2-39AF-4890-964A-D9FE976FDD97}.Release|Any CPU.ActiveCfg = Release|Any CPU {219802C2-39AF-4890-964A-D9FE976FDD97}.Release|Any CPU.Build.0 = Release|Any CPU + {B09C54AB-9462-40D0-8F54-6F0EBD719B7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B09C54AB-9462-40D0-8F54-6F0EBD719B7E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B09C54AB-9462-40D0-8F54-6F0EBD719B7E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B09C54AB-9462-40D0-8F54-6F0EBD719B7E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -241,8 +246,9 @@ Global {334BDF91-AA22-4583-B7D4-560876ACDA36} = {179F7A96-5DE5-47E9-BC61-F5E4276EF60C} {EBC3F606-77A2-4E94-AC2B-060C3A5CE1E7} = {179F7A96-5DE5-47E9-BC61-F5E4276EF60C} {219802C2-39AF-4890-964A-D9FE976FDD97} = {179F7A96-5DE5-47E9-BC61-F5E4276EF60C} + {B09C54AB-9462-40D0-8F54-6F0EBD719B7E} = {179F7A96-5DE5-47E9-BC61-F5E4276EF60C} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {37B13717-0A0F-4D37-A3DA-BA346AE5D064} EndGlobalSection -EndGlobal \ No newline at end of file +EndGlobal From 098108be4217dfb8a2900f6525feb636ea68da8a Mon Sep 17 00:00:00 2001 From: brijrajsinh-bc <109584978+brijrajsinh-bc@users.noreply.github.com> Date: Thu, 27 Apr 2023 20:30:56 +0530 Subject: [PATCH 21/24] 5167 issue with create/update user api groupid not passed error (#1659) --- .../External/UserProfileService.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/api/CcsSso.Core.Service/External/UserProfileService.cs b/api/CcsSso.Core.Service/External/UserProfileService.cs index 19084891..df05164e 100644 --- a/api/CcsSso.Core.Service/External/UserProfileService.cs +++ b/api/CcsSso.Core.Service/External/UserProfileService.cs @@ -136,7 +136,7 @@ public async Task CreateUserAsync(UserProfileEditRequestIn // Set user groups var groupsWithRoleRequiredApproval = new List>>(); - if (_appConfigInfo.UserRoleApproval.Enable && !isUserDomainValid && userProfileRequestInfo.Detail.GroupIds.Any()) + if (_appConfigInfo.UserRoleApproval.Enable && !isUserDomainValid && userProfileRequestInfo.Detail.GroupIds != null && userProfileRequestInfo.Detail.GroupIds.Any()) { groupsWithRoleRequiredApproval = GetGroupsWithApprovalOrgRole(organisation.UserGroups, userProfileRequestInfo.Detail.GroupIds); } @@ -1030,7 +1030,7 @@ join cr in _dataContext.CcsAccessRole on er.CcsAccessRoleId equals cr.Id // list of new groups to check for approval required role if (userProfileRequestInfo.Detail.GroupIds != null) - { + { var newlyAddedGroupIds = userProfileRequestInfo.Detail.GroupIds.Where(x => !previousGroups.Contains(x)).ToList(); if (_appConfigInfo.UserRoleApproval.Enable && !isUserDomainValid && userProfileRequestInfo.Detail.GroupIds != null && newlyAddedGroupIds.Any()) { @@ -1038,7 +1038,10 @@ join cr in _dataContext.CcsAccessRole on er.CcsAccessRoleId equals cr.Id } } - removedGroupIds = previousGroups.Where(x => !userProfileRequestInfo.Detail.GroupIds.Contains(x)).ToList(); + if (userProfileRequestInfo.Detail.GroupIds != null) + { + removedGroupIds = previousGroups.Where(x => !userProfileRequestInfo.Detail.GroupIds.Contains(x)).ToList(); + } // Set groups var userGroupMemberships = new List(); @@ -1075,7 +1078,10 @@ join cr in _dataContext.CcsAccessRole on er.CcsAccessRoleId equals cr.Id }); user.UserAccessRoles = userAccessRoles; - removedRoleIds = previousRoles.Where(x => !userProfileRequestInfo.Detail.RoleIds.Contains(x)).ToList(); + if (userProfileRequestInfo.Detail.RoleIds != null) + { + removedRoleIds = previousRoles.Where(x => !userProfileRequestInfo.Detail.RoleIds.Contains(x)).ToList(); + } // Check the admin group availability in request var noAdminRoleGroupInRequest = userProfileRequestInfo.Detail.GroupIds == null || !userProfileRequestInfo.Detail.GroupIds.Any() || From e690b234922b67f64525770e34af8c818413c4a0 Mon Sep 17 00:00:00 2001 From: VijayHirudayasamy-bc Date: Thu, 4 May 2023 19:18:32 +0100 Subject: [PATCH 22/24] patch for the fix 5516 and 5123- are completed --- .../External/OrganisationGroupService.cs | 85 ++++++++++++------- 1 file changed, 56 insertions(+), 29 deletions(-) diff --git a/api/CcsSso.Core.Service/External/OrganisationGroupService.cs b/api/CcsSso.Core.Service/External/OrganisationGroupService.cs index f89fc882..53162b46 100644 --- a/api/CcsSso.Core.Service/External/OrganisationGroupService.cs +++ b/api/CcsSso.Core.Service/External/OrganisationGroupService.cs @@ -9,6 +9,7 @@ using CcsSso.Domain.Contracts; using CcsSso.Domain.Dtos; using CcsSso.Domain.Exceptions; +using CcsSso.Shared.Cache.Contracts; using CcsSso.Shared.Domain.Constants; using CcsSso.Shared.Domain.Helpers; using Microsoft.EntityFrameworkCore; @@ -33,12 +34,15 @@ public class OrganisationGroupService : IOrganisationGroupService private readonly IServiceRoleGroupMapperService _serviceRoleGroupMapperService; private readonly IOrganisationProfileService _organisationService; private readonly IUserProfileRoleApprovalService _userProfileRoleApprovalService; + private readonly ILocalCacheService _localCacheService; + public OrganisationGroupService(IDataContext dataContext, IUserProfileHelperService userProfileHelperService, IAuditLoginService auditLoginService, ICcsSsoEmailService ccsSsoEmailService, IWrapperCacheService wrapperCacheService, ApplicationConfigurationInfo appConfigInfo, IServiceRoleGroupMapperService serviceRoleGroupMapperService, IOrganisationProfileService organisationService, - IUserProfileRoleApprovalService userProfileRoleApprovalService) + IUserProfileRoleApprovalService userProfileRoleApprovalService, + ILocalCacheService localCacheService) { _dataContext = dataContext; _userProfileHelperService = userProfileHelperService; @@ -49,6 +53,7 @@ public OrganisationGroupService(IDataContext dataContext, IUserProfileHelperServ _serviceRoleGroupMapperService = serviceRoleGroupMapperService; _organisationService = organisationService; _userProfileRoleApprovalService = userProfileRoleApprovalService; + _localCacheService = localCacheService; } public async Task CreateGroupAsync(string ciiOrganisationId, OrganisationGroupNameInfo organisationGroupNameInfo) @@ -318,7 +323,6 @@ public async Task UpdateGroupAsync(string ciiOrganisationId, int groupId, Organi OrganisationUserGroupId = groupId, OrganisationEligibleRoleId = addedRoleId }; - group.GroupEligibleRoles.Add(groupAccess); } }); @@ -373,11 +377,33 @@ public async Task UpdateGroupAsync(string ciiOrganisationId, int groupId, Organi UserId = addedUserId, OrganisationUserGroupId = group.Id }; - addedUsersTupleList.Add(new Tuple(addedUserId, addedUserName)); // for logs group.UserGroupMemberships.Add(userGroupMembership); } }); + + // this will be used in the success page. (Group success page only shows pending status for the added users) + var expiration = new TimeSpan(0, 0, 60); + if (addedUserNameList.Any()) + { + _localCacheService.SetValue(groupId.ToString(), addedUserNameList, expiration); + } + else + { + if (removedUserNameList.Any()) + { + _localCacheService.SetValue(groupId.ToString(), new List { "userremoved" }, expiration); + } + else + { + _localCacheService.Remove(new string[] { groupId.ToString() }); + } + } + } + else + { + _localCacheService.Remove(new string[] { groupId.ToString() }); + } var mfaEnableRoleExists = orgRoleInfo.Any(r => group.GroupEligibleRoles.Any(ge => ge.OrganisationEligibleRoleId == r.Id && !ge.IsDeleted && r.MfaEnable)); @@ -396,7 +422,7 @@ public async Task UpdateGroupAsync(string ciiOrganisationId, int groupId, Organi group.MfaEnabled = mfaEnableRoleExists; await _dataContext.SaveChangesAsync(); - await VerifyAndCreateGroupRolePendingRequest(group, ciiOrganisationId); + await VerifyAndCreateGroupRolePendingRequest(group, ciiOrganisationId, addedUsersTupleList, addedRoleIds); //Log if (hasNameChanged) @@ -442,7 +468,9 @@ await _auditLoginService.CreateLogAsync(AuditLogEvent.GroupeUserRemove, AuditLog await _wrapperCacheService.RemoveCacheAsync(invalidatingCacheKeys.ToArray()); } - private async Task VerifyAndCreateGroupRolePendingRequest(OrganisationUserGroup group, string ciiOrganisationId) + + + private async Task VerifyAndCreateGroupRolePendingRequest(OrganisationUserGroup group, string ciiOrganisationId, List> addedUsersTupleList, List addedRoleIds) { if (!_appConfigInfo.UserRoleApproval.Enable) { @@ -451,12 +479,21 @@ private async Task VerifyAndCreateGroupRolePendingRequest(OrganisationUserGroup var org = await _dataContext.Organisation.FirstOrDefaultAsync(x => x.CiiOrganisationId == ciiOrganisationId); var orgDomain = org?.DomainName?.ToLower(); - var latestExistingUserNames = group.UserGroupMemberships.Where(x => !x.IsDeleted).Select(ugm => ugm.User).ToList(); - var userHasInValidDomain = latestExistingUserNames.Where(user => user.UserName.ToLower().Split('@')?[1] != orgDomain).ToList(); + List newAddedUsers = new(); + if (group.GroupEligibleRoles.Any(x => !x.IsDeleted && addedRoleIds.Contains(x.OrganisationEligibleRoleId) && + x.OrganisationEligibleRole.CcsAccessRole.ApprovalRequired == (int)RoleApprovalRequiredStatus.ApprovalRequired)) + { + newAddedUsers = group.UserGroupMemberships.Where(x => !x.IsDeleted).Select(ugm => ugm.User).ToList(); + } + else + { + newAddedUsers = group.UserGroupMemberships.Where(x => !x.IsDeleted).Select(ugm => ugm.User).Where(u => addedUsersTupleList.Select(x => x.Item1).Contains(u.Id)).ToList(); + } + var userHasInValidDomain = newAddedUsers.Where(user => user.UserName.ToLower().Split('@')?[1] != orgDomain).ToList(); if (userHasInValidDomain.Any()) { - await RemoveGroupRolePendingRequest(group, userHasInValidDomain); + //await RemoveGroupRolePendingRequest(group, userHasInValidDomain); List approvalRequiredRoles = new(); foreach (var role in group.GroupEligibleRoles.Where(x => !x.IsDeleted)) @@ -487,7 +524,7 @@ private async Task VerifyAndCreateGroupRolePendingRequest(OrganisationUserGroup private async Task RemoveGroupRolePendingRequest(OrganisationUserGroup group) { var pendingGroupRequest = await _dataContext.UserAccessRolePending.Where(x => - (x.Status == (int)UserPendingRoleStaus.Pending + (x.Status == (int)UserPendingRoleStaus.Pending || x.Status == (int)UserPendingRoleStaus.Approved || x.Status == (int)UserPendingRoleStaus.Rejected) && x.OrganisationUserGroupId == group.Id).ToListAsync(); @@ -500,23 +537,6 @@ private async Task RemoveGroupRolePendingRequest(OrganisationUserGroup group) await _dataContext.SaveChangesAsync(); } - private async Task RemoveGroupRolePendingRequest(OrganisationUserGroup group, List users) - { - var pendingGroupRequest = await _dataContext.UserAccessRolePending.Where(x => - x.OrganisationUserGroupId == group.Id - && !users.Select(user => user.Id).Contains(x.UserId) - && (x.Status == (int)UserPendingRoleStaus.Pending - || x.Status == (int)UserPendingRoleStaus.Approved - || x.Status == (int)UserPendingRoleStaus.Rejected)).ToListAsync(); - - foreach (var pendingRequest in pendingGroupRequest) - { - pendingRequest.IsDeleted = true; - pendingRequest.Status = (int)UserPendingRoleStaus.Removed; - } - await _dataContext.SaveChangesAsync(); - } - public async Task GetGroupUsersPendingRequestSummary(int groupId, string ciiOrgId, ResultSetCriteria resultSetCriteria, bool isPendingApproval) { var group = await _dataContext.OrganisationUserGroup @@ -535,11 +555,18 @@ public async Task GetGroupUsersPendingRequestSummary(int && (x.Status == (int)UserPendingRoleStaus.Pending || x.Status == (int)UserPendingRoleStaus.Rejected)) .ToListAsync(); - var pendingRequests = pendingAndRejectedRequests.Where(x => x.Status ==(int) UserPendingRoleStaus.Pending && !x.IsDeleted); - + var pendingRequests = pendingAndRejectedRequests.Where(x => x.Status == (int)UserPendingRoleStaus.Pending && !x.IsDeleted); + var filteredUserIds = isPendingApproval ? existingUserIds.Where(x => pendingRequests.Any(y => y.UserId == x)) : existingUserIds.Where(x => !pendingAndRejectedRequests.Any(y => y.UserId == x)); + var addedUsers = _localCacheService.GetValue>(groupId.ToString()); + if (addedUsers != null && addedUsers.Any()) + { + var userIds = _dataContext.User.Where(x => addedUsers.Contains(x.UserName)).Select(x => x.Id); + filteredUserIds = filteredUserIds.Where(x => userIds.Contains(x)).ToArray(); + } + var usersQuery = _dataContext.User.Include(u => u.Party).ThenInclude(p => p.Person).Where(user => !user.IsDeleted && filteredUserIds.Contains(user.Id)).OrderBy(u => u.UserName); var pagedResult = await _dataContext.GetPagedResultAsync(usersQuery, resultSetCriteria); @@ -553,7 +580,7 @@ public async Task GetGroupUsersPendingRequestSummary(int GroupUser = pagedResult.Results?.Select(up => new GroupUser { UserId = up.UserName, - UserPendingRoleStatus = isPendingApproval? UserPendingRoleStaus.Pending: null, // pending and rejected will be shown as users doesn't have the role. + UserPendingRoleStatus = isPendingApproval ? UserPendingRoleStaus.Pending : null, // pending and rejected will be shown as users doesn't have the role. Name = $"{up.Party.Person.FirstName} {up.Party.Person.LastName}", }).ToList() ?? new List() }; From f6d201951eb1300068dadcf472eec8d47fb34d8b Mon Sep 17 00:00:00 2001 From: VijayHirudayasamy-bc <103512567+VijayHirudayasamy-bc@users.noreply.github.com> Date: Fri, 5 May 2023 07:25:18 +0100 Subject: [PATCH 23/24] Missing Unit test file (#1669) --- .../External/OrganisationGroupServiceTest.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/api/CcsSso.Core.Tests/External/OrganisationGroupServiceTest.cs b/api/CcsSso.Core.Tests/External/OrganisationGroupServiceTest.cs index e4e43557..f2fbb82b 100644 --- a/api/CcsSso.Core.Tests/External/OrganisationGroupServiceTest.cs +++ b/api/CcsSso.Core.Tests/External/OrganisationGroupServiceTest.cs @@ -10,6 +10,7 @@ using CcsSso.Domain.Contracts; using CcsSso.Domain.Dtos; using CcsSso.Domain.Exceptions; +using CcsSso.Shared.Cache.Contracts; using CcsSso.Shared.Domain.Contexts; using Microsoft.EntityFrameworkCore; using Moq; @@ -973,9 +974,11 @@ public static OrganisationGroupService UserService(IDataContext dataContext) var mockRolesToServiceRoleGroupMapperService = new Mock(); var mockOrganisationProfileService = new Mock(); var mockUserProfileRoleApprovalService = new Mock(); + var localCacheService = new Mock(); var service = new OrganisationGroupService(dataContext, userProfileHelperService, mockAuditLoginService.Object, mockEmailService.Object, - mockCacheService.Object, applicationConfigurationInfo, mockRolesToServiceRoleGroupMapperService.Object, mockOrganisationProfileService.Object, mockUserProfileRoleApprovalService.Object); + mockCacheService.Object, applicationConfigurationInfo, mockRolesToServiceRoleGroupMapperService.Object, mockOrganisationProfileService.Object, mockUserProfileRoleApprovalService.Object + , localCacheService.Object); return service; } From 024e895b2318f7322c929f27f36f3d8d68251ad0 Mon Sep 17 00:00:00 2001 From: brijeshpatel-bc <108462846+brijeshpatel-bc@users.noreply.github.com> Date: Fri, 5 May 2023 19:03:55 +0530 Subject: [PATCH 24/24] =?UTF-8?q?=E2=80=A2=20Bug=205559:=20Manage=20groups?= =?UTF-8?q?=20-=20All=20the=20fleet=20users=20status=20changed=20to=20"Acc?= =?UTF-8?q?ess=20denied=20for=20fleet=20portal"=20when=20services=20update?= =?UTF-8?q?d=20(#1670)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Bug 5550: Pending approval request got expired to user when expired user is unchecked from the group --- .../External/OrganisationGroupService.cs | 49 ++++++++++++++++--- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/api/CcsSso.Core.Service/External/OrganisationGroupService.cs b/api/CcsSso.Core.Service/External/OrganisationGroupService.cs index 53162b46..ef5130a6 100644 --- a/api/CcsSso.Core.Service/External/OrganisationGroupService.cs +++ b/api/CcsSso.Core.Service/External/OrganisationGroupService.cs @@ -172,7 +172,7 @@ public async Task GetGroupAsync(string ciiOrganis UserId = ugm.User.UserName, Name = $"{ugm.User.Party.Person.FirstName} {ugm.User.Party.Person.LastName}", IsAdmin = ugm.User.UserAccessRoles.Any(r => !r.IsDeleted && r.OrganisationEligibleRole.CcsAccessRole.CcsAccessRoleNameKey == Contstant.OrgAdminRoleNameKey && !r.OrganisationEligibleRole.IsDeleted), - + UserPendingRoleStatus = !isApprovalRequired ? null : getUserRolePendingStatus(ugm), }).ToList(); @@ -186,7 +186,7 @@ public async Task GetGroupAsync(string ciiOrganis .OrderByDescending(y => y.Id) .FirstOrDefault(x => x.UserId == ugm.User.Id && x.OrganisationUserGroupId == ugm.OrganisationUserGroupId); - + return (UserPendingRoleStaus?)(pendingRole?.Status); } @@ -422,7 +422,12 @@ public async Task UpdateGroupAsync(string ciiOrganisationId, int groupId, Organi group.MfaEnabled = mfaEnableRoleExists; await _dataContext.SaveChangesAsync(); - await VerifyAndCreateGroupRolePendingRequest(group, ciiOrganisationId, addedUsersTupleList, addedRoleIds); + if (_appConfigInfo.UserRoleApproval.Enable) + { + await RemoveGroupRolesApproveRequest(groupId, removedRoleIds); + await RemoveGroupUsersApproveRequest(groupId, removedUsersTupleList); + await VerifyAndCreateGroupRolePendingRequest(group, ciiOrganisationId, addedUsersTupleList, addedRoleIds); + } //Log if (hasNameChanged) @@ -468,7 +473,35 @@ await _auditLoginService.CreateLogAsync(AuditLogEvent.GroupeUserRemove, AuditLog await _wrapperCacheService.RemoveCacheAsync(invalidatingCacheKeys.ToArray()); } + private async Task RemoveGroupUsersApproveRequest(int groupId, List> removedUsersTupleList) + { + if (removedUsersTupleList != null && removedUsersTupleList.Any()) + { + var removedUserIds = removedUsersTupleList.Select(x => x.Item1); + + var userAccessRolePendingList = await _dataContext.UserAccessRolePending + .Where(x => removedUserIds.Contains(x.UserId) + && x.OrganisationUserGroupId == groupId).ToListAsync(); + + userAccessRolePendingList.ForEach(l => { l.IsDeleted = true; l.Status = (int)UserPendingRoleStaus.Removed; }); + + await _dataContext.SaveChangesAsync(); + } + } + + private async Task RemoveGroupRolesApproveRequest(int groupId, List removedRoleIds) + { + if (removedRoleIds != null && removedRoleIds.Any()) + { + var userAccessRolePendingList = await _dataContext.UserAccessRolePending + .Where(x => removedRoleIds.Contains(x.OrganisationEligibleRoleId) + && x.OrganisationUserGroupId == groupId).ToListAsync(); + userAccessRolePendingList.ForEach(l => { l.IsDeleted = true; l.Status = (int)UserPendingRoleStaus.Removed; }); + + await _dataContext.SaveChangesAsync(); + } + } private async Task VerifyAndCreateGroupRolePendingRequest(OrganisationUserGroup group, string ciiOrganisationId, List> addedUsersTupleList, List addedRoleIds) { @@ -511,13 +544,13 @@ private async Task VerifyAndCreateGroupRolePendingRequest(OrganisationUserGroup else { // remove any pending request exists for this group - await RemoveGroupRolePendingRequest(group); + //await RemoveGroupRolePendingRequest(group); } } else { // remove any pending request exists for this group - await RemoveGroupRolePendingRequest(group); + //await RemoveGroupRolePendingRequest(group); } } @@ -652,7 +685,7 @@ public async Task UpdateServiceRoleGroupAsync(string ciiOrganisationId, int grou }; await this.UpdateGroupAsync(ciiOrganisationId, groupId, organisationGroupRequestInfo); - } + } private static OrganisationServiceRoleGroupResponseInfo ConvertGroupRoleToServiceRoleGroupResponse(OrganisationGroupResponseInfo organisationGroupResponseInfo) { @@ -712,8 +745,8 @@ private async Task GeneratePendingRequests(OrganisationUserGroup group, List x.Id).FirstOrDefault(x => x.UserId == user.Id); - if (latestRequestOfUser == null || - (latestRequestOfUser.Status != (int)UserPendingRoleStaus.Pending + if (latestRequestOfUser == null || + (latestRequestOfUser.Status != (int)UserPendingRoleStaus.Pending && latestRequestOfUser.Status != (int)UserPendingRoleStaus.Approved && latestRequestOfUser.Status != (int)UserPendingRoleStaus.Rejected)) {