use crate::{internal::user_api::UserId, IronOxideErr, Result};
use regex::Regex;
use std::convert::{TryFrom, TryInto};
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct PolicyGrant {
category: Option<Category>,
sensitivity: Option<Sensitivity>,
data_subject: Option<DataSubject>,
substitute_user: Option<UserId>,
}
impl PolicyGrant {
pub fn new(
category: Option<Category>,
sensitivity: Option<Sensitivity>,
data_subject: Option<DataSubject>,
substitute_user: Option<UserId>,
) -> PolicyGrant {
PolicyGrant {
category,
sensitivity,
data_subject,
substitute_user,
}
}
pub fn category(&self) -> Option<&Category> {
self.category.as_ref()
}
pub fn sensitivity(&self) -> Option<&Sensitivity> {
self.sensitivity.as_ref()
}
pub fn data_subject(&self) -> Option<&DataSubject> {
self.data_subject.as_ref()
}
pub fn substitute_user(&self) -> Option<&UserId> {
self.substitute_user.as_ref()
}
}
impl Default for PolicyGrant {
fn default() -> Self {
PolicyGrant {
category: None,
sensitivity: None,
data_subject: None,
substitute_user: None,
}
}
}
macro_rules! policy_field {
($t: ident, $l: literal) => {
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct $t(pub(crate) String);
impl TryFrom<&str> for $t {
type Error = IronOxideErr;
fn try_from(value: &str) -> Result<Self> {
validate_simple_policy_field_value(value, $l).map(Self)
}
}
impl TryFrom<String> for $t {
type Error = IronOxideErr;
fn try_from(value: String) -> Result<Self> {
value.as_str().try_into()
}
}
impl $t {
pub(crate) const QUERY_PARAM: &'static str = $l;
pub fn inner(&self) -> &str {
self.0.as_str()
}
}
};
}
policy_field!(Category, "category");
policy_field!(DataSubject, "dataSubject");
policy_field!(Sensitivity, "sensitivity");
const NAME_AND_ID_MAX_LEN: usize = 100;
fn validate_simple_policy_field_value(field_id: &str, field_type: &str) -> Result<String> {
let simple_policy_field_regex = Regex::new("^[A-Za-z0-9_-]+$").expect("regex is valid");
let trimmed_id = field_id.trim();
if trimmed_id.is_empty() || trimmed_id.len() > NAME_AND_ID_MAX_LEN {
Err(IronOxideErr::ValidationError(
field_type.to_string(),
format!("'{}' must have length between 1 and 100", trimmed_id),
))
} else if !simple_policy_field_regex.is_match(trimmed_id) {
Err(IronOxideErr::ValidationError(
field_type.to_string(),
format!("'{}' contains invalid characters", trimmed_id),
))
} else {
Ok(trimmed_id.to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::internal::tests::contains;
use galvanic_assert::{matchers::eq, *};
use std::convert::TryInto;
#[test]
fn validate_simple_policy_id_good() {
let name_type = "name_type";
let id = "abc-123";
let result = validate_simple_policy_field_value(id, name_type);
assert_that!(&result, is_variant!(Ok));
let id = "SIMPLE_2";
let result = validate_simple_policy_field_value(id, name_type);
assert_that!(&result, is_variant!(Ok));
let id = "LOTS-O-CHARS_012345678901234567890123456789012345678901234567890123456789012345678901234567890123456";
let result = validate_simple_policy_field_value(id, name_type);
assert_that!(&result, is_variant!(Ok))
}
#[test]
fn validate_simple_policy_id_invalid_chars() {
let name_type = "name_type";
let invalid = "abc!123";
let result = validate_simple_policy_field_value(invalid, name_type);
assert_that!(&result, is_variant!(Err));
let validation_error = result.unwrap_err();
assert_that!(
&validation_error,
is_variant!(IronOxideErr::ValidationError)
);
let invalid = "❤HEART❤";
let result = validate_simple_policy_field_value(invalid, name_type);
assert_that!(&result, is_variant!(Err));
let validation_error = result.unwrap_err();
assert_that!(
&validation_error,
is_variant!(IronOxideErr::ValidationError)
);
let invalid = "spaces not allowed";
let result = validate_simple_policy_field_value(invalid, name_type);
assert_that!(&result, is_variant!(Err));
let validation_error = result.unwrap_err();
assert_that!(
&validation_error,
is_variant!(IronOxideErr::ValidationError)
);
}
#[test]
fn validate_simple_policy_id_invalid_length() {
let name_type = "name_type";
let invalid = "too many chars 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789";
let result = validate_simple_policy_field_value(invalid, name_type);
assert_that!(&result, is_variant!(Err));
let validation_error = result.unwrap_err();
assert_that!(
&validation_error,
is_variant!(IronOxideErr::ValidationError)
);
assert_that!(&format!("{}", validation_error), contains("100"));
}
#[test]
fn can_inspect_policy_grant() -> Result<()> {
let category = "CATEGORY".try_into()?;
let sensitivity = "SENSITIVITY".try_into()?;
let dat_subj = "DATA_SUBJECT".try_into()?;
let sub_userid = "a-user-id".try_into()?;
let policy = PolicyGrant::new(
Some(category),
Some(sensitivity),
Some(dat_subj),
Some(sub_userid),
);
assert_that!(&policy.category().unwrap().inner(), eq("CATEGORY"));
assert_that!(&policy.sensitivity().unwrap().inner(), eq("SENSITIVITY"));
assert_that!(&policy.data_subject().unwrap().inner(), eq("DATA_SUBJECT"));
assert_that!(&policy.substitute_user().unwrap().id(), eq("a-user-id"));
Ok(())
}
}