Skip to content

Commit

Permalink
added test for global read user
Browse files Browse the repository at this point in the history
  • Loading branch information
gmmorris committed Sep 4, 2020
1 parent b226e32 commit 0714965
Show file tree
Hide file tree
Showing 7 changed files with 214 additions and 101 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "1.0.0",
"kibanaVersion": "kibana",
"configPath": ["xpack"],
"requiredPlugins": ["taskManager", "features", "actions", "alerts", "encryptedSavedObjects"],
"requiredPlugins": ["taskManager", "features", "actions", "alerts", "security", "encryptedSavedObjects"],
"optionalPlugins": ["spaces"],
"server": true,
"ui": false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ import { defineAlertTypes } from './alert_types';
import { defineActionTypes } from './action_types';
import { defineRoutes } from './routes';
import { SpacesPluginSetup } from '../../../../../../../plugins/spaces/server';
import { SecurityPluginSetup } from '../../../../../../../plugins/security/server';

export interface FixtureSetupDeps {
features: FeaturesPluginSetup;
actions: ActionsPluginSetup;
alerts: AlertingPluginSetup;
spaces?: SpacesPluginSetup;
security?: SecurityPluginSetup;
}

export interface FixtureStartDeps {
Expand All @@ -28,7 +30,7 @@ export interface FixtureStartDeps {
export class FixturePlugin implements Plugin<void, void, FixtureSetupDeps, FixtureStartDeps> {
public setup(
core: CoreSetup<FixtureStartDeps>,
{ features, actions, alerts, spaces }: FixtureSetupDeps
{ features, actions, alerts, spaces, security }: FixtureSetupDeps
) {
features.registerFeature({
id: 'alertsFixture',
Expand Down Expand Up @@ -102,7 +104,7 @@ export class FixturePlugin implements Plugin<void, void, FixtureSetupDeps, Fixtu

defineActionTypes(core, { actions });
defineAlertTypes(core, { alerts });
defineRoutes(core, { spaces });
defineRoutes(core, { spaces, security });
}

public start() {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,91 @@ import { FixtureSetupDeps, FixtureStartDeps } from './plugin';

export function defineRoutes(
core: CoreSetup<FixtureStartDeps>,
{ spaces }: Partial<FixtureSetupDeps>
{ spaces, security }: Partial<FixtureSetupDeps>
) {
const router = core.http.createRouter();
router.put(
{
path: '/api/alerts_fixture/{id}/replace_api_key',
validate: {
params: schema.object({
id: schema.string(),
}),
body: schema.object({
spaceId: schema.maybe(schema.string()),
}),
},
},
async (
context: RequestHandlerContext,
req: KibanaRequest<any, any, any, any>,
res: KibanaResponseFactory
): Promise<IKibanaResponse<any>> => {
const { id } = req.params;

if (!security) {
return res.ok({
body: {},
});
}

const [{ savedObjects }, { encryptedSavedObjects }] = await core.getStartServices();
const encryptedSavedObjectsWithAlerts = await encryptedSavedObjects.getClient({
includedHiddenTypes: ['alert'],
});
const savedObjectsWithAlerts = await savedObjects.getScopedClient(req, {
excludedWrappers: ['security', 'spaces'],
includedHiddenTypes: ['alert'],
});

let namespace: string | undefined;
if (spaces && req.body.spaceId) {
namespace = spaces.spacesService.spaceIdToNamespace(req.body.spaceId);
}

const user = await security.authc.getCurrentUser(req);
if (!user) {
return res.internalError({});
}

// Create an API key using the new grant API - in this case the Kibana system user is creating the
// API key for the user, instead of having the user create it themselves, which requires api_key
// privileges
const createAPIKeyResult = await security.authc.grantAPIKeyAsInternalUser(req, {
name: `alert:migrated-to-7.10:${user.username}`,
role_descriptors: {},
});

if (!createAPIKeyResult) {
return res.internalError({});
}

const result = await savedObjectsWithAlerts.update<RawAlert>(
'alert',
id,
{
...(
await encryptedSavedObjectsWithAlerts.getDecryptedAsInternalUser<RawAlert>(
'alert',
id,
{
namespace,
}
)
).attributes,
apiKey: Buffer.from(`${createAPIKeyResult.id}:${createAPIKeyResult.api_key}`).toString(
'base64'
),
apiKeyOwner: user.username,
},
{
namespace,
}
);
return res.ok({ body: result });
}
);

router.put(
{
path: '/api/alerts_fixture/saved_object/{type}/{id}',
Expand Down Expand Up @@ -94,66 +176,4 @@ export function defineRoutes(
return res.ok({ body: result });
}
);

router.put(
{
path: '/api/alerts_fixture/swap_api_keys/from/{apiKeyFromId}/to/{apiKeyToId}',
validate: {
params: schema.object({
apiKeyFromId: schema.string(),
apiKeyToId: schema.string(),
}),
body: schema.object({
spaceId: schema.maybe(schema.string()),
}),
},
},
async (
context: RequestHandlerContext,
req: KibanaRequest<any, any, any, any>,
res: KibanaResponseFactory
): Promise<IKibanaResponse<any>> => {
const { apiKeyFromId, apiKeyToId } = req.params;

let namespace: string | undefined;
if (spaces && req.body.spaceId) {
namespace = spaces.spacesService.spaceIdToNamespace(req.body.spaceId);
}
const [{ savedObjects }, { encryptedSavedObjects }] = await core.getStartServices();
const encryptedSavedObjectsWithAlerts = await encryptedSavedObjects.getClient({
includedHiddenTypes: ['alert'],
});
const savedObjectsWithAlerts = await savedObjects.getScopedClient(req, {
excludedWrappers: ['security', 'spaces'],
includedHiddenTypes: ['alert'],
});

const [fromAlert, toAlert] = await Promise.all([
encryptedSavedObjectsWithAlerts.getDecryptedAsInternalUser<RawAlert>(
'alert',
apiKeyFromId,
{
namespace,
}
),
savedObjectsWithAlerts.get<RawAlert>('alert', apiKeyToId, {
namespace,
}),
]);

const result = await savedObjectsWithAlerts.update<RawAlert>(
'alert',
apiKeyToId,
{
...toAlert.attributes,
apiKey: fromAlert.attributes.apiKey,
apiKeyOwner: fromAlert.attributes.apiKeyOwner,
},
{
namespace,
}
);
return res.ok({ body: result });
}
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -271,9 +271,9 @@ export class AlertUtils {
return response;
}

public swapApiKeys(apiKeyFromId: string, apiKeyToId: string) {
public replaceApiKeys(id: string) {
let request = this.supertestWithoutAuth
.put(`/api/alerts_fixture/swap_api_keys/from/${apiKeyFromId}/to/${apiKeyToId}`)
.put(`/api/alerts_fixture/${id}/replace_api_key`)
.set('kbn-xsrf', 'foo');
if (this.user) {
request = request.auth(this.user.username, this.user.password);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,5 @@ export default function alertingTests({ loadTestFile }: FtrProviderContext) {

// note that this test will destroy existing spaces
loadTestFile(require.resolve('./rbac_legacy'));
// loadTestFile(require.resolve('./CREATE_DATA'));
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export default function alertTests({ getService }: FtrProviderContext) {
space_1_all_alerts_none_actions: '6ee9630a-a20e-44af-9465-217a3717d2ab',
space_1_all_with_restricted_fixture: '5cc59319-74ee-4edc-8646-a79ea91067cd',
space_1_all: 'd41a6abb-b93b-46df-a80a-926221ea847c',
global_read: '362e362b-a137-4aa2-9434-43e3d0d84a34',
superuser: 'b384be60-ec53-4b26-857e-0253ee55b277',
};

Expand All @@ -40,15 +41,12 @@ export default function alertTests({ getService }: FtrProviderContext) {
await es.indices.create({ index: authorizationIndex });
await setupSpacesAndUsers(spacesService, securityService);
});
beforeEach(async () => {});

after(async () => {
objectRemover.removeAll();
await esTestIndexTool.destroy();
await es.indices.delete({ index: authorizationIndex });
await esArchiver.unload('alerts_legacy');
});
afterEach(async () => {});

for (const scenario of UserAtSpaceScenarios) {
const { user, space } = scenario;
Expand All @@ -73,7 +71,6 @@ export default function alertTests({ getService }: FtrProviderContext) {
switch (scenario.id) {
case 'no_kibana_privileges at space1':
case 'space_1_all at space2':
case 'global_read at space1':
// These cases are not relevant as we're testing the migration of alerts which
// were valid pre 7.10.0 and which become invalid after the introduction of RBAC in 7.10.0
// these cases were invalid pre 7.10.0 and remain invalid post 7.10.0
Expand All @@ -89,15 +86,35 @@ export default function alertTests({ getService }: FtrProviderContext) {

await updateAlertSoThatItIsNoLongerLegacy(migratedAlertId);

// console.log(
// 'update alert as user with privileges - so it is no longer a legacy alert'
// );
// update alert as user with privileges - so it is no longer a legacy alert
const updatedKeyResponse = await alertUtils.getUpdateApiKeyRequest(migratedAlertId);
expect(updatedKeyResponse.statusCode).to.eql(204);

await ensureAlertIsRunning();
break;
case 'global_read at space1':
await ensureLegacyAlertHasBeenMigrated(migratedAlertId);

await updateMigratedAlertToUseApiKeyOfCurrentUser(migratedAlertId);

await ensureAlertIsRunning();

await updateAlertSoThatItIsNoLongerLegacy(migratedAlertId);

// attempt to update alert as user with no Alerts privileges - as it is no longer a legacy alert
// this should fail, as the user doesn't have the `updateApiKey` privilege for Alerts
const failedUpdateKeyDueToAlertsPrivilegesResponse = await alertUtils.getUpdateApiKeyRequest(
migratedAlertId
);

expect(failedUpdateKeyDueToAlertsPrivilegesResponse.statusCode).to.eql(403);
expect(failedUpdateKeyDueToAlertsPrivilegesResponse.body).to.eql({
error: 'Forbidden',
message:
'Unauthorized to updateApiKey a "test.always-firing" alert for "alertsFixture"',
statusCode: 403,
});
break;
case 'space_1_all_alerts_none_actions at space1':
await ensureLegacyAlertHasBeenMigrated(migratedAlertId);

Expand All @@ -109,12 +126,12 @@ export default function alertTests({ getService }: FtrProviderContext) {

// attempt to update alert as user with no Actions privileges - as it is no longer a legacy alert
// this should fail, as the user doesn't have the `execute` privilege for Actions
const failedUpdateKeyResponse = await alertUtils.getUpdateApiKeyRequest(
const failedUpdateKeyDueToActionsPrivilegesResponse = await alertUtils.getUpdateApiKeyRequest(
migratedAlertId
);

expect(failedUpdateKeyResponse.statusCode).to.eql(403);
expect(failedUpdateKeyResponse.body).to.eql({
expect(failedUpdateKeyDueToActionsPrivilegesResponse.statusCode).to.eql(403);
expect(failedUpdateKeyDueToActionsPrivilegesResponse.body).to.eql({
error: 'Forbidden',
message: 'Unauthorized to execute actions',
statusCode: 403,
Expand All @@ -125,22 +142,15 @@ export default function alertTests({ getService }: FtrProviderContext) {
}

async function ensureLegacyAlertHasBeenMigrated(alertId: string) {
// console.log(`ensureLegacyAlertHasBeenMigrated(${alertId})`);
const getResponse = await supertestWithoutAuth
.get(`${getUrlPrefix(space.id)}/api/alerts/alert/${alertId}`)
.auth(user.username, user.password);
expect(getResponse.status).to.eql(200);
}

async function updateMigratedAlertToUseApiKeyOfCurrentUser(alertId: string) {
// console.log(`updateMigratedAlertToUseApiKeyOfCurrentUser(${alertId})`);
const createNoopAlertResponse = await alertUtils.createNoopAlert({});

// swap out api key to run as user with no Actions privileges
const swapResponse = await alertUtils.swapApiKeys(
createNoopAlertResponse.body.id,
alertId
);
// swap out api key to run as the current user
const swapResponse = await alertUtils.replaceApiKeys(alertId);
expect(swapResponse.statusCode).to.eql(200);
// ensure the alert is till marked as legacy despite the update of the Api key
// this is important as proper update *should* update the legacy status of the alert
Expand All @@ -152,19 +162,16 @@ export default function alertTests({ getService }: FtrProviderContext) {
// otherwise this test will stall for 5 minutes
// no other attributes are touched, only runAt, so unless it would have ran when runAt expired, it
// won't run now
const taskRescheduleResult = await supertest
await supertest
.put(`${getUrlPrefix(space.id)}/api/alerts_fixture/${alertId}/reschedule_task`)
.set('kbn-xsrf', 'foo')
.send({
runAt: getRunAt(2000),
})
.expect(200);

// console.log(JSON.stringify(taskRescheduleResult.body));
}

async function ensureAlertIsRunning() {
// console.log(`ensureAlertIsRunning(${reference})`);
// ensure the alert still runs and that it can schedule actions
const numberOfAlertExecutions = (
await esTestIndexTool.search('alert:test.always-firing', reference)
Expand Down Expand Up @@ -194,7 +201,6 @@ export default function alertTests({ getService }: FtrProviderContext) {
}

async function updateAlertSoThatItIsNoLongerLegacy(alertId: string) {
// console.log(`updateAlertSoThatItIsNoLongerLegacy(${alertId})`);
// update the alert as super user (to avoid privilege limitations) so that it is no longer a legacy alert
await alertUtils.updateAlwaysFiringAction({
alertId,
Expand All @@ -207,14 +213,6 @@ export default function alertTests({ getService }: FtrProviderContext) {
throttle: '2s',
},
});
// return supertest
// .put(`${getUrlPrefix(space.id)}/api/alerts/alert/${alertId}`)
// .set('kbn-xsrf', 'foo')
// .auth(Superuser.username, Superuser.password)
// .send({
// name: 'Updated Alert',
// })
// .expect(200);
}
});
});
Expand Down
Loading

0 comments on commit 0714965

Please sign in to comment.