Skip to content

Commit 6f16cfe

Browse files
committed
on second thought, add hard delete by token
1 parent 424598b commit 6f16cfe

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
@@ -7581,33 +7581,17 @@ impl NexusExternalApi for NexusExternalApiImpl {
75817581
let apictx = rqctx.context();
75827582
let handler = async {
75837583
let nexus = &apictx.context.nexus;
7584-
// this is unique among the hundreds of calls to this function in
7585-
// that we are not using ? to return early on error
7586-
let opctx =
7587-
crate::context::op_context_for_external_api(&rqctx).await;
7584+
// this is kind of a weird one, but we're only doing things here
7585+
// that are authorized directly by the possession of the token,
7586+
// which makes it somewhat like a login
7587+
let opctx = nexus.opctx_external_authn();
75887588
let session_cookie =
75897589
cookies.get(session_cookie::SESSION_COOKIE_COOKIE_NAME);
75907590

7591-
// Look up session and delete it if present. Noop on any errors.
7592-
// This is the ONE spot where we do the hard delete by token and we
7593-
// haven't already looked up the session by token. Looking up the
7594-
// token first works, but it would be nice to avoid it.
7595-
if let Ok(opctx) = opctx {
7596-
if let Some(cookie) = session_cookie {
7597-
let token = cookie.value().to_string();
7598-
match nexus.session_fetch(&opctx, token).await {
7599-
Ok(session) => {
7600-
let id = session.console_session.id();
7601-
// ? here because if this fails, we did not delete the
7602-
// session when we meant to
7603-
nexus.session_hard_delete(&opctx, id).await?;
7604-
}
7605-
// blow up only on errors other than not found, because not
7606-
// found is fine: nothing to delete
7607-
Err(Error::ObjectNotFound { .. }) => {} // noop
7608-
Err(e) => return Err(e.into()),
7609-
};
7610-
}
7591+
// Look up session and delete it if present
7592+
if let Some(cookie) = session_cookie {
7593+
let token = cookie.value().to_string();
7594+
nexus.session_hard_delete_by_token(&opctx, token).await?;
76117595
}
76127596

76137597
// 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
@@ -136,6 +136,19 @@ async fn test_sessions(cptestctx: &ControlPlaneTestContext) {
136136
.await
137137
.expect("failed to log out");
138138

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

0 commit comments

Comments
 (0)