From eedcc7dad2a7236e0b88443b908586718afdcb6f Mon Sep 17 00:00:00 2001
From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Date: Mon, 13 Jan 2025 00:43:48 +1100
Subject: [PATCH] [8.x] [Rules migration] Use user profile UID instead of
 username (#206299) (#206378)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[Rules migration] Use user profile UID instead of username
(#206299)](https://github.com/elastic/kibana/pull/206299)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Ievgen
Sorokopud","email":"ievgen.sorokopud@elastic.co"},"sourceCommit":{"committedDate":"2025-01-12T12:06:13Z","message":"[Rules
migration] Use user profile UID instead of username (#206299)\n\n##
Summary\r\n\r\n[Internal
link](https://github.com/elastic/security-team/issues/10820)\r\nto the
feature details\r\n\r\nWith these changes we switch to using user's
`profile_id` instead of\r\n`username` as a user identification for the
migration rules create and\r\nupdate
operations.","sha":"a1f85326e8166857f51e581ae6a3e94bc480428e","branchLabelMapping":{"^v9.0.0$":"main","^v8.18.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","v9.0.0","Team:Threat
Hunting","Team: SecuritySolution","backport:prev-minor"],"title":"[Rules
migration] Use user profile UID instead of
username","number":206299,"url":"https://github.com/elastic/kibana/pull/206299","mergeCommit":{"message":"[Rules
migration] Use user profile UID instead of username (#206299)\n\n##
Summary\r\n\r\n[Internal
link](https://github.com/elastic/security-team/issues/10820)\r\nto the
feature details\r\n\r\nWith these changes we switch to using user's
`profile_id` instead of\r\n`username` as a user identification for the
migration rules create and\r\nupdate
operations.","sha":"a1f85326e8166857f51e581ae6a3e94bc480428e"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/206299","number":206299,"mergeCommit":{"message":"[Rules
migration] Use user profile UID instead of username (#206299)\n\n##
Summary\r\n\r\n[Internal
link](https://github.com/elastic/security-team/issues/10820)\r\nto the
feature details\r\n\r\nWith these changes we switch to using user's
`profile_id` instead of\r\n`username` as a user identification for the
migration rules create and\r\nupdate
operations.","sha":"a1f85326e8166857f51e581ae6a3e94bc480428e"}}]}]
BACKPORT-->

Co-authored-by: Ievgen Sorokopud <ievgen.sorokopud@elastic.co>
---
 .../data/rule_migrations_data_base_client.ts  | 29 ++++++++++++++++---
 .../rules/data/rule_migrations_data_client.ts | 26 +++++++----------
 ...ule_migrations_data_integrations_client.ts |  8 ++---
 .../rule_migrations_data_lookups_client.ts    | 12 ++++----
 .../rule_migrations_data_resources_client.ts  |  6 ++--
 .../data/rule_migrations_data_rules_client.ts | 17 +++++++----
 .../data/rule_migrations_data_service.ts      |  2 +-
 7 files changed, 63 insertions(+), 37 deletions(-)

diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_base_client.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_base_client.ts
index 31931fce0b1d9..e80e880545f68 100644
--- a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_base_client.ts
+++ b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_base_client.ts
@@ -11,7 +11,12 @@ import type {
   SearchResponse,
   Duration,
 } from '@elastic/elasticsearch/lib/api/types';
-import type { ElasticsearchClient, Logger } from '@kbn/core/server';
+import type {
+  AuthenticatedUser,
+  ElasticsearchClient,
+  IScopedClusterClient,
+  Logger,
+} from '@kbn/core/server';
 import assert from 'assert';
 import type { Stored } from '../types';
 import type { IndexNameProvider } from './rule_migrations_data_client';
@@ -19,12 +24,28 @@ import type { IndexNameProvider } from './rule_migrations_data_client';
 const DEFAULT_PIT_KEEP_ALIVE: Duration = '30s' as const;
 
 export class RuleMigrationsDataBaseClient {
+  protected esClient: ElasticsearchClient;
+
   constructor(
     protected getIndexName: IndexNameProvider,
-    protected username: string,
-    protected esClient: ElasticsearchClient,
+    protected currentUser: AuthenticatedUser,
+    protected esScopedClient: IScopedClusterClient,
     protected logger: Logger
-  ) {}
+  ) {
+    this.esClient = esScopedClient.asInternalUser;
+  }
+
+  protected async getProfileUid() {
+    if (this.currentUser.profile_uid) {
+      return this.currentUser.profile_uid;
+    }
+    const username = this.currentUser.username;
+    const users = await this.esScopedClient.asCurrentUser.security.getUser({
+      username,
+      with_profile_uid: true,
+    });
+    return users[username].profile_uid;
+  }
 
   protected processResponseHits<T extends object>(
     response: SearchResponse<T>,
diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_client.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_client.ts
index 9198a521bcb96..aec7a1931b502 100644
--- a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_client.ts
+++ b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_client.ts
@@ -5,7 +5,7 @@
  * 2.0.
  */
 
-import type { IScopedClusterClient, Logger } from '@kbn/core/server';
+import type { AuthenticatedUser, IScopedClusterClient, Logger } from '@kbn/core/server';
 import type { PackageService } from '@kbn/fleet-plugin/server';
 import { RuleMigrationsDataIntegrationsClient } from './rule_migrations_data_integrations_client';
 import { RuleMigrationsDataPrebuiltRulesClient } from './rule_migrations_data_prebuilt_rules_client';
@@ -26,40 +26,36 @@ export class RuleMigrationsDataClient {
 
   constructor(
     indexNameProviders: IndexNameProviders,
-    username: string,
+    currentUser: AuthenticatedUser,
     esScopedClient: IScopedClusterClient,
     logger: Logger,
     packageService?: PackageService
   ) {
     this.rules = new RuleMigrationsDataRulesClient(
       indexNameProviders.rules,
-      username,
-      esScopedClient.asInternalUser,
+      currentUser,
+      esScopedClient,
       logger
     );
     this.resources = new RuleMigrationsDataResourcesClient(
       indexNameProviders.resources,
-      username,
-      esScopedClient.asInternalUser,
+      currentUser,
+      esScopedClient,
       logger
     );
     this.integrations = new RuleMigrationsDataIntegrationsClient(
       indexNameProviders.integrations,
-      username,
-      esScopedClient.asInternalUser,
+      currentUser,
+      esScopedClient,
       logger,
       packageService
     );
     this.prebuiltRules = new RuleMigrationsDataPrebuiltRulesClient(
       indexNameProviders.prebuiltrules,
-      username,
-      esScopedClient.asInternalUser,
-      logger
-    );
-    this.lookups = new RuleMigrationsDataLookupsClient(
-      username,
-      esScopedClient.asCurrentUser,
+      currentUser,
+      esScopedClient,
       logger
     );
+    this.lookups = new RuleMigrationsDataLookupsClient(currentUser, esScopedClient, logger);
   }
 }
diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_integrations_client.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_integrations_client.ts
index 54a4f14a667e4..5df7cfe517f20 100644
--- a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_integrations_client.ts
+++ b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_integrations_client.ts
@@ -6,7 +6,7 @@
  */
 
 import type { PackageService } from '@kbn/fleet-plugin/server';
-import type { ElasticsearchClient, Logger } from '@kbn/core/server';
+import type { AuthenticatedUser, IScopedClusterClient, Logger } from '@kbn/core/server';
 import type { PackageList } from '@kbn/fleet-plugin/common';
 import type { RuleMigrationIntegration } from '../types';
 import { RuleMigrationsDataBaseClient } from './rule_migrations_data_base_client';
@@ -28,12 +28,12 @@ const INTEGRATIONS = integrationsFile as RuleMigrationIntegration[];
 export class RuleMigrationsDataIntegrationsClient extends RuleMigrationsDataBaseClient {
   constructor(
     getIndexName: IndexNameProvider,
-    username: string,
-    esClient: ElasticsearchClient,
+    currentUser: AuthenticatedUser,
+    esScopedClient: IScopedClusterClient,
     logger: Logger,
     private packageService?: PackageService
   ) {
-    super(getIndexName, username, esClient, logger);
+    super(getIndexName, currentUser, esScopedClient, logger);
   }
 
   async getIntegrationPackages(): Promise<PackageList | undefined> {
diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_lookups_client.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_lookups_client.ts
index 93763d6508cf0..24efc3ae7eb87 100644
--- a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_lookups_client.ts
+++ b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_lookups_client.ts
@@ -6,15 +6,15 @@
  */
 
 import { sha256 } from 'js-sha256';
-import type { ElasticsearchClient, Logger } from '@kbn/core/server';
+import type { AuthenticatedUser, IScopedClusterClient, Logger } from '@kbn/core/server';
 import { retryTransientEsErrors } from '@kbn/index-adapter';
 
 export type LookupData = object[];
 
 export class RuleMigrationsDataLookupsClient {
   constructor(
-    protected username: string,
-    protected esClient: ElasticsearchClient,
+    protected currentUser: AuthenticatedUser,
+    protected esScopedClient: IScopedClusterClient,
     protected logger: Logger
   ) {}
 
@@ -22,7 +22,7 @@ export class RuleMigrationsDataLookupsClient {
     const indexName = `lookup_${lookupName}`;
     try {
       await this.executeEs(() =>
-        this.esClient.indices.create({
+        this.esScopedClient.asCurrentUser.indices.create({
           index: indexName,
           settings: { index: { mode: 'lookup' } },
           mappings: { dynamic: 'runtime' },
@@ -48,7 +48,9 @@ export class RuleMigrationsDataLookupsClient {
     ]);
 
     try {
-      await this.executeEs(() => this.esClient.bulk({ index: indexName, body }));
+      await this.executeEs(() =>
+        this.esScopedClient.asCurrentUser.bulk({ index: indexName, body })
+      );
     } catch (error) {
       if (error?.statusCode !== 404) {
         this.logger.error(`Error indexing data for lookup index ${indexName} - ${error.message}`);
diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_resources_client.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_resources_client.ts
index c2bd1ed2e50f8..fce0bec7e9d24 100644
--- a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_resources_client.ts
+++ b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_resources_client.ts
@@ -40,6 +40,7 @@ const DEFAULT_SEARCH_BATCH_SIZE = 500 as const;
 export class RuleMigrationsDataResourcesClient extends RuleMigrationsDataBaseClient {
   public async upsert(resources: CreateRuleMigrationResourceInput[]): Promise<void> {
     const index = await this.getIndexName();
+    const profileId = await this.getProfileUid();
 
     let resourcesSlice: CreateRuleMigrationResourceInput[];
 
@@ -54,7 +55,7 @@ export class RuleMigrationsDataResourcesClient extends RuleMigrationsDataBaseCli
               doc: {
                 ...resource,
                 '@timestamp': createdAt,
-                updated_by: this.username,
+                updated_by: profileId,
                 updated_at: createdAt,
               },
               doc_as_upsert: true,
@@ -71,6 +72,7 @@ export class RuleMigrationsDataResourcesClient extends RuleMigrationsDataBaseCli
   /** Creates the resources in the index only if they do not exist */
   public async create(resources: CreateRuleMigrationResourceInput[]): Promise<void> {
     const index = await this.getIndexName();
+    const profileId = await this.getProfileUid();
 
     let resourcesSlice: CreateRuleMigrationResourceInput[];
     const createdAt = new Date().toISOString();
@@ -83,7 +85,7 @@ export class RuleMigrationsDataResourcesClient extends RuleMigrationsDataBaseCli
             {
               ...resource,
               '@timestamp': createdAt,
-              updated_by: this.username,
+              updated_by: profileId,
               updated_at: createdAt,
             },
           ]),
diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_rules_client.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_rules_client.ts
index e65921ca2a9ac..675b9f7ed7c33 100644
--- a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_rules_client.ts
+++ b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_rules_client.ts
@@ -66,6 +66,7 @@ export class RuleMigrationsDataRulesClient extends RuleMigrationsDataBaseClient
   /** Indexes an array of rule migrations to be processed */
   async create(ruleMigrations: CreateRuleMigrationInput[]): Promise<void> {
     const index = await this.getIndexName();
+    const profileId = await this.getProfileUid();
 
     let ruleMigrationsSlice: CreateRuleMigrationInput[];
     const createdAt = new Date().toISOString();
@@ -79,8 +80,8 @@ export class RuleMigrationsDataRulesClient extends RuleMigrationsDataBaseClient
               ...ruleMigration,
               '@timestamp': createdAt,
               status: SiemMigrationStatus.PENDING,
-              created_by: this.username,
-              updated_by: this.username,
+              created_by: profileId,
+              updated_by: profileId,
               updated_at: createdAt,
             },
           ]),
@@ -95,6 +96,7 @@ export class RuleMigrationsDataRulesClient extends RuleMigrationsDataBaseClient
   /** Updates an array of rule migrations to be processed */
   async update(ruleMigrations: UpdateRuleMigrationData[]): Promise<void> {
     const index = await this.getIndexName();
+    const profileId = await this.getProfileUid();
 
     let ruleMigrationsSlice: UpdateRuleMigrationData[];
     const updatedAt = new Date().toISOString();
@@ -117,7 +119,7 @@ export class RuleMigrationsDataRulesClient extends RuleMigrationsDataBaseClient
                   elastic_rule: elasticRule,
                   translation_result:
                     translationResult ?? convertEsqlQueryToTranslationResult(elasticRule?.query),
-                  updated_by: this.username,
+                  updated_by: profileId,
                   updated_at: updatedAt,
                 },
               },
@@ -176,6 +178,7 @@ export class RuleMigrationsDataRulesClient extends RuleMigrationsDataBaseClient
    */
   async takePending(migrationId: string, size: number): Promise<StoredRuleMigration[]> {
     const index = await this.getIndexName();
+    const profileId = await this.getProfileUid();
     const query = this.getFilterQuery(migrationId, { status: SiemMigrationStatus.PENDING });
 
     const storedRuleMigrations = await this.esClient
@@ -194,7 +197,7 @@ export class RuleMigrationsDataRulesClient extends RuleMigrationsDataBaseClient
         operations: storedRuleMigrations.flatMap(({ id, status }) => [
           { update: { _id: id, _index: index } },
           {
-            doc: { status, updated_by: this.username, updated_at: new Date().toISOString() },
+            doc: { status, updated_by: profileId, updated_at: new Date().toISOString() },
           },
         ]),
       })
@@ -211,10 +214,11 @@ export class RuleMigrationsDataRulesClient extends RuleMigrationsDataBaseClient
   /** Updates one rule migration with the provided data and sets the status to `completed` */
   async saveCompleted({ id, ...ruleMigration }: StoredRuleMigration): Promise<void> {
     const index = await this.getIndexName();
+    const profileId = await this.getProfileUid();
     const doc = {
       ...ruleMigration,
       status: SiemMigrationStatus.COMPLETED,
-      updated_by: this.username,
+      updated_by: profileId,
       updated_at: new Date().toISOString(),
     };
     await this.esClient.update({ index, id, doc, refresh: 'wait_for' }).catch((error) => {
@@ -226,10 +230,11 @@ export class RuleMigrationsDataRulesClient extends RuleMigrationsDataBaseClient
   /** Updates one rule migration with the provided data and sets the status to `failed` */
   async saveError({ id, ...ruleMigration }: StoredRuleMigration): Promise<void> {
     const index = await this.getIndexName();
+    const profileId = await this.getProfileUid();
     const doc = {
       ...ruleMigration,
       status: SiemMigrationStatus.FAILED,
-      updated_by: this.username,
+      updated_by: profileId,
       updated_at: new Date().toISOString(),
     };
     await this.esClient.update({ index, id, doc, refresh: 'wait_for' }).catch((error) => {
diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_service.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_service.ts
index 5cacaf5592407..094e48ab90771 100644
--- a/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_service.ts
+++ b/x-pack/solutions/security/plugins/security_solution/server/lib/siem_migrations/rules/data/rule_migrations_data_service.ts
@@ -118,7 +118,7 @@ export class RuleMigrationsDataService {
 
     return new RuleMigrationsDataClient(
       indexNameProviders,
-      currentUser.username,
+      currentUser,
       esScopedClient,
       this.logger,
       packageService