1use derive_more::Display;
2use std::{
3 any::type_name,
4 fmt::{self, Debug},
5 ops::{BitAnd, BitOr},
6};
7
8use super::Role;
9use crate::claims::common::ScalarOrArray;
10use serde::{Deserialize, Serialize};
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Display)]
14#[display("{_0}")]
15pub struct Scope(PermissionFlags);
16
17impl Default for Scope {
18 fn default() -> Self {
21 Self::with_no_permissions()
22 }
23}
24
25#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
29pub struct PermissionFlags(u64);
30
31#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)]
35pub enum Permission {
36 #[display("access_key:{_0}")]
37 AccessKey(AccessKeyPermission),
38
39 #[display("dataset:{_0}")]
40 Keyset(KeysetPermission),
41
42 #[display("client:{_0}")]
43 Client(ClientPermission),
44
45 #[display("data_key:{_0}")]
46 DataKey(DataKeyPermission),
47
48 #[display("cs_support:{_0}")]
49 Support(SupportPermission),
50
51 #[display("logging:{_0}")]
52 Logging(LoggingPermission),
53}
54
55#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)]
59#[repr(u8)]
60pub enum SupportPermission {
61 #[display("admin")]
62 Admin = 0b0000_0001,
63}
64
65#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)]
69#[repr(u8)]
70pub enum LoggingPermission {
71 #[display("append")]
72 Append = 0b0000_0001,
73}
74
75#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)]
77#[repr(u8)]
78pub enum AccessKeyPermission {
79 #[display("create")]
81 Create = 0b0000_0001,
82
83 #[display("list")]
85 List = 0b0000_0010,
86
87 #[display("delete")]
89 Delete = 0b0000_0100,
90}
91
92#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)]
94#[repr(u8)]
95pub enum KeysetPermission {
96 #[display("create")]
98 Create = 0b0000_0001,
99
100 #[display("list")]
102 List = 0b0000_0010,
103
104 #[display("disable")]
106 Disable = 0b0000_0100,
107
108 #[display("enable")]
110 Enable = 0b0000_1000,
111
112 #[display("grant")]
114 Grant = 0b0001_0000,
115
116 #[display("modify")]
118 Modify = 0b0010_0000,
119
120 #[display("revoke")]
122 Revoke = 0b0100_0000,
123}
124
125#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)]
127#[repr(u8)]
128pub enum ClientPermission {
129 #[display("create")]
131 Create = 0b0000_0001,
132
133 #[display("list")]
135 List = 0b0000_0010,
136
137 #[display("delete")]
139 Delete = 0b0000_0100,
140}
141
142#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Display)]
144#[repr(u8)]
145pub enum DataKeyPermission {
146 #[display("generate")]
148 Generate = 0b0000_0001,
149
150 #[display("retrieve")]
152 Retrieve = 0b0000_0010,
153}
154
155impl Permission {
156 fn parse(input: &str) -> Option<Self> {
160 match input {
161 s if s.starts_with("keyset:") => Some(Self::Keyset(KeysetPermission::parse(
162 &s["keyset:".len()..],
163 )?)),
164 s if s.starts_with("access_key:") => Some(Self::AccessKey(AccessKeyPermission::parse(
165 &s["access_key:".len()..],
166 )?)),
167 s if s.starts_with("dataset:") => Some(Self::Keyset(KeysetPermission::parse(
169 &s["dataset:".len()..],
170 )?)),
171 s if s.starts_with("data_key:") => Some(Self::DataKey(DataKeyPermission::parse(
172 &s["data_key:".len()..],
173 )?)),
174 s if s.starts_with("client:") => Some(Self::Client(ClientPermission::parse(
175 &s["client:".len()..],
176 )?)),
177 s if s.starts_with("cs_support:") => Some(Self::Support(SupportPermission::parse(
178 &s["cs_support:".len()..],
179 )?)),
180 s if s.starts_with("logging:") => Some(Self::Logging(LoggingPermission::parse(
181 &s["logging:".len()..],
182 )?)),
183 _ => None,
184 }
185 }
186
187 const fn to_flags(self) -> PermissionFlags {
189 const _: () = assert!(size_of::<KeysetPermission>() == 1);
193 const _: () = assert!(size_of::<ClientPermission>() == 1);
194 const _: () = assert!(size_of::<DataKeyPermission>() == 1);
195 const _: () = assert!(size_of::<SupportPermission>() == 1);
196 const _: () = assert!(size_of::<LoggingPermission>() == 1);
197 const _: () = assert!(size_of::<AccessKeyPermission>() == 1);
198
199 match self {
200 Permission::Keyset(permission) => PermissionFlags(permission as u64),
201 Permission::Client(permission) => PermissionFlags((permission as u64) << 8),
202 Permission::DataKey(permission) => PermissionFlags((permission as u64) << 16),
203 Permission::Support(permission) => PermissionFlags((permission as u64) << 24),
204 Permission::Logging(permission) => PermissionFlags((permission as u64) << 32),
205 Permission::AccessKey(permission) => PermissionFlags((permission as u64) << 40),
206 }
207 }
208
209 const ALL: &[Permission] = &[
211 Self::Keyset(KeysetPermission::Create),
213 Self::Keyset(KeysetPermission::List),
214 Self::Keyset(KeysetPermission::Enable),
215 Self::Keyset(KeysetPermission::Disable),
216 Self::Keyset(KeysetPermission::Grant),
217 Self::Keyset(KeysetPermission::Modify),
218 Self::Keyset(KeysetPermission::Revoke),
219 Self::DataKey(DataKeyPermission::Generate),
221 Self::DataKey(DataKeyPermission::Retrieve),
222 Self::Client(ClientPermission::Create),
224 Self::Client(ClientPermission::List),
225 Self::Client(ClientPermission::Delete),
226 Self::AccessKey(AccessKeyPermission::Create),
228 Self::AccessKey(AccessKeyPermission::List),
229 Self::AccessKey(AccessKeyPermission::Delete),
230 Self::Support(SupportPermission::Admin),
231 Self::Logging(LoggingPermission::Append),
232 ];
233}
234
235macro_rules! impl_from {
236 ($variant:ident, $ty:ty) => {
237 impl From<$ty> for Permission {
238 fn from(value: $ty) -> Self {
239 Self::$variant(value)
240 }
241 }
242 };
243}
244
245impl_from!(AccessKey, AccessKeyPermission);
246impl_from!(Keyset, KeysetPermission);
247impl_from!(DataKey, DataKeyPermission);
248impl_from!(Client, ClientPermission);
249impl_from!(Support, SupportPermission);
250impl_from!(Logging, LoggingPermission);
251
252impl PermissionFlags {
253 pub const fn empty() -> Self {
255 Self(0)
256 }
257
258 pub const fn with_flag(flag: Permission) -> Self {
260 Self::empty().add(flag)
261 }
262
263 pub const fn add(self, permission: Permission) -> Self {
265 Self(self.0 | permission.to_flags().0)
266 }
267
268 pub const fn merge(self, other: Self) -> Self {
270 Self(self.0 | other.0)
271 }
272
273 fn count_permissions(&self) -> u32 {
275 self.0.count_ones()
276 }
277}
278
279impl IntoIterator for PermissionFlags {
280 type IntoIter = PermissionsIter;
281 type Item = Permission;
282
283 fn into_iter(self) -> Self::IntoIter {
284 PermissionsIter(self, Permission::ALL)
285 }
286}
287
288impl FromIterator<Permission> for PermissionFlags {
289 fn from_iter<T: IntoIterator<Item = Permission>>(iter: T) -> Self {
290 let iter = iter.into_iter();
291 let mut flags = Self(0);
292 for permission in iter {
293 flags = flags.add(permission);
294 }
295 flags
296 }
297}
298
299pub struct PermissionsIter(PermissionFlags, &'static [Permission]);
301
302impl Iterator for PermissionsIter {
303 type Item = Permission;
304
305 fn next(&mut self) -> Option<Self::Item> {
306 loop {
309 if let Some((first, rest)) = self.1.split_first() {
310 self.1 = rest;
312 if self.0 & first.to_flags() == first.to_flags() {
313 return Some(*first);
314 }
315 } else {
316 return None;
318 }
319 }
320 }
321}
322
323impl BitAnd for PermissionFlags {
324 type Output = Self;
325
326 fn bitand(self, rhs: Self) -> Self::Output {
327 Self(self.0 & rhs.0)
328 }
329}
330
331impl BitOr for PermissionFlags {
332 type Output = Self;
333
334 fn bitor(self, rhs: Self) -> Self::Output {
335 Self(self.0 | rhs.0)
336 }
337}
338
339impl fmt::Display for PermissionFlags {
340 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
341 let count = self.count_permissions() as usize;
342
343 for (idx, scope) in self.into_iter().enumerate() {
345 fmt::Display::fmt(&scope, f)?;
346 if idx < count - 1 {
347 fmt::Display::fmt(", ", f)?;
348 }
349 }
350
351 Ok(())
352 }
353}
354
355impl Debug for PermissionFlags {
356 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
357 let count = self.count_permissions() as usize;
358
359 f.write_fmt(format_args!("{}(", type_name::<Self>()))?;
360
361 for (idx, scope) in self.into_iter().enumerate() {
362 fmt::Display::fmt(&scope, f)?;
363 if idx < count - 1 {
364 fmt::Display::fmt("|", f)?;
365 }
366 }
367
368 f.write_str(")")?;
369
370 Ok(())
371 }
372}
373
374impl KeysetPermission {
375 fn parse(input: &str) -> Option<Self> {
376 match input {
377 "create" => Some(Self::Create),
378 "list" => Some(Self::List),
379 "disable" => Some(Self::Disable),
380 "enable" => Some(Self::Enable),
381 "grant" => Some(Self::Grant),
382 "modify" => Some(Self::Modify),
383 "revoke" => Some(Self::Revoke),
384 _ => None,
385 }
386 }
387}
388
389impl ClientPermission {
390 fn parse(input: &str) -> Option<Self> {
391 match input {
392 "create" => Some(Self::Create),
393 "list" => Some(Self::List),
394 "delete" => Some(Self::Delete),
395 _ => None,
396 }
397 }
398}
399
400impl DataKeyPermission {
401 fn parse(input: &str) -> Option<Self> {
402 match input {
403 "generate" => Some(Self::Generate),
404 "retrieve" => Some(Self::Retrieve),
405 _ => None,
406 }
407 }
408}
409
410impl SupportPermission {
411 fn parse(input: &str) -> Option<Self> {
412 match input {
413 "admin" => Some(Self::Admin),
414 _ => None,
415 }
416 }
417}
418
419impl LoggingPermission {
420 fn parse(input: &str) -> Option<Self> {
421 match input {
422 "append" => Some(Self::Append),
423 _ => None,
424 }
425 }
426}
427
428impl AccessKeyPermission {
429 fn parse(input: &str) -> Option<Self> {
430 match input {
431 "create" => Some(Self::Create),
432 "list" => Some(Self::List),
433 "delete" => Some(Self::Delete),
434 _ => None,
435 }
436 }
437}
438
439impl Scope {
440 pub const fn with_permissions(permissions: PermissionFlags) -> Self {
442 Self(permissions)
443 }
444
445 pub const fn with_permission(permission: Permission) -> Self {
447 Self(PermissionFlags::empty().add(permission))
448 }
449
450 pub fn with_no_permissions() -> Self {
452 Self(PermissionFlags::empty())
453 }
454
455 pub fn merge(self, other: Self) -> Self {
457 Self(self.0 | other.0)
458 }
459
460 pub fn parse(input: &str) -> Self {
463 let permissions: PermissionFlags =
464 input
465 .split_whitespace()
466 .fold(PermissionFlags::empty(), |acc, s| {
467 if let Some(permission) = Permission::parse(s) {
468 acc.add(permission)
469 } else {
470 acc
471 }
472 });
473
474 Self(permissions)
475 }
476
477 pub fn has_permission(&self, permission: Permission) -> bool {
479 self.0 & permission.to_flags() == permission.to_flags()
480 }
481
482 pub fn has_all_permissions(&self, other: Self) -> bool {
484 self.0 & other.0 == other.0
485 }
486
487 pub fn count_permissions(&self) -> u32 {
489 self.0.count_permissions()
490 }
491
492 pub fn permissions(&self) -> impl Iterator<Item = Permission> {
494 self.0.into_iter()
495 }
496}
497
498pub const WORKSPACE_ADMIN_SCOPE: Scope = Scope(
501 PermissionFlags::empty()
502 .add(Permission::AccessKey(AccessKeyPermission::Create))
503 .add(Permission::AccessKey(AccessKeyPermission::List))
504 .add(Permission::AccessKey(AccessKeyPermission::Delete))
505 .add(Permission::Keyset(KeysetPermission::Create))
506 .add(Permission::Keyset(KeysetPermission::List))
507 .add(Permission::Keyset(KeysetPermission::Disable))
508 .add(Permission::Keyset(KeysetPermission::Enable))
509 .add(Permission::Keyset(KeysetPermission::Grant))
510 .add(Permission::Keyset(KeysetPermission::Modify))
511 .add(Permission::Keyset(KeysetPermission::Revoke))
512 .add(Permission::Client(ClientPermission::Create))
513 .add(Permission::Client(ClientPermission::List))
514 .add(Permission::Client(ClientPermission::Delete))
515 .add(Permission::DataKey(DataKeyPermission::Generate))
516 .add(Permission::DataKey(DataKeyPermission::Retrieve)),
517);
518
519pub const WORKSPACE_CONTROL_SCOPE: Scope = Scope(
523 PermissionFlags::empty()
524 .add(Permission::Keyset(KeysetPermission::Create))
525 .add(Permission::Keyset(KeysetPermission::List))
526 .add(Permission::Keyset(KeysetPermission::Disable))
527 .add(Permission::Keyset(KeysetPermission::Enable))
528 .add(Permission::Keyset(KeysetPermission::Grant))
529 .add(Permission::Keyset(KeysetPermission::Modify))
530 .add(Permission::Keyset(KeysetPermission::Revoke))
531 .add(Permission::Client(ClientPermission::List)),
532);
533
534pub const WORKSPACE_MEMBER_SCOPE: Scope = Scope(
537 PermissionFlags::empty()
538 .add(Permission::Keyset(KeysetPermission::List))
539 .add(Permission::DataKey(DataKeyPermission::Generate))
540 .add(Permission::DataKey(DataKeyPermission::Retrieve)),
541);
542
543pub const LOGGING_SCOPE: Scope =
544 Scope(PermissionFlags::empty().add(Permission::Logging(LoggingPermission::Append)));
545
546impl From<Option<Role>> for Scope {
547 fn from(role: Option<Role>) -> Self {
548 match role {
549 Some(role) => role.scope(),
550 None => Default::default(),
551 }
552 }
553}
554
555impl From<Vec<String>> for Scope {
556 fn from(scope_names: Vec<String>) -> Self {
557 Scope(
558 scope_names
559 .into_iter()
560 .fold(PermissionFlags::empty(), |acc, s| {
561 if let Some(permission) = Permission::parse(&s) {
562 acc.merge(permission.to_flags())
563 } else {
564 acc
565 }
566 }),
567 )
568 }
569}
570
571impl Serialize for Permission {
572 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
573 where
574 S: serde::Serializer,
575 {
576 self.to_string().serialize(serializer)
577 }
578}
579
580impl Serialize for Scope {
581 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
582 where
583 S: serde::Serializer,
584 {
585 let flags: Vec<Permission> = self.permissions().collect();
586
587 match &flags[..] {
589 &[] => serializer.serialize_none(),
590 &[permission] => Some(ScalarOrArray::Scalar(permission)).serialize(serializer),
591 flags => Some(ScalarOrArray::Array(flags.iter().collect())).serialize(serializer),
592 }
593 }
594}
595
596impl<'de> Deserialize<'de> for Scope {
598 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
599 where
600 D: serde::Deserializer<'de>,
601 {
602 let items: Option<ScalarOrArray<String>> = Deserialize::deserialize(deserializer)?;
603 match items {
604 Some(ScalarOrArray::Array(items)) => Ok(Scope::from(items)),
605 Some(ScalarOrArray::Scalar(item)) => Ok(Scope(
606 Permission::parse(&item)
607 .map(|p| p.to_flags())
608 .unwrap_or(PermissionFlags::empty()),
609 )),
610 None => Ok(Scope::with_no_permissions()),
611 }
612 }
613}
614
615impl From<String> for Scope {
616 fn from(value: String) -> Self {
617 Scope::parse(&value)
618 }
619}
620
621#[cfg(test)]
622mod tests {
623 use super::*;
624 use serde_json::{json, Value};
625
626 mod parse {
627 use super::*;
628
629 #[test]
630 fn empty_string_parses_to_empty_scope() {
631 let scope = Scope::parse("");
632 assert_eq!(scope, Scope::with_no_permissions());
633 }
634
635 #[test]
636 fn single_scope_parses_correctly() {
637 let scope = Scope::parse("dataset:create");
638 assert!(scope.has_permission(Permission::Keyset(KeysetPermission::Create)));
639 assert_eq!(scope.count_permissions(), 1);
640 }
641
642 #[test]
643 fn multiple_scopes_parse_correctly() {
644 let scope = Scope::parse("dataset:create client:list");
645 assert!(scope.has_permission(Permission::Keyset(KeysetPermission::Create)));
646 assert!(scope.has_permission(Permission::Client(ClientPermission::List)));
647 assert_eq!(scope.count_permissions(), 2);
648 }
649 }
650
651 mod serialize {
652 use super::*;
653
654 #[test]
655 fn empty_scope_serializes_as_null() {
656 let scope = Scope::default();
657 let serialized = serde_json::to_value(scope).unwrap();
658 assert_eq!(serialized, Value::Null);
659 }
660
661 #[test]
662 fn single_scope_serializes_as_string() {
663 let scope = Scope::parse("dataset:create");
664 let serialized = serde_json::to_value(scope).unwrap();
665 assert_eq!(serialized, json!("dataset:create"));
666 }
667
668 #[test]
669 fn multiple_scopes_serializes_as_array() {
670 let scope = Scope::parse("dataset:create client:list");
671 let serialized = serde_json::to_value(scope).unwrap();
672
673 assert!(
674 serialized == json!(["dataset:create", "client:list"])
675 || serialized == json!(["client:list", "dataset:create"]),
676 "Expected serialized scope to be an array of scopes, got: {serialized:?}"
677 );
678 }
679 }
680
681 mod deserialize {
682 use super::*;
683
684 #[test]
685 fn empty_scope_deserializes_from_null() {
686 let json = Value::Null;
687 let scope: Scope = serde_json::from_value(json).unwrap();
688 assert_eq!(scope, Scope::default());
689 }
690
691 #[test]
692 fn empty_string_deserializes_to_empty_scope() {
693 let json = json!("");
694 let scope: Scope = serde_json::from_value(json).unwrap();
695 assert_eq!(scope, Scope::default());
696 }
697
698 #[test]
699 fn single_scope_deserializes_from_string() {
700 let json = json!("dataset:create");
701 let scope: Scope = serde_json::from_value(json).unwrap();
702 assert_eq!(scope, Scope::parse("dataset:create"));
703 }
704
705 #[test]
706 fn multiple_scopes_deserialize_from_array() {
707 let json = json!(["dataset:create", "client:list"]);
708 let scope: Scope = serde_json::from_value(json).unwrap();
709 assert!(scope.has_permission(Permission::Keyset(KeysetPermission::Create)));
710 assert!(scope.has_permission(Permission::Client(ClientPermission::List)));
711 assert_eq!(
712 scope.count_permissions(),
713 2,
714 "Expected scope to contain 2 items"
715 );
716 }
717
718 #[test]
719 fn invalid_scope_deserializes_from_invalid_value() {
720 let json = json!(12345);
721 let result: Result<Scope, _> = serde_json::from_value(json);
722 assert!(
723 result.is_err(),
724 "Expected deserialization to fail for invalid scope"
725 );
726 }
727 }
728
729 mod merge {
730 use super::*;
731
732 #[test]
733 fn non_overlapping() {
734 let scope1 = Scope::parse("dataset:create");
735 let scope2 = Scope::parse("client:list");
736 let merged = scope1.merge(scope2);
737
738 assert!(merged.has_permission(Permission::Keyset(KeysetPermission::Create)));
739 assert!(merged.has_permission(Permission::Client(ClientPermission::List)));
740 assert_eq!(merged.count_permissions(), 2);
741 }
742
743 #[test]
744 fn overlapping() {
745 let scope1 = Scope::parse("dataset:create client:list");
746 let scope2 = Scope::parse("client:list data_key:generate");
747 let merged = scope1.merge(scope2);
748 assert!(merged.has_permission(Permission::Keyset(KeysetPermission::Create)));
749 assert!(merged.has_permission(Permission::Client(ClientPermission::List)));
750 assert!(merged.has_permission(Permission::DataKey(DataKeyPermission::Generate)));
751 assert_eq!(merged.count_permissions(), 3);
752 }
753 }
754
755 mod ensure_bitflag_representation_has_no_collisions {
756 use super::*;
757
758 mod scope_with_all_permissions {
759 use super::*;
760
761 #[test]
762 fn for_each_permission_has_permission_returns_true() {
763 let mut scope = Scope::with_no_permissions();
764 for permission in Permission::ALL {
765 scope = scope.merge(Scope::with_permission(*permission));
766 }
767
768 for permission in Permission::ALL {
769 assert!(scope.has_permission(*permission))
770 }
771 }
772 }
773
774 mod scope_with_all_but_one_permssion {
775 use super::*;
776
777 #[test]
778 fn has_permission_returns_true_for_all_except_one_permission() {
779 for without_permission in Permission::ALL {
780 let permissions: Vec<_> = Permission::ALL
781 .iter()
782 .filter(|p| *p != without_permission)
783 .copied()
784 .collect();
785 let scope = Scope::with_permissions(PermissionFlags::from_iter(
786 permissions.into_iter(),
787 ));
788
789 for permission in Permission::ALL {
790 if permission != without_permission {
791 assert!(scope.has_permission(*permission))
792 } else {
793 assert!(!scope.has_permission(*permission))
794 }
795 }
796 }
797 }
798 }
799 }
800}