-
Notifications
You must be signed in to change notification settings - Fork 8.3k
/
app_authorization.ts
80 lines (67 loc) · 2.82 KB
/
app_authorization.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { HttpServiceSetup, Logger } from '@kbn/core/server';
import type { FeaturesPluginSetup } from '@kbn/features-plugin/server';
import type { AuthorizationServiceSetup } from '@kbn/security-plugin-types-server';
class ProtectedApplications {
private applications: Set<string> | null = null;
constructor(private readonly featuresService: FeaturesPluginSetup) {}
public shouldProtect(appId: string) {
// Currently, once we get the list of features we essentially "lock" additional
// features from being added. This is enforced by the Features plugin. As such,
// we wait until we actually need to consume these before getting them
if (this.applications == null) {
this.applications = new Set(
this.featuresService.getKibanaFeatures().flatMap((feature) => {
// If the feature has explicitly opted out of our RBAC by setting the `privileges` field to `null`, we
// shouldn't check permissions when the app defined by such feature is accessed.
return feature.privileges === null ? [] : feature.app;
})
);
}
return this.applications.has(appId);
}
}
export function initAppAuthorization(
http: HttpServiceSetup,
{
actions,
checkPrivilegesDynamicallyWithRequest,
mode,
}: Pick<AuthorizationServiceSetup, 'actions' | 'checkPrivilegesDynamicallyWithRequest' | 'mode'>,
logger: Logger,
featuresService: FeaturesPluginSetup
) {
const protectedApplications = new ProtectedApplications(featuresService);
http.registerOnPostAuth(async (request, response, toolkit) => {
const path = request.url.pathname!;
// if the path doesn't start with "/app/", just continue
if (!path.startsWith('/app/')) {
return toolkit.next();
}
// if we aren't using RBAC, just continue
if (!mode.useRbacForRequest(request)) {
return toolkit.next();
}
const appId = path.split('/', 3)[2];
if (!protectedApplications.shouldProtect(appId)) {
logger.debug(`not authorizing - "${appId}" isn't a protected application`);
return toolkit.next();
}
const checkPrivileges = checkPrivilegesDynamicallyWithRequest(request);
const appAction = actions.app.get(appId);
const checkPrivilegesResponse = await checkPrivileges({ kibana: appAction });
logger.debug(`authorizing access to "${appId}"`);
// we've actually authorized the request
if (checkPrivilegesResponse.hasAllRequested) {
logger.debug(`authorized for "${appId}"`);
return toolkit.next();
}
logger.debug(`not authorized for "${appId}"`);
return response.forbidden();
});
}