authentication/
group.rs

1// Group management functionality for the Authentication module.
2//
3// This module provides group management capabilities including:
4// - Group creation and management
5// - User group membership tracking
6// - File-based persistent storage of group data
7//
8// All group data is stored as JSON files in the `/System/Groups/` directory,
9// with each group having their own file named after their group name.
10
11use alloc::{
12    borrow::ToOwned,
13    string::{String, ToString},
14    vec,
15    vec::Vec,
16};
17use file_system::{Flags, Mode, Open, Path, PathOwned};
18use miniserde::{Deserialize, Serialize};
19use users::{GroupIdentifier, GroupIdentifierInner, UserIdentifier, UserIdentifierInner};
20use virtual_file_system::{Directory, File, VirtualFileSystem};
21
22use crate::{Error, GROUP_FOLDER_PATH, Result};
23
24/// Represents a user group with associated metadata and member list.
25///
26/// This structure contains all the information needed to represent a group
27/// in the system, including its unique identifier, name, and list of users
28/// that belong to the group.
29#[derive(Clone, Debug, Deserialize, Serialize)]
30pub struct Group {
31    /// Unique identifier for the group
32    identifier: GroupIdentifierInner,
33    /// Human-readable group name
34    name: String,
35    /// List of user identifiers that belong to this group
36    users: Vec<UserIdentifierInner>,
37}
38
39impl Group {
40    /// Creates a new group instance with the provided information.
41    ///
42    /// # Arguments
43    ///
44    /// * `Identifier` - Unique numerical identifier for the group
45    /// * `Name` - Human-readable group name
46    /// * `Users` - List of user identifiers that belong to this group
47    ///
48    /// # Returns
49    ///
50    /// A new `Group_type` instance with the provided data.
51    pub fn new(
52        identifier: GroupIdentifierInner,
53        name: String,
54        users: Vec<UserIdentifierInner>,
55    ) -> Self {
56        Self {
57            identifier,
58            name,
59            users,
60        }
61    }
62
63    /// Returns the group's unique identifier.
64    ///
65    /// # Returns
66    ///
67    /// A `Group_identifier_type` containing the group's unique ID.
68    pub fn get_identifier(&self) -> GroupIdentifier {
69        GroupIdentifier::new(self.identifier)
70    }
71
72    /// Returns the group's name as a string slice.
73    ///
74    /// # Returns
75    ///
76    /// A string slice containing the group name.
77    pub fn get_name(&self) -> &str {
78        &self.name
79    }
80
81    /// Returns the list of users that belong to this group.
82    ///
83    /// This function uses unsafe transmutation to avoid copying the vector,
84    /// since `User_identifier_type` is transparent to `User_identifier_inner_type`.
85    ///
86    /// # Returns
87    ///
88    /// A slice of `User_identifier_type` containing all group members.
89    pub fn get_users(&self) -> &[UserIdentifier] {
90        // Avoid to copy the vector since User_identifier_type is transparent to User_identifier_inner_type.
91        unsafe { core::mem::transmute(self.users.as_slice()) }
92    }
93}
94
95/// Constructs the file system path for a group's data file.
96///
97/// # Arguments
98///
99/// * `Group_name` - The group name to generate a path for
100///
101/// # Returns
102///
103/// Returns `Ok(Path_owned_type)` with the complete path to the group file,
104/// or `Err(Error::Failed_to_get_group_file_path)` if path construction fails.
105pub fn get_group_file_path(group_name: &str) -> Result<PathOwned> {
106    Path::new(GROUP_FOLDER_PATH)
107        .to_owned()
108        .append(group_name)
109        .ok_or(Error::FailedToGetGroupFilePath)
110}
111
112/// Reads and parses a group file from the filesystem.
113///
114/// This function is used internally to load group data from JSON files.
115/// It reads the file contents into the provided buffer and deserializes
116/// the JSON data into a `Group_type` structure.
117///
118/// # Arguments
119///
120/// * `Virtual_file_system` - Reference to the virtual file system
121/// * `Buffer` - Mutable buffer to use for reading file contents
122/// * `File` - Name of the group file to read
123///
124/// # Returns
125///
126/// Returns `Ok(Group_type)` with the parsed group data,
127/// or an appropriate error if reading or parsing fails.
128///
129/// # Errors
130///
131/// - Path construction failures
132/// - File system errors (opening, reading)
133/// - JSON parsing errors
134pub async fn read_group_file<'a>(
135    virtual_file_system: &'a VirtualFileSystem<'a>,
136    buffer: &mut Vec<u8>,
137    file: &str,
138) -> Result<Group> {
139    let group_file_path = Path::new(GROUP_FOLDER_PATH)
140        .to_owned()
141        .append(file)
142        .ok_or(Error::FailedToGetGroupFilePath)?;
143
144    let group_file = File::open(virtual_file_system, group_file_path, Mode::READ_ONLY.into())
145        .await
146        .map_err(Error::FailedToReadGroupDirectory)?;
147
148    buffer.clear();
149
150    group_file
151        .read_to_end(buffer)
152        .await
153        .map_err(Error::FailedToReadGroupFile)?;
154
155    miniserde::json::from_str(core::str::from_utf8(buffer).unwrap())
156        .map_err(Error::FailedToParseGroupFile)
157}
158
159/// Creates a new group with the specified parameters.
160///
161/// This function creates a new group in the system by:
162/// 1. Generating a new group identifier (if not provided)
163/// 2. Adding the group to the Users manager
164/// 3. Creating the group file with the group data
165///
166/// The group is created with an empty user list initially.
167///
168/// # Arguments
169///
170/// * `Virtual_file_system` - Reference to the virtual file system
171/// * `Group_name` - Name for the new group
172/// * `Group_identifier` - Optional specific group identifier (auto-generated if None)
173///
174/// # Returns
175///
176/// Returns `Ok(Group_identifier_type)` with the new group's identifier,
177/// or an appropriate error if creation fails.
178///
179/// # Errors
180///
181/// This function can fail for various reasons including:
182/// - Group identifier generation or assignment failures
183/// - File system operations (directory creation, file writing)
184/// - Users manager operations (adding group)
185pub async fn create_group<'a>(
186    virtual_file_system: &'a VirtualFileSystem<'a>,
187    group_name: &str,
188    group_identifier: Option<GroupIdentifier>,
189) -> Result<GroupIdentifier> {
190    let users_manager = users::get_instance();
191
192    // - New group identifier if not provided.
193    let group_identifier = if let Some(group_identifier) = group_identifier {
194        group_identifier
195    } else {
196        users_manager
197            .get_new_group_identifier()
198            .await
199            .map_err(Error::FailedToGetNewGroupIdentifier)?
200    };
201
202    // - Add it to the users manager.
203    users_manager
204        .add_group(group_identifier, group_name, &[])
205        .await
206        .map_err(Error::FailedToAddGroup)?;
207
208    // - Write group file.
209    let group = Group::new(group_identifier.as_u16(), group_name.to_string(), vec![]);
210
211    match Directory::create(virtual_file_system, GROUP_FOLDER_PATH).await {
212        Ok(_) | Err(file_system::Error::AlreadyExists) => {}
213        Err(error) => Err(Error::FailedToCreateGroupsDirectory(error))?,
214    };
215
216    let group_file_path = get_group_file_path(group_name)?;
217
218    let group_file = File::open(
219        virtual_file_system,
220        group_file_path,
221        Flags::new(Mode::WRITE_ONLY, Some(Open::CREATE_ONLY), None),
222    )
223    .await
224    .map_err(Error::FailedToOpenGroupFile)?;
225
226    let group_json = miniserde::json::to_string(&group);
227
228    group_file
229        .write(group_json.as_bytes())
230        .await
231        .map_err(Error::FailedToWriteGroupFile)?;
232
233    Ok(group_identifier)
234}