use std::collections::HashMap;
use crate::error::Error;
use crate::json::{JsonValue, encode_oson};
use crate::secret_value::SecretValue;
const MAX_END_USER_SECURITY_CONTEXT_LENGTH: usize = 65535;
#[derive(Clone)]
pub enum EndUserIdentity {
Token(String),
DatabaseUser { name: String, key: Option<String> },
}
impl EndUserIdentity {
fn validate(&self) -> Result<(), Error> {
match self {
EndUserIdentity::Token(token) => {
if token.is_empty() {
return Err(Error::invalid_end_user_security_context(
"end user token must be non-empty",
));
}
}
EndUserIdentity::DatabaseUser { name, key } => {
if name.is_empty() {
return Err(Error::invalid_end_user_security_context(
"database user name must be non-empty",
));
}
if let Some(key) = key
&& key.is_empty()
{
return Err(Error::invalid_end_user_security_context(
"database user key must be non-empty if specified",
));
}
}
}
Ok(())
}
}
#[derive(Clone)]
pub struct EndUserSecurityContext {
oson_bytes: SecretValue,
}
pub struct EndUserSecurityContextBuilder {
identity: EndUserIdentity,
database_access_token: String,
data_roles: Option<Vec<String>>,
attributes: Option<HashMap<String, JsonValue>>,
}
impl EndUserSecurityContext {
pub(crate) fn oson_bytes(&self) -> Vec<u8> {
self.oson_bytes.get_value()
}
pub fn builder(
identity: EndUserIdentity,
database_access_token: String,
) -> EndUserSecurityContextBuilder {
EndUserSecurityContextBuilder {
identity,
database_access_token,
data_roles: None,
attributes: None,
}
}
pub fn new(
identity: EndUserIdentity,
database_access_token: String,
) -> Result<Self, Error> {
Self::builder(identity, database_access_token).build()
}
}
impl EndUserSecurityContextBuilder {
pub fn attributes(
mut self,
attributes: HashMap<String, JsonValue>,
) -> Self {
self.attributes = Some(attributes);
self
}
fn validate(&self) -> Result<(), Error> {
if self.database_access_token.is_empty() {
return Err(Error::invalid_end_user_security_context(
"database_access_token must be non-empty",
));
}
self.identity.validate()
}
pub fn build(self) -> Result<EndUserSecurityContext, Error> {
self.validate()?;
let mut payload = HashMap::new();
payload
.insert("ver".to_string(), JsonValue::String("1.0".to_string()));
match self.identity {
EndUserIdentity::Token(token) => {
payload.insert(
"end_user_token".to_string(),
JsonValue::String(token),
);
}
EndUserIdentity::DatabaseUser { name, key } => {
payload.insert(
"end_user_name".to_string(),
JsonValue::String(name),
);
if let Some(key) = key {
payload.insert(
"end_user_contextid".to_string(),
JsonValue::String(key),
);
}
}
}
payload.insert(
"database_access_token".to_string(),
JsonValue::String(self.database_access_token),
);
if let Some(data_roles) = self.data_roles {
payload.insert(
"data_roles".to_string(),
JsonValue::JsonArray(
data_roles.into_iter().map(JsonValue::String).collect(),
),
);
}
if let Some(attributes) = self.attributes {
let attributes = attributes
.into_iter()
.map(|(name, values)| {
let mut attribute = HashMap::new();
attribute
.insert("name".to_string(), JsonValue::String(name));
attribute.insert("values".to_string(), values);
JsonValue::JsonObject(attribute)
})
.collect::<Vec<JsonValue>>();
payload.insert(
"attributes".to_string(),
JsonValue::JsonArray(attributes),
);
}
let oson_bytes = encode_oson(&JsonValue::JsonObject(payload));
if oson_bytes.len() > MAX_END_USER_SECURITY_CONTEXT_LENGTH {
return Err(Error::invalid_end_user_security_context_length(
oson_bytes.len(),
));
}
Ok(EndUserSecurityContext {
oson_bytes: SecretValue::new(&oson_bytes),
})
}
pub fn data_roles(mut self, roles: Vec<String>) -> Self {
self.data_roles = Some(roles);
self
}
}