Description
openedon Jun 21, 2019
Every time when Kibana needs to get access to data saved in elasticsearch it should perform a check whether an end user has access to the data.
Regular mode
This check relays heavily on Security that authenticates a user via elasticsearch API and set authorization
header accordingly. Having user authenticated Kibana performs all requests to elasticsearch on behalf of the user by attaching authorization headers to an incoming request:
callWithRequest(request: Request)
Note: the whole Request object is not required to hit elasticsearch API, but something with interface { headers: { authorization: 'token, [...other authorization headers: string]: string } }
.
FakeRequest mode
There are a couple of cases when the current model doesn't play well
- When we need to check whether
headers.authorization
contains valid credentials.
// in legacy platform
try{
request.headers.authorization = tokenCandidate;
callWithRequest(request)
} catch(e){
delete request.headers.authorization
}
// in new platform
asScoped({headers: {authorization: tokenCandidate }}.callAsCurrentUser()
- When elasticsearch api is called outside of Http request context.
In Reporting plugin
And in Upgrade Assistant
In all those cases we 'mimic' Request object, although we need only authorization
headers to be present.
Discuss
- What core services need to support this "scoping" mechanic? Obviously, it works for elasticsearch and other services that wrap it, like
SavedObjects
. Probably there is something else that I missed. - What should be considered as a valid input for
asScoped()
? Are those use cases forFakeRequest
are going to stay in a long-term? Should we operateRequest
term at all? Probably we could rename it to something less confusing, for exampleOwnerCredentials
to emphasise that something with scope-able interface is a valid input. - Will Alerting plugin use the same
FakeRequest
pattern to perform access data? @mikecote - Should elasticsearch ClusterClient support headers re-configuring? At the moment it's not possible. On the one hand it's a good thing because we explicitly declare the creation of
FakeRequest
to perform a call and declare dependencies on the passed credentials.
const data = await dataClient.asScoped({headers: { authorization: ....}}).callAsInternalUser()
On the other hand, the ergonomic suffers as we have to use asScoped
and cannot leverage coming Handler RFC API to extend a request for custom needs
// or in registerHandler
const dataClient = await dataClient$.pipe(take(1)).toPromise();
const dataClient = dataClient.asScoped(request);
// somewhere below
const data = await dataClient.callAsInternalUser({headers: { authorization: ....}})