steel_core/server/
permissions.rs1use steel_utils::Identifier;
2
3use crate::permission::PermissionMetadataValue;
4
5use super::{
6 Arc, FnServerJob, OP_GROUP, PermissionGroupManager, PermissionGroupManagerError,
7 PermissionGroupUpdateError, PermissionGroupsConfig, PermissionSet, PermissionSubjectState,
8 Player, PlayerPermissionUpdateError, Server, ServerJobContext, Uuid,
9};
10
11const BYPASSES_PLAYER_LIMIT: Identifier =
12 Identifier::new_static(Identifier::STEEL_NAMESPACE, "bypasses_player_limit");
13
14pub(super) fn validate_player_permission_group_update<E>(
15 manager: &PermissionGroupManager,
16 previous_groups: &[String],
17 updated_groups: &[String],
18) -> Result<(), PlayerPermissionUpdateError<E>> {
19 for group in updated_groups {
20 let already_assigned = previous_groups.iter().any(|current| current == group);
21 if !already_assigned && !manager.contains_group(group) {
22 return Err(PlayerPermissionUpdateError::UnknownGroup(group.clone()));
23 }
24 }
25 Ok(())
26}
27
28impl Server {
29 #[must_use]
33 pub(crate) fn can_bypass_player_limit(&self, uuid: Uuid) -> bool {
34 let subject = self.player_permission_state(uuid).unwrap_or_default();
35 self.permission_groups
36 .effective_metadata(subject.groups(), subject.metadata_overrides())
37 .resolve(&BYPASSES_PLAYER_LIMIT)
38 .and_then(PermissionMetadataValue::as_bool)
39 == Some(true)
40 }
41
42 pub(super) fn apply_cached_or_default_permission_state(&self, player: &Player) -> u64 {
43 let state = self
44 .player_permission_states
45 .read()
46 .get(player.gameprofile.id)
47 .cloned()
48 .unwrap_or_default();
49 self.apply_player_permission_state(player, state)
50 }
51
52 fn apply_player_permission_state(&self, player: &Player, state: PermissionSubjectState) -> u64 {
53 let (groups, overrides, metadata_overrides) = state.into_parts();
54 for group in &groups {
55 if !self.permission_groups.contains_group(group) {
56 log::warn!(
57 "Player {} has unknown permission group {group}",
58 player.gameprofile.name
59 );
60 }
61 }
62 let effective = self
63 .permission_groups
64 .effective_permissions(&groups, &overrides);
65 let effective_metadata = self
66 .permission_groups
67 .effective_metadata(&groups, &metadata_overrides);
68 player.set_permission_state(
69 groups,
70 overrides,
71 metadata_overrides,
72 effective,
73 effective_metadata,
74 )
75 }
76
77 #[must_use]
79 pub fn player_permission_state(&self, uuid: Uuid) -> Option<PermissionSubjectState> {
80 self.player_permission_states.read().get(uuid).cloned()
81 }
82
83 #[must_use]
85 pub(crate) fn is_operator(&self, uuid: Uuid) -> bool {
86 self.player_permission_states
87 .read()
88 .get(uuid)
89 .is_some_and(|state| state.groups().iter().any(|group| group == OP_GROUP))
90 }
91
92 #[must_use]
94 pub(crate) fn command_permission_snapshot(&self, uuid: Uuid) -> PermissionSet {
95 let subject = self.player_permission_state(uuid).unwrap_or_default();
96 self.permission_groups
97 .effective_permissions(subject.groups(), subject.overrides())
98 }
99
100 pub async fn try_update_player_permissions<T, E>(
109 self: &Arc<Self>,
110 uuid: Uuid,
111 update: impl FnOnce(PermissionSubjectState) -> Result<(PermissionSubjectState, T), E> + Send,
112 ) -> Result<(PermissionSubjectState, T), PlayerPermissionUpdateError<E>>
113 where
114 T: Send,
115 E: Send,
116 {
117 let _guard = self.player_permission_updates.lock().await;
118 let mut states = self.player_permission_states.read().clone();
119 let current = states.get(uuid).cloned().unwrap_or_default();
120 let previous_groups = current.groups().to_vec();
121 let (updated, result) = update(current).map_err(PlayerPermissionUpdateError::Edit)?;
122 validate_player_permission_group_update(
123 &self.permission_groups,
124 &previous_groups,
125 updated.groups(),
126 )?;
127
128 if updated.is_empty() {
129 states.remove(uuid);
130 } else {
131 states.set(uuid, updated.clone());
132 }
133 self.player_data_storage
134 .save_permission_subjects(&states)
135 .await?;
136
137 *self.player_permission_states.write() = states;
138 self.queue_player_permission_refresh(uuid);
139 Ok((updated, result))
140 }
141
142 pub async fn replace_permission_groups(
148 self: &Arc<Self>,
149 config: PermissionGroupsConfig,
150 ) -> Result<(), PermissionGroupManagerError> {
151 self.permission_groups.replace_config(config).await?;
152 self.queue_online_permission_group_refresh();
153 Ok(())
154 }
155
156 pub async fn update_permission_groups(
162 self: &Arc<Self>,
163 update: impl FnOnce(&mut PermissionGroupsConfig) + Send,
164 ) -> Result<(), PermissionGroupManagerError> {
165 self.permission_groups.update_config(update).await?;
166 self.queue_online_permission_group_refresh();
167 Ok(())
168 }
169
170 pub async fn try_update_permission_groups<T, E>(
176 self: &Arc<Self>,
177 update: impl FnOnce(&mut PermissionGroupsConfig) -> Result<T, E> + Send,
178 ) -> Result<T, PermissionGroupUpdateError<E>>
179 where
180 T: Send,
181 E: Send,
182 {
183 let result = self.permission_groups.try_update_config(update).await?;
184 self.queue_online_permission_group_refresh();
185 Ok(result)
186 }
187
188 fn queue_player_permission_refresh(self: &Arc<Self>, uuid: Uuid) {
189 self.jobs
190 .spawn(FnServerJob::new(move |context: &mut ServerJobContext| {
191 if let Some(server) = context.server() {
192 server.refresh_player_permission_state(uuid);
193 }
194 }));
195 }
196
197 pub(crate) fn refresh_player_permission_state(self: &Arc<Self>, uuid: Uuid) {
198 let Some(player) = self.online_players.get_by_uuid(&uuid) else {
199 return;
200 };
201 let state = self.player_permission_state(uuid).unwrap_or_default();
202 self.apply_player_permission_state(&player, state);
203 self.resend_player_permission_context(&player);
204 }
205
206 fn queue_online_permission_group_refresh(self: &Arc<Self>) {
207 self.jobs
208 .spawn(FnServerJob::new(|context: &mut ServerJobContext| {
209 if let Some(server) = context.server() {
210 server.refresh_online_permission_groups();
211 }
212 }));
213 }
214
215 fn refresh_online_permission_groups(self: &Arc<Self>) {
216 for player in self.get_players() {
217 let state = self
218 .player_permission_state(player.gameprofile.id)
219 .unwrap_or_default();
220 self.apply_player_permission_state(&player, state);
221 self.resend_player_permission_context(&player);
222 }
223 }
224}