Skip to main content

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