Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/System Application/App/Extension Management/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@
"name": "Control Add-Ins",
"publisher": "Microsoft",
"version": "24.3.0.0"
},
{
"id": "c56e3ef4-7ab0-4636-ae87-013a62f12213",
"name": "User Permissions",
"publisher": "Microsoft",
"version": "24.3.0.0"
}
],
"internalsVisibleTo": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ permissionset 2502 "Exten. Mgt. - Admin"
"Guided Experience - View";

Permissions = tabledata "Application Dependency" = Rimd,
tabledata "Application Object Metadata" = Rimd, // r needed for check CanManageExtensions
tabledata "Application Object Metadata" = Rimd,
tabledata "Application Resource" = Rimd,
tabledata "Extension Pending Setup" = rimd,
tabledata "Inplace Installed Application" = Rimd,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@

namespace System.Apps;

using System.Utilities;
using System.Environment.Configuration;
using System.Security.AccessControl;
using System.Security.User;
using System.Utilities;

codeunit 2500 "Extension Installation Impl"
{
Expand Down Expand Up @@ -178,11 +180,55 @@ codeunit 2500 "Extension Installation Impl"
Error(NotSufficientPermissionErr);
end;

procedure CheckPermissions(UserSecurityIdToCheck: Guid)
begin
if not CanManageExtensions(UserSecurityIdToCheck) then
Error(NotSufficientPermissionErr);
end;

procedure CanManageExtensions(): Boolean
var
ApplicationObjectMetadata: Record "Application Object Metadata";
UserPermissions: Codeunit "User Permissions";
Result: Boolean;
begin
OnCanManageExtensions(Result);
if Result then
exit(true);

if UserPermissions.IsSuper(UserSecurityId()) then
exit(true);

exit(CanManageExtensions(UserSecurityId()));
end;

procedure CanManageExtensions(UserSecurityIdToCheck: Guid): Boolean
var
AccessControl: Record "Access Control";
UserPermissions: Codeunit "User Permissions";
CurrentModuleInfo: ModuleInfo;
ExtensionManagementAdminTok: Label 'Exten. Mgt. - Admin', Locked = true;
SuperTok: Label 'SUPER', Locked = true;
NullGuid: Guid;
begin
if UserPermissions.HasUserPermissionSetAssigned(
UserSecurityIdToCheck, '', SuperTok, AccessControl.Scope::System, NullGuid)
then
exit(true);

NavApp.GetCurrentModuleInfo(CurrentModuleInfo);
exit(UserPermissions.HasUserPermissionSetAssigned(
UserSecurityIdToCheck, '', ExtensionManagementAdminTok, AccessControl.Scope::System, CurrentModuleInfo.Id()));
end;

/// <summary>
/// Allows tests to authorize the current test session without modifying platform user records.
/// </summary>
/// <remarks>
/// This event is for testing only and must not be used in production.
/// </remarks>
[InternalEvent(false)]
local procedure OnCanManageExtensions(var Result: Boolean)
begin
exit(ApplicationObjectMetadata.ReadPermission());
end;

procedure UninstallExtension(PackageID: Guid; IsUIEnabled: Boolean): Boolean
Expand Down Expand Up @@ -365,4 +411,3 @@ codeunit 2500 "Extension Installation Impl"
exit(ExtensionDetails.Editable());
end;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// ------------------------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
// ------------------------------------------------------------------------------------------------

namespace System.Test.Apps;

using System.Security.AccessControl;

permissionset 133100 "Ext. Mgt. Nonadmin"
{
Assignable = true;

Permissions = tabledata "Access Control" = R;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@

namespace System.Test.Apps;

using System.Environment.Configuration;
using System.Apps;
using System.Environment.Configuration;
using System.Media;
using System.TestLibraries.Utilities;
using System.Security.AccessControl;
using System.TestLibraries.Security.AccessControl;
using System.TestLibraries.Utilities;

codeunit 133100 "Extension Management Test"
{
EventSubscriberInstance = Manual;

// For the purpose of this test, two sample extensions are pre-published.
// The condition for the tests to pass is that one of the extensions is holding a dependency on the other one, and they are both 1st Party Extensions, version 1.0.
Expand All @@ -28,8 +30,11 @@ codeunit 133100 "Extension Management Test"

var
ExtensionManagement: Codeunit "Extension Management";
ExtensionManagementTest: Codeunit "Extension Management Test";
ExtensionInstallationImplTest: Codeunit "Extension Installation Impl";
Assert: Codeunit "Library Assert";
PermissionsMock: Codeunit "Permissions Mock";
ExtensionManagementTestBound: Boolean;
MainAppId: Guid;
DependingAppId: Guid;
NotInstalledSuccErr: Label 'Extension was not installed succesfully';
Expand All @@ -40,6 +45,7 @@ codeunit 133100 "Extension Management Test"
ExtensionNotInstalledErr: Label 'Extension should be installed.';
PackageIdExistsErr: Label 'The returned extension pakage does not exist';
NullPackageIdErr: Label 'There should not be an extension corresponding to the returned package ID';
NotSufficientPermissionErr: Label 'You do not have sufficient permissions to manage extensions. Please contact your administrator.';
PackageIdExtensionVersionErr: Label 'The package Id does not poin to the correct extension version';

local procedure SetNavAppIds()
Expand All @@ -48,16 +54,122 @@ codeunit 133100 "Extension Management Test"
DependingAppId := 'c4123d81-a537-4062-bdd4-7b9882bcc319';
end;

local procedure CreateUserWithPermissionSet(RoleId: Code[20]; Company: Text): Guid
var
AccessControl: Record "Access Control";
AggregatePermissionSet: Record "Aggregate Permission Set";
UserSecurityId: Guid;
begin
UserSecurityId := CreateGuid();

AggregatePermissionSet.SetRange("Role ID", RoleId);
AggregatePermissionSet.FindFirst();

AccessControl.Init();
AccessControl."User Security ID" := UserSecurityId;
AccessControl."Role ID" := AggregatePermissionSet."Role ID";
AccessControl."Company Name" := CopyStr(Company, 1, MaxStrLen(AccessControl."Company Name"));
AccessControl.Scope := AggregatePermissionSet.Scope;
AccessControl."App ID" := AggregatePermissionSet."App ID";
AccessControl.Insert();

exit(UserSecurityId);
end;

local procedure CreateSuperUser(): Guid
var
AccessControl: Record "Access Control";
UserSecurityId: Guid;
begin
UserSecurityId := CreateGuid();

AccessControl.Init();
AccessControl."User Security ID" := UserSecurityId;
AccessControl."Role ID" := 'SUPER';
AccessControl.Scope := AccessControl.Scope::System;
AccessControl.Insert();

exit(UserSecurityId);
end;

local procedure InitializeExtensions()
var
NAVAppInstalledApp: Record "NAV App Installed App";
begin
if not ExtensionManagementTestBound then begin
BindSubscription(ExtensionManagementTest);
ExtensionManagementTestBound := true;
end;

if NAVAppInstalledApp.Get(MainAppId) then
ExtensionManagement.UninstallExtension(NAVAppInstalledApp."Package ID", false);
if NAVAppInstalledApp.Get(DependingAppId) then
ExtensionManagement.UninstallExtension(NAVAppInstalledApp."Package ID", false);
end;

[Test]
[TransactionModel(TransactionModel::AutoRollback)]
procedure NonAdminCannotManageExtensions()
var
UserSecurityId: Guid;
begin
UserSecurityId := CreateUserWithPermissionSet('Ext. Mgt. Nonadmin', '');

Assert.IsFalse(ExtensionInstallationImplTest.CanManageExtensions(UserSecurityId), 'A non-admin permission set must not grant extension management permission.');
end;

[Test]
[TransactionModel(TransactionModel::AutoRollback)]
procedure NonAdminCannotPassExtensionPermissionCheck()
var
UserSecurityId: Guid;
begin
UserSecurityId := CreateUserWithPermissionSet('Ext. Mgt. Nonadmin', '');

asserterror ExtensionInstallationImplTest.CheckPermissions(UserSecurityId);

Assert.ExpectedError(NotSufficientPermissionErr);
end;

[Test]
[TransactionModel(TransactionModel::AutoRollback)]
procedure ExtensionManagementAdminCanManageExtensions()
var
UserSecurityId: Guid;
begin
UserSecurityId := CreateUserWithPermissionSet('Exten. Mgt. - Admin', '');

Assert.IsTrue(ExtensionInstallationImplTest.CanManageExtensions(UserSecurityId), 'Exten. Mgt. - Admin must grant extension management permission.');
end;

[Test]
[TransactionModel(TransactionModel::AutoRollback)]
procedure SuperCanManageExtensions()
var
UserSecurityId: Guid;
begin
UserSecurityId := CreateSuperUser();

Assert.IsTrue(ExtensionInstallationImplTest.CanManageExtensions(UserSecurityId), 'SUPER must grant extension management permission.');
end;


[Test]
[TransactionModel(TransactionModel::AutoRollback)]
procedure CompanyScopedExtensionManagementAdminCannotManageExtensions()
var
UserSecurityId: Guid;
begin
UserSecurityId := CreateUserWithPermissionSet('Exten. Mgt. - Admin', CompanyName());

Assert.IsFalse(ExtensionInstallationImplTest.CanManageExtensions(UserSecurityId), 'Company-scoped Exten. Mgt. - Admin must not grant tenant-wide extension management permission.');
end;
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Extension Installation Impl", OnCanManageExtensions, '', false, false)]
local procedure AuthorizeTestSession(var Result: Boolean)
begin
Result := true;
end;

[Test]
[Scope('OnPrem')]
procedure InstallUninstallExtension()
Expand Down Expand Up @@ -571,4 +683,3 @@ codeunit 133100 "Extension Management Test"
Reply := true;
end;
}

Loading