Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SECURITY] add telemetry on authentication type #119371

Closed
wants to merge 12 commits into from
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import { i18n } from '@kbn/i18n';
import type { CoreSetup, HttpSetup } from 'src/core/public';

import { SecurityUsageCollectionService } from '../../usage_collection';

interface CreateDeps {
application: CoreSetup['application'];
http: HttpSetup;
Expand All @@ -24,6 +26,7 @@ export const logoutApp = Object.freeze({
appRoute: '/logout',
async mount() {
window.sessionStorage.clear();
window.localStorage.removeItem(SecurityUsageCollectionService.KeyAuthType);

// Redirect user to the server logout endpoint to complete logout.
window.location.href = http.basePath.prepend(
Expand Down
12 changes: 12 additions & 0 deletions x-pack/plugins/security/public/plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { SecurityCheckupService } from './security_checkup';
import { SessionExpired, SessionTimeout, UnauthorizedResponseHttpInterceptor } from './session';
import type { UiApi } from './ui_api';
import { getUiApi } from './ui_api';
import { SecurityUsageCollectionService } from './usage_collection';

export interface PluginSetupDependencies {
licensing: LicensingPluginSetup;
Expand Down Expand Up @@ -63,6 +64,7 @@ export class SecurityPlugin
private readonly managementService = new ManagementService();
private readonly securityCheckupService: SecurityCheckupService;
private readonly anonymousAccessService = new AnonymousAccessService();
private readonly securityUsageCollectionService = new SecurityUsageCollectionService();
private authc!: AuthenticationServiceSetup;

constructor(private readonly initializerContext: PluginInitializerContext) {
Expand Down Expand Up @@ -102,6 +104,10 @@ export class SecurityPlugin
logoutUrl,
});

this.securityUsageCollectionService.setup({
securityLicense: license,
});

accountManagementApp.create({
authc: this.authc,
application: core.application,
Expand Down Expand Up @@ -164,6 +170,11 @@ export class SecurityPlugin
this.anonymousAccessService.start({ http: core.http });
}

this.securityUsageCollectionService.start({
http: core.http,
getCurrentUser: this.authc.getCurrentUser,
});

return {
uiApi: getUiApi({ core }),
navControlService: this.navControlService.start({ core }),
Expand All @@ -176,6 +187,7 @@ export class SecurityPlugin
this.navControlService.stop();
this.securityLicenseService.stop();
this.managementService.stop();
this.securityUsageCollectionService.stop();
}
}

Expand Down
77 changes: 77 additions & 0 deletions x-pack/plugins/security/public/usage_collection/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import type { Subscription } from 'rxjs';

import type { HttpStart } from 'src/core/public';

import type { AuthenticatedUser, SecurityLicense } from '../../common';

interface SecurityUsageCollectionSetupParams {
securityLicense: SecurityLicense;
}

interface SecurityUsageCollectionStartParams {
http: HttpStart;
getCurrentUser: () => Promise<AuthenticatedUser>;
}

interface SecurityUsageCollectionAuthType {
username_hash: string;
timestamp: number;
auth_type: string;
}

export class SecurityUsageCollectionService {
public static KeyAuthType = 'kibana-user-auth-type';
private securityLicense!: SecurityLicense;
private securityFeaturesSubscription?: Subscription;

public setup({ securityLicense }: SecurityUsageCollectionSetupParams) {
this.securityLicense = securityLicense;
}

public async start({ http, getCurrentUser }: SecurityUsageCollectionStartParams) {
// wait for the user to be authenticated before doing UsageCollection
this.securityFeaturesSubscription = this.securityLicense.features$.subscribe(
({ allowRbac }) => {
if (allowRbac) {
getCurrentUser().then(() => this.recordAuthTypeUsageCollection(http));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do we do in the event of a promise rejection? I think it's fine to ignore it, but we probably want to explicitly ignore it with a .catch block.

}
}
);
}

public stop() {
if (this.securityFeaturesSubscription) {
this.securityFeaturesSubscription.unsubscribe();
this.securityFeaturesSubscription = undefined;
}
}

private async recordAuthTypeUsageCollection(http: HttpStart) {
try {
const UsageCollectionAuthTypeStringify = localStorage.getItem(
SecurityUsageCollectionService.KeyAuthType
);
const UsageCollectionAuthTypeObj = await http.post<SecurityUsageCollectionAuthType | null>(
'/internal/security/usage_collection/record_auth_type',
{
body: UsageCollectionAuthTypeStringify,
}
);
if (UsageCollectionAuthTypeObj) {
localStorage.setItem(
SecurityUsageCollectionService.KeyAuthType,
JSON.stringify(UsageCollectionAuthTypeObj)
);
}
} catch (exp) {
// do nothing
}
}
}
1 change: 1 addition & 0 deletions x-pack/plugins/security/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@ export class SecurityPlugin
getFeatureUsageService: this.getFeatureUsageService,
getAuthenticationService: this.getAuthentication,
getAnonymousAccessService: this.getAnonymousAccess,
usageCounter: usageCollection?.createUsageCounter('security'),
});

return Object.freeze<SecurityPluginSetup>({
Expand Down
4 changes: 4 additions & 0 deletions x-pack/plugins/security/server/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type { Observable } from 'rxjs';

import type { PublicMethodsOf } from '@kbn/utility-types';
import type { HttpResources, IBasePath, Logger } from 'src/core/server';
import type { UsageCounter } from 'src/plugins/usage_collection/server';

import type { KibanaFeature } from '../../../features/server';
import type { SecurityLicense } from '../../common';
Expand All @@ -28,6 +29,7 @@ import { defineIndicesRoutes } from './indices';
import { defineRoleMappingRoutes } from './role_mapping';
import { defineSecurityCheckupGetStateRoutes } from './security_checkup';
import { defineSessionManagementRoutes } from './session_management';
import { defineUsageCollectionRoutes } from './usage_collection';
import { defineUsersRoutes } from './users';
import { defineViewRoutes } from './views';

Expand All @@ -48,6 +50,7 @@ export interface RouteDefinitionParams {
getFeatureUsageService: () => SecurityFeatureUsageServiceStart;
getAuthenticationService: () => InternalAuthenticationServiceStart;
getAnonymousAccessService: () => AnonymousAccessServiceStart;
usageCounter?: UsageCounter;
}

export function defineRoutes(params: RouteDefinitionParams) {
Expand All @@ -62,4 +65,5 @@ export function defineRoutes(params: RouteDefinitionParams) {
defineDeprecationsRoutes(params);
defineAnonymousAccessRoutes(params);
defineSecurityCheckupGetStateRoutes(params);
defineUsageCollectionRoutes(params);
}
Loading