diff --git a/x-pack/plugins/security/server/session_management/session_index.test.ts b/x-pack/plugins/security/server/session_management/session_index.test.ts index d0fb5a5d70f5e..c0fc69041c303 100644 --- a/x-pack/plugins/security/server/session_management/session_index.test.ts +++ b/x-pack/plugins/security/server/session_management/session_index.test.ts @@ -96,7 +96,94 @@ describe('Session index', () => { expect(mockElasticsearchClient.indices.deleteTemplate).not.toHaveBeenCalled(); expect(mockElasticsearchClient.indices.putIndexTemplate).not.toHaveBeenCalled(); + expect(mockElasticsearchClient.indices.create).not.toHaveBeenCalled(); + }); + + it('does not create index if alias exists', async () => { + mockElasticsearchClient.indices.existsTemplate.mockResponse(false); + mockElasticsearchClient.indices.existsIndexTemplate.mockResponse(true); + mockElasticsearchClient.indices.exists.mockImplementation( + async ({ index }) => index === aliasName + ); + + await sessionIndex.initialize(); + + expect(mockElasticsearchClient.indices.exists).toHaveBeenCalledWith({ index: aliasName }); + expect(mockElasticsearchClient.indices.exists).toHaveBeenCalledTimes(1); + expect(mockElasticsearchClient.indices.putAlias).not.toHaveBeenCalled(); + + expect(mockElasticsearchClient.indices.deleteTemplate).not.toHaveBeenCalled(); + expect(mockElasticsearchClient.indices.putIndexTemplate).not.toHaveBeenCalled(); + expect(mockElasticsearchClient.indices.create).not.toHaveBeenCalled(); + }); + + it('does not create index if index exists', async () => { + mockElasticsearchClient.indices.existsTemplate.mockResponse(false); + mockElasticsearchClient.indices.existsIndexTemplate.mockResponse(true); + mockElasticsearchClient.indices.exists.mockImplementation( + async ({ index }) => index === indexName + ); + + await sessionIndex.initialize(); + + expect(mockElasticsearchClient.indices.exists).toHaveBeenCalledWith({ index: aliasName }); + expect(mockElasticsearchClient.indices.exists).toHaveBeenCalledWith({ index: indexName }); + expect(mockElasticsearchClient.indices.exists).toHaveBeenCalledTimes(2); + + expect(mockElasticsearchClient.indices.deleteTemplate).not.toHaveBeenCalled(); + expect(mockElasticsearchClient.indices.putIndexTemplate).not.toHaveBeenCalled(); + expect(mockElasticsearchClient.indices.create).not.toHaveBeenCalled(); + }); + + it('creates index if neither index or alias exists', async () => { + mockElasticsearchClient.indices.existsTemplate.mockResponse(false); + mockElasticsearchClient.indices.existsIndexTemplate.mockResponse(true); + mockElasticsearchClient.indices.exists.mockResponse(false); + + await sessionIndex.initialize(); + + expect(mockElasticsearchClient.indices.exists).toHaveBeenCalledWith({ index: aliasName }); + expect(mockElasticsearchClient.indices.exists).toHaveBeenCalledWith({ index: indexName }); + expect(mockElasticsearchClient.indices.exists).toHaveBeenCalledTimes(2); + expect(mockElasticsearchClient.indices.putAlias).not.toHaveBeenCalled(); + + expect(mockElasticsearchClient.indices.deleteTemplate).not.toHaveBeenCalled(); + expect(mockElasticsearchClient.indices.putIndexTemplate).not.toHaveBeenCalled(); + + expect(mockElasticsearchClient.indices.create).toHaveBeenCalled(); + }); + + it('attaches alias if no alias present', async () => { + mockElasticsearchClient.indices.existsTemplate.mockResponse(false); + mockElasticsearchClient.indices.existsIndexTemplate.mockResponse(true); + mockElasticsearchClient.indices.exists.mockImplementation( + async ({ index }) => index === indexName + ); + mockElasticsearchClient.indices.existsAlias.mockResponse(false); + + await sessionIndex.initialize(); + + expect(mockElasticsearchClient.indices.existsAlias).toHaveBeenCalledWith({ name: aliasName }); expect(mockElasticsearchClient.indices.putAlias).toHaveBeenCalledTimes(1); + expect(mockElasticsearchClient.indices.putAlias).toHaveBeenCalledWith({ + index: indexName, + name: aliasName, + }); + + expect(mockElasticsearchClient.indices.create).not.toHaveBeenCalled(); + }); + + it('does not attach alias if alias present', async () => { + mockElasticsearchClient.indices.existsTemplate.mockResponse(false); + mockElasticsearchClient.indices.existsIndexTemplate.mockResponse(true); + mockElasticsearchClient.indices.exists.mockResponse(true); + mockElasticsearchClient.indices.existsAlias.mockResponse(true); + + await sessionIndex.initialize(); + + expect(mockElasticsearchClient.indices.existsAlias).toHaveBeenCalledWith({ name: aliasName }); + expect(mockElasticsearchClient.indices.putAlias).not.toHaveBeenCalled(); + expect(mockElasticsearchClient.indices.create).not.toHaveBeenCalled(); }); @@ -116,7 +203,6 @@ describe('Session index', () => { expect(mockElasticsearchClient.indices.deleteTemplate).not.toHaveBeenCalled(); expect(mockElasticsearchClient.indices.deleteIndexTemplate).not.toHaveBeenCalled(); - expect(mockElasticsearchClient.indices.putAlias).not.toHaveBeenCalled(); expect(mockElasticsearchClient.indices.getMapping).not.toHaveBeenCalled(); expect(mockElasticsearchClient.indices.putMapping).not.toHaveBeenCalled(); expect(mockElasticsearchClient.indices.create).toHaveBeenCalledWith( @@ -140,7 +226,6 @@ describe('Session index', () => { expect(mockElasticsearchClient.indices.deleteTemplate).not.toHaveBeenCalled(); expect(mockElasticsearchClient.indices.deleteIndexTemplate).not.toHaveBeenCalled(); - expect(mockElasticsearchClient.indices.putAlias).not.toHaveBeenCalled(); expect(mockElasticsearchClient.indices.getMapping).not.toHaveBeenCalled(); expect(mockElasticsearchClient.indices.putMapping).not.toHaveBeenCalled(); expect(mockElasticsearchClient.indices.create).toHaveBeenCalledWith( @@ -162,7 +247,6 @@ describe('Session index', () => { expect(mockElasticsearchClient.indices.create).toHaveBeenCalledWith( getSessionIndexSettings({ indexName, aliasName }) ); - expect(mockElasticsearchClient.indices.putAlias).not.toHaveBeenCalled(); }); it('deletes legacy & modern index templates if needed and creates index if it does not exist', async () => { @@ -182,7 +266,6 @@ describe('Session index', () => { expect(mockElasticsearchClient.indices.create).toHaveBeenCalledWith( getSessionIndexSettings({ indexName, aliasName }) ); - expect(mockElasticsearchClient.indices.putAlias).not.toHaveBeenCalled(); }); it('deletes modern index template if needed and creates index if it does not exist', async () => { @@ -197,7 +280,6 @@ describe('Session index', () => { expect(mockElasticsearchClient.indices.deleteIndexTemplate).toHaveBeenCalledWith({ name: indexTemplateName, }); - expect(mockElasticsearchClient.indices.putAlias).not.toHaveBeenCalled(); expect(mockElasticsearchClient.indices.create).toHaveBeenCalledWith( getSessionIndexSettings({ indexName, aliasName }) ); @@ -215,12 +297,6 @@ describe('Session index', () => { expect(mockElasticsearchClient.indices.deleteTemplate).not.toHaveBeenCalled(); expect(mockElasticsearchClient.indices.deleteIndexTemplate).not.toHaveBeenCalled(); expect(mockElasticsearchClient.indices.create).not.toHaveBeenCalled(); - - expect(mockElasticsearchClient.indices.putAlias).toHaveBeenCalledTimes(1); - expect(mockElasticsearchClient.indices.putAlias).toHaveBeenCalledWith({ - index: indexName, - name: aliasName, - }); }); it('updates mappings for existing index without version in the meta', async () => { @@ -241,11 +317,6 @@ describe('Session index', () => { expect(mockElasticsearchClient.indices.deleteIndexTemplate).not.toHaveBeenCalled(); expect(mockElasticsearchClient.indices.create).not.toHaveBeenCalled(); - expect(mockElasticsearchClient.indices.putAlias).toHaveBeenCalledTimes(1); - expect(mockElasticsearchClient.indices.putAlias).toHaveBeenCalledWith({ - index: indexName, - name: aliasName, - }); expect(mockElasticsearchClient.indices.getMapping).toHaveBeenCalledTimes(1); expect(mockElasticsearchClient.indices.getMapping).toHaveBeenCalledWith({ index: indexName }); expect(mockElasticsearchClient.indices.putMapping).toHaveBeenCalledTimes(1); @@ -273,11 +344,6 @@ describe('Session index', () => { expect(mockElasticsearchClient.indices.deleteIndexTemplate).not.toHaveBeenCalled(); expect(mockElasticsearchClient.indices.create).not.toHaveBeenCalled(); - expect(mockElasticsearchClient.indices.putAlias).toHaveBeenCalledTimes(1); - expect(mockElasticsearchClient.indices.putAlias).toHaveBeenCalledWith({ - index: indexName, - name: aliasName, - }); expect(mockElasticsearchClient.indices.getMapping).toHaveBeenCalledTimes(1); expect(mockElasticsearchClient.indices.getMapping).toHaveBeenCalledWith({ index: indexName }); expect(mockElasticsearchClient.indices.putMapping).toHaveBeenCalledTimes(1); @@ -305,11 +371,6 @@ describe('Session index', () => { expect(mockElasticsearchClient.indices.deleteIndexTemplate).not.toHaveBeenCalled(); expect(mockElasticsearchClient.indices.create).not.toHaveBeenCalled(); - expect(mockElasticsearchClient.indices.putAlias).toHaveBeenCalledTimes(1); - expect(mockElasticsearchClient.indices.putAlias).toHaveBeenCalledWith({ - index: indexName, - name: aliasName, - }); expect(mockElasticsearchClient.indices.getMapping).toHaveBeenCalledTimes(1); expect(mockElasticsearchClient.indices.getMapping).toHaveBeenCalledWith({ index: indexName }); expect(mockElasticsearchClient.indices.putMapping).not.toHaveBeenCalled(); @@ -325,7 +386,6 @@ describe('Session index', () => { assertExistenceChecksPerformed(); expect(mockElasticsearchClient.indices.deleteTemplate).not.toHaveBeenCalled(); expect(mockElasticsearchClient.indices.deleteIndexTemplate).not.toHaveBeenCalled(); - expect(mockElasticsearchClient.indices.putAlias).not.toHaveBeenCalled(); expect(mockElasticsearchClient.indices.getMapping).not.toHaveBeenCalled(); expect(mockElasticsearchClient.indices.putMapping).not.toHaveBeenCalled(); expect(mockElasticsearchClient.indices.create).toHaveBeenCalledWith( @@ -462,7 +522,6 @@ describe('Session index', () => { expect(mockElasticsearchClient.indices.exists).toHaveBeenCalledTimes(1); expect(mockElasticsearchClient.indices.create).toHaveBeenCalledTimes(1); - expect(mockElasticsearchClient.indices.putAlias).not.toHaveBeenCalled(); expect(mockElasticsearchClient.search).toHaveBeenCalledTimes(1); expect(mockElasticsearchClient.bulk).toHaveBeenCalledTimes(1); expect(mockElasticsearchClient.closePointInTime).toHaveBeenCalledTimes(1); // since we attempted to delete sessions, we still refresh the index @@ -1445,7 +1504,6 @@ describe('Session index', () => { expect(mockElasticsearchClient.indices.exists).toHaveBeenCalledTimes(1); expect(mockElasticsearchClient.indices.create).toHaveBeenCalledTimes(1); - expect(mockElasticsearchClient.indices.putAlias).not.toHaveBeenCalled(); expect(mockElasticsearchClient.create).toHaveBeenCalledTimes(2); expect(mockElasticsearchClient.create).toHaveBeenNthCalledWith( @@ -1502,7 +1560,6 @@ describe('Session index', () => { expect(mockElasticsearchClient.indices.exists).not.toHaveBeenCalled(); expect(mockElasticsearchClient.indices.create).not.toHaveBeenCalled(); - expect(mockElasticsearchClient.indices.putAlias).not.toHaveBeenCalled(); expect(mockElasticsearchClient.create).toHaveBeenCalledTimes(1); expect(mockElasticsearchClient.create).toHaveBeenCalledWith( diff --git a/x-pack/plugins/security/server/session_management/session_index.ts b/x-pack/plugins/security/server/session_management/session_index.ts index b4bea1900c577..ee51aa84056c4 100644 --- a/x-pack/plugins/security/server/session_management/session_index.ts +++ b/x-pack/plugins/security/server/session_management/session_index.ts @@ -627,6 +627,22 @@ export class SessionIndex { ); } + private async attachAliasToIndex() { + // Prior to https://github.com/elastic/kibana/pull/134900, sessions would be written directly against the session index. + // Now, we write sessions against a new session index alias. This call ensures that the alias exists, and is attached to the index. + // This operation is safe to repeat, even if the alias already exists. This seems safer than retrieving the index details, and inspecting + // it to see if the alias already exists. + try { + await this.options.elasticsearchClient.indices.putAlias({ + index: this.indexName, + name: this.aliasName, + }); + } catch (err) { + this.options.logger.error(`Failed to attach alias to session index: ${err.message}`); + throw err; + } + } + /** * Creates the session index if it doesn't already exist. */ @@ -667,24 +683,22 @@ export class SessionIndex { return; } - this.options.logger.debug( - 'Session index already exists. Attaching alias to the index and ensuring up-to-date mappings...' - ); + const isIndexNameAlias = await this.options.elasticsearchClient.indices.existsAlias({ + name: this.aliasName, + }); - // Prior to https://github.com/elastic/kibana/pull/134900, sessions would be written directly against the session index. - // Now, we write sessions against a new session index alias. This call ensures that the alias exists, and is attached to the index. - // This operation is safe to repeat, even if the alias already exists. This seems safer than retrieving the index details, and inspecting - // it to see if the alias already exists. - try { - await this.options.elasticsearchClient.indices.putAlias({ - index: this.indexName, - name: this.aliasName, - }); - } catch (err) { - this.options.logger.error(`Failed to attach alias to session index: ${err.message}`); - throw err; + if (!isIndexNameAlias) { + this.options.logger.debug( + 'Session index already exists with no alias. Attaching alias to the index.' + ); + + await this.attachAliasToIndex(); } + this.options.logger.debug( + 'Session index already exists. Ensuring up-to-date index mappings...' + ); + let indexMappingsVersion: string | undefined; try { const indexMappings = await this.options.elasticsearchClient.indices.getMapping({