Skip to content

Commit 1b7b518

Browse files
committed
Simplify access token mapping and allow token invalidation
1 parent 2606688 commit 1b7b518

3 files changed

Lines changed: 33 additions & 89 deletions

File tree

Lines changed: 15 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
use super::schema::Columns;
21
use super::schema::{self, AccessToken};
32
use crate::db::main::user::schema::Role;
43
use crate::Result;
54
use rusqlite::{params, Connection};
5+
use schema::Columns::*;
6+
use schema::TABLE;
67

78
pub fn insert(
89
user_id: i64,
@@ -14,15 +15,10 @@ pub fn insert(
1415
let roles: Vec<String> = roles.iter().map(|it| it.to_string()).collect();
1516
let sql = format!(
1617
r#"
17-
INSERT INTO {table} ({user_id}, {name}, {secret}, {roles})
18+
INSERT INTO {TABLE} ({UserId}, {Name}, {Secret}, {Roles})
1819
VALUES (?1, ?2, ?3, json(?4))
1920
RETURNING {projection}
2021
"#,
21-
table = schema::NAME,
22-
user_id = Columns::UserId.as_str(),
23-
name = Columns::Name.as_str(),
24-
secret = Columns::Secret.as_str(),
25-
roles = Columns::Roles.as_str(),
2622
projection = AccessToken::projection(),
2723
);
2824
conn.query_row(
@@ -38,10 +34,9 @@ pub fn select_all(conn: &Connection) -> Result<Vec<AccessToken>> {
3834
let sql = format!(
3935
r#"
4036
SELECT {projection}
41-
FROM {table}
37+
FROM {TABLE}
4238
"#,
4339
projection = AccessToken::projection(),
44-
table = schema::NAME,
4540
);
4641
conn.prepare(&sql)?
4742
.query_map({}, AccessToken::mapper())?
@@ -54,12 +49,10 @@ pub fn select_by_id(id: i64, conn: &Connection) -> Result<AccessToken> {
5449
let sql = format!(
5550
r#"
5651
SELECT {projection}
57-
FROM {table}
58-
WHERE {id} = ?1
52+
FROM {TABLE}
53+
WHERE {Id} = ?1
5954
"#,
6055
projection = AccessToken::projection(),
61-
table = schema::NAME,
62-
id = Columns::Id.as_str(),
6356
);
6457
conn.query_row(&sql, params![id], AccessToken::mapper())
6558
.map_err(Into::into)
@@ -69,34 +62,15 @@ pub fn select_by_secret(secret: &str, conn: &Connection) -> Result<AccessToken>
6962
let sql = format!(
7063
r#"
7164
SELECT {projection}
72-
FROM {table}
73-
WHERE {secret} = ?1
65+
FROM {TABLE}
66+
WHERE {Secret} = ?1 AND {DeletedAt} IS NULL
7467
"#,
7568
projection = AccessToken::projection(),
76-
table = schema::NAME,
77-
secret = Columns::Secret.as_str(),
7869
);
7970
conn.query_row(&sql, params![secret], AccessToken::mapper())
8071
.map_err(Into::into)
8172
}
8273

83-
#[cfg(test)]
84-
pub fn set_roles(token_id: i64, roles: &[Role], conn: &Connection) -> Result<()> {
85-
let sql = format!(
86-
r#"
87-
UPDATE {table}
88-
SET {roles} = json(?1)
89-
WHERE {id} = ?2
90-
"#,
91-
table = schema::NAME,
92-
roles = Columns::Roles.as_str(),
93-
id = Columns::Id.as_str(),
94-
);
95-
let roles: Vec<String> = roles.iter().map(|role| role.to_string()).collect();
96-
conn.execute(&sql, params![serde_json::to_string(&roles)?, token_id])?;
97-
Ok(())
98-
}
99-
10074
#[cfg(test)]
10175
mod test {
10276
use crate::db::main::test::conn;
@@ -106,32 +80,27 @@ mod test {
10680
#[test]
10781
fn insert() -> Result<()> {
10882
let conn = conn();
109-
let user = crate::db::main::user::blocking_queries::insert("test_user", "password", &conn)?;
11083
let name = "name";
11184
let secret = "secret";
11285
let roles = vec![Role::Admin];
113-
114-
let inserted_token = super::insert(user.id, name, secret, &roles, &conn)?;
86+
let inserted_token = super::insert(2, name, secret, &roles, &conn)?;
11587
let selected_token = super::select_by_id(inserted_token.id, &conn)?;
116-
88+
assert_eq!(inserted_token, selected_token);
11789
assert_eq!(selected_token, inserted_token);
118-
11990
assert_eq!(1, selected_token.id);
120-
assert_eq!(user.id, selected_token.user_id);
91+
assert_eq!(2, selected_token.user_id);
12192
assert_eq!(Some(name), selected_token.name.as_deref());
12293
assert_eq!(secret, selected_token.secret);
12394
assert_eq!(roles, selected_token.roles);
12495
assert!(selected_token.deleted_at.is_none());
125-
12696
Ok(())
12797
}
12898

12999
#[test]
130100
fn select_all() -> Result<()> {
131101
let conn = conn();
132-
let user = crate::db::main::user::blocking_queries::insert("test_user", "password", &conn)?;
133-
let token_1 = super::insert(user.id, "name_1", "pwd_1", &[], &conn)?;
134-
let token_2 = super::insert(user.id, "name_2", "pwd_2", &[], &conn)?;
102+
let token_1 = super::insert(1, "name_1", "pwd_1", &[], &conn)?;
103+
let token_2 = super::insert(1, "name_2", "pwd_2", &[], &conn)?;
135104
let query_res = super::select_all(&conn)?;
136105
assert_eq!(2, query_res.len());
137106
assert_eq!(&token_1, query_res.first().unwrap());
@@ -142,8 +111,7 @@ mod test {
142111
#[test]
143112
fn select_by_id() -> Result<()> {
144113
let conn = conn();
145-
let user = crate::db::main::user::blocking_queries::insert("test_user", "password", &conn)?;
146-
let insert_res = super::insert(user.id, "name", "pwd", &[], &conn)?;
114+
let insert_res = super::insert(1, "name", "pwd", &[], &conn)?;
147115
let select_res = super::select_by_id(insert_res.id, &conn)?;
148116
assert_eq!(insert_res, select_res);
149117
Ok(())
@@ -152,22 +120,10 @@ mod test {
152120
#[test]
153121
fn select_by_secret() -> Result<()> {
154122
let conn = conn();
155-
let user = crate::db::main::user::blocking_queries::insert("test_user", "password", &conn)?;
156123
let secret = "xxx";
157-
let token = super::insert(user.id, "", secret, &[], &conn)?;
124+
let token = super::insert(1, "", secret, &[], &conn)?;
158125
let select_res = super::select_by_secret(secret, &conn)?;
159126
assert_eq!(token, select_res);
160127
Ok(())
161128
}
162-
163-
#[test]
164-
fn set_roles() -> Result<()> {
165-
let conn = conn();
166-
let user = crate::db::main::user::blocking_queries::insert("test_user", "password", &conn)?;
167-
let token = super::insert(user.id, "name", "pwd", &[], &conn)?;
168-
let roles = vec![Role::User, Role::Admin];
169-
super::set_roles(token.id, &roles, &conn)?;
170-
assert_eq!(roles, super::select_by_id(token.id, &conn)?.roles);
171-
Ok(())
172-
}
173129
}

‎src/db/main/access_token/mod.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
pub(super) mod blocking_queries;
1+
mod blocking_queries;
22
pub mod queries;
33
pub mod schema;

‎src/db/main/access_token/schema.rs‎

Lines changed: 17 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ use serde_json::Value;
44
use std::{str::FromStr, sync::OnceLock};
55
use time::OffsetDateTime;
66

7-
pub const NAME: &str = "access_token";
7+
pub const TABLE: &str = "access_token";
88

9+
#[derive(strum::AsRefStr, strum::Display)]
10+
#[strum(serialize_all = "snake_case")]
911
pub enum Columns {
1012
Id,
1113
UserId,
@@ -17,21 +19,6 @@ pub enum Columns {
1719
DeletedAt,
1820
}
1921

20-
impl Columns {
21-
pub fn as_str(&self) -> &'static str {
22-
match self {
23-
Columns::Id => "id",
24-
Columns::UserId => "user_id",
25-
Columns::Name => "name",
26-
Columns::Secret => "secret",
27-
Columns::Roles => "roles",
28-
Columns::CreatedAt => "created_at",
29-
Columns::UpdatedAt => "updated_at",
30-
Columns::DeletedAt => "deleted_at",
31-
}
32-
}
33-
}
34-
3522
#[allow(dead_code)]
3623
#[derive(Eq, PartialEq, Debug)]
3724
pub struct AccessToken {
@@ -60,7 +47,7 @@ impl AccessToken {
6047
Columns::DeletedAt,
6148
]
6249
.iter()
63-
.map(Columns::as_str)
50+
.map(AsRef::as_ref)
6451
.collect::<Vec<_>>()
6552
.join(", ")
6653
})
@@ -69,23 +56,24 @@ impl AccessToken {
6956
pub const fn mapper() -> fn(&Row) -> rusqlite::Result<Self> {
7057
|row: &Row| -> rusqlite::Result<Self> {
7158
Ok(AccessToken {
72-
id: row.get(Columns::Id.as_str())?,
73-
user_id: row.get(Columns::UserId.as_str())?,
74-
name: row.get(Columns::Name.as_str())?,
75-
secret: row.get(Columns::Secret.as_str())?,
76-
roles: Self::parse_roles(row.get(Columns::Roles.as_str())?),
77-
created_at: row.get(Columns::CreatedAt.as_str())?,
78-
updated_at: row.get(Columns::UpdatedAt.as_str())?,
79-
deleted_at: row.get(Columns::DeletedAt.as_str())?,
59+
id: row.get(Columns::Id.as_ref())?,
60+
user_id: row.get(Columns::UserId.as_ref())?,
61+
name: row.get(Columns::Name.as_ref())?,
62+
secret: row.get(Columns::Secret.as_ref())?,
63+
roles: Self::parse_roles(row.get(Columns::Roles.as_ref())?)?,
64+
created_at: row.get(Columns::CreatedAt.as_ref())?,
65+
updated_at: row.get(Columns::UpdatedAt.as_ref())?,
66+
deleted_at: row.get(Columns::DeletedAt.as_ref())?,
8067
})
8168
}
8269
}
8370

84-
fn parse_roles(column_value: Value) -> Vec<Role> {
85-
let roles: Vec<String> = serde_json::from_value(column_value).unwrap_or_default();
86-
roles
71+
fn parse_roles(column_value: Value) -> rusqlite::Result<Vec<Role>> {
72+
let roles: Vec<String> = serde_json::from_value(column_value)
73+
.map_err(|e| rusqlite::Error::ToSqlConversionFailure(Box::new(e)))?;
74+
Ok(roles
8775
.into_iter()
8876
.filter_map(|s| Role::from_str(&s).ok())
89-
.collect()
77+
.collect())
9078
}
9179
}

0 commit comments

Comments
 (0)