pub struct UsersFile { /* private fields */ }Expand description
The station’s user list, as a file (UsersFile).
Reached from Engine::users_file. Everything a
station knows about who may log in and what they may do lives here, and this
is the only route to persisting it: User objects created
through Engine::new_user exist in memory until
this file is saved.
The lists are PropertyObject arrays rather than typed collections,
because that is what the engine returns and how it expects them to be
edited. Add or remove an element with the property-object API and the change
lands in the list itself; see user_list.
§Saving
Go through as_property_object_file and
call PropertyObjectFile::save_file_if_modified, passing false so the
engine writes the file instead of asking a person first.
Implementations§
Source§impl UsersFile
impl UsersFile
Sourcepub fn user_list(&self) -> Result<PropertyObject, Error>
pub fn user_list(&self) -> Result<PropertyObject, Error>
The users, as an array of User objects (UsersFile.UserList).
Editing happens through this array, not through a setter: use the property-object API to append or remove an element and the station’s user list changes with it.
§Errors
Error if the COM call fails or returns an unexpected type.
Examples found in repository?
29fn main() -> Result<(), rs_teststand::Error> {
30 let engine = Engine::new()?;
31
32 // Passing no profile means the user inherits nothing.
33 let user = engine.new_user(None)?;
34 user.set_login_name("operator1")?;
35 user.set_full_name("Test Operator")?;
36 user.set_password("ts-secret")?;
37
38 println!("User: {} ({})", user.login_name()?, user.full_name()?);
39
40 // Check a credential without handling the stored value.
41 println!(
42 " password 'ts-secret' valid: {}",
43 user.validate_password("ts-secret")?
44 );
45 println!(
46 " password 'wrong' valid: {}",
47 user.validate_password("wrong")?
48 );
49
50 println!(" privilege checks:");
51 report_privileges(
52 &user,
53 &[
54 UserPrivilege::Operate,
55 UserPrivilege::Execute,
56 UserPrivilege::Develop,
57 UserPrivilege::Debug,
58 UserPrivilege::EditUsers,
59 ],
60 )?;
61
62 // A privilege can also be named by its full path, which is how a leaf
63 // inside a category is reached.
64 println!(
65 " Debug.RunSelectedTests: {}",
66 user.has_privilege_named("Debug.RunSelectedTests")?
67 );
68
69 // The privilege tree is nested: categories hold the individual rights.
70 let privileges = user.privileges()?;
71 let count = privileges.get_num_sub_properties("")?;
72 println!(" privilege tree ({count} categories):");
73 for index in 0..count {
74 let name = privileges.get_nth_sub_property_name("", index, 0)?;
75 let members = privileges
76 .get_property_object(&name, 0)?
77 .get_num_sub_properties("")?;
78 println!(" {name:<12} {members} member(s)");
79 }
80
81 // The station itself, read-only.
82 println!("\nStation:");
83 match engine.current_user()? {
84 Some(current) => println!(" logged in as {}", current.login_name()?),
85 None => println!(" nobody logged in (usual when login is not required)"),
86 }
87 println!(
88 " is 'operator1' a real account here? {}",
89 engine.user_name_exists("operator1")?
90 );
91
92 // Where accounts actually live. Everything above this point exists only in
93 // memory: `new_user` builds a User, it does not enrol one. A user becomes
94 // part of the station when it is inserted into this file's list and the
95 // file is written.
96 let users_file = engine.users_file()?;
97 let file = users_file.as_property_object_file()?;
98 println!(
99 "
100Users file:"
101 );
102 println!(" path {}", file.path()?);
103 println!(
104 " users {}",
105 users_file.user_list()?.get_num_elements()?
106 );
107 println!(
108 " groups {}",
109 users_file.user_group_list()?.get_num_elements()?
110 );
111 println!(
112 " profiles {}",
113 users_file.user_profile_list()?.get_num_elements()?
114 );
115
116 // Deliberately not run: it would edit the station this example is reading.
117 // The two calls a host makes to enrol the user built above are
118 //
119 // users_file.user_list()?.set_num_elements(count + 1, 0)?; // then fill
120 // file.save_file_if_modified(false)?; // then write
121 //
122 // `false` matters: with `true` the engine puts a save dialog on screen, and
123 // a host with no operator would wait for an answer that never comes.
124 println!(
125 "
126 (this example does not write; enrolling a user would alter the station)"
127 );
128
129 Ok(())
130}Sourcepub fn user_group_list(&self) -> Result<PropertyObject, Error>
pub fn user_group_list(&self) -> Result<PropertyObject, Error>
The user groups (UsersFile.UserGroupList).
§Errors
Error if the COM call fails or returns an unexpected type.
Examples found in repository?
29fn main() -> Result<(), rs_teststand::Error> {
30 let engine = Engine::new()?;
31
32 // Passing no profile means the user inherits nothing.
33 let user = engine.new_user(None)?;
34 user.set_login_name("operator1")?;
35 user.set_full_name("Test Operator")?;
36 user.set_password("ts-secret")?;
37
38 println!("User: {} ({})", user.login_name()?, user.full_name()?);
39
40 // Check a credential without handling the stored value.
41 println!(
42 " password 'ts-secret' valid: {}",
43 user.validate_password("ts-secret")?
44 );
45 println!(
46 " password 'wrong' valid: {}",
47 user.validate_password("wrong")?
48 );
49
50 println!(" privilege checks:");
51 report_privileges(
52 &user,
53 &[
54 UserPrivilege::Operate,
55 UserPrivilege::Execute,
56 UserPrivilege::Develop,
57 UserPrivilege::Debug,
58 UserPrivilege::EditUsers,
59 ],
60 )?;
61
62 // A privilege can also be named by its full path, which is how a leaf
63 // inside a category is reached.
64 println!(
65 " Debug.RunSelectedTests: {}",
66 user.has_privilege_named("Debug.RunSelectedTests")?
67 );
68
69 // The privilege tree is nested: categories hold the individual rights.
70 let privileges = user.privileges()?;
71 let count = privileges.get_num_sub_properties("")?;
72 println!(" privilege tree ({count} categories):");
73 for index in 0..count {
74 let name = privileges.get_nth_sub_property_name("", index, 0)?;
75 let members = privileges
76 .get_property_object(&name, 0)?
77 .get_num_sub_properties("")?;
78 println!(" {name:<12} {members} member(s)");
79 }
80
81 // The station itself, read-only.
82 println!("\nStation:");
83 match engine.current_user()? {
84 Some(current) => println!(" logged in as {}", current.login_name()?),
85 None => println!(" nobody logged in (usual when login is not required)"),
86 }
87 println!(
88 " is 'operator1' a real account here? {}",
89 engine.user_name_exists("operator1")?
90 );
91
92 // Where accounts actually live. Everything above this point exists only in
93 // memory: `new_user` builds a User, it does not enrol one. A user becomes
94 // part of the station when it is inserted into this file's list and the
95 // file is written.
96 let users_file = engine.users_file()?;
97 let file = users_file.as_property_object_file()?;
98 println!(
99 "
100Users file:"
101 );
102 println!(" path {}", file.path()?);
103 println!(
104 " users {}",
105 users_file.user_list()?.get_num_elements()?
106 );
107 println!(
108 " groups {}",
109 users_file.user_group_list()?.get_num_elements()?
110 );
111 println!(
112 " profiles {}",
113 users_file.user_profile_list()?.get_num_elements()?
114 );
115
116 // Deliberately not run: it would edit the station this example is reading.
117 // The two calls a host makes to enrol the user built above are
118 //
119 // users_file.user_list()?.set_num_elements(count + 1, 0)?; // then fill
120 // file.save_file_if_modified(false)?; // then write
121 //
122 // `false` matters: with `true` the engine puts a save dialog on screen, and
123 // a host with no operator would wait for an answer that never comes.
124 println!(
125 "
126 (this example does not write; enrolling a user would alter the station)"
127 );
128
129 Ok(())
130}Sourcepub fn user_profile_list(&self) -> Result<PropertyObject, Error>
pub fn user_profile_list(&self) -> Result<PropertyObject, Error>
The user profiles (UsersFile.UserProfileList).
§Errors
Error if the COM call fails or returns an unexpected type.
Examples found in repository?
29fn main() -> Result<(), rs_teststand::Error> {
30 let engine = Engine::new()?;
31
32 // Passing no profile means the user inherits nothing.
33 let user = engine.new_user(None)?;
34 user.set_login_name("operator1")?;
35 user.set_full_name("Test Operator")?;
36 user.set_password("ts-secret")?;
37
38 println!("User: {} ({})", user.login_name()?, user.full_name()?);
39
40 // Check a credential without handling the stored value.
41 println!(
42 " password 'ts-secret' valid: {}",
43 user.validate_password("ts-secret")?
44 );
45 println!(
46 " password 'wrong' valid: {}",
47 user.validate_password("wrong")?
48 );
49
50 println!(" privilege checks:");
51 report_privileges(
52 &user,
53 &[
54 UserPrivilege::Operate,
55 UserPrivilege::Execute,
56 UserPrivilege::Develop,
57 UserPrivilege::Debug,
58 UserPrivilege::EditUsers,
59 ],
60 )?;
61
62 // A privilege can also be named by its full path, which is how a leaf
63 // inside a category is reached.
64 println!(
65 " Debug.RunSelectedTests: {}",
66 user.has_privilege_named("Debug.RunSelectedTests")?
67 );
68
69 // The privilege tree is nested: categories hold the individual rights.
70 let privileges = user.privileges()?;
71 let count = privileges.get_num_sub_properties("")?;
72 println!(" privilege tree ({count} categories):");
73 for index in 0..count {
74 let name = privileges.get_nth_sub_property_name("", index, 0)?;
75 let members = privileges
76 .get_property_object(&name, 0)?
77 .get_num_sub_properties("")?;
78 println!(" {name:<12} {members} member(s)");
79 }
80
81 // The station itself, read-only.
82 println!("\nStation:");
83 match engine.current_user()? {
84 Some(current) => println!(" logged in as {}", current.login_name()?),
85 None => println!(" nobody logged in (usual when login is not required)"),
86 }
87 println!(
88 " is 'operator1' a real account here? {}",
89 engine.user_name_exists("operator1")?
90 );
91
92 // Where accounts actually live. Everything above this point exists only in
93 // memory: `new_user` builds a User, it does not enrol one. A user becomes
94 // part of the station when it is inserted into this file's list and the
95 // file is written.
96 let users_file = engine.users_file()?;
97 let file = users_file.as_property_object_file()?;
98 println!(
99 "
100Users file:"
101 );
102 println!(" path {}", file.path()?);
103 println!(
104 " users {}",
105 users_file.user_list()?.get_num_elements()?
106 );
107 println!(
108 " groups {}",
109 users_file.user_group_list()?.get_num_elements()?
110 );
111 println!(
112 " profiles {}",
113 users_file.user_profile_list()?.get_num_elements()?
114 );
115
116 // Deliberately not run: it would edit the station this example is reading.
117 // The two calls a host makes to enrol the user built above are
118 //
119 // users_file.user_list()?.set_num_elements(count + 1, 0)?; // then fill
120 // file.save_file_if_modified(false)?; // then write
121 //
122 // `false` matters: with `true` the engine puts a save dialog on screen, and
123 // a host with no operator would wait for an answer that never comes.
124 println!(
125 "
126 (this example does not write; enrolling a user would alter the station)"
127 );
128
129 Ok(())
130}Sourcepub fn reload_from_disk(&self) -> Result<(), Error>
pub fn reload_from_disk(&self) -> Result<(), Error>
Re-reads the file from disk (UsersFile.ReloadFromDisk).
Everything read from this file beforehand is stale afterwards. The
engine documents that references to the user list and to individual
users are out of date once this returns, so drop what you are holding
and read it again rather than reusing it. Consuming &self cannot
express that, since the file object itself stays valid; only what came
out of it does not.
§Errors
Error if the COM call fails.
Sourcepub fn as_property_object_file(&self) -> Result<PropertyObjectFile, Error>
pub fn as_property_object_file(&self) -> Result<PropertyObjectFile, Error>
The file view of this object (UsersFile.AsPropertyObjectFile).
Where the path lives, and where saving happens.
§Errors
Error if the COM call fails or returns an unexpected type.
Examples found in repository?
29fn main() -> Result<(), rs_teststand::Error> {
30 let engine = Engine::new()?;
31
32 // Passing no profile means the user inherits nothing.
33 let user = engine.new_user(None)?;
34 user.set_login_name("operator1")?;
35 user.set_full_name("Test Operator")?;
36 user.set_password("ts-secret")?;
37
38 println!("User: {} ({})", user.login_name()?, user.full_name()?);
39
40 // Check a credential without handling the stored value.
41 println!(
42 " password 'ts-secret' valid: {}",
43 user.validate_password("ts-secret")?
44 );
45 println!(
46 " password 'wrong' valid: {}",
47 user.validate_password("wrong")?
48 );
49
50 println!(" privilege checks:");
51 report_privileges(
52 &user,
53 &[
54 UserPrivilege::Operate,
55 UserPrivilege::Execute,
56 UserPrivilege::Develop,
57 UserPrivilege::Debug,
58 UserPrivilege::EditUsers,
59 ],
60 )?;
61
62 // A privilege can also be named by its full path, which is how a leaf
63 // inside a category is reached.
64 println!(
65 " Debug.RunSelectedTests: {}",
66 user.has_privilege_named("Debug.RunSelectedTests")?
67 );
68
69 // The privilege tree is nested: categories hold the individual rights.
70 let privileges = user.privileges()?;
71 let count = privileges.get_num_sub_properties("")?;
72 println!(" privilege tree ({count} categories):");
73 for index in 0..count {
74 let name = privileges.get_nth_sub_property_name("", index, 0)?;
75 let members = privileges
76 .get_property_object(&name, 0)?
77 .get_num_sub_properties("")?;
78 println!(" {name:<12} {members} member(s)");
79 }
80
81 // The station itself, read-only.
82 println!("\nStation:");
83 match engine.current_user()? {
84 Some(current) => println!(" logged in as {}", current.login_name()?),
85 None => println!(" nobody logged in (usual when login is not required)"),
86 }
87 println!(
88 " is 'operator1' a real account here? {}",
89 engine.user_name_exists("operator1")?
90 );
91
92 // Where accounts actually live. Everything above this point exists only in
93 // memory: `new_user` builds a User, it does not enrol one. A user becomes
94 // part of the station when it is inserted into this file's list and the
95 // file is written.
96 let users_file = engine.users_file()?;
97 let file = users_file.as_property_object_file()?;
98 println!(
99 "
100Users file:"
101 );
102 println!(" path {}", file.path()?);
103 println!(
104 " users {}",
105 users_file.user_list()?.get_num_elements()?
106 );
107 println!(
108 " groups {}",
109 users_file.user_group_list()?.get_num_elements()?
110 );
111 println!(
112 " profiles {}",
113 users_file.user_profile_list()?.get_num_elements()?
114 );
115
116 // Deliberately not run: it would edit the station this example is reading.
117 // The two calls a host makes to enrol the user built above are
118 //
119 // users_file.user_list()?.set_num_elements(count + 1, 0)?; // then fill
120 // file.save_file_if_modified(false)?; // then write
121 //
122 // `false` matters: with `true` the engine puts a save dialog on screen, and
123 // a host with no operator would wait for an answer that never comes.
124 println!(
125 "
126 (this example does not write; enrolling a user would alter the station)"
127 );
128
129 Ok(())
130}