Skip to content

Commit fedf92b

Browse files
committed
on second thought, add hard delete by token
1 parent 5ab5cbb commit fedf92b

File tree

5 files changed

+54
-24
lines changed

5 files changed

+54
-24
lines changed

nexus/db-queries/src/db/datastore/console_session.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,4 +175,25 @@ impl DataStore {
175175
))
176176
})
177177
}
178+
179+
pub async fn session_hard_delete_by_token(
180+
&self,
181+
opctx: &OpContext,
182+
token: String,
183+
) -> DeleteResult {
184+
// we don't do an authz check here because the possession of
185+
// the token is the check
186+
use nexus_db_schema::schema::console_session;
187+
diesel::delete(console_session::table)
188+
.filter(console_session::token.eq(token))
189+
.execute_async(&*self.pool_connection_authorized(opctx).await?)
190+
.await
191+
.map(|_rows_deleted| ())
192+
.map_err(|e| {
193+
Error::internal_error(&format!(
194+
"error deleting session by token: {:?}",
195+
e
196+
))
197+
})
198+
}
178199
}

nexus/db-queries/src/db/datastore/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,10 @@ mod test {
700700
datastore.session_hard_delete(&opctx, &authz_session).await;
701701
assert_eq!(delete_again, Ok(()));
702702

703+
let delete_again =
704+
datastore.session_hard_delete_by_token(&opctx, token.clone()).await;
705+
assert_eq!(delete_again, Ok(()));
706+
703707
db.terminate().await;
704708
logctx.cleanup_successful();
705709
}

nexus/src/app/session.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,14 @@ impl super::Nexus {
9494
self.db_datastore.session_hard_delete(opctx, &authz_session).await
9595
}
9696

97+
pub(crate) async fn session_hard_delete_by_token(
98+
&self,
99+
opctx: &OpContext,
100+
token: String,
101+
) -> DeleteResult {
102+
self.db_datastore.session_hard_delete_by_token(opctx, token).await
103+
}
104+
97105
pub(crate) async fn lookup_silo_for_authn(
98106
&self,
99107
opctx: &OpContext,

nexus/src/external_api/http_entrypoints.rs

Lines changed: 8 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7563,33 +7563,17 @@ impl NexusExternalApi for NexusExternalApiImpl {
75637563
let apictx = rqctx.context();
75647564
let handler = async {
75657565
let nexus = &apictx.context.nexus;
7566-
// this is unique among the hundreds of calls to this function in
7567-
// that we are not using ? to return early on error
7568-
let opctx =
7569-
crate::context::op_context_for_external_api(&rqctx).await;
7566+
// this is kind of a weird one, but we're only doing things here
7567+
// that are authorized directly by the possession of the token,
7568+
// which makes it somewhat like a login
7569+
let opctx = nexus.opctx_external_authn();
75707570
let session_cookie =
75717571
cookies.get(session_cookie::SESSION_COOKIE_COOKIE_NAME);
75727572

7573-
// Look up session and delete it if present. Noop on any errors.
7574-
// This is the ONE spot where we do the hard delete by token and we
7575-
// haven't already looked up the session by token. Looking up the
7576-
// token first works, but it would be nice to avoid it.
7577-
if let Ok(opctx) = opctx {
7578-
if let Some(cookie) = session_cookie {
7579-
let token = cookie.value().to_string();
7580-
match nexus.session_fetch(&opctx, token).await {
7581-
Ok(session) => {
7582-
let id = session.console_session.id();
7583-
// ? here because if this fails, we did not delete the
7584-
// session when we meant to
7585-
nexus.session_hard_delete(&opctx, id).await?;
7586-
}
7587-
// blow up only on errors other than not found, because not
7588-
// found is fine: nothing to delete
7589-
Err(Error::ObjectNotFound { .. }) => {} // noop
7590-
Err(e) => return Err(e.into()),
7591-
};
7592-
}
7573+
// Look up session and delete it if present
7574+
if let Some(cookie) = session_cookie {
7575+
let token = cookie.value().to_string();
7576+
nexus.session_hard_delete_by_token(&opctx, token).await?;
75937577
}
75947578

75957579
// If user's session was already expired, they fail auth and their

nexus/tests/integration_tests/console_api.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,19 @@ async fn test_sessions(cptestctx: &ControlPlaneTestContext) {
137137
.await
138138
.expect("failed to log out");
139139

140+
// logout with a nonexistent session token does the same thing: clears it out
141+
RequestBuilder::new(&testctx, Method::POST, "/v1/logout")
142+
.header(header::COOKIE, "session=abc")
143+
.expect_status(Some(StatusCode::NO_CONTENT))
144+
// logout also clears the cookie client-side
145+
.expect_response_header(
146+
header::SET_COOKIE,
147+
"session=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0",
148+
)
149+
.execute()
150+
.await
151+
.expect("failed to log out");
152+
140153
// now the same requests with the same session cookie should 401/302 because
141154
// logout also deletes the session server-side
142155
RequestBuilder::new(&testctx, Method::POST, "/v1/projects")

0 commit comments

Comments
 (0)