assemblyline_models/types/
classification.rs1use std::ops::Deref;
2use std::sync::{Arc, Mutex};
3
4use serde::{Serialize, Deserialize};
5use assemblyline_markings::classification::{ClassificationParser, NormalizeOptions};
6use struct_metadata::Described;
7
8use crate::{ElasticMeta, JsonMap, ModelError};
9
10enum ClassificationMode {
11 Uninitialized,
12 Disabled,
13 Configured(Arc<ClassificationParser>)
14}
15
16static GLOBAL_CLASSIFICATION: Mutex<ClassificationMode> = Mutex::new(ClassificationMode::Uninitialized);
17
18pub fn disable_global_classification() {
24 *GLOBAL_CLASSIFICATION.lock().unwrap() = ClassificationMode::Disabled;
25}
26
27pub fn set_global_classification(config: Arc<ClassificationParser>) {
28 *GLOBAL_CLASSIFICATION.lock().unwrap() = ClassificationMode::Configured(config);
29}
30
31pub fn unrestricted_classification() -> String {
32 match &*GLOBAL_CLASSIFICATION.lock().unwrap() {
33 ClassificationMode::Uninitialized => panic!("classification handling without defining parser"),
34 ClassificationMode::Disabled => "".to_string(),
35 ClassificationMode::Configured(parser) => parser.unrestricted().to_owned(),
36 }
37}
38
39pub fn unrestricted_classification_string() -> ClassificationString {
40 match &*GLOBAL_CLASSIFICATION.lock().unwrap() {
41 ClassificationMode::Uninitialized => panic!("classification handling without defining parser"),
42 ClassificationMode::Disabled => ClassificationString(Default::default()),
43 ClassificationMode::Configured(parser) => ClassificationString::unrestricted(parser),
44 }
45}
46
47pub fn unrestricted_expanding_classification() -> ExpandingClassification {
48 match &*GLOBAL_CLASSIFICATION.lock().unwrap() {
49 ClassificationMode::Uninitialized => panic!("classification handling without defining parser"),
50 ClassificationMode::Disabled => ExpandingClassification {
51 classification: Default::default(),
52 __access_lvl__: Default::default(),
53 __access_req__: Default::default(),
54 __access_grp1__: Default::default(),
55 __access_grp2__: Default::default(),
56 },
57 ClassificationMode::Configured(parser) => ExpandingClassification::unrestricted(parser),
58 }
59}
60
61
62#[derive(Debug, Clone, Eq)]
71pub struct ExpandingClassification<const USER: bool=false> {
72 pub classification: String,
73 pub __access_lvl__: i32,
74 pub __access_req__: Vec<String>,
75 pub __access_grp1__: Vec<String>,
76 pub __access_grp2__: Vec<String>,
77}
78
79impl<const USER: bool> ExpandingClassification<USER> {
80 pub fn new(classification: String, parser: &ClassificationParser) -> Result<Self, ModelError> {
81 if parser.original_definition.enforce {
82
83 let parts = parser.get_classification_parts(&classification, false, true, !USER)?;
84 let classification = parser.get_normalized_classification_text(parts.clone(), false, false)?;
85
86 Ok(Self {
87 classification,
88 __access_lvl__: parts.level,
89 __access_req__: parts.required,
90 __access_grp1__: if parts.groups.is_empty() { vec!["__EMPTY__".to_owned()] } else { parts.groups },
91 __access_grp2__: if parts.subgroups.is_empty() { vec!["__EMPTY__".to_owned()] } else { parts.subgroups },
92 })
93 } else {
94 Ok(Self {
95 classification,
96 __access_lvl__: Default::default(),
97 __access_req__: Default::default(),
98 __access_grp1__: vec!["__EMPTY__".to_owned()],
99 __access_grp2__: vec!["__EMPTY__".to_owned()],
100 })
101 }
102 }
103
104 pub fn try_unrestricted() -> Option<Self> {
105 if let ClassificationMode::Configured(parser) = &*GLOBAL_CLASSIFICATION.lock().unwrap() {
106 Some(Self::unrestricted(parser))
107 } else {
108 None
109 }
110 }
111
112 pub fn unrestricted(parser: &ClassificationParser) -> Self {
113 Self::new(parser.unrestricted().to_string(), parser).unwrap()
114 }
115
116 pub fn as_str(&self) -> &str {
117 &self.classification
118 }
119
120 pub fn insert(parser: &ClassificationParser, output: &mut JsonMap, classification: &str) -> Result<(), ModelError> {
121 use serde_json::json;
122 if parser.original_definition.enforce {
123 let parts = parser.get_classification_parts(classification, true, true, !USER)?;
124 let classification = parser.get_normalized_classification_text(parts.clone(), true, false)?;
125
126 output.insert("classification".to_string(), json!(classification));
127 output.insert("__access_lvl__".to_string(), json!(parts.level));
128 output.insert("__access_req__".to_string(), json!(parts.required));
129 output.insert("__access_grp1__".to_string(), json!(if parts.groups.is_empty() { vec!["__EMPTY__".to_string()] } else { parts.groups }));
130 output.insert("__access_grp2__".to_string(), json!(if parts.subgroups.is_empty() { vec!["__EMPTY__".to_string()] } else { parts.subgroups }));
131 Ok(())
132 } else {
133 output.insert("classification".to_string(), json!(classification));
134 output.insert("__access_lvl__".to_string(), json!(0));
135 output.insert("__access_req__".to_string(), serde_json::Value::Array(Default::default()));
136 output.insert("__access_grp1__".to_string(), json!(&["__EMPTY__"]));
137 output.insert("__access_grp2__".to_string(), json!(&["__EMPTY__"]));
138 Ok(())
139 }
140 }
141}
142
143#[derive(Serialize, Deserialize)]
144struct RawClassification {
145 pub classification: String,
146 #[serde(default)]
147 pub __access_lvl__: i32,
148 #[serde(default)]
149 pub __access_req__: Vec<String>,
150 #[serde(default)]
151 pub __access_grp1__: Vec<String>,
152 #[serde(default)]
153 pub __access_grp2__: Vec<String>,
154}
155
156impl<const U: bool> From<&ExpandingClassification<U>> for RawClassification {
157 fn from(value: &ExpandingClassification<U>) -> Self {
158 Self {
159 classification: value.classification.clone(),
160 __access_lvl__: value.__access_lvl__,
161 __access_req__: value.__access_req__.clone(),
162 __access_grp1__: value.__access_grp1__.clone(),
163 __access_grp2__: value.__access_grp2__.clone(),
164 }
165 }
166}
167
168impl<const U: bool> From<RawClassification> for ExpandingClassification<U> {
169 fn from(value: RawClassification) -> Self {
170 Self {
171 classification: value.classification.clone(),
172 __access_lvl__: value.__access_lvl__,
173 __access_req__: value.__access_req__.clone(),
174 __access_grp1__: value.__access_grp1__.clone(),
175 __access_grp2__: value.__access_grp2__.clone(),
176 }
177 }
178}
179
180
181impl<const U: bool> Serialize for ExpandingClassification<U> {
182 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
183 where S: serde::Serializer
184 {
185 match &*GLOBAL_CLASSIFICATION.lock().unwrap() {
186 ClassificationMode::Uninitialized => return Err(serde::ser::Error::custom("classification engine not initalized")),
187 ClassificationMode::Disabled |
189 ClassificationMode::Configured(_) => {},
191 }
192 RawClassification::from(self).serialize(serializer)
193 }
194}
195
196impl<'de, const U: bool> Deserialize<'de> for ExpandingClassification<U> {
197 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
198 where D: serde::Deserializer<'de>
199 {
200 let parser = match &*GLOBAL_CLASSIFICATION.lock().unwrap() {
201 ClassificationMode::Uninitialized => return Err(serde::de::Error::custom("classification engine not initalized")),
202 ClassificationMode::Disabled => None,
204 ClassificationMode::Configured(parser) => Some(parser.clone()),
206 };
207
208 if let Some(parser) = parser {
209 let raw = RawClassification::deserialize(deserializer)?;
210 Self::new(raw.classification, &parser).map_err(serde::de::Error::custom)
211 } else {
212 Ok(RawClassification::deserialize(deserializer)?.into())
213 }
214 }
215}
216
217impl<const USER: bool> Described<ElasticMeta> for ExpandingClassification<USER> {
218 fn metadata() -> struct_metadata::Descriptor<ElasticMeta> {
219
220 struct_metadata::Descriptor {
227 docs: None,
228 metadata: ElasticMeta{mapping: Some("classification"), ..Default::default()},
229 kind: struct_metadata::Kind::new_struct("ExpandingClassification", vec![
230 struct_metadata::Entry { label: "classification", docs: None, metadata: ElasticMeta{mapping: Some("classification"), ..Default::default()}, type_info: String::metadata(), has_default: false, aliases: &["classification"] },
231 ], &mut [], &mut[]),
236 }
241 }
242}
243
244impl<const U: bool> PartialEq<&str> for ExpandingClassification<U> {
245 fn eq(&self, other: &&str) -> bool {
246 let parser = match &*GLOBAL_CLASSIFICATION.lock().unwrap() {
247 ClassificationMode::Uninitialized | ClassificationMode::Disabled => None,
248 ClassificationMode::Configured(parser) => Some(parser.clone()),
249 };
250
251 if let Some(parser) = parser {
252 match (parser.normalize_classification(&self.classification), parser.normalize_classification(other)) {
253 (Ok(a), Ok(b)) => a == b,
254 _ => false
255 }
256 } else {
257 self.classification.as_str() == *other
258 }
259 }
260}
261
262impl<const U: bool> PartialEq for ExpandingClassification<U> {
263 fn eq(&self, other: &Self) -> bool {
264 self.eq(&other.classification.as_str())
265 }
266}
267
268
269#[derive(Described, Debug, Clone, Eq)]
273#[metadata_type(ElasticMeta)]
274#[metadata(mapping="classification_string")]
275pub struct ClassificationString(pub (crate) String);
276
277impl From<ClassificationString> for String {
278 fn from(value: ClassificationString) -> Self {
279 value.0
280 }
281}
282
283impl Deref for ClassificationString {
284 type Target = str;
285
286 fn deref(&self) -> &Self::Target {
287 &self.0
288 }
289}
290
291impl ClassificationString {
292 pub fn new(classification: String, parser: &Arc<ClassificationParser>) -> Result<Self, ModelError> {
293 Ok(Self(parser.normalize_classification_options(&classification, NormalizeOptions::short())?))
294 }
295
296 pub fn new_unchecked(classification: String) -> Self {
297 Self(classification)
298 }
299
300 pub fn try_unrestricted() -> Option<Self> {
301 if let ClassificationMode::Configured(parser) = &*GLOBAL_CLASSIFICATION.lock().unwrap() {
302 Some(Self::unrestricted(parser))
303 } else {
304 None
305 }
306 }
307
308 pub fn unrestricted(parser: &ClassificationParser) -> Self {
309 Self(parser.unrestricted().to_owned())
310 }
311
312 pub fn as_str(&self) -> &str {
313 &self.0
314 }
315
316 pub fn default_unrestricted() -> ClassificationString {
317 match &*GLOBAL_CLASSIFICATION.lock().unwrap() {
318 ClassificationMode::Uninitialized => panic!("classification handling without defining parser"),
319 ClassificationMode::Disabled => ClassificationString(Default::default()),
320 ClassificationMode::Configured(parser) => ClassificationString::unrestricted(parser),
321 }
322 }
323}
324
325#[derive(Serialize, Deserialize)]
326struct RawClassificationString(String);
327
328
329impl From<&ClassificationString> for RawClassificationString {
330 fn from(value: &ClassificationString) -> Self {
331 Self(value.0.clone())
332 }
333}
334
335impl From<RawClassificationString> for ClassificationString {
336 fn from(value: RawClassificationString) -> Self {
337 Self(value.0.clone())
338 }
339}
340
341
342impl Serialize for ClassificationString {
343 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
344 where S: serde::Serializer
345 {
346 match &*GLOBAL_CLASSIFICATION.lock().unwrap() {
347 ClassificationMode::Uninitialized => return Err(serde::ser::Error::custom("classification engine not initalized")),
348 ClassificationMode::Disabled |
350 ClassificationMode::Configured(_) => {},
352 }
353 RawClassificationString::from(self).serialize(serializer)
354 }
355}
356
357impl<'de> Deserialize<'de> for ClassificationString {
377 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
378 where D: serde::Deserializer<'de>
379 {
380 let parser = match &*GLOBAL_CLASSIFICATION.lock().unwrap() {
381 ClassificationMode::Uninitialized => return Err(serde::de::Error::custom("classification engine not initalized")),
382 ClassificationMode::Disabled => None,
384 ClassificationMode::Configured(parser) => Some(parser.clone()),
386 };
387
388 if let Some(parser) = parser {
389 let raw = RawClassificationString::deserialize(deserializer)?;
390 Self::new(raw.0, &parser).map_err(serde::de::Error::custom)
391 } else {
392 Ok(RawClassificationString::deserialize(deserializer)?.into())
393 }
394 }
395}
396
397impl PartialEq for ClassificationString {
398 fn eq(&self, other: &Self) -> bool {
399 let parser = match &*GLOBAL_CLASSIFICATION.lock().unwrap() {
400 ClassificationMode::Uninitialized | ClassificationMode::Disabled => None,
401 ClassificationMode::Configured(parser) => Some(parser.clone()),
402 };
403
404 if let Some(parser) = parser {
405 match (parser.normalize_classification(&self.0), parser.normalize_classification(&other.0)) {
406 (Ok(a), Ok(b)) => a == b,
407 _ => false
408 }
409 } else {
410 self.0 == other.0
411 }
412 }
413}