Skip to main content

rocketmq_protocol/protocol/body/
user_info.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.
14
15use std::fmt;
16
17use cheetah_string::CheetahString;
18use rocketmq_error::REDACTED;
19use serde::Deserialize;
20use serde::Serialize;
21
22#[derive(Serialize, Deserialize, Default, Clone)]
23#[serde(rename_all = "camelCase")]
24pub struct UserInfo {
25    pub username: Option<CheetahString>,
26    pub password: Option<CheetahString>,
27    pub user_type: Option<CheetahString>,
28    pub user_status: Option<CheetahString>,
29}
30
31impl fmt::Debug for UserInfo {
32    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33        f.debug_struct("UserInfo")
34            .field("username", &self.username)
35            .field("password", &redacted_if_present(self.password.as_ref()))
36            .field("user_type", &self.user_type)
37            .field("user_status", &self.user_status)
38            .finish()
39    }
40}
41
42impl fmt::Display for UserInfo {
43    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44        write!(
45            f,
46            "UserInfo [username={}, password={}, user_type={}, user_status={}]",
47            self.username.as_ref().unwrap_or(&CheetahString::new()),
48            redacted_if_present(self.password.as_ref()).unwrap_or_default(),
49            self.user_type.as_ref().unwrap_or(&CheetahString::new()),
50            self.user_status.as_ref().unwrap_or(&CheetahString::new())
51        )
52    }
53}
54
55fn redacted_if_present<T>(value: Option<T>) -> Option<&'static str> {
56    value.map(|_| REDACTED)
57}
58
59#[cfg(test)]
60mod tests {
61    use serde_json;
62
63    use super::*;
64
65    #[test]
66    fn serialize_user_info() {
67        let user_info = UserInfo {
68            username: Some(CheetahString::from("user")),
69            password: Some(CheetahString::from("pass")),
70            user_type: Some(CheetahString::from("admin")),
71            user_status: Some(CheetahString::from("active")),
72        };
73        let serialized = serde_json::to_string(&user_info).unwrap();
74        assert!(serialized.contains("\"username\":\"user\""));
75        assert!(serialized.contains("\"password\":\"pass\""));
76        assert!(serialized.contains("\"userType\":\"admin\""));
77        assert!(serialized.contains("\"userStatus\":\"active\""));
78    }
79
80    #[test]
81    fn deserialize_user_info() {
82        let json = r#"{
83            "username": "user",
84            "password": "pass",
85            "userType": "admin",
86            "userStatus": "active"
87        }"#;
88        let deserialized: UserInfo = serde_json::from_str(json).unwrap();
89        assert_eq!(deserialized.username, Some(CheetahString::from("user")));
90        assert_eq!(deserialized.password, Some(CheetahString::from("pass")));
91        assert_eq!(deserialized.user_type, Some(CheetahString::from("admin")));
92        assert_eq!(deserialized.user_status, Some(CheetahString::from("active")));
93    }
94
95    #[test]
96    fn deserialize_user_info_missing_optional_fields() {
97        let json = r#"{}"#;
98        let deserialized: UserInfo = serde_json::from_str(json).unwrap();
99        assert!(deserialized.username.is_none());
100        assert!(deserialized.password.is_none());
101        assert!(deserialized.user_type.is_none());
102        assert!(deserialized.user_status.is_none());
103    }
104
105    #[test]
106    fn display_user_info() {
107        let user_info = UserInfo {
108            username: Some(CheetahString::from("user")),
109            password: Some(CheetahString::from("pass")),
110            user_type: Some(CheetahString::from("admin")),
111            user_status: Some(CheetahString::from("active")),
112        };
113        let display = format!("{}", user_info);
114        assert_eq!(
115            display,
116            "UserInfo [username=user, password=<redacted>, user_type=admin, user_status=active]"
117        );
118        assert!(!display.contains("password=pass"));
119    }
120
121    #[test]
122    fn debug_user_info_redacts_password() {
123        let user_info = UserInfo {
124            username: Some(CheetahString::from("user")),
125            password: Some(CheetahString::from("plain-password")),
126            user_type: Some(CheetahString::from("admin")),
127            user_status: Some(CheetahString::from("active")),
128        };
129        let debug = format!("{user_info:?}");
130
131        assert!(debug.contains("user"));
132        assert!(debug.contains(REDACTED));
133        assert!(!debug.contains("plain-password"));
134    }
135}