-
Notifications
You must be signed in to change notification settings - Fork 48
Silo admin endpoints for user logout + listing tokens and sessions #8479
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
Changes from all commits
94874a9
2a47746
d7e8a3b
de80558
b5f6b5d
c82b96d
4b8c0c6
e9d8d9b
b35de2d
b526489
06b263c
301ef0d
a2d27a7
b153a23
f1b11c4
a775b45
e8b561c
f497556
13af0ac
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,19 +9,26 @@ use crate::authn; | |
use crate::authz; | ||
use crate::context::OpContext; | ||
use crate::db::model::ConsoleSession; | ||
use crate::db::pagination::paginated; | ||
use async_bb8_diesel::AsyncRunQueryDsl; | ||
use chrono::TimeDelta; | ||
use chrono::Utc; | ||
use diesel::prelude::*; | ||
use nexus_db_errors::ErrorHandler; | ||
use nexus_db_errors::public_error_from_diesel; | ||
use nexus_db_lookup::LookupPath; | ||
use nexus_db_schema::schema::console_session; | ||
use omicron_common::api::external::CreateResult; | ||
use omicron_common::api::external::DataPageParams; | ||
use omicron_common::api::external::DeleteResult; | ||
use omicron_common::api::external::Error; | ||
use omicron_common::api::external::ListResultVec; | ||
use omicron_common::api::external::LookupResult; | ||
use omicron_common::api::external::LookupType; | ||
use omicron_common::api::external::ResourceType; | ||
use omicron_common::api::external::UpdateResult; | ||
use omicron_uuid_kinds::GenericUuid; | ||
use uuid::Uuid; | ||
|
||
impl DataStore { | ||
/// Look up session by token. The token is a kind of password, so simply | ||
|
@@ -154,4 +161,60 @@ impl DataStore { | |
)) | ||
}) | ||
} | ||
|
||
/// List console sessions for a specific user | ||
/// | ||
/// Have to pass down TTLs because they come from the nexus server config. | ||
pub async fn silo_user_session_list( | ||
&self, | ||
opctx: &OpContext, | ||
authn_list: authz::SiloUserSessionList, | ||
pagparams: &DataPageParams<'_, Uuid>, | ||
idle_ttl: TimeDelta, | ||
abs_ttl: TimeDelta, | ||
Comment on lines
+173
to
+174
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nitpicky, sorry: I really don't like when a function has two or more positional arguments of the same type, because when you call the function, you have to remember which is which, and it's easy to transpose them etc by mistake. It might be nicer to have a quick #[derive(Copy, Clone)]
pub struct SessionTtls {
pub idle: TimeDelta,
pub abs: TimeDelta,
} so they're distinguished by name at the call site instead of by which order they're passed in? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I thought about doing this to make it all less sketchy-feeling. Will do (maybe in a followup PR so I can get this merged ASAP). |
||
) -> ListResultVec<ConsoleSession> { | ||
opctx.authorize(authz::Action::ListChildren, &authn_list).await?; | ||
|
||
let user_id = authn_list.silo_user().id(); | ||
|
||
// HACK: unlike with tokens, we do not have expiration time here, | ||
// so we can't filter out expired sessions by comparing to now. The | ||
// real way to do this would be to change this to time_expires_idle | ||
// and time_expires_abs and just compare them to now directly. Then | ||
// we would not have to pass the TTLs down from the handler. | ||
let now = Utc::now(); | ||
|
||
use nexus_db_schema::schema::console_session::dsl; | ||
paginated(dsl::console_session, dsl::id, &pagparams) | ||
.filter(dsl::silo_user_id.eq(user_id)) | ||
// session is not expired according to abs timeout | ||
.filter(dsl::time_created.ge(now - abs_ttl)) | ||
// session is also not expired according to idle timeout | ||
.filter(dsl::time_last_used.ge(now - idle_ttl)) | ||
.select(ConsoleSession::as_select()) | ||
.load_async(&*self.pool_connection_authorized(opctx).await?) | ||
.await | ||
.map_err(|e| public_error_from_diesel(e, ErrorHandler::Server)) | ||
} | ||
|
||
/// Delete all session for the user | ||
pub async fn silo_user_sessions_delete( | ||
&self, | ||
opctx: &OpContext, | ||
authn_list: &authz::SiloUserSessionList, | ||
) -> Result<(), Error> { | ||
// authz policy enforces that the opctx actor is a silo admin on the | ||
// target user's own silo in particular | ||
opctx.authorize(authz::Action::Modify, authn_list).await?; | ||
|
||
let user_id = authn_list.silo_user().id(); | ||
|
||
use nexus_db_schema::schema::console_session; | ||
diesel::delete(console_session::table) | ||
.filter(console_session::silo_user_id.eq(user_id)) | ||
.execute_async(&*self.pool_connection_authorized(opctx).await?) | ||
.await | ||
.map_err(|e| public_error_from_diesel(e, ErrorHandler::Server)) | ||
.map(|_x| ()) | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.