Skip to content

Commit

Permalink
microsoft#27101 Changes with merged main
Browse files Browse the repository at this point in the history
  • Loading branch information
tstefanovicius committed Oct 30, 2024
1 parent 7ca090c commit dc10695
Show file tree
Hide file tree
Showing 15 changed files with 1,177 additions and 3 deletions.
19 changes: 19 additions & 0 deletions Apps/W1/EDocumentsConnector/app/src/Logiq/LogiqAPIEngine.Enum.al
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
namespace Microsoft.EServices.EDocumentConnector.Logiq;

enum 6380 "Logiq API Engine"
{
Extensible = false;

value(0; " ")
{
Caption = '', Locked = true;
}
value(1; Engine1)
{
Caption = 'Engine 1';
}
value(2; Engine3)
{
Caption = 'Engine 3';
}
}
182 changes: 182 additions & 0 deletions Apps/W1/EDocumentsConnector/app/src/Logiq/LogiqAuth.Codeunit.al
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
namespace Microsoft.EServices.EDocumentConnector.Logiq;

codeunit 6380 "Logiq Auth"
{
internal procedure SetIsolatedStorageValue(var ValueKey: Guid; Value: SecretText; TokenDataScope: DataScope)
begin
if IsNullGuid(ValueKey) then
ValueKey := CreateGuid();

IsolatedStorage.Set(ValueKey, Value, TokenDataScope);
end;

internal procedure SetIsolatedStorageValue(var ValueKey: Guid; Value: SecretText)
begin
SetIsolatedStorageValue(ValueKey, Value, DataScope::Company);
end;

internal procedure GetIsolatedStorageValue(var ValueKey: Guid; var Value: SecretText; TokenDataScope: DataScope)
begin
if IsNullGuid(ValueKey) then
exit;
IsolatedStorage.Get(ValueKey, TokenDataScope, Value);
end;

internal procedure GetIsolatedStorageValue(var ValueKey: Guid; var Value: SecretText)
begin
GetIsolatedStorageValue(ValueKey, Value, DataScope::Company);
end;

[NonDebuggable]
internal procedure GetTokens()
var
LogiqConnectionSetup: Record "Logiq Connection Setup";
LogiqConnectionUserSetup: Record "Logiq Connection User Setup";
Client: HttpClient;
Headers: HttpHeaders;
Content: HttpContent;
RequestMessage: HttpRequestMessage;
ResponseMessage: HttpResponseMessage;
AccessToken, RefreshToken : SecretText;
AccessTokExpires, RefreshTokExpires : DateTime;
AuthenticationFailedErr: Label 'Logiq authentication failed. Please check the user credentials.';
begin
CheckSetup(LogiqConnectionSetup);
CheckUserCredentials(LogiqConnectionUserSetup);

RequestMessage.Method('POST');
RequestMessage.SetRequestUri(LogiqConnectionSetup."Authentication URL");

BuildTokenRequestBody(Content);

Content.GetHeaders(Headers);
Headers.Clear();
Headers.Add('Content-Type', 'application/x-www-form-urlencoded');

RequestMessage.Content(Content);

Client.Send(RequestMessage, ResponseMessage);

if not ResponseMessage.IsSuccessStatusCode() then begin
if GuiAllowed then
Message(AuthenticationFailedErr);
exit;
end;

ParseTokens(ResponseMessage, AccessToken, RefreshToken, AccessTokExpires, RefreshTokExpires);

SaveTokens(AccessToken, RefreshToken, AccessTokExpires, RefreshTokExpires);
end;

local procedure BuildTokenRequestBody(var Content: HttpContent)
var
LogiqConnectionSetup: Record "Logiq Connection Setup";
LogiqConnectionUserSetup: Record "Logiq Connection User Setup";
BodyText: SecretText;
CredentialsBodyTok: Label 'grant_type=password&scope=openid&client_id=%1&client_secret=%2&username=%3&password=%4', Locked = true;
RefreshTokenBodyTok: Label 'grant_type=refresh_token&client_id=%1&client_secret=%2&refresh_token=%3', Locked = true;
begin
LogiqConnectionSetup.Get();
LogiqConnectionUserSetup.Get(UserId());

if (not IsNullGuid(LogiqConnectionUserSetup."Refresh Token")) and (LogiqConnectionUserSetup."Refresh Token Expiration" > (CurrentDateTime + 60 * 1000)) then
BodyText := SecretText.SecretStrSubstNo(RefreshTokenBodyTok, LogiqConnectionSetup."Client ID", LogiqConnectionSetup.GetClientSecret(), LogiqConnectionUserSetup.GetRefreshToken())
else
BodyText := SecretText.SecretStrSubstNo(CredentialsBodyTok, LogiqConnectionSetup."Client ID", LogiqConnectionSetup.GetClientSecret(), LogiqConnectionUserSetup.Username, LogiqConnectionUserSetup.GetPassword());

Content.WriteFrom(BodyText);
end;

internal procedure CheckUserCredentials(var LogiqConnectionUserSetup: Record "Logiq Connection User Setup")
var
NoSetupErr: Label 'No user setup found. Please fill the user setup in the Logiq Connection User Setup page.';
MissingCredentialsErr: Label 'User credentials are missing. Please enter username and password in the Logiq Connection User Setup page.';
begin
if not LogiqConnectionUserSetup.Get(UserId()) then
Error(NoSetupErr);

if (LogiqConnectionUserSetup.Username = '') or (IsNullGuid(LogiqConnectionUserSetup."Password")) then
Error(MissingCredentialsErr);
end;

internal procedure CheckUserSetup(var LogiqConnectionUserSetup: Record "Logiq Connection User Setup")
var
MissingAPIEngineErr: Label 'API Engine is missing. Please select the API Engine in the Logiq Connection User Setup page.';
MissingEndpointsErr: Label 'Endpoints are missing. Please fill the Document Transfer Endpoint and Document Status Endpoint in the Logiq Connection User Setup page.';
begin
CheckUserCredentials(LogiqConnectionUserSetup);

if (LogiqConnectionUserSetup."API Engine" = LogiqConnectionUserSetup."API Engine"::" ") then
Error(MissingAPIEngineErr);

if (LogiqConnectionUserSetup."Document Transfer Endpoint" = '') or (LogiqConnectionUserSetup."Document Status Endpoint" = '') then
Error(MissingEndpointsErr);
end;

internal procedure CheckSetup(var LogiqConnectionSetup: Record "Logiq Connection Setup")
var
NoSetupErr: Label 'No setup found. Please fill the setup in the Logiq Connection Setup page.';
MissingClientInfoErr: Label 'Client ID or Client Secret is missing. Please fill the Client ID and Client Secret in the Logiq Connection Setup page.';
MissingAuthUrlErr: Label 'Authentication URL is missing. Please fill the Authentication URL in the Logiq Connection Setup page.';
MissingBaseUrlErr: Label 'Base URL is missing. Please fill the API Base URL in the Logiq Connection Setup page.';
begin
if not LogiqConnectionSetup.Get() then
Error(NoSetupErr);

if (LogiqConnectionSetup."Client ID" = '') or (IsNullGuid(LogiqConnectionSetup."Client Secret")) then
Error(MissingClientInfoErr);

if LogiqConnectionSetup."Authentication URL" = '' then
Error(MissingAuthUrlErr);

if LogiqConnectionSetup."Base URL" = '' then
Error(MissingBaseUrlErr);
end;

[NonDebuggable]
local procedure ParseTokens(ResponseMessage: HttpResponseMessage; var AccessToken: SecretText; var RefreshToken: SecretText; var AccessTokExpires: DateTime; var RefreshTokExpires: DateTime)
var
ContentJson: JsonObject;
JsonTok: JsonToken;
ResponseTxt: Text;
begin
ResponseMessage.Content.ReadAs(ResponseTxt);
ContentJson.ReadFrom(ResponseTxt);
if ContentJson.Get('access_token', JsonTok) then
AccessToken := JsonTok.AsValue().AsText();
if ContentJson.Get('refresh_token', JsonTok) then
RefreshToken := JsonTok.AsValue().AsText();
if ContentJson.Get('expires_in', JsonTok) then
AccessTokExpires := CurrentDateTime + JsonTok.AsValue().AsInteger() * 1000;
if ContentJson.Get('refresh_expires_in', JsonTok) then
RefreshTokExpires := CurrentDateTime + JsonTok.AsValue().AsInteger() * 1000;
end;

local procedure SaveTokens(AccessToken: SecretText; RefreshToken: SecretText; AccessTokExpires: DateTime; RefreshTokExpires: DateTime)
var
LogiqConnectionUserSetup: Record "Logiq Connection User Setup";
begin
LogiqConnectionUserSetup.Get(UserId());
SetIsolatedStorageValue(LogiqConnectionUserSetup."Access Token", AccessToken, DataScope::User);
SetIsolatedStorageValue(LogiqConnectionUserSetup."Refresh Token", RefreshToken, DataScope::User);
LogiqConnectionUserSetup."Access Token Expiration" := AccessTokExpires;
LogiqConnectionUserSetup."Refresh Token Expiration" := RefreshTokExpires;
LogiqConnectionUserSetup.Modify(false);
end;

internal procedure HasToken(ValueKey: Guid; DataScope: DataScope): Boolean
begin
exit(IsolatedStorage.Contains(ValueKey, DataScope));
end;

internal procedure CheckUpdateTokens()
var
LogiqConnectionUserSetup: Record "Logiq Connection User Setup";
NoSetupErr: Label 'No user setup found. Please fill the user setup in the Logiq Connection User Setup page.';
begin
if not LogiqConnectionUserSetup.Get(UserId()) then
Error(NoSetupErr);
if IsNullGuid(LogiqConnectionUserSetup."Access Token") or (LogiqConnectionUserSetup."Access Token Expiration" < (CurrentDateTime + 5 * 60 * 1000)) then
GetTokens();
end;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
namespace Microsoft.EServices.EDocumentConnector.Logiq;

page 6380 "Logiq Connection Setup"
{
Caption = 'E-Document External Connection Setup';
PageType = Card;
ApplicationArea = Basic, Suite;
UsageCategory = None;
SourceTable = "Logiq Connection Setup";

layout
{
area(Content)
{
group(General)
{
Caption = 'General';

field(ClientID; Rec."Client ID")
{
ShowMandatory = true;
}
field(ClientSecret; ClientSecret)
{
Caption = 'Client Secret';
ToolTip = 'Specifies the client secret token.';
ExtendedDatatype = Masked;
ShowMandatory = true;

trigger OnValidate()
begin
LogiqAuth.SetIsolatedStorageValue(Rec."Client Secret", ClientSecret);
end;
}
field("Authentication URL"; Rec."Authentication URL")
{
}
field("Base URL"; Rec."Base URL")
{
}
field("File List Endpoint"; Rec."File List Endpoint")
{
}
}
}
}

actions
{
area(Processing)
{
action(Connect)
{
ApplicationArea = All;
Caption = 'User Setup';
Image = Setup;
ToolTip = 'Open page for User Setup.';

trigger OnAction()
var
LogiqConnectionUserSetup: Record "Logiq Connection User Setup";
LoqiqConnectionUserSetupPage: Page "Logiq Connection User Setup";
begin
LogiqConnectionUserSetup.FindUserSetup(CopyStr(UserId(), 1, 50));
LoqiqConnectionUserSetupPage.SetRecord(LogiqConnectionUserSetup);
LoqiqConnectionUserSetupPage.Run();
end;
}
}
area(Promoted)
{
actionref(Connect_Promoted; Connect)
{
}
}
}

var
LogiqAuth: Codeunit "Logiq Auth";
[NonDebuggable]
ClientSecret: Text;

trigger OnOpenPage()
begin
if not Rec.Get() then begin
Rec.Init();
Rec.Insert(true);
end;

if LogiqAuth.HasToken(Rec."Client Secret", DataScope::Company) then
ClientSecret := '*';
end;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
namespace Microsoft.EServices.EDocumentConnector.Logiq;

table 6380 "Logiq Connection Setup"
{
Caption = 'Logiq Connection Setup';
DataClassification = CustomerContent;
fields
{
field(1; PK; Code[10])
{
DataClassification = CustomerContent;
}
field(21; "Authentication URL"; Text[100])
{
Caption = 'Authorization URL';
DataClassification = CustomerContent;
ToolTip = 'Specifies the Authorization URL.';
}
field(22; "Base URL"; Text[100])
{
Caption = 'Base URL';
DataClassification = CustomerContent;
ToolTip = 'Specifies the Base URL.';
}
field(25; "File List Endpoint"; Text[100])
{
Caption = 'File List Endpoint';
DataClassification = CustomerContent;
ToolTip = 'Specifies the Endpoint to list available files.';
}
field(31; "Client ID"; Text[100])
{
Caption = 'Client ID';
ToolTip = 'Specifies the client ID token.';
DataClassification = EndUserIdentifiableInformation;
}
field(32; "Client Secret"; Guid)
{
Caption = 'Client Secret';
DataClassification = EndUserIdentifiableInformation;
}
}

keys
{
key(PK; PK)
{
Clustered = true;
}
}

var
LogiqAuth: Codeunit "Logiq Auth";

trigger OnInsert()
var
AuthenticationUrlTok: Label 'https://pilot-sso.logiq.no/auth/realms/connect-api/protocol/openid-connect/token', Locked = true;
FileListTok: Label '1.0/listfiles', Locked = true;
BaseUrlTok: Label 'https://pilot-api.logiq.no/edi/connect/', Locked = true;
begin
Rec."Authentication URL" := AuthenticationUrlTok;
Rec."File List Endpoint" := FileListTok;
Rec."Base URL" := BaseUrlTok;
end;

internal procedure GetClientSecret(): SecretText
var
ClientSecret: SecretText;
begin
LogiqAuth.GetIsolatedStorageValue(Rec."Client Secret", ClientSecret);
exit(ClientSecret);
end;
}
Loading

0 comments on commit dc10695

Please sign in to comment.