Skip to content
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

Add approval by default to privacy notices #2368

Merged
merged 12 commits into from
Nov 26, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -124,15 +124,13 @@ codeunit 7772 "Azure OpenAI Impl"
case PrivacyNotice.GetPrivacyNoticeApprovalState(CopilotCapabilityImpl.GetAzureOpenAICategory(), false) of
Enum::"Privacy Notice Approval State"::Agreed:
exit(true);
Enum::"Privacy Notice Approval State"::Disagreed:
begin
if not Silent then begin
CopilotNotAvailable.SetCopilotCapability(Capability);
CopilotNotAvailable.Run();
end;

exit(false);
Enum::"Privacy Notice Approval State"::Disagreed, Enum::"Privacy Notice Approval State"::DisagreedAdmin:
if not Silent then begin
CopilotNotAvailable.SetCopilotCapability(Capability);
CopilotNotAvailable.Run();
end;

exit(false);
else
exit(true);
end;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,21 @@ codeunit 1563 "Privacy Notice"
exit(PrivacyNoticeImpl.CreatePrivacyNotice(Id, IntegrationName));
end;

/// <summary>
/// Creates a privacy notice.
/// </summary>
/// <param name="Id">Identification of the privacy notice.</param>
/// <param name="IntegrationName">The name of the integration.</param>
/// <param name="Link">Link to the privacy terms.</param>
/// <param name="ApproveByDefault">Determines if the notice should be approved by default.</param>
/// <returns>Whether the privacy notice was created.</returns>
procedure CreatePrivacyNotice(Id: Code[50]; IntegrationName: Text[250]; Link: Text[2048]; ApproveByDefault: Boolean): Boolean
var
PrivacyNoticeImpl: Codeunit "Privacy Notice Impl.";
begin
exit(PrivacyNoticeImpl.CreatePrivacyNotice(Id, IntegrationName, Link, ApproveByDefault));
end;

/// <summary>
/// Gets the default text for the privacy notice.
/// </summary>
Expand Down Expand Up @@ -166,7 +181,19 @@ codeunit 1563 "Privacy Notice"
var
PrivacyNoticeImpl: Codeunit "Privacy Notice Impl.";
begin
exit(PrivacyNoticeImpl.CheckPrivacyNoticeApprovalState(Id) = "Privacy Notice Approval State"::Disagreed);
exit(PrivacyNoticeImpl.IsApprovalStateDisagreed(Id));
end;

/// <summary>
/// Determines whether the admin or user has disagreed with the Privacy Notice.
/// </summary>
/// <param name="State">The approval state.</param>
/// <returns>Whether the Privacy Notice was disagreed to.</returns>
procedure IsApprovalStateDisagreed(State: Enum "Privacy Notice Approval State"): Boolean
var
PrivacyNoticeImpl: Codeunit "Privacy Notice Impl.";
begin
exit(PrivacyNoticeImpl.IsApprovalStateDisagreed(State));
end;

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ codeunit 1564 "Privacy Notice Approval"
procedure SetApprovalState(PrivacyNoticeId: Code[50]; UserSID: Guid; PrivacyNoticeApprovalState: Enum "Privacy Notice Approval State")
var
PrivacyNoticeApproval: Record "Privacy Notice Approval";
PrivacyNotice: Record "Privacy Notice";
PrivacyNoticeApprovedLbl: Label 'Privacy Notice Approval ID %1 provided by UserSecurityId %2.', Locked = true;
begin
if PrivacyNoticeApprovalState = "Privacy Notice Approval State"::"Not set" then begin
Expand All @@ -30,16 +31,27 @@ codeunit 1564 "Privacy Notice Approval"
PrivacyNoticeApproval.Approved := PrivacyNoticeApprovalState = "Privacy Notice Approval State"::Agreed;
PrivacyNoticeApproval.Modify();
Session.LogAuditMessage(StrSubstNo(PrivacyNoticeApprovedLbl, PrivacyNoticeId, UserSID), SecurityOperationResult::Success, AuditCategory::ApplicationManagement, 4, 0);

