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}