Skip to content

Commit

Permalink
[8.x] [Rules migration] Use user profile UID instead of username (#20…
Browse files Browse the repository at this point in the history
…6299) (#206378)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[Rules migration] Use user profile UID instead of username
(#206299)](#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":"[email protected]"},"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 <[email protected]>
  • Loading branch information
kibanamachine and e40pud authored Jan 12, 2025
1 parent a71eb6a commit eedcc7d
Show file tree
Hide file tree
Showing 7 changed files with 63 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,41 @@ 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';

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>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,23 @@
*/

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
) {}

async create(lookupName: string, data: LookupData): Promise<string> {
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' },
Expand All @@ -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}`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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[];

Expand All @@ -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,
Expand All @@ -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();
Expand All @@ -83,7 +85,7 @@ export class RuleMigrationsDataResourcesClient extends RuleMigrationsDataBaseCli
{
...resource,
'@timestamp': createdAt,
updated_by: this.username,
updated_by: profileId,
updated_at: createdAt,
},
]),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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,
},
]),
Expand All @@ -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();
Expand All @@ -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,
},
},
Expand Down Expand Up @@ -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
Expand All @@ -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() },
},
]),
})
Expand All @@ -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) => {
Expand All @@ -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) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ export class RuleMigrationsDataService {

return new RuleMigrationsDataClient(
indexNameProviders,
currentUser.username,
currentUser,
esScopedClient,
this.logger,
packageService
Expand Down

0 comments on commit eedcc7d

Please sign in to comment.