Self-managed SAML Duo Seat Assignment

What does this MR do and why?

This merge request adds support for Duo add-on group assignment at the instance level for SAML authentication. Previously, Duo add-on assignment was only available at the group level, but now it can be configured globally through the GitLab configuration file. The changes introduce a new duo_add_on_groups setting in the SAML configuration that allows administrators to specify which SAML groups should automatically receive Duo add-on assignments.

When users authenticate via SAML, the system now checks if they belong to any of these designated Duo groups. If they do, they're automatically assigned a Duo seat; if they're removed from these groups, their Duo seat is automatically unassigned. This functionality is implemented through a new DuoAddOnAssignmentUpdater class that handles the assignment logic, with appropriate tests to verify the behavior under various conditions.

References

Please include cross links to any resources that are relevant to this MR. This will give reviewers and future readers helpful context to give an efficient review of the changes introduced.

MR acceptance checklist

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

Screenshots or screen recordings

Screenshots are required for UI changes, and strongly recommended for all other merge requests.

Before After

How to set up and validate locally

Numbered steps to set up and validate the change are strongly suggested.

  1. Configure your GDK for Duo: https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/development/ai_features/ai_development_license.md#option-b-local-license-with-rake-task-in-self-managed-mode-single-tenant-setup

  2. Configure self-managed SAML including the new duo_add_on_groups option.

          - {
            name: "saml",
            groups_attribute: 'Groups',
            duo_add_on_groups: ['Everyone'],
            args: {
              name: "saml",
              assertion_consumer_service_url: "https://gdk.test:3443/users/auth/saml/callback",
              ...
              issuer: "https://gdk.test:3443",
            }
          }
  3. Restart both rails-web and rails-background-jobs.

  4. Sign-in as a SAML user that is in the configured add-on group. Observe that a Duo add-on seat has been assigned for the user.

    [1] pry(main)> add_on = ::GitlabSubscriptions::Duo.enterprise_or_pro_for_namespace(nil)
    => #<GitlabSubscriptions::AddOnPurchase:0x00000003217fbce0
     id: 1,
     created_at: Wed, 09 Apr 2025 19:45:38.974758000 UTC +00:00,
     updated_at: Wed, 09 Apr 2025 19:45:38.974758000 UTC +00:00,
     subscription_add_on_id: 3,
     namespace_id: nil,
     quantity: 10,
     expires_on: Fri, 20 Feb 2026,
     purchase_xid: "123456",
     last_assigned_users_refreshed_at: Wed, 09 Apr 2025 19:45:40.251987000 UTC +00:00,
     trial: false,
     started_at: Wed, 09 Apr 2025,
     organization_id: 1>
    [2] pry(main)> add_on.assigned_users
    => [#<GitlabSubscriptions::UserAddOnAssignment:0x0000000321a91e78
      id: 3,
      add_on_purchase_id: 1,
      user_id: 201,
      created_at: Wed, 09 Apr 2025 20:10:52.218077000 UTC +00:00,
      updated_at: Wed, 09 Apr 2025 20:10:52.218077000 UTC +00:00,
      organization_id: 1>]
  5. If desired, repeat the processes but change parameters:

    1. Change the duo_add_on_groups to something your SAML test user is not a member of. Their assigned seat should be removed.
    2. Remove the duo_add_on_groups configuration altogether and observe that the user's assigned Duo seat is not removed on sign-in.
Edited by Drew Blessing

Merge request reports

Loading