Skip to content

Commit 1a1c340

Browse files
authored
add default role api (#517)
This PR adds an API to set default role to be assigned to any new OIDC user. This is needed for identity providers like Google where there is no group membership for a given user. Fixes #513
1 parent 1bbdf89 commit 1a1c340

File tree

7 files changed

+65
-20
lines changed

7 files changed

+65
-20
lines changed

server/src/handlers/http.rs

+6
Original file line numberDiff line numberDiff line change
@@ -261,8 +261,14 @@ pub fn configure_routes(
261261
.authorize(Action::QueryLLM),
262262
),
263263
);
264+
264265
let role_api = web::scope("/role")
265266
.service(resource("").route(web::get().to(role::list).authorize(Action::ListRole)))
267+
.service(
268+
resource("/default")
269+
.route(web::put().to(role::put_default).authorize(Action::PutRole))
270+
.route(web::get().to(role::get_default).authorize(Action::GetRole)),
271+
)
266272
.service(
267273
resource("/{name}")
268274
.route(web::put().to(role::put).authorize(Action::PutRole))

server/src/handlers/http/oidc.rs

+17-12
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ use crate::{
3434
oidc::{Claims, DiscoveredClient},
3535
option::CONFIG,
3636
rbac::{
37-
map::SessionKey,
37+
map::{SessionKey, DEFAULT_ROLE},
3838
user::{User, UserType},
3939
Users,
4040
},
@@ -263,22 +263,27 @@ async fn put_user(
263263
group: Option<HashSet<String>>,
264264
) -> Result<User, ObjectStorageError> {
265265
let mut metadata = get_metadata().await?;
266-
let user = match metadata
266+
let group = group.unwrap_or_else(|| {
267+
DEFAULT_ROLE
268+
.lock()
269+
.unwrap()
270+
.clone()
271+
.map(|role| HashSet::from([role]))
272+
.unwrap_or_default()
273+
});
274+
275+
let user = metadata
267276
.users
268277
.iter()
269278
.find(|user| user.username() == username)
270-
{
271-
Some(user) => user.clone(),
272-
None => {
273-
let mut user = User::new_oauth(username.to_owned());
274-
if let Some(group) = group {
275-
user.roles = group
276-
}
279+
.cloned()
280+
.unwrap_or_else(|| {
281+
let user = User::new_oauth(username.to_owned(), group);
277282
metadata.users.push(user.clone());
278-
put_metadata(&metadata).await?;
279283
user
280-
}
281-
};
284+
});
285+
286+
put_metadata(&metadata).await?;
282287
Users.put_user(user.clone());
283288
Ok(user)
284289
}

server/src/handlers/http/role.rs

+26-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ use http::StatusCode;
2121

2222
use crate::{
2323
option::CONFIG,
24-
rbac::{map::mut_roles, role::model::DefaultPrivilege},
24+
rbac::{
25+
map::{mut_roles, DEFAULT_ROLE},
26+
role::model::DefaultPrivilege,
27+
},
2528
storage::{self, ObjectStorageError, StorageMetadata},
2629
};
2730

@@ -71,6 +74,28 @@ pub async fn delete(name: web::Path<String>) -> Result<impl Responder, RoleError
7174
Ok(HttpResponse::Ok().finish())
7275
}
7376

77+
// Handler for PUT /api/v1/role/default
78+
// Delete existing role
79+
pub async fn put_default(name: web::Json<String>) -> Result<impl Responder, RoleError> {
80+
let name = name.into_inner();
81+
let mut metadata = get_metadata().await?;
82+
metadata.default_role = Some(name.clone());
83+
*DEFAULT_ROLE.lock().unwrap() = Some(name);
84+
put_metadata(&metadata).await?;
85+
Ok(HttpResponse::Ok().finish())
86+
}
87+
88+
// Handler for GET /api/v1/role/default
89+
// Delete existing role
90+
pub async fn get_default() -> Result<impl Responder, RoleError> {
91+
let res = match DEFAULT_ROLE.lock().unwrap().clone() {
92+
Some(role) => serde_json::Value::String(role),
93+
None => serde_json::Value::Null,
94+
};
95+
96+
Ok(web::Json(res))
97+
}
98+
7499
async fn get_metadata() -> Result<crate::storage::StorageMetadata, ObjectStorageError> {
75100
let metadata = CONFIG
76101
.storage()

server/src/main.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ async fn main() -> anyhow::Result<()> {
6565
migration::run_metadata_migration(&CONFIG).await?;
6666
let metadata = storage::resolve_parseable_metadata().await?;
6767
banner::print(&CONFIG, &metadata).await;
68-
rbac::map::init(metadata.users.clone(), metadata.roles.clone());
68+
rbac::map::init(&metadata);
6969
metadata.set_global();
7070
let prometheus = metrics::build_metrics_handler();
7171
CONFIG.storage().register_store_metrics(&prometheus);

server/src/rbac/map.rs

+10-4
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,23 @@
1616
*
1717
*/
1818

19-
use crate::option::CONFIG;
2019
use crate::rbac::user::User;
21-
use std::collections::HashMap;
20+
use crate::{option::CONFIG, storage::StorageMetadata};
21+
use std::{collections::HashMap, sync::Mutex};
2222

2323
use super::{
2424
role::{model::DefaultPrivilege, Action, Permission, RoleBuilder},
2525
user,
2626
};
2727
use chrono::{DateTime, Utc};
28-
use once_cell::sync::OnceCell;
28+
use once_cell::sync::{Lazy, OnceCell};
2929
use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
3030

3131
pub type Roles = HashMap<String, Vec<DefaultPrivilege>>;
3232

3333
pub static USERS: OnceCell<RwLock<Users>> = OnceCell::new();
3434
pub static ROLES: OnceCell<RwLock<Roles>> = OnceCell::new();
35+
pub static DEFAULT_ROLE: Lazy<Mutex<Option<String>>> = Lazy::new(|| Mutex::new(None));
3536
pub static SESSIONS: OnceCell<RwLock<Sessions>> = OnceCell::new();
3637

3738
pub fn users() -> RwLockReadGuard<'static, Users> {
@@ -86,7 +87,12 @@ pub fn mut_sessions() -> RwLockWriteGuard<'static, Sessions> {
8687
// the user_map is initialized from the config file and has a list of all users
8788
// the auth_map is initialized with admin user only and then gets lazily populated
8889
// as users authenticate
89-
pub fn init(users: Vec<User>, mut roles: Roles) {
90+
pub fn init(metadata: &StorageMetadata) {
91+
let users = metadata.users.clone();
92+
let mut roles = metadata.roles.clone();
93+
94+
*DEFAULT_ROLE.lock().unwrap() = metadata.default_role.clone();
95+
9096
let admin_privilege = DefaultPrivilege::Admin;
9197
let admin_permissions = RoleBuilder::from(&admin_privilege).build();
9298
roles.insert("admin".to_string(), vec![admin_privilege]);

server/src/rbac/user.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,10 @@ impl User {
5757
)
5858
}
5959

60-
pub fn new_oauth(username: String) -> Self {
60+
pub fn new_oauth(username: String, roles: HashSet<String>) -> Self {
6161
Self {
6262
ty: UserType::OAuth(OAuth { userid: username }),
63-
roles: HashSet::new(),
63+
roles,
6464
}
6565
}
6666

server/src/storage/store_metadata.rs

+3
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ pub struct StorageMetadata {
5757
pub streams: Vec<String>,
5858
#[serde(default)]
5959
pub roles: HashMap<String, Vec<DefaultPrivilege>>,
60+
#[serde(default)]
61+
pub default_role: Option<String>,
6062
}
6163

6264
impl StorageMetadata {
@@ -70,6 +72,7 @@ impl StorageMetadata {
7072
users: Vec::new(),
7173
streams: Vec::new(),
7274
roles: HashMap::default(),
75+
default_role: None,
7376
}
7477
}
7578

0 commit comments

Comments
 (0)