Skip to content

Commit c10a9aa

Browse files
committed
Add DELETE /tokens/current endpoint
1 parent 0099c08 commit c10a9aa

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
@@ -110,3 +110,21 @@ pub fn revoke(req: &mut dyn RequestExt) -> EndpointResult {
110110
struct R {}
111111
Ok(req.json(&R {}))
112112
}
113+
114+
/// Handles the `DELETE /tokens/current` route.
115+
pub fn revoke_current(req: &mut dyn RequestExt) -> EndpointResult {
116+
let authenticated_user = req.authenticate()?;
117+
let api_token_id = authenticated_user
118+
.api_token_id()
119+
.ok_or_else(|| bad_request("token not provided"))?;
120+
let conn = req.db_conn()?;
121+
diesel::delete({
122+
use self::api_tokens::dsl::*;
123+
api_tokens.filter(id.eq(api_token_id))
124+
})
125+
.execute(&*conn)?;
126+
127+
#[derive(Serialize)]
128+
struct R {}
129+
Ok(req.json(&R {}))
130+
}

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
@@ -273,6 +273,55 @@ fn revoke_token_success() {
273273
});
274274
}
275275

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

0 commit comments

Comments
 (0)