Codeunit 104030 Upgrade Plan Permissions

App
Base Application
Namespace
Microsoft.Upgrade
Versions
17-28

Procedures, 3

Versions171819202122232425262728

Source29

Source in 29

src/Layers/W1/BaseApp/OtherCapabilities/Upgrade/UpgradePlanPermissions.Codeunit.al787 lines, Copyright (c) Microsoft Corporation. MIT

// ------------------------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
// ------------------------------------------------------------------------------------------------
namespace Microsoft.Upgrade;

using System.Azure.Identity;
using System.Environment;
using System.Environment.Configuration;
using System.Reflection;
using System.Security.AccessControl;
using System.Upgrade;

codeunit 104030 "Upgrade Plan Permissions"
{
    Subtype = Upgrade;
    Permissions = TableData "User Group Plan" = rimd;

    var
        AutomationTok: Label 'D365 AUTOMATION', MaxLength = 20, Locked = true;
        BackupRestoreTok: Label 'D365 BACKUP/RESTORE', Locked = true;
        BackupRestoreDescriptionTxt: Label 'Backup or restore database', Comment = 'Maximum length is 30';
        BasicTok: Label 'D365 BASIC', Locked = true;
        BasicISVTok: Label 'D365 BASIC ISV', Locked = true;
        BusFullTok: Label 'D365 BUS FULL ACCESS', Locked = true;
        PremiumBusFullTok: Label 'D365 BUS PREMIUM', Locked = true;
        FullTok: Label 'D365 FULL ACCESS', Locked = true;
        OnPremBasicTok: Label 'BASIC', Locked = true;
        ReadTok: Label 'D365 READ', Locked = true;
        SecurityTok: Label 'SECURITY', Locked = true;
        TeamMemberTok: Label 'D365 TEAM MEMBER', Locked = true;
        EditInExcelTok: Label 'Edit in Excel - View', Locked = true, MaxLength = 20;
        ExcelExportActionTok: Label 'EXCEL EXPORT ACTION', Locked = true, MaxLength = 20;
        ExcelExportActionDescriptionTxt: Label 'D365 Excel Export Action', Locked = true, MaxLength = 30;
        AutomateExecPermissionSetTok: Label 'Automate - Exec', Locked = true, MaxLength = 20;
        AutomateActionUserGroupTok: Label 'AUTOMATE ACTION', Locked = true;
        AutomateActionUserGroupDescriptionTxt: Label 'Allow action Automate', Locked = true, MaxLength = 30;
        D365MonitorFieldsTxt: Label 'D365 Monitor Fields', Locked = true;
        SecurityUserGroupTok: Label 'D365 SECURITY', Locked = true;
        TeamsUsersTok: Label 'TEAMS USERS', Locked = true;
        TeamsUsersDescriptionTxt: Label 'Microsoft Teams internal users', Locked = true, MaxLength = 30;
        EmployeeTok: Label 'EMPLOYEE', Locked = true;
        LoginTok: Label 'LOGIN', Locked = true;
        CannotCreatePermissionSetLbl: Label 'Permission Set %1 is missing from this environment and cannot be created.', Locked = true;
        CouldNotInsertAccessControlTelemetryErr: Label 'Could not insert Access Control with App ID %1', Locked = true;
        BaseApplicationAppIdTok: Label '{437dbf0e-84ff-417a-965d-ed2bb9650972}', Locked = true;
        SystemApplicationAppIdTok: Label '{63ca2fa4-4f03-4f2b-a480-172fef340d3f}', Locked = true;
        EmptyAppId: Guid;


    trigger OnUpgradePerDatabase()
    begin
        RunUpgrade();
    end;

    internal procedure RunUpgrade()
    var
        HybridDeployment: Codeunit "Hybrid Deployment";
    begin
        if not HybridDeployment.VerifyCanStartUpgrade('') then
            exit;

        SetBackupRestorePermissions();
        SetExcelExportActionPermissions();
        SetAutomateActionPermissions();
        SetAutomateAccessControl();
        RemoveExtensionManagementFromPlan();
        RemoveExtensionManagementFromUsers();
        SetMonitorSensitiveFieldPermissions();
        AddFeatureDataUpdatePermissions();
        CreateMicrosoft365Permissions();
        CreateD365EssentialAttachPermissions();
        CreateBCAdminPermissions();
    end;

    local procedure AddFeatureDataUpdatePermissions()
    var
        Permission: Record Permission;
        UpgradeTag: Codeunit "Upgrade Tag";
        UpgradeTagDefinitions: Codeunit "Upgrade Tag Definitions";
    begin
        if UpgradeTag.HasUpgradeTag(UpgradeTagDefinitions.GetAddFeatureDataUpdatePermissionsUpgradeTag()) then
            exit;

        InsertPermission(AutomationTok, Permission."Object Type"::"Table Data", DATABASE::"Feature Data Update Status", 1, 1, 1, 1, 0);
        InsertPermission(BasicTok, Permission."Object Type"::"Table Data", DATABASE::"Feature Data Update Status", 1, 1, 1, 1, 0);
        InsertPermission(BasicISVTok, Permission."Object Type"::"Table Data", DATABASE::"Feature Data Update Status", 1, 1, 1, 1, 0);
        InsertPermission(BusFullTok, Permission."Object Type"::"Table Data", DATABASE::"Feature Data Update Status", 1, 1, 1, 1, 0);
        InsertPermission(PremiumBusFullTok, Permission."Object Type"::"Table Data", DATABASE::"Feature Data Update Status", 1, 1, 1, 1, 0);
        InsertPermission(FullTok, Permission."Object Type"::"Table Data", DATABASE::"Feature Data Update Status", 1, 1, 1, 1, 0);
        InsertPermission(ReadTok, Permission."Object Type"::"Table Data", DATABASE::"Feature Data Update Status", 1, 0, 0, 0, 0);
        InsertPermission(TeamMemberTok, Permission."Object Type"::"Table Data", DATABASE::"Feature Data Update Status", 1, 0, 1, 0, 0);
        InsertPermission(OnPremBasicTok, Permission."Object Type"::"Table Data", DATABASE::"Feature Data Update Status", 1, 1, 1, 1, 0);
        InsertPermission(SecurityTok, Permission."Object Type"::"Table Data", DATABASE::"Feature Data Update Status", 1, 1, 1, 1, 0);

        UpgradeTag.SetUpgradeTag(UpgradeTagDefinitions.GetAddFeatureDataUpdatePermissionsUpgradeTag());
    end;

    local procedure RemoveExtensionManagementFromPlan()
    var
        UserGroupPlan: Record "User Group Plan";
        UpgradeTag: Codeunit "Upgrade Tag";
        UpgradeTagDefinitions: Codeunit "Upgrade Tag Definitions";
    begin
        if UpgradeTag.HasUpgradeTag(UpgradeTagDefinitions.GetRemoveExtensionManagementFromPlanUpgradeTag()) then
            exit;

        UserGroupPlan.SetRange("User Group Code", 'D365 EXTENSION MGT');
        UserGroupPlan.DeleteAll();

        UpgradeTag.SetUpgradeTag(UpgradeTagDefinitions.GetRemoveExtensionManagementFromPlanUpgradeTag());
    end;

    local procedure RemoveExtensionManagementFromUsers()
    var
        AccessControl: Record "Access Control";
        UpgradeTag: Codeunit "Upgrade Tag";
        UpgradeTagDefinitions: Codeunit "Upgrade Tag Definitions";
    begin
        if UpgradeTag.HasUpgradeTag(UpgradeTagDefinitions.GetRemoveExtensionManagementFromUsersUpgradeTag()) then
            exit;

        AccessControl.SetRange("Role ID", 'D365 EXTENSION MGT');
        AccessControl.DeleteAll();

        UpgradeTag.SetUpgradeTag(UpgradeTagDefinitions.GetRemoveExtensionManagementFromUsersUpgradeTag());
    end;

    local procedure SetBackupRestorePermissions()
    var
        UpgradeTag: Codeunit "Upgrade Tag";
        UpgradeTagDefinitions: Codeunit "Upgrade Tag Definitions";
    begin
        if UpgradeTag.HasUpgradeTag(UpgradeTagDefinitions.GetAddBackupRestorePermissionSetUpgradeTag()) then
            exit;

        if RunUserGroupUpgrade() then begin
            AddBackupRestorePermissionSet();
            AddBackupRestoreUserGroup();
            AddBackupRestorePermissionSetToGroup();
            AddBackupRestoreUserGroupToDelegatedAdminPlan();
            AddBackupRestoreUserGroupToNavInternalAdmin();
        end;

        UpgradeTag.SetUpgradeTag(UpgradeTagDefinitions.GetAddBackupRestorePermissionSetUpgradeTag());
    end;

    local procedure AddBackupRestorePermissionSet(): Boolean
    var
        PermissionSet: Record "Permission Set";
        Permission: Record Permission;
    begin
        if TryInsertPermissionSet(CopyStr(BackupRestoreTok, 1, MaxStrLen(PermissionSet."Role ID")),
            CopyStr(BackupRestoreDescriptionTxt, 1, MaxStrLen(PermissionSet.Name))) then begin
            InsertPermission(CopyStr(BackupRestoreTok, 1, MaxStrLen(Permission."Role ID")), 10, 5410, 1, 1, 1, 1, 1);
            InsertPermission(CopyStr(BackupRestoreTok, 1, MaxStrLen(Permission."Role ID")), 10, 5420, 1, 1, 1, 1, 1);
            exit(true);
        end;
    end;

    local procedure AddBackupRestoreUserGroup();
    var
        UserGroup: Record "User Group";
    begin
        InsertUserGroup(CopyStr(BackupRestoreTok, 1, MaxStrLen(UserGroup.Code)),
            CopyStr(BackupRestoreDescriptionTxt, 1, MaxStrLen(UserGroup.Name)), false);
    end;

    local procedure AddBackupRestorePermissionSetToGroup();
    var
        UserGroupPermissionSet: Record "User Group Permission Set";
    begin
        AddPermissionSetToUserGroup(CopyStr(BackupRestoreTok, 1, MaxStrLen(UserGroupPermissionSet."Role ID")),
            CopyStr(BackupRestoreTok, 1, MaxStrLen(UserGroupPermissionSet."User Group Code")));
    end;

    local procedure AddBackupRestoreUserGroupToDelegatedAdminPlan();
    var
        UserGroupPlan: Record "User Group Plan";
        PlanIds: Codeunit "Plan Ids";
    begin
        AddUserGroupToPlan(CopyStr(BackupRestoreTok, 1, MaxStrLen(UserGroupPlan."User Group Code")),
            PlanIds.GetDelegatedAdminPlanId());
    end;

    local procedure AddBackupRestoreUserGroupToNavInternalAdmin();
    var
        UserGroupPlan: Record "User Group Plan";
        PlanIds: Codeunit "Plan Ids";
    begin
        AddUserGroupToPlan(CopyStr(BackupRestoreTok, 1, MaxStrLen(UserGroupPlan."User Group Code")),
            PlanIds.GetGlobalAdminPlanId());
    end;

    local procedure TryInsertPermissionSet(PermissionSetID: Code[20]; PermissionSetName: Text[30]): Boolean
    var
        PermissionSet: Record "Permission Set";
        EnvironmentInformation: Codeunit "Environment Information";
        ServerSetting: Codeunit "Server Setting";
        TelemetryCustomDimensions: Dictionary of [Text, Text];
    begin
        if ServerSetting.GetUsePermissionSetsFromExtensions() then
            exit(false);

        if PermissionSet.Get(PermissionSetID) then
            exit(false);

        if EnvironmentInformation.IsSaaS() then begin
            TelemetryCustomDimensions.Add(PermissionSet.FieldCaption("Role ID"), PermissionSetID);
            TelemetryCustomDimensions.Add(PermissionSet.FieldCaption(Name), PermissionSetName);
            Session.LogMessage('0000DW1', StrSubstNo(CannotCreatePermissionSetLbl, PermissionSetID), Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::All, TelemetryCustomDimensions);
            exit(false);
        end;

        PermissionSet."Role ID" := PermissionSetID;
        PermissionSet.Name := PermissionSetName;
        PermissionSet.Insert();
        exit(true);
    end;

    local procedure InsertPermission(PermissionSetID: Code[20]; ObjType: Option; ObjId: Integer; ReadPerm: Option; InsertPerm: Option; ModifyPerm: Option; DeletePerm: Option; ExecutePerm: Option)
    var
        Permission: Record Permission;
        PermissionSet: Record "Permission Set";
        EnvironmentInformation: Codeunit "Environment Information";
        ServerSetting: Codeunit "Server Setting";
    begin
        if not PermissionSet.Get(PermissionSetID) then
            exit;
        if Permission.Get(PermissionSetID, ObjType, ObjId) then
            exit;

        if EnvironmentInformation.IsSaaS() then
            exit;

        if ServerSetting.GetUsePermissionSetsFromExtensions() then
            exit;

        Permission."Role ID" := PermissionSetID;
        Permission."Object Type" := ObjType;
        Permission."Object ID" := ObjId;
        Permission."Read Permission" := ReadPerm;
        Permission."Insert Permission" := InsertPerm;
        Permission."Modify Permission" := ModifyPerm;
        Permission."Delete Permission" := DeletePerm;
        Permission."Execute Permission" := ExecutePerm;
        Permission.Insert();
    end;

    local procedure InsertUserGroup(UserGroupCode: Code[20]; UserGroupName: Text[50]; AssignToAllNewUsers: Boolean)
    var
        UserGroup: Record "User Group";
    begin
        if UserGroup.Get(UserGroupCode) then
            exit;

        UserGroup.Code := UserGroupCode;
        UserGroup.Name := UserGroupName;
        UserGroup."Assign to All New Users" := AssignToAllNewUsers;
        UserGroup.Insert();
    end;

    local procedure AddPermissionSetToUserGroup(PermissionSetId: Code[20]; UserGroupCode: Code[20])
    var
        UserGroupPermissionSet: Record "User Group Permission Set";
        AggregatePermissionSet: Record "Aggregate Permission Set";
    begin
        AggregatePermissionSet.SetRange("Role ID", PermissionSetId);
        if not AggregatePermissionSet.FindFirst() then
            exit;
        if UserGroupPermissionSet.Get(UserGroupCode, PermissionSetId, UserGroupPermissionSet.Scope::System, AggregatePermissionSet."App Id") then
            exit;

        UserGroupPermissionSet."Role ID" := PermissionSetId;
        UserGroupPermissionSet."User Group Code" := UserGroupCode;
        UserGroupPermissionSet.Scope := UserGroupPermissionSet.Scope::System;
        UserGroupPermissionSet."App ID" := AggregatePermissionSet."App Id";
        UserGroupPermissionSet.Insert();

        // User Group Access Control needs to be updated explicitly in v25+
        AddUserGroupPermissionSet(UserGroupCode, PermissionSetId, AggregatePermissionSet."App Id", UserGroupPermissionSet.Scope::System);
    end;

    local procedure AddPermissionSetToPlan(PlanId: Guid; RoleId: Code[20]; AppId: Guid)
    var
        AggregatePermissionSet: Record "Aggregate Permission Set";
        PlanConfiguration: Codeunit "Plan Configuration";
        Scope: Option System,Tenant;
    begin
        if AggregatePermissionSet.Get(Scope::System, AppId, RoleId) then
            PlanConfiguration.AddDefaultPermissionSetToPlan(PlanId, RoleId, AppId, Scope::System);
    end;

    local procedure AddUserGroupToPlan(UserGroupCode: Code[20]; PlanId: Guid)
    var
        UserGroupPlan: Record "User Group Plan";
    begin
        if UserGroupPlan.Get(PlanId, UserGroupCode) then
            exit;

        UserGroupPlan."Plan ID" := PlanId;
        UserGroupPlan."User Group Code" := UserGroupCode;
        UserGroupPlan.Insert();
    end;

    local procedure AddUserGroupToUsers(UserGroupCode: Code[20])
    var
        UserGroupMember: Record "User Group Member";
    begin
        UserGroupMember.SetCurrentKey("User Security ID", "Company Name");
        UserGroupMember.SetFilter("User Group Code", '<>%1', UserGroupCode);

        if UserGroupMember.FindSet() then
            repeat
                AddUserToUserGroupForCompany(UserGroupMember."User Security ID", UserGroupCode, UserGroupMember."Company Name");

                if UserGroupMember."Company Name" = '' then
                    UserGroupMember.SetFilter("User Security ID", '<>%1', UserGroupMember."User Security ID");
            until UserGroupMember.Next() = 0;
    end;

    local procedure AddUserToUserGroupForCompany(UserSID: Guid; UserGroupCode: Code[20]; CompanyName: Text[30])
    var
        UserGroupMember: Record "User Group Member";
    begin
        if UserGroupMember.Get(UserGroupCode, UserSID, CompanyName) then
            exit;

        UserGroupMember."User Security ID" := UserSID;
        UserGroupMember."User Group Code" := UserGroupCode;
        UserGroupMember."Company Name" := CompanyName;
        UserGroupMember.Insert(true);

        // The trigger code is removed in v25+, update manually if needed
        AddUserGroupMember(UserGroupCode, UserSID, CompanyName);
    end;

    local procedure SetExcelExportActionPermissions()
    var
        UpgradeTag: Codeunit "Upgrade Tag";
        UpgradeTagDefinitions: Codeunit "Upgrade Tag Definitions";
    begin
        if UpgradeTag.HasUpgradeTag(UpgradeTagDefinitions.GetExcelExportActionPermissionSetUpgradeTag()) then
            exit;

        if RunUserGroupUpgrade() then begin
            AddExcelExportActionPermissionSet();
            AddExcelExportActionUserGroup();
            AddExcelExportActionPermissionSetToGroup();
            AddExcelExportActionUserGroupToExistingPlans();
        end;

        UpgradeTag.SetUpgradeTag(UpgradeTagDefinitions.GetExcelExportActionPermissionSetUpgradeTag());
    end;

    local procedure AddExcelExportActionPermissionSet(): Boolean
    begin
        if TryInsertPermissionSet(EditInExcelTok, ExcelExportActionDescriptionTxt) then begin
            InsertPermission(EditInExcelTok, 10, 6110, 0, 0, 0, 0, 1);
            exit(true);
        end;
    end;

    local procedure AddExcelExportActionUserGroup();
    begin
        InsertUserGroup(ExcelExportActionTok, ExcelExportActionDescriptionTxt, true);
    end;

    local procedure AddExcelExportActionPermissionSetToGroup();
    begin
        AddPermissionSetToUserGroup(EditInExcelTok, ExcelExportActionTok);
    end;

    local procedure AddExcelExportActionUserGroupToExistingPlans();
    var
        PlanIds: Codeunit "Plan Ids";
    begin
        AddUserGroupToPlan(ExcelExportActionTok, PlanIds.GetBasicPlanId());
        AddUserGroupToPlan(ExcelExportActionTok, PlanIds.GetTeamMemberPlanId());
        AddUserGroupToPlan(ExcelExportActionTok, PlanIds.GetEssentialPlanId());
        AddUserGroupToPlan(ExcelExportActionTok, PlanIds.GetPremiumPlanId());
        AddUserGroupToPlan(ExcelExportActionTok, PlanIds.GetViralSignupPlanId());
        AddUserGroupToPlan(ExcelExportActionTok, PlanIds.GetExternalAccountantPlanId());
        AddUserGroupToPlan(ExcelExportActionTok, PlanIds.GetDelegatedAdminPlanId());
        AddUserGroupToPlan(ExcelExportActionTok, PlanIds.GetGlobalAdminPlanId());
        AddUserGroupToPlan(ExcelExportActionTok, PlanIds.GetTeamMemberISVPlanId());
        AddUserGroupToPlan(ExcelExportActionTok, PlanIds.GetEssentialISVPlanId());
        AddUserGroupToPlan(ExcelExportActionTok, PlanIds.GetPremiumISVPlanId());
        AddUserGroupToPlan(ExcelExportActionTok, PlanIds.GetDeviceISVPlanId());
        AddUserGroupToPlan(ExcelExportActionTok, PlanIds.GetDevicePlanId());
        AddUserGroupToPlan(ExcelExportActionTok, PlanIds.GetBasicFinancialsISVPlanId());
        AddUserGroupToPlan(ExcelExportActionTok, PlanIds.GetAccountantHubPlanId());
        AddUserGroupToPlan(ExcelExportActionTok, PlanIds.GetHelpDeskPlanId());
        AddUserGroupToPlan(ExcelExportActionTok, PlanIds.GetInfrastructurePlanId());
    end;

    local procedure SetAutomateActionPermissions()
    var
        UserGroup: Record "User Group";
        UpgradeTag: Codeunit "Upgrade Tag";
        UpgradeTagDefinitions: Codeunit "Upgrade Tag Definitions";
        UpgradeUserGroups: Codeunit "Upgrade User Groups";
        EnvironmentInformation: Codeunit "Environment Information";
    begin
        if UpgradeTag.HasUpgradeTag(UpgradeTagDefinitions.GetAutomateActionPermissionSetUpgradeTag()) then
            exit;

        if (not UserGroup.IsEmpty()) and (not UpgradeUserGroups.IsUserGroupObsoleteStateRemoved()) then
            if EnvironmentInformation.IsSaaS() then begin
                AddAutomateActionUserGroup();
                AddAutomateActionPermissionSetToGroup();
                AddAutomateActionUserGroupToExistingPlans();
                AddUserGroupToUsers(AutomateActionUserGroupTok);
            end;

        UpgradeTag.SetUpgradeTag(UpgradeTagDefinitions.GetAutomateActionPermissionSetUpgradeTag());
    end;

    local procedure SetAutomateAccessControl()
    var
        UserGroupMember: Record "User Group Member";
        AccessControl: Record "Access Control";
        UpgradeTag: Codeunit "Upgrade Tag";
        UpgradeTagDefinitions: Codeunit "Upgrade Tag Definitions";
        EnvironmentInformation: Codeunit "Environment Information";
    begin
        if UpgradeTag.HasUpgradeTag(UpgradeTagDefinitions.GetAutomateActionAccessControlUpgradeTag()) then
            exit;

        if EnvironmentInformation.IsSaaS() then begin
            UserGroupMember.SetRange("User Group Code", AutomateActionUserGroupTok);

            if UserGroupMember.FindSet() then
                repeat
                    AccessControl.SetRange("Role ID", AutomateExecPermissionSetTok);
                    AccessControl.SetRange("User Security ID", UserGroupMember."User Security ID");
                    AccessControl.SetRange("Company Name", UserGroupMember."Company Name");

                    if AccessControl.IsEmpty() then begin
                        // Bug 460562: Automate user group was inserted without trigger, so the related access control records were never created
                        UserGroupMember.Delete();
                        AddUserToUserGroupForCompany(UserGroupMember."User Security ID", UserGroupMember."User Group Code", UserGroupMember."Company Name");
                    end;
                until UserGroupMember.Next() = 0;
        end;

        UpgradeTag.SetUpgradeTag(UpgradeTagDefinitions.GetAutomateActionAccessControlUpgradeTag());
    end;

    local procedure AddAutomateActionUserGroup();
    begin
        InsertUserGroup(AutomateActionUserGroupTok, AutomateActionUserGroupDescriptionTxt, true);
    end;

    local procedure AddAutomateActionPermissionSetToGroup();
    begin
        AddPermissionSetToUserGroup(AutomateExecPermissionSetTok, AutomateActionUserGroupTok);
    end;

    local procedure AddAutomateActionUserGroupToExistingPlans();
    var
        PlanIds: Codeunit "Plan Ids";
    begin
        AddUserGroupToPlan(AutomateActionUserGroupTok, PlanIds.GetBasicPlanId());
        AddUserGroupToPlan(AutomateActionUserGroupTok, PlanIds.GetTeamMemberPlanId());
        AddUserGroupToPlan(AutomateActionUserGroupTok, PlanIds.GetEssentialPlanId());
        AddUserGroupToPlan(AutomateActionUserGroupTok, PlanIds.GetPremiumPlanId());
        AddUserGroupToPlan(AutomateActionUserGroupTok, PlanIds.GetViralSignupPlanId());
        AddUserGroupToPlan(AutomateActionUserGroupTok, PlanIds.GetExternalAccountantPlanId());
        AddUserGroupToPlan(AutomateActionUserGroupTok, PlanIds.GetDelegatedAdminPlanId());
        AddUserGroupToPlan(AutomateActionUserGroupTok, PlanIds.GetGlobalAdminPlanId());
        AddUserGroupToPlan(AutomateActionUserGroupTok, PlanIds.GetTeamMemberISVPlanId());
        AddUserGroupToPlan(AutomateActionUserGroupTok, PlanIds.GetEssentialISVPlanId());
        AddUserGroupToPlan(AutomateActionUserGroupTok, PlanIds.GetPremiumISVPlanId());
        AddUserGroupToPlan(AutomateActionUserGroupTok, PlanIds.GetDeviceISVPlanId());
        AddUserGroupToPlan(AutomateActionUserGroupTok, PlanIds.GetDevicePlanId());
        AddUserGroupToPlan(AutomateActionUserGroupTok, PlanIds.GetBasicFinancialsISVPlanId());
        AddUserGroupToPlan(AutomateActionUserGroupTok, PlanIds.GetAccountantHubPlanId());
        AddUserGroupToPlan(AutomateActionUserGroupTok, PlanIds.GetHelpDeskPlanId());
        AddUserGroupToPlan(AutomateActionUserGroupTok, PlanIds.GetInfrastructurePlanId());
        AddUserGroupToPlan(AutomateActionUserGroupTok, PlanIds.GetPremiumPartnerSandboxPlanId());
    end;

    local procedure SetMonitorSensitiveFieldPermissions()
    var
        UpgradeTag: Codeunit "Upgrade Tag";
        UpgradeTagDefinitions: Codeunit "Upgrade Tag Definitions";
    begin
        if UpgradeTag.HasUpgradeTag(UpgradeTagDefinitions.GetMonitorSensitiveFieldPermissionUpgradeTag()) then
            exit;

        AddD365MonitorFieldsToSecurityGroup();

        UpgradeTag.SetUpgradeTag(UpgradeTagDefinitions.GetMonitorSensitiveFieldPermissionUpgradeTag());
    end;

    local procedure AddD365MonitorFieldsToSecurityGroup()
    var
        UserGroupPermissionSet: Record "User Group Permission Set";
    begin
        AddPermissionSetToUserGroup(CopyStr(D365MonitorFieldsTxt, 1, MaxStrLen(UserGroupPermissionSet."Role ID")),
           CopyStr(SecurityUserGroupTok, 1, MaxStrLen(UserGroupPermissionSet."User Group Code")));
    end;

    local procedure CreateMicrosoft365Permissions()
    var
        UserGroupPermissionSet: Record "User Group Permission Set";
        UpgradeTag: Codeunit "Upgrade Tag";
        UpgradeTagDefinitions: Codeunit "Upgrade Tag Definitions";
        PlanIds: Codeunit "Plan Ids";
    begin
        if UpgradeTag.HasUpgradeTag(UpgradeTagDefinitions.GetTeamsUsersUserGroupUpgradeTag()) then
            exit;

        if RunUserGroupUpgrade() then begin
            // Insert TEAMS USERS user group and add it to the plan
            InsertUserGroup(TeamsUsersTok, TeamsUsersDescriptionTxt, false);
            UpdateUserGroupProfile(TeamsUsersTok, Page::"Blank Role Center");
            AddUserGroupToPlan(TeamsUsersTok, PlanIds.GetMicrosoft365PlanId());

            // Add LOGIN permission sets to TEAMS USERS user group
            AddPermissionSetToUserGroup(CopyStr(LoginTok, 1, MaxStrLen(UserGroupPermissionSet."Role ID")),
                CopyStr(TeamsUsersTok, 1, MaxStrLen(UserGroupPermissionSet."User Group Code")));
        end;

        UpgradeTag.SetUpgradeTag(UpgradeTagDefinitions.GetTeamsUsersUserGroupUpgradeTag());
    end;

    local procedure CreateD365EssentialAttachPermissions()
    var
        UpgradeTag: Codeunit "Upgrade Tag";
        UpgradeTagDefinitions: Codeunit "Upgrade Tag Definitions";
        PlanIds: Codeunit "Plan Ids";
    begin
        if UpgradeTag.HasUpgradeTag(UpgradeTagDefinitions.GetEssentialAttachUserGroupUpgradeTag()) then
            exit;

        if RunUserGroupUpgrade() then begin
            AddPermissionSetToPlan(PlanIDs.GetEssentialAttachPlanId(), 'AUTOMATE - EXEC', SystemApplicationAppIdTok);
            AddPermissionSetToPlan(PlanIDs.GetEssentialAttachPlanId(), 'D365 BUS FULL ACCESS', BaseApplicationAppIdTok);
            AddPermissionSetToPlan(PlanIDs.GetEssentialAttachPlanId(), 'EXCEL EXPORT ACTION', SystemApplicationAppIdTok);
            AddPermissionSetToPlan(PlanIDs.GetEssentialAttachPlanId(), 'LOCAL', BaseApplicationAppIdTok);
            AddPermissionSetToPlan(PlanIDs.GetEssentialAttachPlanId(), 'LOGIN', SystemApplicationAppIdTok);
        end else begin
            AddUserGroupToPlan(AutomateActionUserGroupTok, PlanIds.GetEssentialAttachPlanId());
            AddUserGroupToPlan(BusFullTok, PlanIds.GetEssentialAttachPlanId());
            AddUserGroupToPlan(ExcelExportActionTok, PlanIds.GetEssentialAttachPlanId());
        end;

        UpgradeTag.SetUpgradeTag(UpgradeTagDefinitions.GetEssentialAttachUserGroupUpgradeTag());
    end;

    local procedure CreateBCAdminPermissions()
    var
        UpgradeTag: Codeunit "Upgrade Tag";
        UpgradeTagDefinitions: Codeunit "Upgrade Tag Definitions";
        PlanIds: Codeunit "Plan Ids";
    begin
        if UpgradeTag.HasUpgradeTag(UpgradeTagDefinitions.GetBCUserGroupUpgradeTag()) then
            exit;

        AddPermissionSetToPlan(PlanIDs.GetDelegatedBCAdminPlanId(), 'AUTOMATE - EXEC', SystemApplicationAppIdTok);
        AddPermissionSetToPlan(PlanIDs.GetDelegatedBCAdminPlanId(), 'D365 BACKUP/RESTORE', SystemApplicationAppIdTok);
        AddPermissionSetToPlan(PlanIDs.GetDelegatedBCAdminPlanId(), 'D365 FULL ACCESS', BaseApplicationAppIdTok);
        AddPermissionSetToPlan(PlanIDs.GetDelegatedBCAdminPlanId(), 'D365 RAPIDSTART', BaseApplicationAppIdTok);
        AddPermissionSetToPlan(PlanIDs.GetDelegatedBCAdminPlanId(), 'EXCEL EXPORT ACTION', SystemApplicationAppIdTok);
        AddPermissionSetToPlan(PlanIDs.GetDelegatedBCAdminPlanId(), 'LOCAL', BaseApplicationAppIdTok);
        AddPermissionSetToPlan(PlanIDs.GetDelegatedBCAdminPlanId(), 'LOGIN', SystemApplicationAppIdTok);
        AddPermissionSetToPlan(PlanIDs.GetDelegatedBCAdminPlanId(), 'TROUBLESHOOT TOOLS', SystemApplicationAppIdTok);

        AddPermissionSetToPlan(PlanIDs.GetBCAdminPlanId(), 'AUTOMATE - EXEC', SystemApplicationAppIdTok);
        AddPermissionSetToPlan(PlanIDs.GetBCAdminPlanId(), 'D365 BACKUP/RESTORE', SystemApplicationAppIdTok);
        AddPermissionSetToPlan(PlanIDs.GetBCAdminPlanId(), 'D365 READ', BaseApplicationAppIdTok);
        AddPermissionSetToPlan(PlanIDs.GetBCAdminPlanId(), 'EXCEL EXPORT ACTION', SystemApplicationAppIdTok);
        AddPermissionSetToPlan(PlanIDs.GetBCAdminPlanId(), 'LOCAL', BaseApplicationAppIdTok);
        AddPermissionSetToPlan(PlanIDs.GetBCAdminPlanId(), 'LOGIN', SystemApplicationAppIdTok);
        AddPermissionSetToPlan(PlanIDs.GetBCAdminPlanId(), 'SECURITY', EmptyAppId);
        AddPermissionSetToPlan(PlanIDs.GetBCAdminPlanId(), 'TROUBLESHOOT TOOLS', SystemApplicationAppIdTok);

        UpgradeTag.SetUpgradeTag(UpgradeTagDefinitions.GetBCUserGroupUpgradeTag());
    end;

    local procedure UpdateUserGroupProfile(UserGroupCode: Code[20]; RoleCenterId: Integer)
    var
        AllProfile: Record "All Profile";
        UserGroup: Record "User Group";
    begin
        // Both Teams Users and Blank profile uses Blank Role Center. So we need to get the profile by Profile ID explicitly. Rest we can find by role center id.
        if (UserGroupCode = TeamsUsersTok) then begin
            AllProfile.SetRange("Profile ID", EmployeeTok);
            AllProfile.FindFirst();
        end else begin
            AllProfile.SetRange("Role Center ID", RoleCenterId);
            AllProfile.FindFirst();
        end;

        UserGroup.Get(UserGroupCode);
        UserGroup."Default Profile ID" := AllProfile."Profile ID";
        UserGroup."Default Profile App ID" := AllProfile."App ID";
        UserGroup."Default Profile Scope" := AllProfile.Scope;
        UserGroup.Modify();
    end;

    internal procedure ReplaceUserGroupPermissionSets(UserGroupCode: Code[20]; NewComposedPermissionSet: Code[20]; Scope: Option System,Tenant)
    var
        AggregatePermissionSet: Record "Aggregate Permission Set";
        UserGroupPermissionSet: Record "User Group Permission Set";
        UserGroupAccessControl: Record "User Group Access Control";
    begin
        AggregatePermissionSet.SetRange(Scope, Scope);
        AggregatePermissionSet.SetRange("Role ID", NewComposedPermissionSet);
        if not AggregatePermissionSet.FindFirst() then
            exit;

        // Leave LOGIN and LOCAL assigned to users, but disconnect them from user groups
        UserGroupAccessControl.SetRange("User Group Code", UserGroupCode);
        UserGroupAccessControl.SetRange(Scope, Scope::System);
        UserGroupAccessControl.SetFilter("Role ID", '%1|%2', 'LOGIN', 'LOCAL');
        UserGroupAccessControl.DeleteAll();

        UserGroupPermissionSet.SetRange("User Group Code", UserGroupCode);
        // In v25+ need to update User Group Access Control manually
        if UserGroupPermissionSet.FindSet() then
            repeat
                RemoveUserGroupPermissionSet(UserGroupCode, UserGroupPermissionSet."Role ID", UserGroupPermissionSet."App ID", UserGroupPermissionSet.Scope);
            until UserGroupPermissionSet.Next() = 0;

        UserGroupPermissionSet.DeleteAll();

        UserGroupPermissionSet."User Group Code" := UserGroupCode;
        UserGroupPermissionSet."Role ID" := NewComposedPermissionSet;
        UserGroupPermissionSet."App ID" := AggregatePermissionSet."App ID";
        UserGroupPermissionSet.Scope := Scope;
        UserGroupPermissionSet.Insert();

        // In v25+ need to update User Group Access Control manually
        AddUserGroupPermissionSet(UserGroupCode, NewComposedPermissionSet, AggregatePermissionSet."App ID", Scope);
    end;

    internal procedure AddUserGroupMember(UserGroupCode: Code[20]; UserSecurityID: Guid; SelectedCompany: Text[30])
    var
        UserGroupPermissionSet: Record "User Group Permission Set";
    begin
        UserGroupPermissionSet.SetRange("User Group Code", UserGroupCode);
        if UserGroupPermissionSet.FindSet() then
            repeat
                AddPermissionSetToUser(
                  UserGroupCode, UserSecurityID, SelectedCompany, UserGroupPermissionSet."Role ID", UserGroupPermissionSet."App ID",
                  UserGroupPermissionSet.Scope);
            until UserGroupPermissionSet.Next() = 0;
    end;

    local procedure AddUserGroupPermissionSet(UserGroupCode: Code[20]; RoleID: Code[20]; AppID: Guid; ItemScope: Integer)
    var
        UserGroupMember: Record "User Group Member";
    begin
        UserGroupMember.SetRange("User Group Code", UserGroupCode);
        if UserGroupMember.FindSet() then
            repeat
                AddPermissionSetToUser(
                  UserGroupCode, UserGroupMember."User Security ID", UserGroupMember."Company Name", RoleID, AppID, ItemScope);
            until UserGroupMember.Next() = 0;
    end;

    local procedure RemoveUserGroupPermissionSet(UserGroupCode: Code[20]; RoleID: Code[20]; AppID: Guid; ItemScope: Integer)
    var
        UserGroupMember: Record "User Group Member";
    begin
        UserGroupMember.SetRange("User Group Code", UserGroupCode);
        if UserGroupMember.FindSet() then
            repeat
                RemovePermissionSetFromUser(
                  UserGroupCode, UserGroupMember."User Security ID", UserGroupMember."Company Name", RoleID, AppID, ItemScope);
            until UserGroupMember.Next() = 0;
    end;

    local procedure RunUserGroupUpgrade(): boolean
    var
        UserGroup: Record "User Group";
        UpgradeUserGroups: Codeunit "Upgrade User Groups";
    begin
        if UserGroup.IsEmpty() then
            exit(false);

        if UpgradeUserGroups.IsUserGroupObsoleteStateRemoved() then
            exit(false);

        exit(true);
    end;

    local procedure AddPermissionSetToUser(UserGroupCode: Code[20]; UserSecurityID: Guid; SelectedCompany: Text[30]; RoleID: Code[20]; AppID: Guid; ItemScope: Integer)
    var
        UserGroupAccessControl: Record "User Group Access Control";
        AccessControl: Record "Access Control";
        ServerSetting: Codeunit "Server Setting";
        AccessControlExists: Boolean;
        NullGuid: Guid;
    begin
        if UserGroupAccessControl.Get(UserGroupCode, UserSecurityID, RoleID, SelectedCompany, ItemScope, AppID) then
            exit;

        // Filter on an App ID only when UsePermissionSetsFromExtensions is set to true.
        // The following filtering is to try find the correct Access Control since there has been cases of corrupt data,
        // such as System permission sets with NULL GUID-s
        if ServerSetting.GetUsePermissionSetsFromExtensions() then begin
            AccessControl.SetRange("User Security ID", UserSecurityID);
            AccessControl.SetRange("Role ID", RoleID);
            AccessControl.SetRange("Company Name", SelectedCompany);
            AccessControl.SetRange(Scope, ItemScope);

            // SUPER and SECURITY always have null guids
            if RoleID in ['SUPER', 'SECURITY'] then
                AccessControl.SetRange("App ID", NullGuid)
            else
                // If scope is system and App ID is null, filter to non-null App IDs
                if (ItemScope = AccessControl.Scope::System) and IsNullGuid(AppID) then
                    AccessControl.SetFilter("App ID", '<>%1', NullGuid)
                else
                    AccessControl.SetRange("App ID", AppID);

            AccessControlExists := not AccessControl.IsEmpty();
        end else
            AccessControlExists := AccessControl.Get(UserSecurityID, RoleID, SelectedCompany, ItemScope, AppID);

        UserGroupAccessControl.Reset();
        UserGroupAccessControl.Init();
        UserGroupAccessControl."User Group Code" := '';
        UserGroupAccessControl."User Security ID" := UserSecurityID;
        UserGroupAccessControl."Role ID" := RoleID;
        UserGroupAccessControl."Company Name" := SelectedCompany;
        UserGroupAccessControl."App ID" := AppID;
        UserGroupAccessControl.Scope := ItemScope;
        if AccessControlExists then begin
            UserGroupAccessControl.SetRange("User Security ID", UserSecurityID);
            UserGroupAccessControl.SetRange("Role ID", RoleID);
            UserGroupAccessControl.SetRange("Company Name", SelectedCompany);
            UserGroupAccessControl.SetRange(Scope, ItemScope);
            UserGroupAccessControl.SetRange("App ID", AppID);

            // If this is the first assignment via a user group and the user already had a manually defined access control,
            // we add a 'null' record for it.
            if UserGroupAccessControl.IsEmpty() then
                UserGroupAccessControl.Insert();
        end;
        UserGroupAccessControl."User Group Code" := UserGroupCode;
        UserGroupAccessControl.Insert();
        if not AccessControlExists then begin
            AccessControl.Init();
            AccessControl."User Security ID" := UserSecurityID;
            AccessControl."Role ID" := RoleID;
            AccessControl."Company Name" := SelectedCompany;
            AccessControl.Scope := ItemScope;
            AccessControl."App ID" := AppID;
            if not AccessControl.Insert() then
                Session.LogMessage('0000JSY', StrSubstNo(CouldNotInsertAccessControlTelemetryErr, AppID), Verbosity::Error, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', 'Upgrade');
        end;
    end;

    local procedure RemovePermissionSetFromUser(UserGroupCode: Code[20]; UserSecurityID: Guid; SelectedCompany: Text[30]; RoleID: Code[20]; AppID: Guid; ItemScope: Integer)
    var
        UserGroupAccessControl: Record "User Group Access Control";
        AccessControl: Record "Access Control";
        AccessControlExists: Boolean;
        ReferenceExists: Boolean;
    begin
        // If this is the last assignment via a user group and the user does not have a manually defined access control,
        // we remove the 'null' record for it if it exists.
        if not UserGroupAccessControl.Get(UserGroupCode, UserSecurityID, RoleID, SelectedCompany, ItemScope, AppID) then
            exit;
        UserGroupAccessControl.Delete();
        AccessControlExists := AccessControl.Get(UserSecurityID, RoleID, SelectedCompany, ItemScope, AppID);
        if AccessControlExists then begin
            UserGroupAccessControl.Reset();
            UserGroupAccessControl.SetRange("User Security ID", UserSecurityID);
            UserGroupAccessControl.SetRange("Role ID", RoleID);
            UserGroupAccessControl.SetRange("Company Name", SelectedCompany);
            UserGroupAccessControl.SetRange(Scope, ItemScope);
            UserGroupAccessControl.SetRange("App ID", AppID);
            ReferenceExists := UserGroupAccessControl.FindLast();
            if not ReferenceExists then
                AccessControl.Delete(true);
            if ReferenceExists and (UserGroupAccessControl."User Group Code" = '') then
                UserGroupAccessControl.Delete();
        end;
    end;
}