assemblyline_models/datastore/
user.rs

1use std::collections::HashMap;
2
3use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5use serde_with::{DeserializeFromStr, SerializeDisplay};
6use struct_metadata::Described;
7use strum::IntoEnumIterator;
8
9use crate::{ElasticMeta, Readable, };
10use crate::types::{Email, UpperString, ExpandingClassification};
11
12
13
14#[derive(SerializeDisplay, DeserializeFromStr, strum::Display, strum::EnumString, strum::FromRepr, Described, Clone, Copy, Debug)]
15#[metadata_type(ElasticMeta)]
16#[strum(serialize_all = "snake_case")]
17pub enum UserType {
18    /// Perform administartive task and has access to all roles
19    Admin = 0,
20    /// Normal user of the system
21    User = 1,
22    /// Super user that also has access to roles for managing signatures in the system
23    SignatureManager = 2,
24    /// Has access to roles for importing signatures in the system
25    SignatureImporter = 3,
26    /// User that can only view the data
27    Viewer = 4,
28    /// User that can only start submissions
29    Submitter = 5,
30    /// Has custom roles selected
31    Custom = 6,
32}
33
34#[derive(SerializeDisplay, DeserializeFromStr, strum::Display, strum::EnumString, Described, Clone, Copy)]
35#[metadata_type(ElasticMeta)]
36#[strum(serialize_all = "lowercase")]
37pub enum Scope {R, W, RW, C}
38
39
40#[derive(SerializeDisplay, DeserializeFromStr, strum::Display, strum::EnumString, strum::FromRepr, strum::EnumIter, Described, Clone, Copy, PartialEq, Eq, Debug, PartialOrd, Ord)]
41#[metadata_type(ElasticMeta)]
42#[strum(serialize_all = "snake_case")]
43pub enum UserRole {
44    /// Modify labels, priority, status, verdict or owner of alerts
45    AlertManage = 0,
46    /// View alerts in the system
47    AlertView = 1,
48    /// Allow access via API keys
49    ApikeyAccess = 2,
50    /// Create bundle of a submission
51    BundleDownload = 3,
52    /// View files in the file viewer
53    FileDetail = 4,
54    /// Download files from the system
55    FileDownload = 5,
56    /// Purge files from the filestore
57    FilePurge = 33,
58    /// View heuristics of the system
59    HeuristicView = 6,
60    /// Allow access via On Behalf Off tokens
61    OboAccess = 7,
62    /// Allow submission to be replayed on another server
63    ReplayTrigger = 8,
64    /// View safelist items
65    SafelistView = 9,
66    /// Manage (add/delete) safelist items
67    SafelistManage = 10,
68    /// Download signatures from the system
69    SignatureDownload = 11,
70    /// View signatures
71    SignatureView = 12,
72    /// Create a submission in the system
73    SubmissionCreate = 13,
74    /// Delete submission from the system
75    SubmissionDelete = 14,
76    /// Set user verdict on submissions
77    SubmissionManage = 15,
78    /// View submission's results
79    SubmissionView = 16,
80    /// Manage (add/delete) workflows
81    WorkflowManage = 17,
82    /// View workflows
83    WorkflowView = 18,
84    /// Perform administrative tasks
85    Administration = 19,
86    /// Manage status of file/submission/alerts during the replay process
87    ReplaySystem = 20,
88    /// Import signatures in the system
89    SignatureImport = 21,
90    /// Manage signatures sources in the system
91    SignatureManage = 22,
92    /// View archived data in the system
93    ArchiveView = 23,
94    /// Modify attributes of archived Submissions/Files/Results
95    ArchiveManage = 24,
96    /// Send Submission, files and results to the archive
97    ArchiveTrigger = 25,
98    /// Download file from the archive
99    ArchiveDownload = 26,
100    /// Manage currently logged in user settings
101    SelfManage = 27,
102    /// View yara searches
103    RetrohuntView = 28,
104    /// Run yara searches
105    RetrohuntRun = 29,
106    /// Allow federated searches against external systems
107    ExternalQuery = 30,
108    /// View badlist items
109    BadlistView = 31,
110    /// Manage (add/delete) badlist items
111    BadlistManage = 32,
112    /// Comment on archived files
113    ArchiveComment = 35,
114    /// Use the Assemblyline Assistant
115    AssistantUse = 34,
116    /// Create a submission without a profile using custom settings
117    SubmissionCustomize = 36,
118}
119
120const USER_ROLES_BASIC: [UserRole; 31] = [
121    UserRole::AlertManage,
122    UserRole::AlertView,
123    UserRole::ArchiveTrigger,
124    UserRole::ArchiveView,
125    UserRole::ArchiveManage,
126    UserRole::ArchiveDownload,
127    UserRole::ArchiveComment,
128    UserRole::ApikeyAccess,
129    UserRole::BundleDownload,
130    UserRole::ExternalQuery,
131    UserRole::FileDetail,
132    UserRole::FileDownload,
133    UserRole::HeuristicView,
134    UserRole::OboAccess,
135    UserRole::ReplayTrigger,
136    UserRole::SafelistView,
137    UserRole::SafelistManage,
138    UserRole::SelfManage,
139    UserRole::SignatureDownload,
140    UserRole::SignatureView,
141    UserRole::SubmissionCreate,
142    UserRole::SubmissionDelete,
143    UserRole::SubmissionManage,
144    UserRole::SubmissionView,
145    UserRole::WorkflowManage,
146    UserRole::WorkflowView,
147    UserRole::RetrohuntView,
148    UserRole::RetrohuntRun,
149    UserRole::BadlistView,
150    UserRole::BadlistManage,
151    UserRole::SubmissionCustomize,
152];
153
154// USER_ROLES = USER_ROLES_BASIC.union({
155//     ROLES.administration,      # 
156//     ROLES.file_purge,          # 
157//     ROLES.replay_system,       # 
158//     ROLES.signature_import,    # 
159//     ROLES.signature_manage,    # 
160//     ROLES.assistant_use,       # 
161// })
162
163impl UserType {
164    #[must_use]
165    pub fn roles(&self) -> Vec<UserRole> {
166        match self {
167            UserType::Admin => UserRole::iter().collect(),
168            UserType::SignatureImporter => vec![
169                UserRole::BadlistManage,
170                UserRole::SafelistManage,
171                UserRole::SelfManage,
172                UserRole::SignatureDownload,
173                UserRole::SignatureImport,
174                UserRole::SignatureView
175            ],
176            UserType::SignatureManager => {
177                let mut roles: Vec<_> = UserRole::iter().collect();
178                roles.push(UserRole::SignatureManage);
179                roles
180            },
181            UserType::User => USER_ROLES_BASIC.into_iter().collect(),
182            UserType::Viewer => vec![
183                UserRole::AlertView,
184                UserRole::ApikeyAccess,
185                UserRole::BadlistView,
186                UserRole::FileDetail,
187                UserRole::OboAccess,
188                UserRole::HeuristicView,
189                UserRole::SafelistView,
190                UserRole::SelfManage,
191                UserRole::SignatureView,
192                UserRole::SubmissionView,
193                UserRole::WorkflowView,
194            ],
195            UserType::Submitter => vec![
196                UserRole::ApikeyAccess,
197                UserRole::OboAccess,
198                UserRole::SelfManage,
199                UserRole::SubmissionCreate,
200                UserRole::ReplayTrigger,
201                UserRole::RetrohuntRun,
202            ],
203            // custom is not a hardcoded list
204            UserType::Custom => vec![],
205        }
206    }
207}
208
209
210#[derive(SerializeDisplay, DeserializeFromStr, strum::Display, strum::EnumString, Described, Clone, Copy, Debug, PartialEq, Eq)]
211#[metadata_type(ElasticMeta)]
212pub enum AclCatagory {R, W, E, C}
213
214impl AclCatagory {
215    #[must_use]
216    pub fn roles(&self) -> &[UserRole] {
217        match self {
218            AclCatagory::R => &[
219                UserRole::AlertView,
220                UserRole::ArchiveView,
221                UserRole::ArchiveDownload,
222                UserRole::BadlistView,
223                UserRole::BundleDownload,
224                UserRole::ExternalQuery,
225                UserRole::FileDetail,
226                UserRole::FileDownload,
227                UserRole::HeuristicView,
228                UserRole::SafelistView,
229                UserRole::SignatureDownload,
230                UserRole::SignatureView,
231                UserRole::SubmissionView,
232                UserRole::WorkflowView,
233                UserRole::RetrohuntView,
234            ],
235            AclCatagory::W => &[
236                UserRole::AlertManage,
237                UserRole::ArchiveTrigger,
238                UserRole::ArchiveManage,
239                UserRole::BadlistManage,
240                UserRole::ReplayTrigger,
241                UserRole::SafelistManage,
242                UserRole::SubmissionCreate,
243                UserRole::SubmissionDelete,
244                UserRole::SubmissionManage,
245                UserRole::RetrohuntRun,
246                UserRole::SubmissionCustomize,
247            ],
248            AclCatagory::E => &[
249                UserRole::Administration,
250                UserRole::ApikeyAccess,
251                UserRole::FilePurge,
252                UserRole::OboAccess,
253                UserRole::ReplaySystem,
254                UserRole::SelfManage,
255                UserRole::SignatureImport,
256                UserRole::SignatureManage,
257                UserRole::WorkflowManage
258            ],
259            AclCatagory::C => &[],
260        }
261    }
262}
263
264
265pub fn load_roles_form_acls(acls: &[AclCatagory], current_roles: &[UserRole]) -> Vec<UserRole> {
266    // Check if we have current roles first
267    if !current_roles.is_empty() {
268        return current_roles.to_vec()
269    }
270
271    // Otherwise load the roles from the api_key ACLs
272    let mut roles = vec![];
273    for acl in acls {
274        roles.extend_from_slice(acl.roles())
275    }
276
277    // Return roles as a list
278    roles.sort_unstable();
279    roles.dedup();
280    return roles
281}
282
283
284pub fn load_roles(types: &[UserType], current_roles: &[UserRole]) -> Vec<UserRole> {
285    // Check if we have current roles first
286    if !current_roles.is_empty() {
287        return current_roles.to_vec()
288    }
289
290    // Otherwise load the roles from the user type
291    let mut roles = vec![];
292    for user_type in types {
293        roles.extend_from_slice(&user_type.roles());
294    }
295
296    // Return roles as a deduplicated list
297    roles.sort_unstable();
298    roles.dedup();
299    return roles
300}
301
302/// Model of Apps used of OBO (On Behalf Of)
303#[derive(Serialize, Deserialize, Described)]
304#[metadata_type(ElasticMeta)]
305#[metadata(index=false, store=false)]
306pub struct Apps {
307    /// Username allowed to impersonate the current user
308    pub client_id: String,
309    /// DNS hostname for the server
310    pub netloc: String,
311    /// Scope of access for the App token
312    pub scope: Scope,
313    /// Name of the server that has access
314    pub server: String,
315    /// List of roles tied to the App token
316    #[serde(default)]
317    pub roles: Vec<UserRole>,
318}
319
320/// Model of User
321#[derive(Serialize, Deserialize, Described)]
322#[metadata_type(ElasticMeta)]
323#[metadata(index=true, store=true)]
324pub struct User {
325    /// Date the user agree with terms of service
326    #[metadata(index=false, store=false)]
327    #[serde(default)]
328    pub agrees_with_tos: Option<DateTime<Utc>>,
329    /// Maximum number of concurrent API requests (0: No Quota)
330    #[metadata(store=false, mapping="integer")]
331    #[serde(default)]
332    pub api_quota: Option<u64>,
333    /// Maximum number of API calls a user can do daily (0: No Quota)
334    #[metadata(store=false, mapping="integer")]
335    #[serde(default)]
336    pub api_daily_quota: Option<u64>,
337    // /// Mapping of API keys
338    // #[metadata(index=false, store=false)]
339    // #[serde(default)]
340    // pub apikeys: HashMap<String, ApiKey>,
341    /// Applications with access to the account
342    #[metadata(index=false, store=false)]
343    #[serde(default)]
344    pub apps: HashMap<String, Apps>,
345    /// Allowed to query on behalf of others?
346    #[metadata(index=false, store=false)]
347    #[serde(default)]
348    pub can_impersonate: bool,
349    /// Maximum classification for the user
350    #[metadata(copyto="__text__")]
351    #[serde(flatten, default="unrestricted_expanding_classification")]
352    pub classification: ExpandingClassification<true>,
353    /// User's LDAP DN
354    #[metadata(store=false, copyto="__text__")]
355    #[serde(default)]
356    pub dn: Option<String>,
357    /// User's email address
358    #[metadata(copyto="__text__")]
359    #[serde(default)]
360    pub email: Option<Email>,
361    /// List of groups the user submits to
362    #[metadata(copyto="__text__")]
363    #[serde(default)]
364    pub groups: Vec<UpperString>,
365    /// ID of the matching object in your identity provider (used for logging in as another application)
366    #[metadata(copyto="__text__", store=false)]
367    #[serde(default)]
368    identity_id: Option<String>,
369    /// Is the user active?
370    #[serde(default="default_user_is_active")]
371    pub is_active: bool,
372    /// Full name of the user
373    #[metadata(copyto="__text__")]
374    pub name: String,
375    /// Secret key to generate one time passwords
376    #[metadata(index=false, store=false)]    
377    #[serde(default)]
378    pub otp_sk: Option<String>,
379    /// BCrypt hash of the user's password
380    #[metadata(index=false, store=false)]
381    pub password: String,
382    /// Maximum number of concurrent submissions (0: No Quota)
383    #[metadata(store=false, mapping="integer")]
384    #[serde(default)]
385    pub submission_quota: Option<u64>,
386    /// Maximum number of concurrent async submission (0: No Quota)
387    #[metadata(store=false, mapping="integer")]
388    #[serde(default)]
389    pub submission_async_quota: Option<u64>,
390    /// Maximum number of submissions a user can do daily (0: No Quota)
391    #[metadata(store=false, mapping="integer")]
392    #[serde(default)]
393    pub submission_daily_quota: Option<u64>,
394    /// Type of user
395    #[serde(rename="type", default="default_user_types")]
396    pub user_types: Vec<UserType>,
397    /// Default roles for user
398    #[serde(default)]
399    pub roles: Vec<UserRole>,
400    /// Map of security tokens
401    #[metadata(index=false, store=false)]
402    #[serde(default)]
403    pub security_tokens: HashMap<String, String>,
404    /// Username
405    #[metadata(copyto="__text__")]
406    pub uname: String,
407}
408
409pub fn default_user_types() -> Vec<UserType> { vec![UserType::User] }
410fn default_user_is_active() -> bool { true }
411
412impl Readable for User {
413    fn set_from_archive(&mut self, _from_archive: bool) {}
414}
415
416impl User {
417    pub fn create_test_user() -> Self {
418        User {
419            agrees_with_tos: None,
420            api_quota: None,
421            api_daily_quota: None,
422            // apikeys: Default::default(),
423            apps: Default::default(),
424            can_impersonate: false,
425            classification: ExpandingClassification::try_unrestricted().unwrap(),
426            dn: None,
427            email: None,
428            groups: Default::default(),
429            identity_id: None,
430            is_active: default_user_is_active(),
431            name: "User".to_owned(),
432            otp_sk: None,
433            password: Default::default(),
434            submission_quota: None,
435            submission_async_quota: None,
436            submission_daily_quota: None,
437            user_types: default_user_types(),
438            roles: Default::default(),
439            security_tokens: Default::default(),
440            uname: "user".to_owned(),
441        }
442    }
443}
444
445// #[test]
446// fn sample_admin_user() {
447//     let data = r#"{
448//         "agrees_with_tos": "2025-01-30T19:24:57.559049Z", 
449//         "api_quota": null, 
450//         "api_daily_quota": null, 
451//         "apikeys": {}, 
452//         "apps": {}, 
453//         "can_impersonate": false, 
454//         "classification": "", 
455//         "dn": null, 
456//         "email": null, 
457//         "groups": [], 
458//         "identity_id": null, 
459//         "is_active": true, 
460//         "name": "Administrator", 
461//         "otp_sk": null, 
462//         "password": "password", 
463//         "submission_quota": null, 
464//         "submission_async_quota": null, 
465//         "submission_daily_quota": null, 
466//         "type": ["admin", "user", "signature_importer"], 
467//         "roles": [], 
468//         "security_tokens": {}, 
469//         "uname": "admin"
470//     }"#;
471//     let _user: User = serde_json::from_str(&data).unwrap();
472// }