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