forked from microsoft/ALAppExtensions
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
microsoft#27101 Changes with merged main
- Loading branch information
1 parent
7ca090c
commit dc10695
Showing
15 changed files
with
1,177 additions
and
3 deletions.
There are no files selected for viewing
19 changes: 19 additions & 0 deletions
19
Apps/W1/EDocumentsConnector/app/src/Logiq/LogiqAPIEngine.Enum.al
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
182
Apps/W1/EDocumentsConnector/app/src/Logiq/LogiqAuth.Codeunit.al
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
93 changes: 93 additions & 0 deletions
93
Apps/W1/EDocumentsConnector/app/src/Logiq/LogiqConnectionSetup.Page.al
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
73 changes: 73 additions & 0 deletions
73
Apps/W1/EDocumentsConnector/app/src/Logiq/LogiqConnectionSetup.Table.al
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
Oops, something went wrong.