cts_common/auth/claims/
azp.rs1use std::fmt::Display;
2
3use serde::{Deserialize, Serialize};
4use uuid::Uuid;
5
6#[derive(Clone, Copy, Debug, PartialEq)]
18pub enum Azp {
19 RootProvider,
20
21 Provider(Uuid),
23
24 AccessKey(Uuid),
26}
27
28impl Azp {
29 pub fn is_root_provider(&self) -> bool {
31 matches!(self, Self::RootProvider)
32 }
33
34 pub fn is_provider(&self) -> bool {
36 matches!(self, Self::Provider(_))
37 }
38
39 pub fn is_access_key(&self) -> bool {
41 matches!(self, Self::AccessKey(_))
42 }
43}
44
45impl Display for Azp {
46 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47 match self {
48 Self::RootProvider => write!(f, "OIDC"),
49 Self::Provider(provider_id) => write!(f, "OIDC|{provider_id}"),
50 Self::AccessKey(access_key_id) => write!(f, "CSAK|{access_key_id}"),
51 }
52 }
53}
54
55impl Serialize for Azp {
56 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
57 where
58 S: serde::Serializer,
59 {
60 serializer.serialize_str(self.to_string().as_str())
61 }
62}
63
64impl<'de> Deserialize<'de> for Azp {
65 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
66 where
67 D: serde::Deserializer<'de>,
68 {
69 let s = String::deserialize(deserializer)?;
70 if s == "OIDC" {
71 Ok(Self::RootProvider)
72 } else if let Some((prefix, id)) = s.split_once('|') {
73 match prefix {
74 "OIDC" => Ok(Self::Provider(
75 Uuid::parse_str(id).map_err(serde::de::Error::custom)?,
76 )),
77 "CSAK" => Ok(Self::AccessKey(
78 Uuid::parse_str(id).map_err(serde::de::Error::custom)?,
79 )),
80 _ => Err(serde::de::Error::custom(format!("Invalid azp value: {s}"))),
81 }
82 } else {
83 Err(serde::de::Error::custom(format!("Invalid azp value: {s}")))
84 }
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91 use serde_json::json;
92
93 mod root {
94 use super::*;
95
96 #[test]
97 fn serializes_as_oidc() {
98 let azp = Azp::RootProvider;
99 let serialized = serde_json::to_value(azp).unwrap();
100 assert_eq!(serialized, json!("OIDC"));
101 }
102
103 #[test]
104 fn deserializes_from_oidc() {
105 let json = json!("OIDC");
106 let azp: Azp = serde_json::from_value(json).unwrap();
107 assert_eq!(azp, Azp::RootProvider);
108 }
109 }
110
111 mod provider {
112 use super::*;
113
114 #[test]
115 fn serializes_with_provider_id() {
116 let uuid = Uuid::new_v4();
117 let azp = Azp::Provider(uuid);
118 let serialized = serde_json::to_value(azp).unwrap();
119 assert_eq!(serialized, json!(format!("OIDC|{uuid}")));
120 }
121
122 #[test]
123 fn deserializes_with_provider_id() {
124 let uuid = Uuid::new_v4();
125 let json = json!(format!("OIDC|{uuid}"));
126 let azp: Azp = serde_json::from_value(json).unwrap();
127 assert_eq!(azp, Azp::Provider(uuid));
128 }
129 }
130
131 mod access_key {
132 use super::*;
133
134 #[test]
135 fn serializes_with_access_key_id() {
136 let uuid = Uuid::new_v4();
137 let azp = Azp::AccessKey(uuid);
138 let serialized = serde_json::to_value(azp).unwrap();
139 assert_eq!(serialized, json!(format!("CSAK|{uuid}")));
140 }
141
142 #[test]
143 fn deserializes_with_access_key_id() {
144 let uuid = Uuid::new_v4();
145 let json = json!(format!("CSAK|{uuid}"));
146 let azp: Azp = serde_json::from_value(json).unwrap();
147 assert_eq!(azp, Azp::AccessKey(uuid));
148 }
149 }
150
151 mod invalid {
152 use super::*;
153
154 #[test]
155 fn deserializes_invalid_azp() {
156 let json = json!("INVALID|123e4567-e89b-12d3-a456-426614174000");
157 let result: Result<Azp, _> = serde_json::from_value(json);
158 assert!(result.is_err());
159 }
160
161 #[test]
162 fn deserializes_invalid_format() {
163 let json = json!("OIDC|INVALID_UUID");
164 let result: Result<Azp, _> = serde_json::from_value(json);
165 assert!(result.is_err());
166 }
167 }
168
169 mod empty {
170 use super::*;
171
172 #[test]
173 fn fails_on_empty_string() {
174 let json = json!("");
175 let result: Result<Azp, _> = serde_json::from_value(json);
176 assert!(result.is_err());
177 }
178
179 #[test]
180 fn fails_on_null() {
181 let json = json!(null);
182 let result: Result<Azp, _> = serde_json::from_value(json);
183 assert!(result.is_err());
184 }
185 }
186
187 mod malformed {
188 use super::*;
189
190 #[test]
191 fn fails_on_malformed_string() {
192 let json = json!("OIDC|123e4567-e89b-12d3-a456-426614174000|extra");
193 let result: Result<Azp, _> = serde_json::from_value(json);
194 assert!(result.is_err());
195 }
196
197 #[test]
198 fn fails_on_missing_prefix() {
199 let json = json!("123e4567-e89b-12d3-a456-426614174000");
200 let result: Result<Azp, _> = serde_json::from_value(json);
201 assert!(result.is_err());
202 }
203
204 #[test]
205 fn fails_on_invalid_uuid() {
206 let json = json!("OIDC|invalid-uuid");
207 let result: Result<Azp, _> = serde_json::from_value(json);
208 assert!(result.is_err());
209 }
210
211 #[test]
212 fn fails_on_invalid_access_key_uuid() {
213 let json = json!("CSAK|invalid-uuid");
214 let result: Result<Azp, _> = serde_json::from_value(json);
215 assert!(result.is_err());
216 }
217 }
218}