Skip to content

Commit

Permalink
[Rules migration] Use user profile UID instead of username (elastic#2…
Browse files Browse the repository at this point in the history
…06299)

## Summary

[Internal link](elastic/security-team#10820)
to the feature details

With these changes we switch to using user's `profile_id` instead of
`username` as a user identification for the migration rules create and
update operations.
  • Loading branch information
e40pud authored Jan 12, 2025
1 parent 52cda77 commit a1f8532
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 a1f8532

Please sign in to comment.