Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ codeunit 48526 "Fabric Platform Admin Client"
PrincipalIdRequiredErr: Label 'Principal ID is missing. Ask your Microsoft Entra administrator for the object ID of the enterprise application, enter it on the Fabric setup page, and try again.';
ReadPrincipalIdSetupInstructionsLbl: Label 'Read setup instructions';
PrincipalIdSetupHelpUrlTok: Label 'https://go.microsoft.com/fwlink/?LinkId=2386618', Locked = true;
AddSPTokenErr: Label 'Service principal could not be added to the Fabric workspace. Add it directly in Fabric with Contributor role.';
AddSPTransportErr: Label 'Failed to add service principal to workspace: transport error.';
AddSPHttpGenericErr: Label 'Failed to add service principal to workspace. Contact your administrator if the problem persists.';
AddSPHttpStatusErr: Label 'Failed to add service principal to workspace. HTTP %1.', Comment = '%1 = HTTP status code';
Expand Down Expand Up @@ -56,7 +57,7 @@ codeunit 48526 "Fabric Platform Admin Client"
TempBuffer.Reset();
TempBuffer.DeleteAll(false);

AccessToken := CredMgt.AcquireFabricApiTokenDelegated();
AccessToken := CredMgt.AcquireFabricApiTokenDelegated(false);
RestClientResult := HttpClient.CreateClientWithBearer(AccessToken);
Req := HttpClient.BuildJsonRequest('GET', 'https://api.fabric.microsoft.com/v1/workspaces', '');
if not HttpClient.TrySend(RestClientResult, Req, Resp) then
Expand Down Expand Up @@ -112,7 +113,7 @@ codeunit 48526 "Fabric Platform Admin Client"
TempBuffer.Reset();
TempBuffer.DeleteAll(false);

AccessToken := CredMgt.AcquireFabricApiTokenDelegated();
AccessToken := CredMgt.AcquireFabricApiTokenDelegated(false);
RestClientResult := HttpClient.CreateClientWithBearer(AccessToken);
Req := HttpClient.BuildJsonRequest(
'GET',
Expand Down Expand Up @@ -143,7 +144,6 @@ codeunit 48526 "Fabric Platform Admin Client"

procedure AddServicePrincipalToWorkspace(WorkspaceId: Text; PrincipalId: Text)
var
CredMgt: Codeunit "Fabric Platform Credential Mgt";
HttpClient: Codeunit "Fabric Platform Http Client";
FabricPrivacyNotice: Codeunit "Fabric Privacy Notice";
RestClientResult: Codeunit "Rest Client";
Expand All @@ -165,7 +165,8 @@ codeunit 48526 "Fabric Platform Admin Client"
if WorkspaceIdForUrl = '' then
Error(WorkspaceRequiredForSPErr);

AccessToken := CredMgt.AcquireFabricApiTokenDelegated();
if not TryAcquireWorkspaceWriteToken(AccessToken) then
Error(CreateAddSPTokenErrorInfo(GetLastErrorText()));

PrincipalObj.Add('id', PrincipalId);
PrincipalObj.Add('type', 'ServicePrincipal');
Expand All @@ -190,6 +191,15 @@ codeunit 48526 "Fabric Platform Admin Client"
Error(CreateAddSPHttpErrorInfo(Resp.GetHttpStatusCode()));
end;

[TryFunction]
[NonDebuggable]
local procedure TryAcquireWorkspaceWriteToken(var AccessToken: SecretText)
var
CredMgt: Codeunit "Fabric Platform Credential Mgt";
begin
AccessToken := CredMgt.AcquireFabricApiTokenDelegated(true);
end;

local procedure HasRequiredWorkspaceRole(WorkspaceId: Text; PrincipalId: Text; AccessToken: SecretText): Boolean
var
HttpClient: Codeunit "Fabric Platform Http Client";
Expand Down Expand Up @@ -301,6 +311,14 @@ codeunit 48526 "Fabric Platform Admin Client"
Hyperlink(PrincipalIdSetupHelpUrlTok);
end;

local procedure CreateAddSPTokenErrorInfo(DetailedMessage: Text) ErrInfo: ErrorInfo
begin
ErrInfo.Message(AddSPTokenErr);
ErrInfo.DetailedMessage(DetailedMessage);
ErrInfo.DataClassification(DataClassification::CustomerContent);
ErrInfo.AddAction(ReadPrincipalIdSetupInstructionsLbl, Codeunit::"Fabric Platform Admin Client", 'OpenPrincipalIdSetupHelp');
end;

local procedure CreateRetrieveWorkspacesHttpErrorInfo(StatusCode: Integer) ErrInfo: ErrorInfo
begin
ErrInfo.Message(RetrieveWorkspacesHttpGenericErr);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,29 +180,35 @@ codeunit 48524 "Fabric Platform Credential Mgt"
LookupState.ClearFabricApiToken();
end;

/// <summary>
/// Acquires a delegated Fabric API token via On-Behalf-Of for the first-party app.
/// </summary>
[NonDebuggable]
procedure AcquireFabricApiTokenDelegated(): SecretText
procedure AcquireFabricApiTokenDelegated(RequireWriteAccess: Boolean): SecretText
var
OAuth2: Codeunit OAuth2;
LookupState: Codeunit "Fabric Platform Lookup State";
AccessToken: SecretText;
RedirectUrl: Text;
Scopes: List of [Text];
begin
// Test-injection seam: unit tests pre-seed a token to bypass the platform flow.
if LookupState.HasFabricApiToken() then
exit(LookupState.GetFabricApiToken());

if IsCustomAppEnabled() then
exit(AcquireFabricApiTokenWithCustomApp());

Scopes.Add('https://api.fabric.microsoft.com/Workspace.ReadWrite.All');
OAuth2.GetDefaultRedirectUrl(RedirectUrl);
OAuth2.AcquireOnBehalfOfToken(RedirectUrl, Scopes, AccessToken);
if AccessToken.IsEmpty() then
if not RequireWriteAccess then begin
Scopes.Add('https://api.fabric.microsoft.com/Workspace.Read.All');
if OAuth2.AcquireOnBehalfOfToken(RedirectUrl, Scopes, AccessToken) then
if not AccessToken.IsEmpty() then
exit(AccessToken);

Clear(Scopes);
Clear(AccessToken);
ClearLastError();
end;

Scopes.Add('https://api.fabric.microsoft.com/Workspace.ReadWrite.All');
if (not OAuth2.AcquireOnBehalfOfToken(RedirectUrl, Scopes, AccessToken)) or AccessToken.IsEmpty() then
Error(FabricApiTokenErr);
exit(AccessToken);
end;
Expand Down
Loading