Skip to content
Clone

fix(auth): wire JWT deny-list across repo/issues/chat + restrict SSH + suspend-as-inactive (H5)

zhenyi committed
parent deeed92 commit b782c17 Browse files

9 files changed

+73 −2

File tree 9
Modified app/livechat/src/main.rs View app/livechat/src/main.rs
@@ -116,7 +116,11 @@
116116 queue.clone(),
117117 app.chat.topic_prefix.clone(),
118118 ));
119 let auth = AuthBridge::new(Arc::new(jwt.clone()));
119 let auth = AuthBridge::with_revocation(
120 Arc::new(jwt.clone()),
121 Arc::new(rate_limit::AccessTokenBlacklist::new(kv.clone())),
122 (*db).clone(),
123 );
120124 let media = Arc::new(MediaService::new(
121125 store.clone(),
122126 storage.clone(),
Modified app/livechat/Cargo.toml View app/livechat/Cargo.toml
@@ -19,6 +19,7 @@
1919queue.workspace = true
2020storage.workspace = true
2121jwt.workspace = true
22rate_limit.workspace = true
2223
2324axum.workspace = true
2425tokio.workspace = true
Modified chat/chatlayer/auth_bridge.rs View chat/chatlayer/auth_bridge.rs
@@ -1,7 +1,10 @@
11use std::sync::Arc;
22
3use db::conn::Db;
34use jwt::JwtError;
45use jwt::signer::{decode_access_token, extract_kid};
6use rate_limit::AccessTokenBlacklist;
7use sea_orm::EntityTrait;
58use storagelayer::repository::ChatStorageCtx;
69use uuid::Uuid;
710
@@ -15,6 +18,8 @@
1518
1619pub struct AuthBridge {
1720 jwt: Arc<jwt::JwtState>,
21 blacklist: Option<Arc<AccessTokenBlacklist>>,
22 db: Option<Db>,
1823}
1924
2025impl std::fmt::Debug for AuthBridge {
@@ -25,7 +30,23 @@
2530
2631impl AuthBridge {
2732 pub fn new(jwt: Arc<jwt::JwtState>) -> Self {
28 Self { jwt }
33 Self {
34 jwt,
35 blacklist: None,
36 db: None,
37 }
38 }
39
40 pub fn with_revocation(
41 jwt: Arc<jwt::JwtState>,
42 blacklist: Arc<AccessTokenBlacklist>,
43 db: Db,
44 ) -> Self {
45 Self {
46 jwt,
47 blacklist: Some(blacklist),
48 db: Some(db),
49 }
2950 }
3051
3152 pub async fn verify(&self, token: &str) -> ChatLayerResult<AuthIdentity> {
@@ -42,6 +63,27 @@
4263 &self.jwt.settings.audience,
4364 self.jwt.settings.clock_skew_seconds,
4465 )?;
66
67 if let Some(blacklist) = &self.blacklist {
68 if blacklist.is_revoked(claims.jti, claims.sub).await? {
69 return Err(ChatLayerError::Jwt(JwtError::SessionRevoked));
70 }
71 }
72 if let Some(db) = &self.db {
73 let conn = db.reader();
74 let user = db::user::users::Entity::find_by_id(claims.sub)
75 .one(&conn)
76 .await
77 .map_err(|e| ChatLayerError::Internal(format!("user lookup failed: {e}")))?;
78 match user {
79 Some(model) => match model.passwd_version {
80 Some(current) if current == claims.pwd_version => {}
81 _ => return Err(ChatLayerError::Jwt(JwtError::SessionRevoked)),
82 },
83 None => return Err(ChatLayerError::Jwt(JwtError::SessionRevoked)),
84 }
85 }
86
4587 Ok(AuthIdentity {
4688 user_id: claims.sub,
4789 session_id: Some(claims.jti.to_string()),
Modified chat/chatlayer/Cargo.toml View chat/chatlayer/Cargo.toml
@@ -19,6 +19,9 @@
1919cache.workspace = true
2020storage.workspace = true
2121jwt.workspace = true
22db.workspace = true
23rate_limit.workspace = true
24sea-orm.workspace = true
2225
2326tokio.workspace = true
2427tokio-util = { version = "0.7", features = ["io", "rt"] }
Modified dvcs/gitssh/handler/auth.rs View dvcs/gitssh/handler/auth.rs
@@ -97,6 +97,9 @@
9797 }
9898 match self.find_user_by_public_key(user, public_key).await {
9999 Ok(Some(found)) => {
100 if found.user.is_restricted || found.user.prohibit_login {
101 return Ok(Auth::reject());
102 }
100103 let key_id = found.key_id;
101104 self.operator = Some(found.user);
102105 self.update_key_last_used(key_id);
Modified lib/issues/service.rs View lib/issues/service.rs
@@ -42,6 +42,12 @@
4242 fn jwt_state(&self) -> &JwtState {
4343 &self.jwt
4444 }
45
46 fn access_blacklist(&self) -> Option<Arc<rate_limit::AccessTokenBlacklist>> {
47 Some(Arc::new(rate_limit::AccessTokenBlacklist::new(
48 self.kv.clone(),
49 )))
50 }
4551}
4652
4753impl rate_limit::RateLimitProvider for IssueService {
Modified lib/repo/service.rs View lib/repo/service.rs
@@ -48,6 +48,12 @@
4848 fn jwt_state(&self) -> &JwtState {
4949 &self.jwt
5050 }
51
52 fn access_blacklist(&self) -> Option<Arc<rate_limit::AccessTokenBlacklist>> {
53 Some(Arc::new(rate_limit::AccessTokenBlacklist::new(
54 self.kv.clone(),
55 )))
56 }
5157}
5258
5359impl RateLimitProvider for RepoService {
Modified lib/user/features/suspensions/usecase.rs View lib/user/features/suspensions/usecase.rs
@@ -64,6 +64,7 @@
6464 .await?
6565 .ok_or_else(|| UserError::NotFound(format!("user {target}")))?;
6666 let mut user_active: user_entity::ActiveModel = user.into();
67 user_active.is_active = Set(false);
6768 user_active.is_restricted = Set(true);
6869 user_active.updated_at = Set(now);
6970 user_active.update(&txn).await?;
@@ -141,6 +142,7 @@
141142 && let Some(user) = user_entity::Entity::find_by_id(user_id).one(&txn).await?
142143 {
143144 let mut user_active: user_entity::ActiveModel = user.into();
145 user_active.is_active = Set(true);
144146 user_active.is_restricted = Set(false);
145147 user_active.updated_at = Set(now);
146148 user_active.update(&txn).await?;
Modified Cargo.lock View Cargo.lock
@@ -990,11 +990,14 @@
990990dependencies = [
991991 "cache",
992992 "config 1.94.0",
993 "db",
993994 "futures",
994995 "jwt",
995996 "metrics",
996997 "object_store",
997998 "queue",
999 "rate_limit",
1000 "sea-orm",
9981001 "serde",
9991002 "serde_json",
10001003 "storage",
@@ -4455,6 +4458,7 @@
44554458 "live",
44564459 "obs",
44574460 "queue",
4461 "rate_limit",
44584462 "serde",
44594463 "storage",
44604464 "storagelayer",