Skip to content

Commit

Permalink
[Fleet] Change uninstall tokens space when changing agent policies sp…
Browse files Browse the repository at this point in the history
…aces (elastic#199536)
  • Loading branch information
nchaulet authored Nov 12, 2024
1 parent 7e06f55 commit f9e8aa0
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 @@ -240,6 +240,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 f9e8aa0

Please sign in to comment.