Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fleet] Change uninstall tokens space when changing agent policies spaces #199536

Merged
merged 3 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading