Skip to content

Commit 8aac73b

Browse files
committed
Added delete op
1 parent 255d687 commit 8aac73b

9 files changed

Lines changed: 388 additions & 5 deletions

File tree

vcs-worker/src/operations/mod.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ pub use registry::OperationRegistry;
3030
pub use system::StatusOperation;
3131
pub use user::{
3232
StatOperation, UserAddPermissionOperation, UserCreateOperation, UserDeleteApiKeyOperation,
33-
UserDisableOperation, UserEnableOperation, UserGenerateApiKeyOperation, UserListOperation,
34-
UserRemovePermissionOperation,
33+
UserDeleteOperation, UserDisableOperation, UserEnableOperation, UserGenerateApiKeyOperation,
34+
UserListOperation, UserRemovePermissionOperation,
3535
};
3636
pub use workspace::{WorkspaceListOperation, WorkspaceSubmitOperation};
3737

@@ -231,6 +231,7 @@ pub fn create_registry_with_config(
231231
registry.register(UserCreateOperation::new(database.users().clone()));
232232
registry.register(UserDisableOperation::new(database.users().clone()));
233233
registry.register(UserEnableOperation::new(database.users().clone()));
234+
registry.register(UserDeleteOperation::new(database.users().clone()));
234235
registry.register(UserAddPermissionOperation::new(database.users().clone()));
235236
registry.register(UserRemovePermissionOperation::new(database.users().clone()));
236237
registry.register(UserGenerateApiKeyOperation::new(database.users().clone()));

vcs-worker/src/operations/user/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ mod stat_op;
22
mod user_add_permission_op;
33
mod user_create_op;
44
mod user_delete_api_key_op;
5+
mod user_delete_op;
56
mod user_disable_op;
67
mod user_enable_op;
78
mod user_generate_api_key_op;
@@ -12,6 +13,7 @@ pub use stat_op::StatOperation;
1213
pub use user_add_permission_op::UserAddPermissionOperation;
1314
pub use user_create_op::UserCreateOperation;
1415
pub use user_delete_api_key_op::UserDeleteApiKeyOperation;
16+
pub use user_delete_op::UserDeleteOperation;
1517
pub use user_disable_op::UserDisableOperation;
1618
pub use user_enable_op::UserEnableOperation;
1719
pub use user_generate_api_key_op::UserGenerateApiKeyOperation;

vcs-worker/src/operations/user/user_add_permission_op.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ impl UserAddPermissionOperation {
2424
"Clone" => Ok(Permission::Clone),
2525
"CreateUser" | "Create_User" => Ok(Permission::CreateUser),
2626
"DisableUser" | "Disable_User" => Ok(Permission::DisableUser),
27+
"DeleteUser" | "Delete_User" => Ok(Permission::DeleteUser),
2728
"ManagePermissions" | "Manage_Permissions" => Ok(Permission::ManagePermissions),
2829
"ManageApiKeys" | "Manage_Api_Keys" => Ok(Permission::ManageApiKeys),
2930
_ => Err(format!("Unknown permission: {perm_str}")),
@@ -48,8 +49,8 @@ impl Operation for UserAddPermissionOperation {
4849
"Grants a specific permission to a user account. Permissions control what operations a user \
4950
can perform in the system. Available permissions include: ApproveChanges (approve code reviews), \
5051
SubmitChanges (submit changes for review), Clone (clone repositories), CreateUser (create new users), \
51-
DisableUser (disable/enable users), ManagePermissions (grant/revoke permissions), and ManageApiKeys \
52-
(manage API keys for other users). This operation requires the ManagePermissions permission."
52+
DisableUser (disable/enable users), DeleteUser (delete users), ManagePermissions (grant/revoke permissions), \
53+
and ManageApiKeys (manage API keys for other users). This operation requires the ManagePermissions permission."
5354
}
5455

5556
fn parameters(&self) -> Vec<OperationParameter> {
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
use crate::operations::{Operation, OperationExample, OperationParameter, OperationRoute};
2+
use crate::providers::user::UserProvider;
3+
use axum::http::Method;
4+
use std::sync::Arc;
5+
use tracing::{error, info};
6+
7+
use crate::types::{Permission, User};
8+
9+
/// Delete user operation
10+
#[derive(Clone)]
11+
pub struct UserDeleteOperation {
12+
user_provider: Arc<dyn UserProvider>,
13+
}
14+
15+
impl UserDeleteOperation {
16+
pub fn new(user_provider: Arc<dyn UserProvider>) -> Self {
17+
Self { user_provider }
18+
}
19+
}
20+
21+
impl Operation for UserDeleteOperation {
22+
fn name(&self) -> &'static str {
23+
"user/delete"
24+
}
25+
26+
fn response_content_type(&self) -> &'static str {
27+
"text/x-moo"
28+
}
29+
30+
fn description(&self) -> &'static str {
31+
"Delete a user from the system"
32+
}
33+
34+
fn philosophy(&self) -> &'static str {
35+
"Permanently deletes a user account from the system, removing all their data, permissions, \
36+
and API keys. This operation is irreversible and should be used with caution. System users \
37+
(like Wizard and Everyone) cannot be deleted to maintain system integrity. This operation \
38+
requires the DeleteUser permission and is intended for removing accounts that are no longer \
39+
needed or were created in error."
40+
}
41+
42+
fn parameters(&self) -> Vec<OperationParameter> {
43+
vec![OperationParameter {
44+
name: "user_id".to_string(),
45+
description: "ID of the user to delete".to_string(),
46+
required: true,
47+
}]
48+
}
49+
50+
fn examples(&self) -> Vec<OperationExample> {
51+
vec![OperationExample {
52+
description: "Delete a user".to_string(),
53+
moocode: r#"result = worker_request("vcs", {"user/delete", "alice"});
54+
// Permanently deletes user 'alice' and all associated data"#
55+
.to_string(),
56+
http_curl: Some(
57+
r#"curl -X POST http://localhost:8081/api/user/delete \
58+
-H "Content-Type: application/json" \
59+
-d '{"operation": "user/delete", "args": ["alice"]}'
60+
"#
61+
.to_string(),
62+
),
63+
}]
64+
}
65+
66+
fn routes(&self) -> Vec<OperationRoute> {
67+
vec![OperationRoute {
68+
path: "/api/user/delete".to_string(),
69+
method: Method::POST,
70+
is_json: true,
71+
}]
72+
}
73+
74+
fn responses(&self) -> Vec<crate::operations::OperationResponse> {
75+
use crate::operations::OperationResponse;
76+
vec![
77+
OperationResponse::success(
78+
"Operation executed successfully",
79+
r#""Operation completed successfully""#,
80+
),
81+
OperationResponse::new(
82+
400,
83+
"Bad Request - Invalid arguments",
84+
r#"E_INVARG("Error: Invalid operation arguments")"#,
85+
),
86+
OperationResponse::new(
87+
404,
88+
"Not Found - Resource not found",
89+
r#"E_INVARG("Error: Resource not found")"#,
90+
),
91+
OperationResponse::new(
92+
500,
93+
"Internal Server Error - Database or system error",
94+
r#"E_INVARG("Error: Database error: operation failed")"#,
95+
),
96+
]
97+
}
98+
99+
fn execute(&self, args: Vec<String>, user: &User) -> moor_var::Var {
100+
info!("Executing user/delete operation for user: {}", user.id);
101+
102+
// Check permission
103+
if !user.has_permission(&Permission::DeleteUser) {
104+
error!("User {} does not have DeleteUser permission", user.id);
105+
return moor_var::v_error(
106+
moor_var::E_INVARG.msg("Error: You do not have permission to delete users"),
107+
);
108+
}
109+
110+
// Validate arguments
111+
if args.is_empty() {
112+
error!("Invalid arguments for user/delete: expected 1, got 0");
113+
return moor_var::v_error(
114+
moor_var::E_INVARG.msg("Error: Expected 1 argument: user_id"),
115+
);
116+
}
117+
118+
let target_user_id = &args[0];
119+
120+
// Delete the user
121+
match self.user_provider.delete_user(target_user_id) {
122+
Ok(true) => {
123+
info!("Deleted user '{}'", target_user_id);
124+
moor_var::v_str(&format!("Successfully deleted user '{target_user_id}'"))
125+
}
126+
Ok(false) => {
127+
error!("User '{}' not found", target_user_id);
128+
moor_var::v_error(moor_var::E_INVARG.msg(format!(
129+
"Error: User '{target_user_id}' not found"
130+
)))
131+
}
132+
Err(e) => {
133+
error!("Failed to delete user: {}", e);
134+
moor_var::v_error(moor_var::E_INVARG.msg(format!("Error: {e}")))
135+
}
136+
}
137+
}
138+
}
139+

