From b0d92f33023afb99fec95ec34996e2435da144d9 Mon Sep 17 00:00:00 2001
From: Joe Snyder <joe.snyder@kitware.com>
Date: Tue, 18 Apr 2023 11:19:41 +0000
Subject: [PATCH] Add user_identities field to JWTv2

Add the code to the JWTv2 which appends the user's identities to
the fields if the user has elected to send them.

This is asked through a delegated field on the user which is delegated to
the user preferences.

Changelog: added
---
 app/models/user.rb                |  1 +
 lib/gitlab/ci/jwt_v2.rb           | 18 +++++++++++++++---
 spec/lib/gitlab/ci/jwt_v2_spec.rb | 20 +++++++++++++++++++-
 3 files changed, 35 insertions(+), 4 deletions(-)

diff --git a/app/models/user.rb b/app/models/user.rb
index 16b4e604a9e6c..0a61de9c79824 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -355,6 +355,7 @@ def update_tracked_fields!(request)
             :time_format_in_24h, :time_format_in_24h=,
             :show_whitespace_in_diffs, :show_whitespace_in_diffs=,
             :view_diffs_file_by_file, :view_diffs_file_by_file=,
+            :pass_user_identities_to_ci_jwt, :pass_user_identities_to_ci_jwt=,
             :tab_width, :tab_width=,
             :sourcegraph_enabled, :sourcegraph_enabled=,
             :gitpod_enabled, :gitpod_enabled=,
diff --git a/lib/gitlab/ci/jwt_v2.rb b/lib/gitlab/ci/jwt_v2.rb
index cfefa79d9e0b2..fdff5035d3707 100644
--- a/lib/gitlab/ci/jwt_v2.rb
+++ b/lib/gitlab/ci/jwt_v2.rb
@@ -20,11 +20,23 @@ def initialize(build, ttl:, aud:)
       attr_reader :aud
 
       def reserved_claims
-        super.merge(
+        super.merge({
           iss: Settings.gitlab.base_url,
           sub: "project_path:#{project.full_path}:ref_type:#{ref_type}:ref:#{source_ref}",
-          aud: aud
-        )
+          aud: aud,
+          user_identities: user_identities
+        }.compact)
+      end
+
+      def user_identities
+        return unless user&.pass_user_identities_to_ci_jwt
+
+        user.identities.map do |identity|
+          {
+            provider: identity.provider.to_s,
+            extern_uid: identity.extern_uid.to_s
+          }
+        end
       end
     end
   end
diff --git a/spec/lib/gitlab/ci/jwt_v2_spec.rb b/spec/lib/gitlab/ci/jwt_v2_spec.rb
index 5eeab658a8e9f..21fd7e3adcfb7 100644
--- a/spec/lib/gitlab/ci/jwt_v2_spec.rb
+++ b/spec/lib/gitlab/ci/jwt_v2_spec.rb
@@ -5,7 +5,13 @@
 RSpec.describe Gitlab::Ci::JwtV2 do
   let(:namespace) { build_stubbed(:namespace) }
   let(:project) { build_stubbed(:project, namespace: namespace) }
-  let(:user) { build_stubbed(:user) }
+  let(:user) do
+    build_stubbed(
+      :user,
+      identities: [build_stubbed(:identity, extern_uid: '1', provider: 'github')]
+    )
+  end
+
   let(:pipeline) { build_stubbed(:ci_pipeline, ref: 'auto-deploy-2020-03-19') }
   let(:aud) { described_class::DEFAULT_AUD }
 
@@ -33,6 +39,18 @@
       end
     end
 
+    it 'includes user identities when enabled' do
+      expect(user).to receive(:pass_user_identities_to_ci_jwt).and_return(true)
+      identities = payload[:user_identities].map { |identity| identity.slice(:extern_uid, :provider) }
+      expect(identities).to eq([{ extern_uid: '1', provider: 'github' }])
+    end
+
+    it 'does not include user identities when disabled' do
+      expect(user).to receive(:pass_user_identities_to_ci_jwt).and_return(false)
+
+      expect(payload).not_to include(:user_identities)
+    end
+
     context 'when given an aud' do
       let(:aud) { 'AWS' }
 
-- 
GitLab