Skip to main content

users_manage/
users_manage.rs

1//! Example: create a user, check its credentials, and query privileges.
2//!
3//! ```text
4//! cargo run --example users_manage
5//! ```
6//!
7//! Everything happens in memory. The station's users file is not written and no
8//! existing account is touched.
9//!
10//! Effective privileges normally come from the groups a user belongs to, which
11//! are configured on the station, so this shows creating and interrogating a
12//! user rather than granting rights.
13
14use rs_teststand::{Engine, User, UserPrivilege};
15
16/// Prints which of a selection of privileges a user holds.
17fn report_privileges(user: &User, privileges: &[UserPrivilege]) -> Result<(), rs_teststand::Error> {
18    for privilege in privileges {
19        // has_privilege answers for the user *and* any group they belong to.
20        println!(
21            "    {:<20} {}",
22            privilege.name(),
23            user.has_privilege(*privilege)?
24        );
25    }
26    Ok(())
27}
28
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}