if PrivacyNotice.Get(PrivacyNoticeId) then begin
PrivacyNotice.ApprovedByDefault := false;
PrivacyNotice.Modify();
end;
end;

procedure ResetApproval(PrivacyNoticeId: Code[50]; UserSID: Guid)
var
PrivacyNotice: Record "Privacy Notice";
PrivacyNoticeApproval: Record "Privacy Notice Approval";
PrivacyNoticeResetLbl: Label 'Privacy Notice Approval ID %1 has been reset by UserSecurityId %2.', Locked = true;
begin
PrivacyNoticeApproval.SetRange(ID, PrivacyNoticeId);
PrivacyNoticeApproval.SetRange("User SID", UserSID);
PrivacyNoticeApproval.DeleteAll();
Session.LogAuditMessage(StrSubstNo(PrivacyNoticeResetLbl, PrivacyNoticeId, UserSID), SecurityOperationResult::Success, AuditCategory::ApplicationManagement, 4, 0);

if PrivacyNotice.Get(PrivacyNoticeId) then begin
PrivacyNotice.ApprovedByDefault := false;
PrivacyNotice.Modify();
end;
end;
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,11 @@ enum 1563 "Privacy Notice Approval State"
value(2; Disagreed)
{
}

/// <summary>
/// The Privacy Notice was disagreed by the admin.
/// </summary>
value(3; DisagreedAdmin)
{
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ codeunit 1565 "Privacy Notice Impl."
ShowingPrivacyNoticeTelemetryTxt: Label 'Showing privacy notice', Locked = true;
PrivacyNoticeApprovalResultTelemetryTxt: Label 'Approval State after showing privacy notice: %1', Locked = true;
CheckPrivacyNoticeApprovalStateTelemetryTxt: Label 'Checking privacy approval state', Locked = true;
PrivacyNoticeApprovedByDefaultTxt: Label 'The privacy notice was approved by default', Locked = true;
AdminPrivacyApprovalStateTelemetryTxt: Label 'Admin privacy approval state: %1', Locked = true;
UserPrivacyApprovalStateTelemetryTxt: Label 'User privacy approval state: %1', Locked = true;
RegisteringPrivacyNoticesFailedTelemetryErr: Label 'Privacy notices could not be registered', Locked = true;
Expand All @@ -47,7 +48,14 @@ codeunit 1565 "Privacy Notice Impl."
var
PrivacyNotice: Record "Privacy Notice";
begin
exit(CreatePrivacyNotice(PrivacyNotice, Id, IntegrationName, Link));
exit(this.CreatePrivacyNotice(PrivacyNotice, Id, IntegrationName, Link, false));
end;

procedure CreatePrivacyNotice(Id: Code[50]; IntegrationName: Text[250]; Link: Text[2048]; ApproveByDefault: Boolean): Boolean
var
PrivacyNotice: Record "Privacy Notice";
begin
exit(this.CreatePrivacyNotice(PrivacyNotice, Id, IntegrationName, Link, ApproveByDefault));
end;

procedure CreatePrivacyNotice(Id: Code[50]; IntegrationName: Text[250]): Boolean
Expand Down Expand Up @@ -135,14 +143,19 @@ codeunit 1565 "Privacy Notice Impl."
exit("Privacy Notice Approval State"::"Not set"); // If there are no Privacy Notice then it is by default "Not set".
end;

if PrivacyNotice.ApprovedByDefault then begin
Session.LogMessage('0000GKD', this.PrivacyNoticeApprovedByDefaultTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', this.TelemetryCategoryTxt);
exit("Privacy Notice Approval State"::Agreed);
end;

// First check if admin has made decision on this privacy notice and return that
if PrivacyNotice.Enabled then begin
Session.LogMessage('0000GKD', StrSubstNo(AdminPrivacyApprovalStateTelemetryTxt, "Privacy Notice Approval State"::Agreed), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTxt);
exit("Privacy Notice Approval State"::Agreed);
end;
if PrivacyNotice.Disabled then begin
Session.LogMessage('0000GKE', StrSubstNo(AdminPrivacyApprovalStateTelemetryTxt, "Privacy Notice Approval State"::Disagreed), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTxt);
exit("Privacy Notice Approval State"::Disagreed);
Session.LogMessage('0000GKE', StrSubstNo(this.AdminPrivacyApprovalStateTelemetryTxt, "Privacy Notice Approval State"::DisagreedAdmin), Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', this.TelemetryCategoryTxt);
exit("Privacy Notice Approval State"::DisagreedAdmin);
end;

// Admin did not make any decision
Expand Down Expand Up @@ -171,13 +184,18 @@ codeunit 1565 "Privacy Notice Impl."
procedure SetApprovalState(PrivacyNoticeId: Code[50]; PrivacyNoticeApprovalState: Enum "Privacy Notice Approval State")
var
PrivacyNoticeApproval: Codeunit "Privacy Notice Approval";
PrivacyNotice: Codeunit "Privacy Notice";
edwardUL99 marked this conversation as resolved.
Show resolved Hide resolved
begin
CreateDefaultPrivacyNotices(); // Ensure all default Privacy Notices are created
if CanCurrentUserApproveForOrganization() then
PrivacyNoticeApproval.SetApprovalState(PrivacyNoticeId, EmptyGuid, PrivacyNoticeApprovalState)
else
if PrivacyNoticeApprovalState <> "Privacy Notice Approval State"::Disagreed then // We do not store rejected user approvals
if this.CanCurrentUserApproveForOrganization() then begin
if PrivacyNoticeApprovalState = "Privacy Notice Approval State"::Disagreed then begin
PrivacyNoticeApprovalState := "Privacy Notice Approval State"::DisagreedAdmin;
end;
PrivacyNoticeApproval.SetApprovalState(PrivacyNoticeId, this.EmptyGuid, PrivacyNoticeApprovalState);
end else begin
if not IsApprovalStateDisagreed(PrivacyNoticeApprovalState) then begin // We do not store rejected user approvals
PrivacyNoticeApproval.SetApprovalState(PrivacyNoticeId, UserSecurityId(), PrivacyNoticeApprovalState);
end;
end;
end;

procedure ShowOneTimePrivacyNotice(IntegrationName: Text[250]): Enum "Privacy Notice Approval State"
Expand All @@ -190,7 +208,7 @@ codeunit 1565 "Privacy Notice Impl."
TempPrivacyNotice: Record "Privacy Notice" temporary;
PrivacyNoticePage: Page "Privacy Notice";
begin
CreatePrivacyNotice(TempPrivacyNotice, '', IntegrationName, Link);
CreatePrivacyNotice(TempPrivacyNotice, '', IntegrationName, Link, false);

PrivacyNoticePage.SetRecord(TempPrivacyNotice);
PrivacyNoticePage.RunModal();
Expand Down Expand Up @@ -235,7 +253,7 @@ codeunit 1565 "Privacy Notice Impl."
PrivacyNoticeInterface.OnRegisterPrivacyNotices(PrivacyNotice);
end;

local procedure CreatePrivacyNotice(var PrivacyNotice: Record "Privacy Notice"; Id: Code[50]; IntegrationName: Text[250]; Link: Text[2048]): Boolean
local procedure CreatePrivacyNotice(var PrivacyNotice: Record "Privacy Notice"; Id: Code[50]; IntegrationName: Text[250]; Link: Text[2048]; ApproveByDefault: Boolean): Boolean
begin
Session.LogMessage('0000GK7', CreatePrivacyNoticeTelemetryTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', TelemetryCategoryTxt);

Expand All @@ -245,6 +263,7 @@ codeunit 1565 "Privacy Notice Impl."
PrivacyNotice.Id := Id;
PrivacyNotice."Integration Service Name" := IntegrationName;
PrivacyNotice.Link := Link;
PrivacyNotice.ApprovedByDefault := ApproveByDefault;
exit(PrivacyNotice.Insert());
end;

Expand Down Expand Up @@ -303,6 +322,29 @@ codeunit 1565 "Privacy Notice Impl."
IsApproved := false;
end;

/// <summary>
/// Determines whether the admin or user has disagreed with the Privacy Notice.
/// </summary>
/// <param name="Id">Identification of an existing privacy notice.</param>
/// <returns>Whether the Privacy Notice was disagreed to.</returns>
procedure IsApprovalStateDisagreed(Id: Code[50]): Boolean
var
State: Enum "Privacy Notice Approval State";
begin
State := CheckPrivacyNoticeApprovalState(Id);
exit(IsApprovalStateDisagreed(State));
end;

/// <summary>
/// Determines whether the admin or user has disagreed with the Privacy Notice.
/// </summary>
/// <param name="State">The approval state.</param>
/// <returns>Whether the Privacy Notice was disagreed to.</returns>
procedure IsApprovalStateDisagreed(State: Enum "Privacy Notice Approval State"): Boolean
begin
exit((State = "Privacy Notice Approval State"::Disagreed) or (State = "Privacy Notice Approval State"::DisagreedAdmin));
end;

[EventSubscriber(ObjectType::Codeunit, Codeunit::"System Action Triggers", GetPrivacyNoticeApprovalState, '', true, true)]
local procedure GetPrivacyNoticeApprovalState(PrivacyNoticeIntegrationName: Text; var PrivacyNoticeApprovalState: Integer)
var
Expand All @@ -311,4 +353,20 @@ codeunit 1565 "Privacy Notice Impl."
PrivacyNoticeId := CopyStr(PrivacyNoticeIntegrationName, 1, 50);
PrivacyNoticeApprovalState := CheckPrivacyNoticeApprovalState(PrivacyNoticeId).AsInteger();
end;

[EventSubscriber(ObjectType::Codeunit, Codeunit::"System Action Triggers", CreatePrivacyNotice, '', false, false)]
local procedure OnCreatePrivacyNotice(PrivacyNoticeIntegrationName: Text; ApproveByDefault: Boolean; var PrivacyNoticeApprovalState: Integer)
var
PrivacyNoticeId: Code[50];
PrivacyNotice: Record "Privacy Notice";
begin
PrivacyNoticeId := CopyStr(PrivacyNoticeIntegrationName, 1, 50);

if PrivacyNotice.Get(PrivacyNoticeId) then begin
PrivacyNoticeApprovalState := CheckPrivacyNoticeApprovalState(PrivacyNoticeId).AsInteger();
end else begin
if CreatePrivacyNotice(PrivacyNoticeId, PrivacyNoticeIntegrationName, MicrosoftPrivacyLinkTxt, ApproveByDefault) then
PrivacyNoticeApprovalState := this.CheckPrivacyNoticeApprovalState(PrivacyNoticeId).AsInteger();
end;
end;
}
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@ page 1565 "Privacy Notices"
SetRecordApprovalState();
end;
}
field(ApprovedByDefault; Rec.ApprovedByDefault)
{
Caption = 'Agreed by Default';
ToolTip = 'Specifies that the integration''s privacy notice has been agreed to by default. (the default state)';
Editable = false;
}
#pragma warning disable AA0218
field(Accepted2; Rec.Enabled)
{
Expand Down Expand Up @@ -158,9 +164,9 @@ page 1565 "Privacy Notices"

trigger OnAfterGetRecord()
begin
Accepted := Rec.Enabled;
Rejected := Rec.Disabled;
UserDecides := not (Accepted or Rejected);
Accepted := (Rec.ApprovedByDefault or Rec.Enabled);
Rejected := (not Rec.ApprovedByDefault and Rec.Disabled);
UserDecides := not Rec.ApprovedByDefault and not (Accepted or Rejected);
end;

local procedure SetRecordApprovalState()
Expand Down
Loading