Skip to content

Commit 8f6279b

Browse files
committed
feat: Introduce User Groups to Parseable
- Added support for user groups - Migrated `PUT /user/{username}/role` to `PATCH /user/{username}/role/add` and `PATCH /user/{username}/role/remove`
1 parent 33ed89c commit 8f6279b

File tree

18 files changed

+1103
-106
lines changed

18 files changed

+1103
-106
lines changed

src/handlers/http/cluster/mod.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,20 +160,29 @@ pub async fn sync_streams_with_ingestors(
160160
pub async fn sync_users_with_roles_with_ingestors(
161161
username: &str,
162162
role: &HashSet<String>,
163+
operation: &str,
163164
) -> Result<(), RBACError> {
165+
match operation {
166+
"add" | "remove" => {}
167+
_ => return Err(RBACError::InvalidSyncOperation(operation.to_string())),
168+
}
169+
164170
let role_data = to_vec(&role.clone()).map_err(|err| {
165171
error!("Fatal: failed to serialize role: {:?}", err);
166172
RBACError::SerdeError(err)
167173
})?;
168174

169175
let username = username.to_owned();
170176

177+
let op = operation.to_string();
178+
171179
for_each_live_ingestor(move |ingestor| {
172180
let url = format!(
173-
"{}{}/user/{}/role/sync",
181+
"{}{}/user/{}/role/sync/{}",
174182
ingestor.domain_name,
175183
base_path_without_preceding_slash(),
176-
username
184+
username,
185+
op
177186
);
178187

179188
let role_data = role_data.clone();

src/handlers/http/modal/ingest/ingestor_rbac.rs

Lines changed: 72 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ pub async fn post_user(
4848
let _ = storage::put_staging_metadata(&metadata);
4949
let created_role = user.roles.clone();
5050
Users.put_user(user.clone());
51-
Users.put_role(&username, created_role.clone());
51+
Users.add_roles(&username, created_role.clone());
5252
}
5353

5454
Ok(generated_password)
@@ -73,14 +73,45 @@ pub async fn delete_user(username: web::Path<String>) -> Result<impl Responder,
7373
Ok(format!("deleted user: {username}"))
7474
}
7575

76-
// Handler PUT /user/{username}/roles => Put roles for user
77-
// Put roles for given user
78-
pub async fn put_role(
76+
// // Handler PUT /user/{username}/roles => Put roles for user
77+
// // Put roles for given user
78+
// pub async fn put_role(
79+
// username: web::Path<String>,
80+
// role: web::Json<HashSet<String>>,
81+
// ) -> Result<String, RBACError> {
82+
// let username = username.into_inner();
83+
// let role = role.into_inner();
84+
85+
// if !Users.contains(&username) {
86+
// return Err(RBACError::UserDoesNotExist);
87+
// };
88+
// // update parseable.json first
89+
// let mut metadata = get_metadata().await?;
90+
// if let Some(user) = metadata
91+
// .users
92+
// .iter_mut()
93+
// .find(|user| user.username() == username)
94+
// {
95+
// user.roles.clone_from(&role);
96+
// } else {
97+
// // should be unreachable given state is always consistent
98+
// return Err(RBACError::UserDoesNotExist);
99+
// }
100+
101+
// let _ = storage::put_staging_metadata(&metadata);
102+
// // update in mem table
103+
// Users.add_roles(&username.clone(), role.clone());
104+
105+
// Ok(format!("Roles updated successfully for {username}"))
106+
// }
107+
108+
// Handler PATCH /user/{username}/role/sync/add => Add roles to a user
109+
pub async fn add_roles_to_user(
79110
username: web::Path<String>,
80-
role: web::Json<HashSet<String>>,
111+
roles_to_add: web::Json<HashSet<String>>,
81112
) -> Result<String, RBACError> {
82113
let username = username.into_inner();
83-
let role = role.into_inner();
114+
let roles_to_add = roles_to_add.into_inner();
84115

85116
if !Users.contains(&username) {
86117
return Err(RBACError::UserDoesNotExist);
@@ -92,15 +123,48 @@ pub async fn put_role(
92123
.iter_mut()
93124
.find(|user| user.username() == username)
94125
{
95-
user.roles.clone_from(&role);
126+
user.roles.extend(roles_to_add.clone());
96127
} else {
97128
// should be unreachable given state is always consistent
98129
return Err(RBACError::UserDoesNotExist);
99130
}
100131

101132
let _ = storage::put_staging_metadata(&metadata);
102133
// update in mem table
103-
Users.put_role(&username.clone(), role.clone());
134+
Users.add_roles(&username.clone(), roles_to_add.clone());
135+
136+
Ok(format!("Roles updated successfully for {username}"))
137+
}
138+
139+
// Handler PATCH /user/{username}/role/sync/add => Add roles to a user
140+
pub async fn remove_roles_from_user(
141+
username: web::Path<String>,
142+
roles_to_remove: web::Json<HashSet<String>>,
143+
) -> Result<String, RBACError> {
144+
let username = username.into_inner();
145+
let roles_to_remove = roles_to_remove.into_inner();
146+
147+
if !Users.contains(&username) {
148+
return Err(RBACError::UserDoesNotExist);
149+
};
150+
// update parseable.json first
151+
let mut metadata = get_metadata().await?;
152+
if let Some(user) = metadata
153+
.users
154+
.iter_mut()
155+
.find(|user| user.username() == username)
156+
{
157+
let diff: HashSet<String> =
158+
HashSet::from_iter(user.roles.difference(&roles_to_remove).cloned());
159+
user.roles = diff;
160+
} else {
161+
// should be unreachable given state is always consistent
162+
return Err(RBACError::UserDoesNotExist);
163+
}
164+
165+
let _ = storage::put_staging_metadata(&metadata);
166+
// update in mem table
167+
Users.remove_roles(&username.clone(), roles_to_remove.clone());
104168

105169
Ok(format!("Roles updated successfully for {username}"))
106170
}

src/handlers/http/modal/ingest_server.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -198,11 +198,21 @@ impl IngestServer {
198198
.wrap(DisAllowRootUser),
199199
)
200200
.service(
201-
web::resource("/{username}/role/sync")
202-
// PUT /user/{username}/roles => Put roles for user
201+
web::resource("/{username}/role/sync/add")
202+
// PATCH /user/{username}/role/sync/add => Add roles to a user
203203
.route(
204-
web::put()
205-
.to(ingestor_rbac::put_role)
204+
web::patch()
205+
.to(ingestor_rbac::add_roles_to_user)
206+
.authorize(Action::PutUserRoles)
207+
.wrap(DisAllowRootUser),
208+
),
209+
)
210+
.service(
211+
web::resource("/{username}/role/sync/remove")
212+
// PATCH /user/{username}/role/sync/remove => Remove roles from a user
213+
.route(
214+
web::patch()
215+
.to(ingestor_rbac::remove_roles_from_user)
206216
.authorize(Action::PutUserRoles)
207217
.wrap(DisAllowRootUser),
208218
),

0 commit comments

Comments
 (0)