Skip to content

Commit 51018aa

Browse files
committed
Add DELETE /tokens/current endpoint
1 parent 82cb342 commit 51018aa

File tree

3 files changed

+68
-0
lines changed

3 files changed

+68
-0
lines changed

src/controllers/token.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,21 @@ pub fn revoke(req: &mut dyn RequestExt) -> EndpointResult {
108108
struct R {}
109109
Ok(req.json(&R {}))
110110
}
111+
112+
/// Handles the `DELETE /tokens/current` route.
113+
pub fn revoke_current(req: &mut dyn RequestExt) -> EndpointResult {
114+
let authenticated_user = req.authenticate()?;
115+
let api_token_id = authenticated_user
116+
.api_token_id()
117+
.ok_or_else(|| bad_request("token not provided"))?;
118+
let conn = req.db_conn()?;
119+
diesel::delete({
120+
use self::api_tokens::dsl::*;
121+
api_tokens.filter(id.eq(api_token_id))
122+
})
123+
.execute(&*conn)?;
124+
125+
#[derive(Serialize)]
126+
struct R {}
127+
Ok(req.json(&R {}))
128+
}

src/router.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ pub fn build_router(app: &App) -> R404 {
8080
api_router.get("/me/tokens", C(token::list));
8181
api_router.put("/me/tokens", C(token::new));
8282
api_router.delete("/me/tokens/:id", C(token::revoke));
83+
api_router.delete("/tokens/current", C(token::revoke_current));
8384
api_router.get(
8485
"/me/crate_owner_invitations",
8586
C(crate_owner_invitation::list),

src/tests/token.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,55 @@ fn revoke_token_success() {
274274
});
275275
}
276276

277+
#[test]
278+
fn revoke_current_token_success() {
279+
let (app, _, user, token) = TestApp::init().with_token();
280+
281+
// List tokens contains the token
282+
app.db(|conn| {
283+
let tokens = t!(ApiToken::belonging_to(user.as_model()).load::<ApiToken>(conn));
284+
assert_eq!(tokens.len(), 1);
285+
assert_eq!(tokens[0].name, token.as_model().name);
286+
});
287+
288+
// Revoke the token
289+
let _json: RevokedResponse = token.delete("/api/v1/tokens/current").good();
290+
291+
// List tokens no longer contains the token
292+
app.db(|conn| {
293+
let count = ApiToken::belonging_to(user.as_model())
294+
.filter(api_tokens::revoked.eq(false))
295+
.count()
296+
.get_result(conn);
297+
assert_eq!(count, Ok(0));
298+
});
299+
}
300+
301+
#[test]
302+
fn revoke_current_token_fail() {
303+
let (app, _, user, token) = TestApp::init().with_token();
304+
305+
// List tokens contains the token
306+
app.db(|conn| {
307+
let tokens = t!(ApiToken::belonging_to(user.as_model()).load::<ApiToken>(conn));
308+
assert_eq!(tokens.len(), 1);
309+
assert_eq!(tokens[0].name, token.as_model().name);
310+
});
311+
312+
// Revoke the token
313+
user.delete::<()>("/api/v1/tokens/current")
314+
.assert_status(StatusCode::BAD_REQUEST);
315+
316+
// List tokens no longer contains the token
317+
app.db(|conn| {
318+
let count = ApiToken::belonging_to(user.as_model())
319+
.filter(api_tokens::revoked.eq(false))
320+
.count()
321+
.get_result(conn);
322+
assert_eq!(count, Ok(1));
323+
});
324+
}
325+
277326
#[test]
278327
fn token_gives_access_to_me() {
279328
let url = "/api/v1/me";

0 commit comments

Comments
 (0)