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)]
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)]
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)]
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
265// def load_roles_form_acls(acls, curRoles):
266//     # Check if we have current roles first
267//     if curRoles:
268//         return curRoles
269
270//     # Otherwise load the roles from the api_key ACLs
271//     roles = set({})
272//     for acl in ACL_MAP.keys():
273//         if acl in acls:
274//             roles = roles.union(ACL_MAP[acl])
275
276//     # Return roles as a list
277//     return list(roles)
278
279
280// def load_roles(types, curRoles):
281//     # Check if we have current roles first
282//     if curRoles:
283//         return curRoles
284
285//     # Otherwise load the roles from the user type
286//     roles = set({})
287//     for user_type in USER_TYPE_DEP.keys():
288//         if user_type in types:
289//             roles = roles.union(USER_TYPE_DEP[user_type])
290
291//     # Return roles as a list
292//     return list(roles)
293
294/// Model for API keys
295#[derive(Serialize, Deserialize, Described)]
296#[metadata_type(ElasticMeta)]
297#[metadata(index=false, store=false)]
298pub struct ApiKey {
299    /// Access Control List for the API key
300    pub acl: Vec<AclCatagory>,
301    /// BCrypt hash of the password for the apikey
302    pub password: String,
303    /// List of roles tied to the API key
304    #[serde(default)]
305    pub roles: Vec<UserRole>,
306}
307
308/// Model of Apps used of OBO (On Behalf Of)
309#[derive(Serialize, Deserialize, Described)]
310#[metadata_type(ElasticMeta)]
311#[metadata(index=false, store=false)]
312pub struct Apps {
313    /// Username allowed to impersonate the current user
314    pub client_id: String,
315    /// DNS hostname for the server
316    pub netloc: String,
317    /// Scope of access for the App token
318    pub scope: Scope,
319    /// Name of the server that has access
320    pub server: String,
321    /// List of roles tied to the App token
322    #[serde(default)]
323    pub roles: Vec<UserRole>,
324}
325
326/// Model of User
327#[derive(Serialize, Deserialize, Described)]
328#[metadata_type(ElasticMeta)]
329#[metadata(index=true, store=true)]
330pub struct User {
331    /// Date the user agree with terms of service
332    #[metadata(index=false, store=false)]
333    #[serde(default)]
334    pub agrees_with_tos: Option<DateTime<Utc>>,
335    /// Maximum number of concurrent API requests (0: No Quota)
336    #[metadata(store=false, mapping="integer")]
337    #[serde(default)]
338    pub api_quota: Option<u64>,
339    /// Maximum number of API calls a user can do daily (0: No Quota)
340    #[metadata(store=false, mapping="integer")]
341    #[serde(default)]
342    pub api_daily_quota: Option<u64>,
343    /// Mapping of API keys
344    #[metadata(index=false, store=false)]
345    #[serde(default)]
346    pub apikeys: HashMap<String, ApiKey>,
347    /// Applications with access to the account
348    #[metadata(index=false, store=false)]
349    #[serde(default)]
350    pub apps: HashMap<String, Apps>,
351    /// Allowed to query on behalf of others?
352    #[metadata(index=false, store=false)]
353    #[serde(default)]
354    pub can_impersonate: bool,
355    /// Maximum classification for the user
356    #[metadata(copyto="__text__")]
357    #[serde(flatten, default="unrestricted_expanding_classification")]
358    pub classification: ExpandingClassification<true>,
359    /// User's LDAP DN
360    #[metadata(store=false, copyto="__text__")]
361    #[serde(default)]
362    pub dn: Option<String>,
363    /// User's email address
364    #[metadata(copyto="__text__")]
365    #[serde(default)]
366    pub email: Option<Email>,
367    /// List of groups the user submits to
368    #[metadata(copyto="__text__")]
369    #[serde(default)]
370    pub groups: Vec<UpperString>,
371    /// ID of the matching object in your identity provider (used for logging in as another application)
372    #[metadata(copyto="__text__", store=false)]
373    #[serde(default)]
374    identity_id: Option<String>,
375    /// Is the user active?
376    #[serde(default="default_user_is_active")]
377    pub is_active: bool,
378    /// Full name of the user
379    #[metadata(copyto="__text__")]
380    pub name: String,
381    /// Secret key to generate one time passwords
382    #[metadata(index=false, store=false)]    
383    #[serde(default)]
384    pub otp_sk: Option<String>,
385    /// BCrypt hash of the user's password
386    #[metadata(index=false, store=false)]
387    pub password: String,
388    /// Maximum number of concurrent submissions (0: No Quota)
389    #[metadata(store=false, mapping="integer")]
390    #[serde(default)]
391    pub submission_quota: Option<u64>,
392    /// Maximum number of concurrent async submission (0: No Quota)
393    #[metadata(store=false, mapping="integer")]
394    #[serde(default)]
395    pub submission_async_quota: Option<u64>,
396    /// Maximum number of submissions a user can do daily (0: No Quota)
397    #[metadata(store=false, mapping="integer")]
398    #[serde(default)]
399    pub submission_daily_quota: Option<u64>,
400    /// Type of user
401    #[serde(rename="type", default="default_user_types")]
402    pub user_types: Vec<UserType>,
403    /// Default roles for user
404    #[serde(default)]
405    pub roles: Vec<UserRole>,
406    /// Map of security tokens
407    #[metadata(index=false, store=false)]
408    #[serde(default)]
409    pub security_tokens: HashMap<String, String>,
410    /// Username
411    #[metadata(copyto="__text__")]
412    pub uname: String,
413}
414
415fn default_user_types() -> Vec<UserType> { vec![UserType::User] }
416fn default_user_is_active() -> bool { true }
417
418impl Readable for User {
419    fn set_from_archive(&mut self, _from_archive: bool) {}
420}
421
422impl User {
423    pub fn create_test_user() -> Self {
424        User {
425            agrees_with_tos: None,
426            api_quota: None,
427            api_daily_quota: None,
428            apikeys: Default::default(),
429            apps: Default::default(),
430            can_impersonate: false,
431            classification: ExpandingClassification::try_unrestricted().unwrap(),
432            dn: None,
433            email: None,
434            groups: Default::default(),
435            identity_id: None,
436            is_active: default_user_is_active(),
437            name: "User".to_owned(),
438            otp_sk: None,
439            password: Default::default(),
440            submission_quota: None,
441            submission_async_quota: None,
442            submission_daily_quota: None,
443            user_types: default_user_types(),
444            roles: Default::default(),
445            security_tokens: Default::default(),
446            uname: "user".to_owned(),
447        }
448    }
449}
450
451// #[test]
452// fn sample_admin_user() {
453//     let data = r#"{
454//         "agrees_with_tos": "2025-01-30T19:24:57.559049Z", 
455//         "api_quota": null, 
456//         "api_daily_quota": null, 
457//         "apikeys": {}, 
458//         "apps": {}, 
459//         "can_impersonate": false, 
460//         "classification": "", 
461//         "dn": null, 
462//         "email": null, 
463//         "groups": [], 
464//         "identity_id": null, 
465//         "is_active": true, 
466//         "name": "Administrator", 
467//         "otp_sk": null, 
468//         "password": "password", 
469//         "submission_quota": null, 
470//         "submission_async_quota": null, 
471//         "submission_daily_quota": null, 
472//         "type": ["admin", "user", "signature_importer"], 
473//         "roles": [], 
474//         "security_tokens": {}, 
475//         "uname": "admin"
476//     }"#;
477//     let _user: User = serde_json::from_str(&data).unwrap();
478// }