-
Notifications
You must be signed in to change notification settings - Fork 8.2k
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
[New platform] HTTP & Security integration #34631
Conversation
f129cf2
to
f417624
Compare
💚 Build Succeeded |
src/core/server/http/http_server.ts
Outdated
this.server.ext('onRequest', adoptToHapiOnRequestFormat(fn)); | ||
}; | ||
|
||
private registerAuth = async <T>(fn: Authenticate<T>, cookieOptions: CookieOptions) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NOTE: right now security config defines CookieOptions
values but doesn't specify session storage implementation as cookie-storage
. Seems to be okay if we don't want to add premature abstraction, later we can change API
interface CookieSessionStorage {
type: 'cookie'
options: CookieOptions
}
interface RedisSessionStorage {
type: 'redis'
options: ...
}
type SessionStorageParams = CookieSessionStorage | RedisSessionStorage | ...
registerAuth (fn: Authenticate<T>, sessionStorageParams: SessionStorageParams){}
are we going to support this extension point compatibility for external users?
h: ResponseToolkit | ||
): Promise<Lifecycle.ReturnValue> { | ||
try { | ||
const result = await fn(req, sessionStorage.asScoped(req), toolkit); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note it does not support returning an error nor throwing an error from authenticate
function
res.ok({ content: 'ok' }) | ||
); | ||
// TODO fix me when registerRouter is available before HTTP server is run | ||
(root as any).server.http.registerRouter(router); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note right now we don't have a separation between setup
and start
phase, consequently, we don't provide any way to add route handler in userland. That seems to be a blocker because even Security cannot add their route handlers to perform login
. we have to address that in following PRs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With the work going on in #18843, I think we'll be able to get the legacy code on the frontend more compatible with the lifecycle events in the new platform sooner than later. This would unblock adding back the start
event.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe it's fine to introduce start
only on the server side for the time being. That can be implemented without leaking lifecycle semantic to plugins:
- the server runs
setup
for core services and plugins. - the server runs
start
for core services only (until [browser] Application service #18843 is ready). - the server runs
legacy server
.
src/core/server/http/http_server.ts
Outdated
@@ -127,4 +137,31 @@ export class HttpServer { | |||
const routePathStartIndex = routerPath.endsWith('/') && routePath.startsWith('/') ? 1 : 0; | |||
return `${routerPath}${routePath.slice(routePathStartIndex)}`; | |||
} | |||
|
|||
private registerOnRequest = (fn: OnRequest) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not directly related to Security integration directly, but interesting to check consistency in registerOnRequest
and registerAuth
API
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason this is defined as an arrow function instead of a class method?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this function is exposed as a part of setup
contract. Otherwise, we either have to bind it in setup
method or expose an unbound version.
sessionStorage.set({ value: user, expires: Date.now() + 1000 }); | ||
return t.authenticated({ credentials: user }); | ||
} else { | ||
return t.rejected(Boom.unauthorized()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
are we going to restrict Boom
usage? there is an issue, but don't see any discussions #12947
export function createRootWithSettings(...settings: Array<Record<string, any>>) { | ||
export function createRootWithSettings( | ||
settings: Record<string, any>, | ||
cliArgs: Partial<CliArgs> = {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had to hack createRoot
to be able to specify env vars because additional plugins can be used in dev
mode only.
even more interesting that we run integration_tests
in production
mode, but functional tests in development
. what is the difference? I'd expect dev/prod/testing/staging parity https://12factor.net/dev-prod-parity
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, agree, we should unify environment these tests run in at some point.
@epixa @elastic/kibana-security @elastic/kibana-platform may I ask you about preliminary code review? |
💚 Build Succeeded |
Pinging @elastic/kibana-platform |
src/core/server/http/http_server.ts
Outdated
this.server.ext('onRequest', adoptToHapiOnRequestFormat(fn)); | ||
}; | ||
|
||
private registerAuth = async <T>(fn: Authenticate<T>, cookieOptions: CookieOptions<T>) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right now we provide an access to the raw Hapi request object. Will be restricted specific data and methods in following PRs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the PR @restrry! I have a couple comments, but I don't think there's anything that'll prevent you from moving forward. I didn't do an in-depth review, as you only asked for a preliminary one :)
I know that registering routes is still a WIP, but something we're relying on for Feature Controls is the ability for routes to add tags. These tags inform the security plugin that specific authorization is required above and beyond the initial authentication. We don't necessarily need/want tags specifically going forward, but the ability for a request interceptor to inspect the current route config, and determine if any additional work needs to be done. See api_authorization.ts for an example of this.
I still encourage @azasypkin and/or @kobelb to take a look too, in case I'm overlooking anything.
|
||
const sessionStorage = await createCookieSessionStorageFor<T>(this.server, cookieOptions); | ||
|
||
this.server.auth.scheme('login', () => ({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think hapi will complain if the same scheme is registered more than once. If hapi ends up throwing an error here, then this will be leaking hapi internals to the consumer. Might want to prevent this from being called more than once, and/or wrap the underlying errors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, I'm going to add a check that it is called only once 👍
that's why the whole hapi |
Will create separate issues to do in follow-ups:
possible improvements to discuss:
|
💔 Build Failed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM w/ planned follow up PRs
request: Request, | ||
sessionStorage: SessionStorage<T>, | ||
t: AuthToolkit | ||
) => Promise<AuthResult>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm going to introduce another mechanism to control this header in following PR.
SGTM, I didn't see the prior comment. We could probably construct KibanaRequest with a filtered set of headers for regular route handlers and only expose the Authenication header to AuthenticationHandlers.
comments are addressed, Eli is on sick leave to re-review
💔 Build Failed |
retest |
💚 Build Succeeded |
* Add Auth session * add lifecycles * add types for hapi-auth-cookie * expose interceptors from http service * add integration tests * update tests * session storage cleanup * get SessionStorage type safe * add redirect, clear cookie security integration tests * add tests for onRequest * add tests for onAuth * register Auth interceptor only once * refactor redirect tests * fix typings, change error message, test suit naming * add integration test for session validation * add tests for cookie session storage * update docs * add integration tests for onRequest * update docs * cleanup onRequest integration tests * Generate docs for AuthToolkit & OnRequestToolkit * add test for an exception in interceptor * add test OnRequest interceptors dont share request object * cleanup * address comments from @eli * improve typings for onRequest * improve plugin typings * re-generate docs * only server defines cookie path * cookieOptions.password --> cookieOptions.encryptionKey * CookieOption --> SessionStorageCookieOptions * address comments @joshdover * resolve conflict leftovers * update @types/hapi-auth-cookie deps * update docs
* Add Auth session * add lifecycles * add types for hapi-auth-cookie * expose interceptors from http service * add integration tests * update tests * session storage cleanup * get SessionStorage type safe * add redirect, clear cookie security integration tests * add tests for onRequest * add tests for onAuth * register Auth interceptor only once * refactor redirect tests * fix typings, change error message, test suit naming * add integration test for session validation * add tests for cookie session storage * update docs * add integration tests for onRequest * update docs * cleanup onRequest integration tests * Generate docs for AuthToolkit & OnRequestToolkit * add test for an exception in interceptor * add test OnRequest interceptors dont share request object * cleanup * address comments from @eli * improve typings for onRequest * improve plugin typings * re-generate docs * only server defines cookie path * cookieOptions.password --> cookieOptions.encryptionKey * CookieOption --> SessionStorageCookieOptions * address comments @joshdover * resolve conflict leftovers * update @types/hapi-auth-cookie deps * update docs
With the introduction of elastic#34631 OSS Kibana now has a dependency on hapi-auth-cookie, but it's missing from the package.json
With the introduction of #34631 OSS Kibana now has a dependency on hapi-auth-cookie, but it's missing from the package.json
With the introduction of elastic#34631 OSS Kibana now has a dependency on hapi-auth-cookie, but it's missing from the package.json
Summary
Parent issue: #33775
Short recap of the issue:
Auth interceptor
Provide
Authenticate
hook for Security plugin to register incoming requests intercepter.Request intercepter is meant to be registered only once.
Security plugin performs user authentication and authorization checks for incoming requests.
Authenticate
may finish Auth operation with one of next outcomes:Third party plugins should have an access to (optional) Security plugin to register their access to authorization information.
Request interceptor
Provide
onRequest
hook for plugins to register custom logic for incoming requests. Request interceptor doesn't have access to hapi Request object, but KibanaRequestonRequest
may finish Auth operation with one of next outcomes:Checklist
Use
strikethroughsto remove checklist items you don't feel are applicable to this PR.[ ] This was checked for cross-browser compatibility, including a check against IE11[ ] Any text added follows EUI's writing guidelines, uses sentence case text and includes i18n support[ ] This was checked for keyboard-only and screenreader accessibilityFor maintainers