-
Notifications
You must be signed in to change notification settings - Fork 622
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[E-Document Connector] Logiq E-Document Connector #27562
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mark it as internal. |
||
|
||
value(0; " ") | ||
{ | ||
Caption = '', Locked = true; | ||
} | ||
value(1; Engine1) | ||
{ | ||
Caption = 'Engine 1'; | ||
} | ||
value(2; Engine3) | ||
{ | ||
Caption = 'Engine 3'; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
namespace Microsoft.EServices.EDocumentConnector.Logiq; | ||
|
||
codeunit 6380 "Logiq Auth" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please move the code to EDocumentConnectors Create app for the connector. In the mean time we will start looking at the code. |
||
{ | ||
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.'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Labels needs to be on global var. |
||
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.'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Labels to global var |
||
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'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Different name. Use connector name |
||
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]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 20 is standard. |
||
{ | ||
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; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dont prefix with Logiq