Skip to main content

rocketmq_auth/authentication/model/
user.rs

1// Copyright 2023 The RocketMQ Rust Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14use std::sync::OnceLock;
15
16use cheetah_string::CheetahString;
17use serde::Deserialize;
18use serde::Serialize;
19
20use crate::authentication::enums::subject_type::SubjectType;
21use crate::authentication::enums::user_status::UserStatus;
22use crate::authentication::enums::user_type::UserType;
23use crate::authentication::model::subject::Subject;
24
25#[derive(Debug, Clone, Serialize, Deserialize)]
26pub struct User {
27    username: CheetahString,
28    password: Option<CheetahString>,
29    #[serde(rename = "userType")]
30    user_type: Option<UserType>,
31    #[serde(rename = "userStatus")]
32    user_status: Option<UserStatus>,
33    #[serde(skip)]
34    subject_key_cache: OnceLock<String>,
35}
36
37impl User {
38    pub fn of(username: impl Into<CheetahString>) -> Self {
39        User {
40            username: username.into(),
41            password: None,
42            user_type: None,
43            user_status: None,
44            subject_key_cache: OnceLock::new(),
45        }
46    }
47
48    pub fn of_with_password(username: impl Into<CheetahString>, password: impl Into<CheetahString>) -> Self {
49        User {
50            username: username.into(),
51            password: Some(password.into()),
52            user_type: None,
53            user_status: None,
54            subject_key_cache: OnceLock::new(),
55        }
56    }
57
58    pub fn of_with_type(
59        username: impl Into<CheetahString>,
60        password: impl Into<CheetahString>,
61        user_type: UserType,
62    ) -> Self {
63        User {
64            username: username.into(),
65            password: Some(password.into()),
66            user_type: Some(user_type),
67            user_status: None,
68            subject_key_cache: OnceLock::new(),
69        }
70    }
71}
72
73impl User {
74    pub fn username(&self) -> &CheetahString {
75        &self.username
76    }
77
78    pub fn password(&self) -> Option<&CheetahString> {
79        self.password.as_ref()
80    }
81
82    pub fn user_type(&self) -> Option<UserType> {
83        self.user_type
84    }
85
86    pub fn user_status(&self) -> Option<UserStatus> {
87        self.user_status
88    }
89
90    pub fn set_user_status(&mut self, status: UserStatus) {
91        self.user_status = Some(status);
92    }
93
94    pub fn set_password(&mut self, password: impl Into<CheetahString>) {
95        self.password = Some(password.into());
96    }
97
98    pub fn set_user_type(&mut self, user_type: UserType) {
99        self.user_type = Some(user_type);
100    }
101
102    pub fn username_from_subject_key(subject_key: &str) -> &str {
103        subject_key
104            .split_once(':')
105            .map(|(_, username)| username)
106            .unwrap_or(subject_key)
107    }
108}
109
110impl Subject for User {
111    fn subject_key(&self) -> &str {
112        self.subject_key_cache
113            .get_or_init(|| format!("{}:{}", SubjectType::User.name(), self.username))
114            .as_str()
115    }
116
117    fn subject_type(&self) -> SubjectType {
118        SubjectType::User
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125
126    #[test]
127    fn test_user_of() {
128        let username = "test_user";
129        let user = User::of(username);
130        assert_eq!(user.username(), &CheetahString::from(username));
131        assert!(user.password().is_none());
132        assert!(user.user_type().is_none());
133        assert!(user.user_status().is_none());
134    }
135
136    #[test]
137    fn test_user_of_with_password() {
138        let username = "test_user";
139        let password = "test_password";
140        let user = User::of_with_password(username, password);
141        assert_eq!(user.username(), &CheetahString::from(username));
142        assert_eq!(user.password(), Some(&CheetahString::from(password)));
143        assert!(user.user_type().is_none());
144        assert!(user.user_status().is_none());
145    }
146
147    #[test]
148    fn test_user_of_with_type() {
149        let username = "test_user";
150        let password = "test_password";
151        let user_type = UserType::Normal;
152        let user = User::of_with_type(username, password, user_type);
153        assert_eq!(user.username(), &CheetahString::from(username));
154        assert_eq!(user.password(), Some(&CheetahString::from(password)));
155        assert_eq!(user.user_type(), Some(user_type));
156        assert!(user.user_status().is_none());
157    }
158
159    #[test]
160    fn test_user_set_user_status() {
161        let mut user = User::of("test_user");
162        user.set_user_status(UserStatus::Enable);
163        assert_eq!(user.user_status(), Some(UserStatus::Enable));
164    }
165
166    #[test]
167    fn test_user_subject_trait() {
168        let username = "test_user";
169        let user = User::of(username);
170        assert_eq!(user.subject_key(), "User:test_user");
171        assert_eq!(user.subject_type(), SubjectType::User);
172    }
173}