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::{AccessFlags, 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 `GroupIdentifier` 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 `UserIdentifier` is transparent to `User_identifier_inner_type`.
85    ///
86    /// # Returns
87    ///
88    /// A slice of `UserIdentifier` containing all group members.
89    pub fn get_users(&self) -> &[UserIdentifier] {
90        // Avoid to copy the vector since UserIdentifier 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(
135    virtual_file_system: &VirtualFileSystem,
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 task = task::get_instance().get_current_task_identifier().await;
145
146    let mut group_file = File::open(
147        virtual_file_system,
148        task,
149        group_file_path,
150        AccessFlags::Read.into(),
151    )
152    .await
153    .map_err(Error::FailedToReadGroupDirectory)?;
154
155    buffer.clear();
156
157    group_file
158        .read_to_end(buffer, 32)
159        .await
160        .map_err(Error::FailedToReadGroupFile)?;
161
162    group_file
163        .close(virtual_file_system)
164        .await
165        .map_err(Error::FailedToCloseFile)?;
166
167    miniserde::json::from_str(core::str::from_utf8(buffer).unwrap())
168        .map_err(Error::FailedToParseGroupFile)
169}
170
171/// Creates a new group with the specified parameters.
172///
173/// This function creates a new group in the system by:
174/// 1. Generating a new group identifier (if not provided)
175/// 2. Adding the group to the Users manager
176/// 3. Creating the group file with the group data
177///
178/// The group is created with an empty user list initially.
179///
180/// # Arguments
181///
182/// * `Virtual_file_system` - Reference to the virtual file system
183/// * `Group_name` - Name for the new group
184/// * `Group_identifier` - Optional specific group identifier (auto-generated if None)
185///
186/// # Returns
187///
188/// Returns `Ok(GroupIdentifier)` with the new group's identifier,
189/// or an appropriate error if creation fails.
190///
191/// # Errors
192///
193/// This function can fail for various reasons including:
194/// - Group identifier generation or assignment failures
195/// - File system operations (directory creation, file writing)
196/// - Users manager operations (adding group)
197pub async fn create_group(
198    virtual_file_system: &VirtualFileSystem,
199    group_name: &str,
200    group_identifier: Option<GroupIdentifier>,
201) -> Result<GroupIdentifier> {
202    let users_manager = users::get_instance();
203
204    // - New group identifier if not provided.
205    let group_identifier = if let Some(group_identifier) = group_identifier {
206        group_identifier
207    } else {
208        users_manager
209            .get_new_group_identifier()
210            .await
211            .map_err(Error::FailedToGetNewGroupIdentifier)?
212    };
213
214    // - Add it to the users manager.
215    users_manager
216        .add_group(group_identifier, group_name, &[])
217        .await
218        .map_err(Error::FailedToAddGroup)?;
219
220    // - Write group file.
221    let group = Group::new(group_identifier.as_u16(), group_name.to_string(), vec![]);
222
223    let task = task::get_instance().get_current_task_identifier().await;
224
225    match Directory::create(virtual_file_system, task, GROUP_FOLDER_PATH).await {
226        Ok(_) | Err(virtual_file_system::Error::AlreadyExists) => {}
227        Err(error) => Err(Error::FailedToCreateGroupsDirectory(error))?,
228    };
229
230    let group_file_path = get_group_file_path(group_name)?;
231
232    let group_json = miniserde::json::to_string(&group);
233
234    File::write_to_path(
235        virtual_file_system,
236        task,
237        &group_file_path,
238        group_json.as_bytes(),
239    )
240    .await
241    .map_err(Error::FailedToWriteGroupFile)?;
242
243    Ok(group_identifier)
244}