From e5a6e75ce81720fbac3f6eb14924d5248aed3088 Mon Sep 17 00:00:00 2001 From: Pietro Albini Date: Sun, 3 Mar 2019 21:19:49 +0100 Subject: permissions: add support for bors permissions --- src/permissions.rs | 124 ++++++++++++++++++++++++++++++++++++++++++++++------- src/schema.rs | 6 +-- src/validate.rs | 43 +++++++++++++------ 3 files changed, 141 insertions(+), 32 deletions(-) (limited to 'src') diff --git a/src/permissions.rs b/src/permissions.rs index f181e21..3670f59 100644 --- a/src/permissions.rs +++ b/src/permissions.rs @@ -1,46 +1,140 @@ use crate::data::Data; -use failure::Error; +use failure::{Error, bail}; use std::collections::HashSet; -#[macro_export] macro_rules! permissions { - ($vis:vis struct $name:ident { $($key:ident,)* }) => { + ( + booleans { + $($boolean:ident,)* + } + bors_repos { + $($bors:ident,)* + } + ) => { + #[derive(serde_derive::Deserialize, Debug)] + #[serde(deny_unknown_fields)] + pub(crate) struct BorsACL { + #[serde(default)] + review: bool, + #[serde(rename = "try", default)] + try_: bool, + } + + impl Default for BorsACL { + fn default() -> Self { + BorsACL { + review: false, + try_: false, + } + } + } + + #[derive(serde_derive::Deserialize, Debug)] + #[serde(deny_unknown_fields)] + pub(crate) struct BorsPermissions { + $( + #[serde(default)] + $bors: BorsACL, + )* + } + + impl Default for BorsPermissions { + fn default() -> Self { + BorsPermissions { + $($bors: BorsACL::default(),)* + } + } + } + #[derive(serde_derive::Deserialize, Debug)] #[serde(rename_all = "kebab-case", deny_unknown_fields)] - $vis struct $name { + pub(crate) struct Permissions { $( #[serde(default)] - $key: bool, + $boolean: bool, )* + #[serde(default)] + bors: BorsPermissions, } - impl Default for $name { + impl Default for Permissions { fn default() -> Self { - $name { - $($key: false,)* + Permissions { + $($boolean: false,)* + bors: BorsPermissions::default(), } } } - impl $name { - $vis const AVAILABLE: &'static [&'static str] = &[$(stringify!($key),)*]; + impl Permissions { + pub(crate) const AVAILABLE: &'static [&'static str] = &[ + $(stringify!($boolean),)* + $(concat!("bors.", stringify!($bors), ".review"),)* + $(concat!("bors.", stringify!($bors), ".try"),)* + ]; - $vis fn has(&self, permission: &str) -> bool { + pub(crate) fn has(&self, permission: &str) -> bool { + $( + if permission == stringify!($boolean) { + return self.$boolean; + } + )* $( - if permission == stringify!($key) { - return self.$key; + if permission == concat!("bors.", stringify!($bors), ".review") { + return self.bors.$bors.review; } )* + $( + if permission == concat!("bors.", stringify!($bors), ".try") { + return self.bors.$bors.try_ || self.bors.$bors.review; + } + )* + false + } + + pub(crate) fn has_any(&self) -> bool { false + $(|| self.$boolean)* + $(|| self.bors.$bors.review)* + $(|| self.bors.$bors.try_)* } - $vis fn has_any(&self) -> bool { - false $(|| self.$key)* + pub(crate) fn validate(&self, what: String) -> Result<(), Error> { + $( + if self.bors.$bors.try_ == true && self.bors.$bors.review == true { + bail!( + "{} has both the `bors.{}.review` and `bors.{}.try` permissions", + what, + stringify!($bors), + stringify!($bors), + ); + } + )* + Ok(()) } } } } +permissions! { + booleans { + perf, + crater, + } + bors_repos { + cargo, + clippy, + compiler_builtins, + crater, + crates_io, + libc, + regex, + rls, + rust, + rustup_rs, + } +} + pub(crate) fn allowed_github_users( data: &Data, permission: &str, diff --git a/src/schema.rs b/src/schema.rs index f53fd19..0143301 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -1,4 +1,5 @@ use crate::data::Data; +pub(crate) use crate::permissions::Permissions; use failure::{bail, err_msg, Error}; use std::collections::HashSet; @@ -217,11 +218,6 @@ struct TeamPeople { include_all_team_members: bool, } -permissions!(pub(crate) struct Permissions { - perf, - crater, -}); - pub(crate) struct DiscordInvite<'a> { pub(crate) url: &'a str, pub(crate) channel: &'a str, diff --git a/src/validate.rs b/src/validate.rs index 93a87ac..97fb655 100644 --- a/src/validate.rs +++ b/src/validate.rs @@ -4,21 +4,28 @@ use failure::{bail, ensure, Error}; use regex::Regex; use std::collections::HashSet; +static CHECKS: &[fn(&Data, &mut Vec)] = &[ + validate_wg_names, + validate_subteam_of, + validate_team_leads, + validate_team_members, + validate_inactive_members, + validate_list_email_addresses, + validate_list_extra_people, + validate_list_extra_teams, + validate_list_addresses, + validate_people_addresses, + validate_discord_name, + validate_duplicate_permissions, + validate_permissions, +]; + pub(crate) fn validate(data: &Data) -> Result<(), Error> { let mut errors = Vec::new(); - validate_wg_names(data, &mut errors); - validate_subteam_of(data, &mut errors); - validate_team_leads(data, &mut errors); - validate_team_members(data, &mut errors); - validate_inactive_members(data, &mut errors); - validate_list_email_addresses(data, &mut errors); - validate_list_extra_people(data, &mut errors); - validate_list_extra_teams(data, &mut errors); - validate_list_addresses(data, &mut errors); - validate_people_addresses(data, &mut errors); - validate_discord_name(data, &mut errors); - validate_duplicate_permissions(data, &mut errors); + for check in CHECKS { + check(data, &mut errors); + } if !errors.is_empty() { errors.sort(); @@ -260,6 +267,18 @@ fn validate_duplicate_permissions(data: &Data, errors: &mut Vec) { }); } +/// Ensure the permissions are valid +fn validate_permissions(data: &Data, errors: &mut Vec) { + wrapper(data.teams(), errors, |team, _| { + team.permissions().validate(format!("team `{}`", team.name()))?; + Ok(()) + }); + wrapper(data.people(), errors, |person, _| { + person.permissions().validate(format!("user `{}`", person.github()))?; + Ok(()) + }); +} + fn wrapper(iter: I, errors: &mut Vec, mut func: F) where I: Iterator, -- cgit v1.2.3