Skip to content
Clone

feat(chat): add storagelayer auth helpers

zhenyi committed
parent cf6e897 commit 50e8674 Browse files

1 file changed

+170 −0

Added chat/storagelayer/auth.rs View chat/storagelayer/auth.rs
@@ -0,0 +1,170 @@
1use sea_orm::EntityTrait;
2use uuid::Uuid;
3
4use crate::error::{ChatStorageError, ChatStorageResult};
5use crate::models::attachment::AttachmentOwner;
6use crate::repository::ChatStorageCtx;
7
8use db::attachment::attachments as att_entity;
9use db::chat::chat_members as member_entity;
10use db::chat::chat_messages as message_entity;
11use db::chat::chats as chat_entity;
12
13pub async fn require_authenticated(ctx: &ChatStorageCtx) -> ChatStorageResult<Uuid> {
14 ctx.require_user()
15}
16
17pub fn require_self(ctx: &ChatStorageCtx, target_user_id: Uuid) -> ChatStorageResult<Uuid> {
18 let user_id = ctx.require_user()?;
19 if user_id != target_user_id {
20 return Err(ChatStorageError::PermissionDenied(format!(
21 "user {user_id} cannot act on behalf of {target_user_id}"
22 )));
23 }
24 Ok(user_id)
25}
26
27pub async fn require_member(
28 db: &db::conn::Db,
29 ctx: &ChatStorageCtx,
30 chat_id: Uuid,
31) -> ChatStorageResult<Uuid> {
32 let user_id = require_authenticated(ctx).await?;
33 let member = member_entity::Entity::find_by_id((chat_id, user_id))
34 .one(&db.reader())
35 .await?;
36 if member.is_none() {
37 return Err(ChatStorageError::PermissionDenied(format!(
38 "user {user_id} is not a member of chat {chat_id}"
39 )));
40 }
41 Ok(user_id)
42}
43
44pub async fn require_admin(
45 db: &db::conn::Db,
46 ctx: &ChatStorageCtx,
47 chat_id: Uuid,
48) -> ChatStorageResult<Uuid> {
49 let user_id = require_authenticated(ctx).await?;
50 let chat = chat_entity::Entity::find_by_id(chat_id)
51 .one(&db.reader())
52 .await?
53 .ok_or(ChatStorageError::ConversationNotFound(chat_id))?;
54 if chat.creator_user_id != user_id {
55 return Err(ChatStorageError::PermissionDenied(format!(
56 "user {user_id} is not admin of chat {chat_id}"
57 )));
58 }
59 Ok(user_id)
60}
61
62pub async fn require_sender_or_admin(
63 db: &db::conn::Db,
64 ctx: &ChatStorageCtx,
65 message_id: Uuid,
66) -> ChatStorageResult<Uuid> {
67 let user_id = require_authenticated(ctx).await?;
68 let msg = message_entity::Entity::find_by_id(message_id)
69 .one(&db.reader())
70 .await?
71 .ok_or(ChatStorageError::MessageNotFound(message_id))?;
72 if msg.sender_id == user_id {
73 return Ok(user_id);
74 }
75 let chat = chat_entity::Entity::find_by_id(msg.chat_id)
76 .one(&db.reader())
77 .await?
78 .ok_or(ChatStorageError::ConversationNotFound(msg.chat_id))?;
79 if chat.creator_user_id != user_id {
80 return Err(ChatStorageError::PermissionDenied(format!(
81 "user {user_id} is neither sender nor admin of message {message_id}"
82 )));
83 }
84 Ok(user_id)
85}
86
87pub async fn require_uploader_or_admin(
88 db: &db::conn::Db,
89 ctx: &ChatStorageCtx,
90 attachment_id: Uuid,
91) -> ChatStorageResult<Uuid> {
92 let user_id = require_authenticated(ctx).await?;
93 let att = att_entity::Entity::find_by_id(attachment_id)
94 .one(&db.reader())
95 .await?
96 .ok_or(ChatStorageError::AttachmentNotFound(attachment_id))?;
97 if att.uploaded_by == Some(user_id) {
98 return Ok(user_id);
99 }
100 let owner_msg = message_entity::Entity::find_by_id(att.owner_id)
101 .one(&db.reader())
102 .await?;
103 let chat_id = match owner_msg {
104 Some(m) => m.chat_id,
105 None => {
106 return Err(ChatStorageError::PermissionDenied(format!(
107 "user {user_id} cannot delete attachment {attachment_id}: owner not found"
108 )));
109 }
110 };
111 let chat = chat_entity::Entity::find_by_id(chat_id)
112 .one(&db.reader())
113 .await?
114 .ok_or(ChatStorageError::ConversationNotFound(chat_id))?;
115 if chat.creator_user_id != user_id {
116 return Err(ChatStorageError::PermissionDenied(format!(
117 "user {user_id} cannot delete attachment {attachment_id}"
118 )));
119 }
120 Ok(user_id)
121}
122
123pub async fn chat_id_for_attachment_owner(
124 db: &db::conn::Db,
125 owner: AttachmentOwner,
126 owner_id: Uuid,
127) -> ChatStorageResult<Option<Uuid>> {
128 match owner {
129 AttachmentOwner::ChatMessage => {
130 let msg = message_entity::Entity::find_by_id(owner_id)
131 .one(&db.reader())
132 .await?
133 .ok_or(ChatStorageError::MessageNotFound(owner_id))?;
134 Ok(Some(msg.chat_id))
135 }
136 _ => Ok(None),
137 }
138}
139
140pub async fn require_chat_member_for_attachment_owner(
141 db: &db::conn::Db,
142 ctx: &ChatStorageCtx,
143 owner: AttachmentOwner,
144 owner_id: Uuid,
145) -> ChatStorageResult<Uuid> {
146 let user_id = require_authenticated(ctx).await?;
147 if let Some(chat_id) = chat_id_for_attachment_owner(db, owner, owner_id).await? {
148 let member = member_entity::Entity::find_by_id((chat_id, user_id))
149 .one(&db.reader())
150 .await?;
151 if member.is_none() {
152 return Err(ChatStorageError::PermissionDenied(format!(
153 "user {user_id} is not a member of chat {chat_id}"
154 )));
155 }
156 }
157 Ok(user_id)
158}
159
160#[allow(dead_code)]
161pub async fn chat_id_for_message(
162 db: &db::conn::Db,
163 message_id: Uuid,
164) -> ChatStorageResult<Uuid> {
165 let msg = message_entity::Entity::find_by_id(message_id)
166 .one(&db.reader())
167 .await?
168 .ok_or(ChatStorageError::MessageNotFound(message_id))?;
169 Ok(msg.chat_id)
170}
No newline at end of file