Add infrastructure for Duo privacy notice in non-public Slack contexts

What does this MR do and why?

Infrastructure foundation for the Duo-in-Slack privacy notice, split out of !244130 (closed) per reviewer suggestion (!244130 (comment 3550890954)) to separate the parts requiring database review from the feature behavior and docs.

Duo-from-Slack sessions are recorded in the shared duo-workspace project and visible to everyone with access to it, which may be a wider audience than a private channel or group DM in Slack. A follow-up MR shows a one-time ephemeral privacy notice when the bot is mentioned in a non-public conversation. This MR adds the pieces that notice needs:

  1. Database (requires database review): adds a nullable duo_privacy_notice_acknowledged_at timestamp column to chat_names, with ChatName#duo_privacy_notice_acknowledged? and ChatName#acknowledge_duo_privacy_notice! helpers. The acknowledgement is stored per user per Slack workspace.
  2. Slack scopes and API: requests the channels:read, groups:read, im:read, and mpim:read bot scopes on app installation and adds Slack::API#conversation_info (conversations.info) so the app can determine whether a conversation is public, private, a DM, or a group DM. Also allows passing Block Kit blocks to Slack::API#post_ephemeral so the notice can render interactive buttons.
  3. SlackBlockActions::BaseHandler: a base class for Slack block action button handlers, providing shared button-value parsing, Slack installation lookup, and response_url message replacement. This directly addresses reviewer feedback about duplicated logic between the two privacy notice handlers (!244130 (comment 3550890942)); both handlers in the follow-up MR inherit from it.

No user-facing behavior changes in this MR: the new column is unread, the new scopes are only requested on (re)installation, and the base handler has no subclasses yet.

Split plan

MR Content
This MR Infrastructure foundation (database, Slack scopes/API, base handler)
Follow-up Feature behavior (privacy notice in AppMentionedService, acknowledge/decline handlers)
Follow-up Docs (requires technical writer review)

References

Screenshots or screen recordings

No visible changes.

How to set up and validate locally

  1. Run the migration and confirm the column exists:

    bin/rails db:migrate
    ChatName.column_names.include?('duo_privacy_notice_acknowledged_at') # => true
  2. Check the helper methods:

    chat_name = ChatName.first
    chat_name.duo_privacy_notice_acknowledged? # => false
    chat_name.acknowledge_duo_privacy_notice!
    chat_name.duo_privacy_notice_acknowledged? # => true
  3. Verify the new scopes are part of the installation request:

    SlackIntegration::SCOPES # includes channels:read, groups:read, im:read, mpim:read
  4. With a Slack app configured against your GDK, Slack::API#conversation_info can be exercised from the console:

    slack_installation = SlackIntegration.with_bot.first
    Slack::API.new(slack_installation).conversation_info(channel: '<channel-id>')

Database

Adds a nullable timestamptz column (no default, no index, no FK) to chat_names, a small table. Migration tested in the parent MR against GitLab.com data: 4.2 s runtime, +0.00 B size change (see the database testing pipeline comment on the parent MR). The db:gitlabcom-database-testing job has been triggered on this MR and will post its results as a comment.

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by Jannik Lehmann

Merge request reports

Loading
Loading