Skip to content

Commit

Permalink
[8.x] [Fleet] Change uninstall tokens space when changing agent polic…
Browse files Browse the repository at this point in the history
…ies spaces (#199536) (#199793)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[Fleet] Change uninstall tokens space when changing agent policies
spaces (#199536)](#199536)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Nicolas
Chaulet","email":"[email protected]"},"sourceCommit":{"committedDate":"2024-11-12T13:28:54Z","message":"[Fleet]
Change uninstall tokens space when changing agent policies spaces
(#199536)","sha":"f9e8aa07b79e6d81d691a4f166c04f74335fdf7f","branchLabelMapping":{"^v9.0.0$":"main","^v8.17.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","Team:Fleet","v9.0.0","backport:prev-minor"],"title":"[Fleet]
Change uninstall tokens space when changing agent policies
spaces","number":199536,"url":"https://github.com/elastic/kibana/pull/199536","mergeCommit":{"message":"[Fleet]
Change uninstall tokens space when changing agent policies spaces
(#199536)","sha":"f9e8aa07b79e6d81d691a4f166c04f74335fdf7f"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/199536","number":199536,"mergeCommit":{"message":"[Fleet]
Change uninstall tokens space when changing agent policies spaces
(#199536)","sha":"f9e8aa07b79e6d81d691a4f166c04f74335fdf7f"}}]}]
BACKPORT-->

Co-authored-by: Nicolas Chaulet <[email protected]>
Co-authored-by: Elastic Machine <[email protected]>
  • Loading branch information
3 people authored Dec 10, 2024
1 parent 826d30d commit 58421bf
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 26 deletions.
1 change: 1 addition & 0 deletions x-pack/plugins/fleet/server/services/app_context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ class AppContextService {
// soClient as kibana internal users, be careful on how you use it, security is not enabled
return appContextService.getSavedObjects().getScopedClient(fakeRequest, {
excludedExtensions: [SECURITY_EXTENSION_ID, SPACES_EXTENSION_ID],
includedHiddenTypes: [UNINSTALL_TOKENS_SAVED_OBJECT_TYPE],
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ import { appContextService } from '../../app_context';
import { agentPolicyService, getAgentPolicySavedObjectType } from '../../agent_policy';
import { isSpaceAwarenessEnabled } from '../../spaces/helpers';

interface UninstallTokenSOAttributes {
export interface UninstallTokenSOAttributes {
policy_id: string;
token: string;
token_plain: string;
Expand Down
28 changes: 28 additions & 0 deletions x-pack/plugins/fleet/server/services/spaces/agent_policy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,22 @@ describe('updateAgentPolicySpaces', () => {
jest
.mocked(appContextService.getInternalUserSOClientWithoutSpaceExtension())
.updateObjectsSpaces.mockResolvedValue({ objects: [] });

jest
.mocked(appContextService.getInternalUserSOClientWithoutSpaceExtension())
.find.mockResolvedValue({
total: 1,
page: 1,
per_page: 100,
saved_objects: [
{
id: 'token1',
attributes: {
namespaces: ['default'],
},
} as any,
],
});
});

it('does nothings if agent policy already in correct space', async () => {
Expand Down Expand Up @@ -87,6 +103,18 @@ describe('updateAgentPolicySpaces', () => {
['default'],
{ namespace: 'default', refresh: 'wait_for' }
);

expect(
jest.mocked(appContextService.getInternalUserSOClientWithoutSpaceExtension()).bulkUpdate
).toBeCalledWith([
{
id: 'token1',
type: 'fleet-uninstall-tokens',
attributes: {
namespaces: ['test'],
},
},
]);
});

it('throw when trying to change space to a policy with reusable package policies', async () => {
Expand Down
22 changes: 22 additions & 0 deletions x-pack/plugins/fleet/server/services/spaces/agent_policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import {
AGENTS_INDEX,
AGENT_POLICY_SAVED_OBJECT_TYPE,
PACKAGE_POLICY_SAVED_OBJECT_TYPE,
SO_SEARCH_LIMIT,
UNINSTALL_TOKENS_SAVED_OBJECT_TYPE,
} from '../../../common/constants';

import { appContextService } from '../app_context';
Expand All @@ -22,6 +24,7 @@ import { packagePolicyService } from '../package_policy';
import { FleetError, HostedAgentPolicyRestrictionRelatedError } from '../../errors';

import { isSpaceAwarenessEnabled } from './helpers';
import type { UninstallTokenSOAttributes } from '../security/uninstall_token_service';

export async function updateAgentPolicySpaces({
agentPolicyId,
Expand Down Expand Up @@ -112,6 +115,25 @@ export async function updateAgentPolicySpaces({
}
}

// Update uninstall tokens
const uninstallTokensRes = await soClient.find<UninstallTokenSOAttributes>({
perPage: SO_SEARCH_LIMIT,
type: UNINSTALL_TOKENS_SAVED_OBJECT_TYPE,
filter: `${UNINSTALL_TOKENS_SAVED_OBJECT_TYPE}.attributes.policy_id:"${agentPolicyId}"`,
});

if (uninstallTokensRes.total > 0) {
await soClient.bulkUpdate(
uninstallTokensRes.saved_objects.map((so) => ({
id: so.id,
type: UNINSTALL_TOKENS_SAVED_OBJECT_TYPE,
attributes: {
namespaces: newSpaceIds,
},
}))
);
}

// Update fleet server index agents, enrollment api keys
await esClient.updateByQuery({
index: ENROLLMENT_API_KEYS_INDEX,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,18 +109,29 @@ export default function (providerContext: FtrProviderContext) {
})
.catch(() => {});
});
async function assertPolicyAvailableInSpace(spaceId?: string) {
await apiClient.getAgentPolicy(defaultSpacePolicy1.item.id, spaceId);

async function assertPackagePolicyAvailableInSpace(spaceId?: string) {
await apiClient.getPackagePolicy(defaultPackagePolicy1.item.id, spaceId);
}

async function assertPackagePolicyNotAvailableInSpace(spaceId?: string) {
await expectToRejectWithNotFound(() =>
apiClient.getPackagePolicy(defaultPackagePolicy1.item.id, spaceId)
);
}

async function assertAgentPolicyAvailableInSpace(policyId: string, spaceId?: string) {
await apiClient.getAgentPolicy(policyId, spaceId);
const enrollmentApiKeys = await apiClient.getEnrollmentApiKeys(spaceId);
expect(
enrollmentApiKeys.items.find((item) => item.policy_id === defaultSpacePolicy1.item.id)
).not.to.be(undefined);
expect(enrollmentApiKeys.items.find((item) => item.policy_id === policyId)).not.to.be(
undefined
);

const agents = await apiClient.getAgents(spaceId);
expect(
agents.items.filter((a) => a.policy_id === defaultSpacePolicy1.item.id).length
).to.be(1);
expect(agents.items.filter((a) => a.policy_id === policyId).length).to.be(1);

const uninstallTokens = await apiClient.getUninstallTokens(spaceId);
expect(uninstallTokens.items.filter((t) => t.policy_id === policyId).length).to.be(1);
}

async function assertEnrollemntApiKeysForSpace(spaceId?: string, policyIds?: string[]) {
Expand All @@ -136,23 +147,19 @@ export default function (providerContext: FtrProviderContext) {
expect([...foundPolicyIds].sort()).to.eql(policyIds?.sort());
}

async function assertPolicyNotAvailableInSpace(spaceId?: string) {
await expectToRejectWithNotFound(() =>
apiClient.getPackagePolicy(defaultPackagePolicy1.item.id, spaceId)
);
await expectToRejectWithNotFound(() =>
apiClient.getAgentPolicy(defaultSpacePolicy1.item.id, spaceId)
);
async function assertAgentPolicyNotAvailableInSpace(policyId: string, spaceId?: string) {
await expectToRejectWithNotFound(() => apiClient.getAgentPolicy(policyId, spaceId));

const enrollmentApiKeys = await apiClient.getEnrollmentApiKeys(spaceId);
expect(
enrollmentApiKeys.items.find((item) => item.policy_id === defaultSpacePolicy1.item.id)
).to.be(undefined);
expect(enrollmentApiKeys.items.find((item) => item.policy_id === policyId)).to.be(
undefined
);

const agents = await apiClient.getAgents(spaceId);
expect(
agents.items.filter((a) => a.policy_id === defaultSpacePolicy1.item.id).length
).to.be(0);
expect(agents.items.filter((a) => a.policy_id === policyId).length).to.be(0);

const uninstallTokens = await apiClient.getUninstallTokens(spaceId);
expect(uninstallTokens.items.filter((t) => t.policy_id === policyId).length).to.be(0);
}

async function assertAgentSpaces(agentId: string, expectedSpaces: string[]) {
Expand All @@ -173,8 +180,11 @@ export default function (providerContext: FtrProviderContext) {
space_ids: ['default', TEST_SPACE_1],
});

await assertPolicyAvailableInSpace();
await assertPolicyAvailableInSpace(TEST_SPACE_1);
await assertAgentPolicyAvailableInSpace(defaultSpacePolicy1.item.id);
await assertAgentPolicyAvailableInSpace(defaultSpacePolicy1.item.id, TEST_SPACE_1);

await assertPackagePolicyAvailableInSpace();
await assertPackagePolicyAvailableInSpace(TEST_SPACE_1);

await assertAgentSpaces(policy1AgentId, ['default', TEST_SPACE_1]);
await assertAgentSpaces(policy2AgentId, ['default']);
Expand All @@ -184,6 +194,9 @@ export default function (providerContext: FtrProviderContext) {
defaultSpacePolicy2.item.id,
]);
await assertEnrollemntApiKeysForSpace(TEST_SPACE_1, [defaultSpacePolicy1.item.id]);
// Ensure no side effect on other policies
await assertAgentPolicyAvailableInSpace(defaultSpacePolicy2.item.id);
await assertAgentPolicyNotAvailableInSpace(defaultSpacePolicy2.item.id, TEST_SPACE_1);
});

it('should allow set policy in test space only', async () => {
Expand All @@ -194,12 +207,17 @@ export default function (providerContext: FtrProviderContext) {
space_ids: [TEST_SPACE_1],
});

await assertPolicyNotAvailableInSpace();
await assertPolicyAvailableInSpace(TEST_SPACE_1);
await assertAgentPolicyNotAvailableInSpace(defaultSpacePolicy1.item.id);
await assertAgentPolicyAvailableInSpace(defaultSpacePolicy1.item.id, TEST_SPACE_1);
await assertPackagePolicyAvailableInSpace(TEST_SPACE_1);
await assertPackagePolicyNotAvailableInSpace();
await assertAgentSpaces(policy1AgentId, [TEST_SPACE_1]);
await assertAgentSpaces(policy2AgentId, ['default']);
await assertEnrollemntApiKeysForSpace('default', [defaultSpacePolicy2.item.id]);
await assertEnrollemntApiKeysForSpace(TEST_SPACE_1, [defaultSpacePolicy1.item.id]);
// Ensure no side effect on other policies
await assertAgentPolicyAvailableInSpace(defaultSpacePolicy2.item.id);
await assertAgentPolicyNotAvailableInSpace(defaultSpacePolicy2.item.id, TEST_SPACE_1);
});

it('should not allow add policy to a space where user do not have access', async () => {
Expand Down

0 comments on commit 58421bf

Please sign in to comment.