Skip to content

Commit 52fba21

Browse files
legregojportner
andauthored
Introduce telemetry for security features (#74530)
Co-authored-by: Joe Portner <5295965+jportner@users.noreply.github.com>
1 parent e2cbd89 commit 52fba21

File tree

14 files changed

+706
-4
lines changed

14 files changed

+706
-4
lines changed

x-pack/plugins/security/common/licensing/index.mock.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { SecurityLicense } from '.';
99

1010
export const licenseMock = {
1111
create: (): jest.Mocked<SecurityLicense> => ({
12+
isLicenseAvailable: jest.fn(),
1213
isEnabled: jest.fn().mockReturnValue(true),
1314
getFeatures: jest.fn(),
1415
features$: of(),

x-pack/plugins/security/common/licensing/license_service.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ describe('license features', function () {
1313
const serviceSetup = new SecurityLicenseService().setup({
1414
license$: of(undefined as any),
1515
});
16+
expect(serviceSetup.license.isLicenseAvailable()).toEqual(false);
1617
expect(serviceSetup.license.getFeatures()).toEqual({
1718
showLogin: true,
1819
allowLogin: false,
@@ -34,6 +35,7 @@ describe('license features', function () {
3435
const serviceSetup = new SecurityLicenseService().setup({
3536
license$: of(rawLicenseMock),
3637
});
38+
expect(serviceSetup.license.isLicenseAvailable()).toEqual(false);
3739
expect(serviceSetup.license.getFeatures()).toEqual({
3840
showLogin: true,
3941
allowLogin: false,
@@ -60,6 +62,7 @@ describe('license features', function () {
6062
const subscriptionHandler = jest.fn();
6163
const subscription = serviceSetup.license.features$.subscribe(subscriptionHandler);
6264
try {
65+
expect(serviceSetup.license.isLicenseAvailable()).toEqual(false);
6366
expect(subscriptionHandler).toHaveBeenCalledTimes(1);
6467
expect(subscriptionHandler.mock.calls[0]).toMatchInlineSnapshot(`
6568
Array [
@@ -80,6 +83,7 @@ describe('license features', function () {
8083
`);
8184

8285
rawLicense$.next(licenseMock.createLicenseMock());
86+
expect(serviceSetup.license.isLicenseAvailable()).toEqual(true);
8387
expect(subscriptionHandler).toHaveBeenCalledTimes(2);
8488
expect(subscriptionHandler.mock.calls[1]).toMatchInlineSnapshot(`
8589
Array [
@@ -112,6 +116,7 @@ describe('license features', function () {
112116
const serviceSetup = new SecurityLicenseService().setup({
113117
license$: of(mockRawLicense),
114118
});
119+
expect(serviceSetup.license.isLicenseAvailable()).toEqual(true);
115120
expect(serviceSetup.license.getFeatures()).toEqual({
116121
showLogin: true,
117122
allowLogin: true,
@@ -136,6 +141,7 @@ describe('license features', function () {
136141
const serviceSetup = new SecurityLicenseService().setup({
137142
license$: of(mockRawLicense),
138143
});
144+
expect(serviceSetup.license.isLicenseAvailable()).toEqual(true);
139145
expect(serviceSetup.license.getFeatures()).toEqual({
140146
showLogin: false,
141147
allowLogin: false,
@@ -159,6 +165,7 @@ describe('license features', function () {
159165
const serviceSetup = new SecurityLicenseService().setup({
160166
license$: of(mockRawLicense),
161167
});
168+
expect(serviceSetup.license.isLicenseAvailable()).toEqual(true);
162169
expect(serviceSetup.license.getFeatures()).toEqual({
163170
showLogin: true,
164171
allowLogin: true,
@@ -182,6 +189,7 @@ describe('license features', function () {
182189
const serviceSetup = new SecurityLicenseService().setup({
183190
license$: of(mockRawLicense),
184191
});
192+
expect(serviceSetup.license.isLicenseAvailable()).toEqual(true);
185193
expect(serviceSetup.license.getFeatures()).toEqual({
186194
showLogin: true,
187195
allowLogin: true,
@@ -205,6 +213,7 @@ describe('license features', function () {
205213
const serviceSetup = new SecurityLicenseService().setup({
206214
license$: of(mockRawLicense),
207215
});
216+
expect(serviceSetup.license.isLicenseAvailable()).toEqual(true);
208217
expect(serviceSetup.license.getFeatures()).toEqual({
209218
showLogin: true,
210219
allowLogin: true,

x-pack/plugins/security/common/licensing/license_service.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { ILicense } from '../../../licensing/common/types';
1010
import { SecurityLicenseFeatures } from './license_features';
1111

1212
export interface SecurityLicense {
13+
isLicenseAvailable(): boolean;
1314
isEnabled(): boolean;
1415
getFeatures(): SecurityLicenseFeatures;
1516
features$: Observable<SecurityLicenseFeatures>;
@@ -31,6 +32,8 @@ export class SecurityLicenseService {
3132

3233
return {
3334
license: Object.freeze({
35+
isLicenseAvailable: () => rawLicense?.isAvailable ?? false,
36+
3437
isEnabled: () => this.isSecurityEnabledFromRawLicense(rawLicense),
3538

3639
getFeatures: () => this.calculateFeaturesFromRawLicense(rawLicense),

x-pack/plugins/security/kibana.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"kibanaVersion": "kibana",
55
"configPath": ["xpack", "security"],
66
"requiredPlugins": ["data", "features", "licensing", "taskManager"],
7-
"optionalPlugins": ["home", "management"],
7+
"optionalPlugins": ["home", "management", "usageCollection"],
88
"server": true,
99
"ui": true,
1010
"requiredBundles": [

x-pack/plugins/security/public/management/roles/edit_role/privileges/es/__snapshots__/elasticsearch_privileges.test.tsx.snap

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

x-pack/plugins/security/public/plugin.test.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ describe('Security Plugin', () => {
4141
__legacyCompat: { logoutUrl: '/some-base-path/logout', tenant: '/some-base-path' },
4242
authc: { getCurrentUser: expect.any(Function), areAPIKeysEnabled: expect.any(Function) },
4343
license: {
44+
isLicenseAvailable: expect.any(Function),
4445
isEnabled: expect.any(Function),
4546
getFeatures: expect.any(Function),
4647
features$: expect.any(Observable),
@@ -67,6 +68,7 @@ describe('Security Plugin', () => {
6768
expect(setupManagementServiceMock).toHaveBeenCalledWith({
6869
authc: { getCurrentUser: expect.any(Function), areAPIKeysEnabled: expect.any(Function) },
6970
license: {
71+
isLicenseAvailable: expect.any(Function),
7072
isEnabled: expect.any(Function),
7173
getFeatures: expect.any(Function),
7274
features$: expect.any(Observable),

x-pack/plugins/security/server/config.test.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -904,11 +904,13 @@ describe('createConfig()', () => {
904904
},
905905
"sortedProviders": Array [
906906
Object {
907+
"hasAccessAgreement": false,
907908
"name": "saml",
908909
"order": 0,
909910
"type": "saml",
910911
},
911912
Object {
913+
"hasAccessAgreement": false,
912914
"name": "basic",
913915
"order": 1,
914916
"type": "basic",
@@ -982,6 +984,63 @@ describe('createConfig()', () => {
982984
).toBe(true);
983985
});
984986

987+
it('indicates which providers have the access agreement enabled', () => {
988+
expect(
989+
createConfig(
990+
ConfigSchema.validate({
991+
authc: {
992+
providers: {
993+
basic: { basic1: { order: 3 } },
994+
saml: {
995+
saml1: { order: 2, realm: 'saml1', accessAgreement: { message: 'foo' } },
996+
saml2: { order: 1, realm: 'saml2' },
997+
},
998+
oidc: {
999+
oidc1: { order: 0, realm: 'oidc1', accessAgreement: { message: 'foo' } },
1000+
oidc2: { order: 4, realm: 'oidc2' },
1001+
},
1002+
},
1003+
},
1004+
}),
1005+
loggingSystemMock.create().get(),
1006+
{ isTLSEnabled: true }
1007+
).authc.sortedProviders
1008+
).toMatchInlineSnapshot(`
1009+
Array [
1010+
Object {
1011+
"hasAccessAgreement": true,
1012+
"name": "oidc1",
1013+
"order": 0,
1014+
"type": "oidc",
1015+
},
1016+
Object {
1017+
"hasAccessAgreement": false,
1018+
"name": "saml2",
1019+
"order": 1,
1020+
"type": "saml",
1021+
},
1022+
Object {
1023+
"hasAccessAgreement": true,
1024+
"name": "saml1",
1025+
"order": 2,
1026+
"type": "saml",
1027+
},
1028+
Object {
1029+
"hasAccessAgreement": false,
1030+
"name": "basic1",
1031+
"order": 3,
1032+
"type": "basic",
1033+
},
1034+
Object {
1035+
"hasAccessAgreement": false,
1036+
"name": "oidc2",
1037+
"order": 4,
1038+
"type": "oidc",
1039+
},
1040+
]
1041+
`);
1042+
});
1043+
9851044
it('correctly sorts providers based on the `order`', () => {
9861045
expect(
9871046
createConfig(
@@ -1000,26 +1059,31 @@ describe('createConfig()', () => {
10001059
).toMatchInlineSnapshot(`
10011060
Array [
10021061
Object {
1062+
"hasAccessAgreement": false,
10031063
"name": "oidc1",
10041064
"order": 0,
10051065
"type": "oidc",
10061066
},
10071067
Object {
1068+
"hasAccessAgreement": false,
10081069
"name": "saml2",
10091070
"order": 1,
10101071
"type": "saml",
10111072
},
10121073
Object {
1074+
"hasAccessAgreement": false,
10131075
"name": "saml1",
10141076
"order": 2,
10151077
"type": "saml",
10161078
},
10171079
Object {
1080+
"hasAccessAgreement": false,
10181081
"name": "basic1",
10191082
"order": 3,
10201083
"type": "basic",
10211084
},
10221085
Object {
1086+
"hasAccessAgreement": false,
10231087
"name": "oidc2",
10241088
"order": 4,
10251089
"type": "oidc",

x-pack/plugins/security/server/config.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,13 +255,19 @@ export function createConfig(
255255
type: keyof ProvidersConfigType;
256256
name: string;
257257
order: number;
258+
hasAccessAgreement: boolean;
258259
}> = [];
259260
for (const [type, providerGroup] of Object.entries(providers)) {
260-
for (const [name, { enabled, order }] of Object.entries(providerGroup ?? {})) {
261+
for (const [name, { enabled, order, accessAgreement }] of Object.entries(providerGroup ?? {})) {
261262
if (!enabled) {
262263
delete providerGroup![name];
263264
} else {
264-
sortedProviders.push({ type: type as any, name, order });
265+
sortedProviders.push({
266+
type: type as any,
267+
name,
268+
order,
269+
hasAccessAgreement: !!accessAgreement?.message,
270+
});
265271
}
266272
}
267273
}

x-pack/plugins/security/server/plugin.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ describe('Security Plugin', () => {
108108
},
109109
"getFeatures": [Function],
110110
"isEnabled": [Function],
111+
"isLicenseAvailable": [Function],
111112
},
112113
"registerSpacesService": [Function],
113114
}

x-pack/plugins/security/server/plugin.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import { combineLatest } from 'rxjs';
88
import { first, map } from 'rxjs/operators';
99
import { TypeOf } from '@kbn/config-schema';
10+
import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
1011
import {
1112
deepFreeze,
1213
CoreSetup,
@@ -32,6 +33,7 @@ import { AuditService, SecurityAuditLogger, AuditServiceSetup } from './audit';
3233
import { SecurityFeatureUsageService, SecurityFeatureUsageServiceStart } from './feature_usage';
3334
import { ElasticsearchService } from './elasticsearch';
3435
import { SessionManagementService } from './session_management';
36+
import { registerSecurityUsageCollector } from './usage_collector';
3537

3638
export type SpacesService = Pick<
3739
SpacesPluginSetup['spacesService'],
@@ -74,6 +76,7 @@ export interface PluginSetupDependencies {
7476
features: FeaturesPluginSetup;
7577
licensing: LicensingPluginSetup;
7678
taskManager: TaskManagerSetupContract;
79+
usageCollection?: UsageCollectionSetup;
7780
}
7881

7982
export interface PluginStartDependencies {
@@ -123,7 +126,7 @@ export class Plugin {
123126

124127
public async setup(
125128
core: CoreSetup<PluginStartDependencies>,
126-
{ features, licensing, taskManager }: PluginSetupDependencies
129+
{ features, licensing, taskManager, usageCollection }: PluginSetupDependencies
127130
) {
128131
const [config, legacyConfig] = await combineLatest([
129132
this.initializerContext.config.create<TypeOf<typeof ConfigSchema>>().pipe(
@@ -151,6 +154,8 @@ export class Plugin {
151154

152155
this.featureUsageService.setup({ featureUsage: licensing.featureUsage });
153156

157+
registerSecurityUsageCollector({ usageCollection, config, license });
158+
154159
const audit = this.auditService.setup({ license, config: config.audit });
155160
const auditLogger = new SecurityAuditLogger(audit.getLogger());
156161

0 commit comments

Comments
 (0)