vcs-worker/src/operations/user/user_remove_permission_op.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ impl UserRemovePermissionOperation {
2424
"Clone" => Ok(Permission::Clone),
2525
"CreateUser" | "Create_User" => Ok(Permission::CreateUser),
2626
"DisableUser" | "Disable_User" => Ok(Permission::DisableUser),
27+
"DeleteUser" | "Delete_User" => Ok(Permission::DeleteUser),
2728
"ManagePermissions" | "Manage_Permissions" => Ok(Permission::ManagePermissions),
2829
"ManageApiKeys" | "Manage_Api_Keys" => Ok(Permission::ManageApiKeys),
2930
_ => Err(format!("Unknown permission: {perm_str}")),

vcs-worker/src/providers/user.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ impl UserProviderImpl {
162162
user.add_permission(Permission::Clone);
163163
user.add_permission(Permission::CreateUser);
164164
user.add_permission(Permission::DisableUser);
165+
user.add_permission(Permission::DeleteUser);
165166
user.add_permission(Permission::ManagePermissions);
166167
user.add_permission(Permission::ManageApiKeys);
167168
user
@@ -386,6 +387,7 @@ impl UserProvider for UserProviderImpl {
386387
wizard_user.add_permission(Permission::Clone);
387388
wizard_user.add_permission(Permission::CreateUser);
388389
wizard_user.add_permission(Permission::DisableUser);
390+
wizard_user.add_permission(Permission::DeleteUser);
389391
wizard_user.add_permission(Permission::ManagePermissions);
390392
wizard_user.add_permission(Permission::ManageApiKeys);
391393
// Ensure it's marked as a system user

vcs-worker/src/types.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ pub enum Permission {
231231
Clone,
232232
CreateUser,
233233
DisableUser,
234+
DeleteUser,
234235
ManagePermissions,
235236
ManageApiKeys,
236237
}
@@ -243,6 +244,7 @@ impl std::fmt::Display for Permission {
243244
Permission::Clone => "Clone",
244245
Permission::CreateUser => "Create_User",
245246
Permission::DisableUser => "Disable_User",
247+
Permission::DeleteUser => "Delete_User",
246248
Permission::ManagePermissions => "Manage_Permissions",
247249
Permission::ManageApiKeys => "Manage_Api_Keys",
248250
};

vcs-worker/tests/operations/test_wizard_user.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ async fn test_wizard_user_exists_with_all_permissions() {
5757
wizard_user.has_permission(&Permission::DisableUser),
5858
"Wizard should have DisableUser permission"
5959
);
60+
assert!(
61+
wizard_user.has_permission(&Permission::DeleteUser),
62+
"Wizard should have DeleteUser permission"
63+
);
6064
assert!(
6165
wizard_user.has_permission(&Permission::ManagePermissions),
6266
"Wizard should have ManagePermissions permission"
@@ -66,7 +70,7 @@ async fn test_wizard_user_exists_with_all_permissions() {
6670
"Wizard should have ManageApiKeys permission"
6771
);
6872

69-
println!("✅ Wizard has all permissions: ApproveChanges, SubmitChanges, Clone, CreateUser, DisableUser, ManagePermissions, ManageApiKeys");
73+
println!("✅ Wizard has all permissions: ApproveChanges, SubmitChanges, Clone, CreateUser, DisableUser, DeleteUser, ManagePermissions, ManageApiKeys");
7074

7175
// Step 3: Verify Wizard has the default API key
7276
println!("\nStep 3: Verifying Wizard has the API key...");

0 commit comments

Comments
 (0)