rs_teststand/users/user.rs
1//! A station user.
2
3use rs_teststand_sys::{Dispatch, Value};
4
5use crate::Error;
6use crate::dispids::user;
7use crate::property::PropertyObject;
8use crate::users::UserPrivilege;
9
10/// A user account (`User`).
11///
12/// Obtained from [`Engine::new_user`](crate::Engine::new_user),
13/// [`Engine::get_user`](crate::Engine::get_user), or
14/// [`Engine::current_user`](crate::Engine::current_user).
15///
16/// A user object built in memory is not part of the station until it is written
17/// to the users file; creating and configuring one here changes nothing on disk.
18///
19/// Effective privileges usually come from the groups a user belongs to rather
20/// than from the user directly, which is why
21/// [`has_privilege`](Self::has_privilege) answers for the user *and* their
22/// groups, while [`privileges`](Self::privileges) exposes only what is set on
23/// the user itself.
24#[derive(Debug)]
25pub struct User {
26 dispatch: Box<dyn Dispatch>,
27}
28
29impl User {
30 /// Wraps a dispatch handle returned by the engine.
31 pub(crate) fn new(dispatch: Box<dyn Dispatch>) -> Self {
32 Self { dispatch }
33 }
34
35 /// The login name (`User.LoginName`).
36 ///
37 /// # Errors
38 /// [`Error`] if the COM call fails or returns an unexpected type.
39 pub fn login_name(&self) -> Result<String, Error> {
40 Ok(self.dispatch.get(user::LOGIN_NAME)?.into_string()?)
41 }
42
43 /// Sets the login name (`User.LoginName`).
44 ///
45 /// # Errors
46 /// [`Error`] if the COM call fails.
47 pub fn set_login_name(&self, name: &str) -> Result<(), Error> {
48 self.dispatch
49 .put(user::LOGIN_NAME, Value::Str(name.to_owned()))?;
50 Ok(())
51 }
52
53 /// The display name (`User.FullName`).
54 ///
55 /// # Errors
56 /// [`Error`] if the COM call fails or returns an unexpected type.
57 pub fn full_name(&self) -> Result<String, Error> {
58 Ok(self.dispatch.get(user::FULL_NAME)?.into_string()?)
59 }
60
61 /// Sets the display name (`User.FullName`).
62 ///
63 /// # Errors
64 /// [`Error`] if the COM call fails.
65 pub fn set_full_name(&self, name: &str) -> Result<(), Error> {
66 self.dispatch
67 .put(user::FULL_NAME, Value::Str(name.to_owned()))?;
68 Ok(())
69 }
70
71 /// Sets the password (`User.Password`).
72 ///
73 /// # Errors
74 /// [`Error`] if the COM call fails.
75 pub fn set_password(&self, password: &str) -> Result<(), Error> {
76 self.dispatch
77 .put(user::PASSWORD, Value::Str(password.to_owned()))?;
78 Ok(())
79 }
80
81 /// Reads the stored password field (`User.Password`).
82 ///
83 /// Prefer [`validate_password`](Self::validate_password) for checking a
84 /// credential: it compares without the caller handling the stored value.
85 ///
86 /// # Errors
87 /// [`Error`] if the COM call fails or returns an unexpected type.
88 pub fn password(&self) -> Result<String, Error> {
89 Ok(self.dispatch.get(user::PASSWORD)?.into_string()?)
90 }
91
92 /// Whether `password` matches this user's (`User.ValidatePassword`).
93 ///
94 /// # Errors
95 /// [`Error`] if the COM call fails or returns an unexpected type.
96 pub fn validate_password(&self, password: &str) -> Result<bool, Error> {
97 Ok(self
98 .dispatch
99 .call(user::VALIDATE_PASSWORD, &[Value::Str(password.to_owned())])?
100 .as_bool()?)
101 }
102
103 /// Whether the user, or any group they belong to, holds a privilege
104 /// (`User.HasPrivilege`).
105 ///
106 /// # Errors
107 /// [`Error`] if the COM call fails or returns an unexpected type.
108 pub fn has_privilege(&self, privilege: UserPrivilege) -> Result<bool, Error> {
109 self.has_privilege_named(privilege.name())
110 }
111
112 /// Whether the user holds a privilege named by string
113 /// (`User.HasPrivilege`).
114 ///
115 /// Takes either a base name or a full path such as
116 /// `Debug.RunSelectedTests`. Prefer
117 /// [`has_privilege`](Self::has_privilege) for the built-in set; this exists
118 /// for custom privileges, which no enum can enumerate.
119 ///
120 /// # Errors
121 /// [`Error`] if the COM call fails or returns an unexpected type.
122 pub fn has_privilege_named(&self, privilege: &str) -> Result<bool, Error> {
123 Ok(self
124 .dispatch
125 .call(user::HAS_PRIVILEGE, &[Value::Str(privilege.to_owned())])?
126 .as_bool()?)
127 }
128
129 /// The privilege settings held on the user itself (`User.Privileges`).
130 ///
131 /// This is not the answer to "can this user do X", group membership is not
132 /// reflected here. Use [`has_privilege`](Self::has_privilege) for that.
133 ///
134 /// # Errors
135 /// [`Error`] if the COM call fails or returns an unexpected type.
136 pub fn privileges(&self) -> Result<PropertyObject, Error> {
137 Ok(PropertyObject::new(
138 self.dispatch.get(user::PRIVILEGES)?.into_object()?,
139 ))
140 }
141
142 /// The member list of a user *group* (`User.Members`).
143 ///
144 /// Only meaningful when this object represents a group rather than a person.
145 ///
146 /// # Errors
147 /// [`Error`] if the COM call fails or returns an unexpected type.
148 pub fn members(&self) -> Result<PropertyObject, Error> {
149 Ok(PropertyObject::new(
150 self.dispatch.get(user::MEMBERS)?.into_object()?,
151 ))
152 }
153
154 /// The user as a plain property tree (`User.AsPropertyObject`).
155 ///
156 /// # Errors
157 /// [`Error`] if the COM call fails or returns an unexpected type.
158 pub fn as_property_object(&self) -> Result<PropertyObject, Error> {
159 Ok(PropertyObject::new(
160 self.dispatch
161 .call(user::AS_PROPERTY_OBJECT, &[])?
162 .into_object()?,
163 ))
164 }
165
166 /// An owned handle to the same user, for passing it back to the engine.
167 pub(crate) fn duplicate_dispatch(&self) -> Option<Box<dyn Dispatch>> {
168 self.dispatch.duplicate()
169 }
170}