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}