From eccdd4577a54b230460de6733ed7b003b6f8f182 Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Fri, 4 Jan 2019 19:24:21 -0800 Subject: add superuser role/flag --- rust/src/api_wrappers.rs | 20 ++++++++++---------- rust/src/auth.rs | 32 +++++++++++++++++++++----------- rust/src/database_models.rs | 1 + rust/src/database_schema.rs | 1 + 4 files changed, 33 insertions(+), 21 deletions(-) (limited to 'rust/src') diff --git a/rust/src/api_wrappers.rs b/rust/src/api_wrappers.rs index c663c11d..614a0007 100644 --- a/rust/src/api_wrappers.rs +++ b/rust/src/api_wrappers.rs @@ -85,7 +85,7 @@ macro_rules! wrap_entity_handlers { ) -> Box + Send> { let conn = self.db_pool.get().expect("db_pool error"); let ret = match conn.transaction(|| { - let auth_context = self.auth_confectionary.require_auth(&conn, &context.auth_data)?; + let auth_context = self.auth_confectionary.require_auth(&conn, &context.auth_data, Some(stringify!($post_fn)))?; auth_context.require_role(FatcatRole::Editor)?; let editgroup_id = if let Some(s) = editgroup_id { let eg_id = FatCatId::from_str(&s)?; @@ -137,7 +137,7 @@ macro_rules! wrap_entity_handlers { ) -> Box + Send> { let conn = self.db_pool.get().expect("db_pool error"); let ret = match conn.transaction(|| { - let auth_context = self.auth_confectionary.require_auth(&conn, &context.auth_data)?; + let auth_context = self.auth_confectionary.require_auth(&conn, &context.auth_data, Some(stringify!($post_batch_fn)))?; auth_context.require_role(FatcatRole::Editor)?; let editgroup_id = if let Some(s) = editgroup_id { let eg_id = FatCatId::from_str(&s)?; @@ -187,7 +187,7 @@ macro_rules! wrap_entity_handlers { ) -> Box + Send> { let conn = self.db_pool.get().expect("db_pool error"); let ret = match conn.transaction(|| { - let auth_context = self.auth_confectionary.require_auth(&conn, &context.auth_data)?; + let auth_context = self.auth_confectionary.require_auth(&conn, &context.auth_data, Some(stringify!($update_fn)))?; auth_context.require_role(FatcatRole::Editor)?; let entity_id = FatCatId::from_str(&ident)?; let editgroup_id = if let Some(s) = editgroup_id { @@ -243,7 +243,7 @@ macro_rules! wrap_entity_handlers { ) -> Box + Send> { let conn = self.db_pool.get().expect("db_pool error"); let ret = match conn.transaction(|| { - let auth_context = self.auth_confectionary.require_auth(&conn, &context.auth_data)?; + let auth_context = self.auth_confectionary.require_auth(&conn, &context.auth_data, Some(stringify!($delete_fn)))?; auth_context.require_role(FatcatRole::Editor)?; let entity_id = FatCatId::from_str(&ident)?; let editgroup_id: Option = match editgroup_id { @@ -397,7 +397,7 @@ macro_rules! wrap_entity_handlers { let conn = self.db_pool.get().expect("db_pool error"); let ret = match conn.transaction(|| { let edit_id = Uuid::from_str(&edit_id)?; - let auth_context = self.auth_confectionary.require_auth(&conn, &context.auth_data)?; + let auth_context = self.auth_confectionary.require_auth(&conn, &context.auth_data, Some(stringify!($delete_edit_fn)))?; auth_context.require_role(FatcatRole::Editor)?; let edit = $model::db_get_edit(&conn, edit_id)?; auth_context.require_editgroup(&conn, FatCatId::from_uuid(&edit.editgroup_id))?; @@ -920,7 +920,7 @@ impl Api for Server { } let auth_context = self .auth_confectionary - .require_auth(&conn, &context.auth_data)?; + .require_auth(&conn, &context.auth_data, Some("update_editor"))?; let editor_id = FatCatId::from_str(&editor_id)?; // DANGER! these permissions are for username updates only! if editor_id == auth_context.editor_id { @@ -988,7 +988,7 @@ impl Api for Server { let editgroup_id = FatCatId::from_str(&editgroup_id)?; let auth_context = self .auth_confectionary - .require_auth(&conn, &context.auth_data)?; + .require_auth(&conn, &context.auth_data, Some("accept_editgroup"))?; auth_context.require_role(FatcatRole::Admin)?; // NOTE: this is currently redundant, but zero-cost auth_context.require_editgroup(&conn, editgroup_id)?; @@ -1060,7 +1060,7 @@ impl Api for Server { let ret = match conn.transaction(|| { let auth_context = self .auth_confectionary - .require_auth(&conn, &context.auth_data)?; + .require_auth(&conn, &context.auth_data, Some("create_editgroup"))?; auth_context.require_role(FatcatRole::Editor)?; let mut entity = entity.clone(); match entity.editor_id.clone() { @@ -1151,8 +1151,8 @@ impl Api for Server { let ret = match conn.transaction(|| { let auth_context = self .auth_confectionary - .require_auth(&conn, &context.auth_data)?; - auth_context.require_role(FatcatRole::Admin)?; + .require_auth(&conn, &context.auth_data, Some("auth_oidc"))?; + auth_context.require_role(FatcatRole::Superuser)?; let (editor, created) = self.auth_oidc_handler(params, &conn)?; // create an auth token; leave it to webface to attenuate to a given duration let token = self.auth_confectionary.create_token( diff --git a/rust/src/auth.rs b/rust/src/auth.rs index 4b608a96..0160d2e8 100644 --- a/rust/src/auth.rs +++ b/rust/src/auth.rs @@ -18,13 +18,14 @@ use std::str::FromStr; // 32 bytes max (!) static DUMMY_KEY: &[u8] = b"dummy-key-a-one-two-three-a-la"; -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub enum FatcatRole { Public, Editor, Bot, Human, Admin, + Superuser, } #[derive(Clone)] @@ -35,6 +36,10 @@ pub struct AuthContext { impl AuthContext { pub fn has_role(&self, role: FatcatRole) -> bool { + if !self.editor_row.is_active { + // if account is disabled, only allow public role + return role == FatcatRole::Public; + } if self.editor_row.is_admin { return true; } @@ -44,15 +49,15 @@ impl AuthContext { FatcatRole::Bot => self.editor_row.is_bot, FatcatRole::Human => !self.editor_row.is_bot, FatcatRole::Admin => self.editor_row.is_admin, + FatcatRole::Superuser => self.editor_row.is_superuser, } } pub fn require_role(&self, role: FatcatRole) -> Result<()> { match self.has_role(role) { true => Ok(()), - // TODO: better message false => Err(ErrorKind::InsufficientPrivileges( - "doesn't have required role".to_string(), + format!("doesn't have required role: {:?}", role), ) .into()), } @@ -225,8 +230,7 @@ impl AuthConfectionary { Ok(BASE64.encode(&raw)) } - /// On success, returns Some((editor_id, scopes)), where `scopes` is a vector of strings. - pub fn parse_macaroon_token(&self, conn: &DbConn, s: &str) -> Result { + pub fn parse_macaroon_token(&self, conn: &DbConn, s: &str, endpoint: Option<&str>) -> Result { let raw = BASE64.decode(s.as_bytes())?; let mac = match Macaroon::deserialize(&raw) { Ok(m) => m, @@ -258,6 +262,10 @@ impl AuthConfectionary { } let editor_id = editor_id.expect("expected an editor_id caveat"); verifier.satisfy_exact(&format!("editor_id = {}", editor_id.to_string())); + if let Some(endpoint) = endpoint { + // API endpoint + verifier.satisfy_exact(&format!("endpoint = {}", endpoint)); + } let mut created: Option> = None; for caveat in mac.first_party_caveats() { if caveat.predicate().starts_with("created = ") { @@ -329,6 +337,7 @@ impl AuthConfectionary { &self, conn: &DbConn, auth_data: &Option, + endpoint: Option<&str>, ) -> Result> { let token: Option = match auth_data { Some(AuthData::ApiKey(header)) => { @@ -355,15 +364,15 @@ impl AuthConfectionary { Some(t) => t, None => return Ok(None), }; - let editor_row = self.parse_macaroon_token(conn, &token)?; + let editor_row = self.parse_macaroon_token(conn, &token, endpoint)?; Ok(Some(AuthContext { editor_id: FatCatId::from_uuid(&editor_row.id), editor_row: editor_row, })) } - pub fn require_auth(&self, conn: &DbConn, auth_data: &Option) -> Result { - match self.parse_swagger(conn, auth_data)? { + pub fn require_auth(&self, conn: &DbConn, auth_data: &Option, endpoint: Option<&str>) -> Result { + match self.parse_swagger(conn, auth_data, endpoint)? { Some(auth) => Ok(auth), None => Err(ErrorKind::InvalidCredentials("no token supplied".to_string()).into()), } @@ -384,7 +393,7 @@ impl AuthConfectionary { for caveat in mac.first_party_caveats() { println!("caveat: {}", caveat.predicate()); } - println!("verify: {:?}", self.parse_macaroon_token(conn, token)); + println!("verify: {:?}", self.parse_macaroon_token(conn, token, None)); Ok(()) } } @@ -416,11 +425,12 @@ pub fn revoke_tokens_everyone(conn: &DbConn) -> Result<()> { pub fn print_editors(conn: &DbConn) -> Result<()> { // iterate over all editors. format id, print flags, auth_epoch let all_editors: Vec = editor::table.load(conn)?; - println!("editor_id\t\t\tis_admin/is_bot\tauth_epoch\t\t\tusername\twrangler_id"); + println!("editor_id\t\t\tsuper/admin/bot\tauth_epoch\t\t\tusername\twrangler_id"); for e in all_editors { println!( - "{}\t{}\t{}\t{}\t{}\t{:?}", + "{}\t{}/{}/{}\t{}\t{}\t{:?}", FatCatId::from_uuid(&e.id).to_string(), + e.is_superuser, e.is_admin, e.is_bot, e.auth_epoch, diff --git a/rust/src/database_models.rs b/rust/src/database_models.rs index 5c8e17d3..59953f6b 100644 --- a/rust/src/database_models.rs +++ b/rust/src/database_models.rs @@ -577,6 +577,7 @@ impl EditgroupRow { pub struct EditorRow { pub id: Uuid, pub username: String, + pub is_superuser: bool, pub is_admin: bool, pub is_bot: bool, pub is_active: bool, diff --git a/rust/src/database_schema.rs b/rust/src/database_schema.rs index 49863fc7..0c553b40 100644 --- a/rust/src/database_schema.rs +++ b/rust/src/database_schema.rs @@ -107,6 +107,7 @@ table! { editor (id) { id -> Uuid, username -> Text, + is_superuser -> Bool, is_admin -> Bool, is_bot -> Bool, is_active -> Bool, -- cgit v1.2.3