use std::sync::Arc;
use authly_common::{
id::{AttrId, Eid},
proto::service::{self as proto, authly_service_client::AuthlyServiceClient},
service::NamespacePropertyMapping,
};
use fnv::FnvHashSet;
use http::header::AUTHORIZATION;
use tonic::{transport::Channel, Request};
use crate::{error, id_codec_error, token::AccessToken, Client, Error};
pub struct AccessControlRequestBuilder<'c> {
client: &'c Client,
property_mapping: Arc<NamespacePropertyMapping>,
access_token: Option<Arc<AccessToken>>,
resource_attributes: FnvHashSet<AttrId>,
peer_entity_ids: FnvHashSet<Eid>,
}
impl<'c> AccessControlRequestBuilder<'c> {
pub(crate) fn new(client: &'c Client) -> Self {
Self {
client,
property_mapping: client.state.resource_property_mapping.load_full(),
access_token: None,
resource_attributes: Default::default(),
peer_entity_ids: Default::default(),
}
}
pub fn resource_attribute(
mut self,
namespace_label: &str,
property_label: &str,
attribute_label: &str,
) -> Result<Self, Error> {
let attr_id = self
.property_mapping
.attribute_object_id(namespace_label, property_label, attribute_label)
.ok_or(Error::InvalidPropertyAttributeLabel)?;
self.resource_attributes.insert(attr_id);
Ok(self)
}
pub fn access_token(mut self, token: Arc<AccessToken>) -> Self {
self.access_token = Some(token);
self
}
pub fn peer_entity_id(mut self, entity_id: Eid) -> Self {
self.peer_entity_ids.insert(entity_id);
self
}
pub async fn send(self) -> Result<bool, Error> {
let mut request = Request::new(proto::AccessControlRequest {
resource_attributes: self
.resource_attributes
.into_iter()
.map(|attr| attr.to_raw_array().to_vec())
.collect(),
peer_entity_attributes: vec![],
peer_entity_ids: self
.peer_entity_ids
.into_iter()
.map(|eid| eid.to_raw_array().to_vec())
.collect(),
});
if let Some(access_token) = self.access_token {
request.metadata_mut().append(
AUTHORIZATION.as_str(),
format!("Bearer {}", access_token.token)
.parse()
.map_err(error::unclassified)?,
);
}
let access_control_response = self
.client
.current_service()
.access_control(request)
.await
.map_err(error::tonic)?
.into_inner();
Ok(access_control_response.value > 0)
}
}
pub(crate) async fn get_resource_property_mapping(
mut service: AuthlyServiceClient<Channel>,
) -> Result<Arc<NamespacePropertyMapping>, Error> {
let response = service
.get_resource_property_mappings(proto::Empty::default())
.await
.map_err(error::tonic)?;
let mut property_mapping = NamespacePropertyMapping::default();
for namespace in response.into_inner().namespaces {
let ns = property_mapping.namespace_mut(namespace.label);
for property in namespace.properties {
let ns_prop = ns.property_mut(property.label);
for attribute in property.attributes {
ns_prop.put(
attribute.label,
AttrId::from_raw_bytes(&attribute.obj_id).ok_or_else(id_codec_error)?,
);
}
}
}
Ok(Arc::new(property_mapping))
}