Skip to content

Commit

Permalink
Add session timeout usage data
Browse files Browse the repository at this point in the history
  • Loading branch information
jportner committed Aug 31, 2021
1 parent bc1842a commit f7730fc
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 62 deletions.
11 changes: 9 additions & 2 deletions x-pack/plugins/security/server/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -391,11 +391,18 @@ export function createConfig(
function getSessionConfig(session: RawConfigType['session'], providers: ProvidersConfigType) {
return {
cleanupInterval: session.cleanupInterval,
getExpirationTimeouts({ type, name }: AuthenticationProvider) {
getExpirationTimeouts(provider?: AuthenticationProvider) {
// Both idle timeout and lifespan from the provider specific session config can have three
// possible types of values: `Duration`, `null` and `undefined`. The `undefined` type means that
// provider doesn't override session config and we should fall back to the global one instead.
const providerSessionConfig = providers[type as keyof ProvidersConfigType]?.[name]?.session;
// Note: using an `undefined` provider argument returns the global timeouts.
let providerSessionConfig:
| { idleTimeout?: Duration | null; lifespan?: Duration | null }
| undefined;
if (provider) {
const { type, name } = provider;
providerSessionConfig = providers[type as keyof ProvidersConfigType]?.[name]?.session;
}
const [idleTimeout, lifespan] = [
[session.idleTimeout, providerSessionConfig?.idleTimeout],
[session.lifespan, providerSessionConfig?.lifespan],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@ describe('Security UsageCollector', () => {
};

const collectorFetchContext = createCollectorFetchContextMock();
const DEFAULT_USAGE = {
auditLoggingEnabled: false,
accessAgreementEnabled: false,
authProviderCount: 1,
enabledAuthProviders: ['basic'],
loginSelectorEnabled: false,
httpAuthSchemes: ['apikey'],
sessionIdleTimeoutMinutes: 60,
sessionLifespanMinutes: 43200,
};

describe('initialization', () => {
it('handles an undefined usage collector', () => {
Expand Down Expand Up @@ -75,14 +85,7 @@ describe('Security UsageCollector', () => {
.getCollectorByType('security')
?.fetch(collectorFetchContext);

expect(usage).toEqual({
auditLoggingEnabled: false,
accessAgreementEnabled: false,
authProviderCount: 1,
enabledAuthProviders: ['basic'],
loginSelectorEnabled: false,
httpAuthSchemes: ['apikey'],
});
expect(usage).toEqual(DEFAULT_USAGE);
});

it('reports correctly when security is disabled in Elasticsearch', async () => {
Expand All @@ -103,6 +106,8 @@ describe('Security UsageCollector', () => {
enabledAuthProviders: [],
loginSelectorEnabled: false,
httpAuthSchemes: [],
sessionIdleTimeoutMinutes: 0,
sessionLifespanMinutes: 0,
});
});

Expand Down Expand Up @@ -140,14 +145,7 @@ describe('Security UsageCollector', () => {
.getCollectorByType('security')
?.fetch(collectorFetchContext);

expect(usage).toEqual({
auditLoggingEnabled: false,
accessAgreementEnabled: false,
authProviderCount: 1,
enabledAuthProviders: ['basic'],
loginSelectorEnabled: false,
httpAuthSchemes: ['apikey'],
});
expect(usage).toEqual(DEFAULT_USAGE);
});

it('reports the types and count of enabled auth providers', async () => {
Expand Down Expand Up @@ -190,12 +188,10 @@ describe('Security UsageCollector', () => {
?.fetch(collectorFetchContext);

expect(usage).toEqual({
auditLoggingEnabled: false,
accessAgreementEnabled: false,
...DEFAULT_USAGE,
authProviderCount: 3,
enabledAuthProviders: ['saml', 'pki'],
loginSelectorEnabled: true,
httpAuthSchemes: ['apikey'],
});
});
});
Expand Down Expand Up @@ -228,12 +224,9 @@ describe('Security UsageCollector', () => {
?.fetch(collectorFetchContext);

expect(usage).toEqual({
auditLoggingEnabled: false,
...DEFAULT_USAGE,
accessAgreementEnabled: true,
authProviderCount: 1,
enabledAuthProviders: ['saml'],
loginSelectorEnabled: false,
httpAuthSchemes: ['apikey'],
});
});
it('does not report the access agreement if the license does not permit it', async () => {
Expand Down Expand Up @@ -266,12 +259,9 @@ describe('Security UsageCollector', () => {
?.fetch(collectorFetchContext);

expect(usage).toEqual({
auditLoggingEnabled: false,
...DEFAULT_USAGE,
accessAgreementEnabled: false,
authProviderCount: 1,
enabledAuthProviders: ['saml'],
loginSelectorEnabled: false,
httpAuthSchemes: ['apikey'],
});
});

Expand Down Expand Up @@ -307,12 +297,9 @@ describe('Security UsageCollector', () => {
?.fetch(collectorFetchContext);

expect(usage).toEqual({
auditLoggingEnabled: false,
...DEFAULT_USAGE,
accessAgreementEnabled: false,
authProviderCount: 1,
enabledAuthProviders: ['saml'],
loginSelectorEnabled: false,
httpAuthSchemes: ['apikey'],
});
});
});
Expand Down Expand Up @@ -346,12 +333,9 @@ describe('Security UsageCollector', () => {
?.fetch(collectorFetchContext);

expect(usage).toEqual({
auditLoggingEnabled: false,
accessAgreementEnabled: false,
authProviderCount: 1,
...DEFAULT_USAGE,
enabledAuthProviders: ['saml'],
loginSelectorEnabled: true,
httpAuthSchemes: ['apikey'],
});
});
});
Expand Down Expand Up @@ -379,13 +363,9 @@ describe('Security UsageCollector', () => {
?.fetch(collectorFetchContext);

expect(usage).toEqual({
...DEFAULT_USAGE,
auditLoggingEnabled: true,
auditLoggingType: 'legacy',
accessAgreementEnabled: false,
authProviderCount: 1,
enabledAuthProviders: ['basic'],
loginSelectorEnabled: false,
httpAuthSchemes: ['apikey'],
});
});

Expand All @@ -411,13 +391,9 @@ describe('Security UsageCollector', () => {
?.fetch(collectorFetchContext);

expect(usage).toEqual({
...DEFAULT_USAGE,
auditLoggingEnabled: true,
auditLoggingType: 'ecs',
accessAgreementEnabled: false,
authProviderCount: 1,
enabledAuthProviders: ['basic'],
loginSelectorEnabled: false,
httpAuthSchemes: ['apikey'],
});
});

Expand All @@ -438,12 +414,9 @@ describe('Security UsageCollector', () => {
?.fetch(collectorFetchContext);

expect(usage).toEqual({
...DEFAULT_USAGE,
auditLoggingEnabled: false,
accessAgreementEnabled: false,
authProviderCount: 1,
enabledAuthProviders: ['basic'],
loginSelectorEnabled: false,
httpAuthSchemes: ['apikey'],
auditLoggingType: undefined,
});
});
});
Expand All @@ -468,11 +441,7 @@ describe('Security UsageCollector', () => {
?.fetch(collectorFetchContext);

expect(usage).toEqual({
auditLoggingEnabled: false,
accessAgreementEnabled: false,
authProviderCount: 1,
enabledAuthProviders: ['basic'],
loginSelectorEnabled: false,
...DEFAULT_USAGE,
httpAuthSchemes: ['basic', 'Negotiate'],
});
});
Expand All @@ -496,13 +465,33 @@ describe('Security UsageCollector', () => {
?.fetch(collectorFetchContext);

expect(usage).toEqual({
auditLoggingEnabled: false,
accessAgreementEnabled: false,
authProviderCount: 1,
enabledAuthProviders: ['basic'],
loginSelectorEnabled: false,
...DEFAULT_USAGE,
httpAuthSchemes: ['basic', 'Negotiate'],
});
});
});

describe('session expirations', () => {
// Note: can't easily test deprecated 'sessionTimeout' value here because of the way that config deprecation renaming works
it('reports customized session idleTimeout and lifespan', async () => {
const config = createSecurityConfig(
ConfigSchema.validate({
session: { idleTimeout: '123m', lifespan: '456m' },
})
);
const usageCollection = usageCollectionPluginMock.createSetupContract();
const license = createSecurityLicense({ isLicenseAvailable: true, allowAuditLogging: false });
registerSecurityUsageCollector({ usageCollection, config, license });

const usage = await usageCollection
.getCollectorByType('security')
?.fetch(collectorFetchContext);

expect(usage).toEqual({
...DEFAULT_USAGE,
sessionIdleTimeoutMinutes: 123,
sessionLifespanMinutes: 456,
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ interface Usage {
authProviderCount: number;
enabledAuthProviders: string[];
httpAuthSchemes: string[];
sessionIdleTimeoutMinutes: number;
sessionLifespanMinutes: number;
}

interface Deps {
Expand Down Expand Up @@ -106,6 +108,20 @@ export function registerSecurityUsageCollector({ usageCollection, config, licens
},
},
},
sessionIdleTimeoutMinutes: {
type: 'long',
_meta: {
description:
'The global session idle timeout expiration that is configured, in minutes (0 if disabled).',
},
},
sessionLifespanMinutes: {
type: 'long',
_meta: {
description:
'The global session lifespan expiration that is configured, in minutes (0 if disabled).',
},
},
},
fetch: () => {
const {
Expand All @@ -122,6 +138,8 @@ export function registerSecurityUsageCollector({ usageCollection, config, licens
authProviderCount: 0,
enabledAuthProviders: [],
httpAuthSchemes: [],
sessionIdleTimeoutMinutes: 0,
sessionLifespanMinutes: 0,
};
}

Expand Down Expand Up @@ -154,6 +172,10 @@ export function registerSecurityUsageCollector({ usageCollection, config, licens
WELL_KNOWN_AUTH_SCHEMES.includes(scheme.toLowerCase())
);

const sessionExpirations = config.session.getExpirationTimeouts(); // get global expiration values
const sessionIdleTimeoutMinutes = sessionExpirations.idleTimeout?.asMinutes() ?? 0;
const sessionLifespanMinutes = sessionExpirations.lifespan?.asMinutes() ?? 0;

return {
auditLoggingEnabled: legacyAuditLoggingEnabled || ecsAuditLoggingEnabled,
auditLoggingType,
Expand All @@ -162,6 +184,8 @@ export function registerSecurityUsageCollector({ usageCollection, config, licens
authProviderCount,
enabledAuthProviders,
httpAuthSchemes,
sessionIdleTimeoutMinutes,
sessionLifespanMinutes,
};
},
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5496,6 +5496,18 @@
"description": "The set of enabled http auth schemes. Used for api-based usage, and when credentials are provided via reverse-proxy."
}
}
},
"sessionIdleTimeoutMinutes": {
"type": "long",
"_meta": {
"description": "The global session idle timeout expiration that is configured, in minutes (0 if disabled)."
}
},
"sessionLifespanMinutes": {
"type": "long",
"_meta": {
"description": "The global session lifespan expiration that is configured, in minutes (0 if disabled)."
}
}
}
},
Expand Down

0 comments on commit f7730fc

Please sign in to comment.