mirror of
https://github.com/pfzetto/rebacs
synced 2024-11-21 18:52:50 +01:00
rename to rebacs, proto rework and implemented missing functions
This commit is contained in:
parent
0b1af2771c
commit
21f0c705fe
9 changed files with 413 additions and 255 deletions
|
@ -1,5 +1,5 @@
|
|||
[package]
|
||||
name = "themis"
|
||||
name = "rebacs"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
|
|
2
build.rs
2
build.rs
|
@ -1,6 +1,6 @@
|
|||
fn main() {
|
||||
tonic_build::configure()
|
||||
.build_server(true)
|
||||
.compile(&["proto/themis.proto"], &["proto"])
|
||||
.compile(&["proto/rebacs.proto"], &["proto"])
|
||||
.unwrap();
|
||||
}
|
||||
|
|
95
proto/rebacs.proto
Normal file
95
proto/rebacs.proto
Normal file
|
@ -0,0 +1,95 @@
|
|||
syntax = "proto3";
|
||||
package eu.zettoit.rebacs;
|
||||
|
||||
service RelationService {
|
||||
rpc Create(RelationCreateReq) returns (RelationCreateRes);
|
||||
rpc Delete(RelationDeleteReq) returns (RelationDeleteRes);
|
||||
rpc Exists(RelationExistsReq) returns (RelationExistsRes);
|
||||
}
|
||||
|
||||
service QueryService {
|
||||
// check if one object or objectset is related to another by a relation
|
||||
rpc IsRelatedTo(QueryIsRelatedToReq) returns (QueryIsRelatedToRes);
|
||||
|
||||
// get all objects that are related to one object by a relation
|
||||
rpc GetRelated(QueryGetRelatedReq) returns (QueryGetRelatedRes);
|
||||
|
||||
// get all objects that the given object has a relation with
|
||||
rpc GetRelations(QueryGetRelationsReq) returns (QueryGetRelationsRes);
|
||||
}
|
||||
|
||||
message RelationCreateReq{
|
||||
ObjectOrSet src = 1;
|
||||
Object dst = 2;
|
||||
string relation = 3;
|
||||
}
|
||||
message RelationCreateRes{}
|
||||
|
||||
message RelationDeleteReq{
|
||||
ObjectOrSet src = 1;
|
||||
Object dst = 3;
|
||||
string relation = 4;
|
||||
}
|
||||
message RelationDeleteRes{}
|
||||
|
||||
message RelationExistsReq{
|
||||
ObjectOrSet src = 1;
|
||||
Object dst = 2;
|
||||
string relation = 3;
|
||||
}
|
||||
message RelationExistsRes{
|
||||
bool exists = 1;
|
||||
}
|
||||
|
||||
message QueryIsRelatedToReq{
|
||||
ObjectOrSet src = 1;
|
||||
Object dst = 2;
|
||||
string relation = 3;
|
||||
}
|
||||
message QueryIsRelatedToRes{
|
||||
bool related = 1;
|
||||
}
|
||||
|
||||
message QueryGetRelatedReq{
|
||||
Object dst = 1;
|
||||
optional string relation = 2;
|
||||
optional string namespace = 3;
|
||||
optional uint32 depth = 4;
|
||||
}
|
||||
message QueryGetRelatedRes{
|
||||
repeated QueryGetRelatedItem objects = 1;
|
||||
}
|
||||
message QueryGetRelatedItem{
|
||||
string relation = 1;
|
||||
Object src = 2;
|
||||
}
|
||||
|
||||
message QueryGetRelationsReq{
|
||||
Object src = 1;
|
||||
optional string relation = 2;
|
||||
optional string namespace = 3;
|
||||
optional uint32 depth = 4;
|
||||
}
|
||||
message QueryGetRelationsRes{
|
||||
repeated QueryGetRelationsItem related = 1;
|
||||
}
|
||||
message QueryGetRelationsItem{
|
||||
string relation = 1;
|
||||
Object dst = 2;
|
||||
}
|
||||
|
||||
message Object{
|
||||
string namespace = 1;
|
||||
string id = 2;
|
||||
}
|
||||
message Set{
|
||||
string namespace = 1;
|
||||
string id = 2;
|
||||
string relation = 3;
|
||||
}
|
||||
|
||||
message ObjectOrSet {
|
||||
string namespace = 1;
|
||||
string id = 2;
|
||||
optional string relation = 3;
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
syntax = "proto3";
|
||||
package eu.zettoit.themis;
|
||||
|
||||
service RelationService {
|
||||
rpc Create(Relation) returns (Empty);
|
||||
rpc Delete(Relation) returns (Empty);
|
||||
rpc Exists(Relation) returns (ExistsResponse);
|
||||
}
|
||||
|
||||
service QueryService {
|
||||
// check if one object or objectset is related to another by a relation
|
||||
rpc IsRelatedTo(Relation) returns (IsRelatedToResponse);
|
||||
|
||||
// get all objects that are related to one object by a relation
|
||||
rpc GetRelatedTo(Set) returns (GetRelatedToResponse);
|
||||
|
||||
// get all objects that the given object has a relation with
|
||||
rpc GetRelations(GetRelationsRequest) returns (GetRelationsResponse);
|
||||
}
|
||||
|
||||
message ExistsResponse {
|
||||
bool exists = 1;
|
||||
}
|
||||
|
||||
message IsRelatedToResponse{
|
||||
bool related = 1;
|
||||
}
|
||||
|
||||
message GetRelatedToResponse{
|
||||
repeated Object objects = 1;
|
||||
}
|
||||
|
||||
message GetRelationsRequest{
|
||||
Object object = 1;
|
||||
string relation = 2;
|
||||
}
|
||||
message GetRelationsResponse{
|
||||
repeated Object objects = 1;
|
||||
}
|
||||
|
||||
message Object{
|
||||
string namespace = 1;
|
||||
string id = 2;
|
||||
}
|
||||
message Set{
|
||||
string namespace = 1;
|
||||
string id = 2;
|
||||
string relation = 3;
|
||||
}
|
||||
|
||||
message ObjectOrSet {
|
||||
oneof object_or_set{
|
||||
Object object = 1;
|
||||
Set set = 2;
|
||||
};
|
||||
}
|
||||
|
||||
message Relation{
|
||||
oneof src{
|
||||
Object src_obj = 1;
|
||||
Set src_set = 2;
|
||||
};
|
||||
Object dst = 3;
|
||||
string relation = 4;
|
||||
}
|
||||
|
||||
message Empty{}
|
|
@ -8,12 +8,14 @@ use tokio::sync::Mutex;
|
|||
use tonic::metadata::MetadataMap;
|
||||
use tonic::{Request, Response, Status};
|
||||
|
||||
use crate::relation_set::{ObjectOrSet, RelationSet};
|
||||
use crate::themis_proto::{
|
||||
query_service_server::QueryService, relation::Src, relation_service_server::RelationService,
|
||||
Empty, ExistsResponse, GetRelatedToResponse, GetRelationsRequest, GetRelationsResponse,
|
||||
IsRelatedToResponse, Relation, Set,
|
||||
use crate::rebacs_proto::{
|
||||
query_service_server::QueryService, relation_service_server::RelationService, Object,
|
||||
QueryGetRelatedItem, QueryGetRelatedReq, QueryGetRelatedRes, QueryGetRelationsItem,
|
||||
QueryGetRelationsReq, QueryGetRelationsRes, QueryIsRelatedToReq, QueryIsRelatedToRes,
|
||||
RelationCreateReq, RelationCreateRes, RelationDeleteReq, RelationDeleteRes, RelationExistsReq,
|
||||
RelationExistsRes,
|
||||
};
|
||||
use crate::relation_set::{ObjectOrSet, RelationSet};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GraphService {
|
||||
|
@ -22,9 +24,15 @@ pub struct GraphService {
|
|||
pub save_trigger: Sender<()>,
|
||||
}
|
||||
|
||||
const API_KEY_NS: &str = "rebacs_key";
|
||||
const NAMESPACE_NS: &str = "rebacs_ns";
|
||||
|
||||
#[tonic::async_trait]
|
||||
impl RelationService for GraphService {
|
||||
async fn create(&self, request: Request<Relation>) -> Result<Response<Empty>, Status> {
|
||||
async fn create(
|
||||
&self,
|
||||
request: Request<RelationCreateReq>,
|
||||
) -> Result<Response<RelationCreateRes>, Status> {
|
||||
let mut graph = self.graph.lock().await;
|
||||
|
||||
let api_key = api_key_from_req(request.metadata(), &self.api_keys).await?;
|
||||
|
@ -52,9 +60,9 @@ impl RelationService for GraphService {
|
|||
}
|
||||
|
||||
if !graph.has_recursive(
|
||||
("themis_key", &*api_key),
|
||||
(API_KEY_NS, &*api_key),
|
||||
"write",
|
||||
("themis_ns", &*req_dst.namespace),
|
||||
(NAMESPACE_NS, &*req_dst.namespace),
|
||||
u32::MAX,
|
||||
) {
|
||||
return Err(Status::permission_denied(
|
||||
|
@ -62,32 +70,21 @@ impl RelationService for GraphService {
|
|||
))?;
|
||||
}
|
||||
|
||||
let src: Result<ObjectOrSet, Status> = match req_src {
|
||||
Src::SrcObj(obj) => {
|
||||
if obj.namespace.is_empty() {
|
||||
if req_src.namespace.is_empty() {
|
||||
return Err(Status::invalid_argument("src.namespace must be set"));
|
||||
}
|
||||
if obj.id.is_empty() {
|
||||
if req_src.id.is_empty() {
|
||||
return Err(Status::invalid_argument("src.id must be set"));
|
||||
}
|
||||
|
||||
Ok((&*obj.namespace, &*obj.id).into())
|
||||
}
|
||||
Src::SrcSet(set) => {
|
||||
if set.namespace.is_empty() {
|
||||
return Err(Status::invalid_argument("src.namespace must be set"));
|
||||
}
|
||||
if set.id.is_empty() {
|
||||
return Err(Status::invalid_argument("src.id must be set"));
|
||||
}
|
||||
if set.relation.is_empty() {
|
||||
let src: ObjectOrSet = if let Some(req_src_relation) = req_src.relation.as_deref() {
|
||||
if req_src_relation.is_empty() {
|
||||
return Err(Status::invalid_argument("src.relation must be set"));
|
||||
}
|
||||
|
||||
Ok((&*set.namespace, &*set.id, &*set.relation).into())
|
||||
}
|
||||
(&*req_src.namespace, &*req_src.id, req_src_relation).into()
|
||||
} else {
|
||||
(&*req_src.namespace, &*req_src.id).into()
|
||||
};
|
||||
let src = src?;
|
||||
|
||||
graph.insert(
|
||||
src.clone(),
|
||||
|
@ -99,9 +96,12 @@ impl RelationService for GraphService {
|
|||
|
||||
self.save_trigger.send(()).await.unwrap();
|
||||
|
||||
Ok(Response::new(Empty {}))
|
||||
Ok(Response::new(RelationCreateRes {}))
|
||||
}
|
||||
async fn delete(&self, request: Request<Relation>) -> Result<Response<Empty>, Status> {
|
||||
async fn delete(
|
||||
&self,
|
||||
request: Request<RelationDeleteReq>,
|
||||
) -> Result<Response<RelationDeleteRes>, Status> {
|
||||
let mut graph = self.graph.lock().await;
|
||||
|
||||
let api_key = api_key_from_req(request.metadata(), &self.api_keys).await?;
|
||||
|
@ -129,41 +129,31 @@ impl RelationService for GraphService {
|
|||
}
|
||||
|
||||
if !graph.has_recursive(
|
||||
("themis_key", &*api_key),
|
||||
(API_KEY_NS, &*api_key),
|
||||
"write",
|
||||
("themis_ns", &*req_dst.namespace),
|
||||
(NAMESPACE_NS, &*req_dst.namespace),
|
||||
u32::MAX,
|
||||
) {
|
||||
return Err(Status::permission_denied(
|
||||
"missing dst.namespace write permissions",
|
||||
))?;
|
||||
}
|
||||
let src: Result<ObjectOrSet, Status> = match req_src {
|
||||
Src::SrcObj(obj) => {
|
||||
if obj.namespace.is_empty() {
|
||||
return Err(Status::invalid_argument("src.namespace must be set"));
|
||||
}
|
||||
if obj.id.is_empty() {
|
||||
return Err(Status::invalid_argument("src.id must be set"));
|
||||
}
|
||||
|
||||
Ok((&*obj.namespace, &*obj.id).into())
|
||||
}
|
||||
Src::SrcSet(set) => {
|
||||
if set.namespace.is_empty() {
|
||||
if req_src.namespace.is_empty() {
|
||||
return Err(Status::invalid_argument("src.namespace must be set"));
|
||||
}
|
||||
if set.id.is_empty() {
|
||||
if req_src.id.is_empty() {
|
||||
return Err(Status::invalid_argument("src.id must be set"));
|
||||
}
|
||||
if set.relation.is_empty() {
|
||||
let src: ObjectOrSet = if let Some(req_src_relation) = req_src.relation.as_deref() {
|
||||
if req_src_relation.is_empty() {
|
||||
return Err(Status::invalid_argument("src.relation must be set"));
|
||||
}
|
||||
|
||||
Ok((&*set.namespace, &*set.id, &*set.relation).into())
|
||||
}
|
||||
(&*req_src.namespace, &*req_src.id, req_src_relation).into()
|
||||
} else {
|
||||
(&*req_src.namespace, &*req_src.id).into()
|
||||
};
|
||||
let src = src?;
|
||||
|
||||
graph.remove(src, req_rel.as_str(), (&*req_dst.namespace, &*req_dst.id));
|
||||
|
||||
|
@ -171,9 +161,12 @@ impl RelationService for GraphService {
|
|||
|
||||
self.save_trigger.send(()).await.unwrap();
|
||||
|
||||
Ok(Response::new(Empty {}))
|
||||
Ok(Response::new(RelationDeleteRes {}))
|
||||
}
|
||||
async fn exists(&self, request: Request<Relation>) -> Result<Response<ExistsResponse>, Status> {
|
||||
async fn exists(
|
||||
&self,
|
||||
request: Request<RelationExistsReq>,
|
||||
) -> Result<Response<RelationExistsRes>, Status> {
|
||||
let graph = self.graph.lock().await;
|
||||
|
||||
let api_key = api_key_from_req(request.metadata(), &self.api_keys).await?;
|
||||
|
@ -201,45 +194,34 @@ impl RelationService for GraphService {
|
|||
}
|
||||
|
||||
if !graph.has_recursive(
|
||||
("themis_key", &*api_key),
|
||||
(API_KEY_NS, &*api_key),
|
||||
"read",
|
||||
("themis_ns", &*req_dst.namespace),
|
||||
(NAMESPACE_NS, &*req_dst.namespace),
|
||||
u32::MAX,
|
||||
) {
|
||||
return Err(Status::permission_denied(
|
||||
"missing dst.namespace write permissions",
|
||||
))?;
|
||||
}
|
||||
let src: Result<ObjectOrSet, Status> = match req_src {
|
||||
Src::SrcObj(obj) => {
|
||||
if obj.namespace.is_empty() {
|
||||
if req_src.namespace.is_empty() {
|
||||
return Err(Status::invalid_argument("src.namespace must be set"));
|
||||
}
|
||||
if obj.id.is_empty() {
|
||||
if req_src.id.is_empty() {
|
||||
return Err(Status::invalid_argument("src.id must be set"));
|
||||
}
|
||||
|
||||
Ok((&*obj.namespace, &*obj.id).into())
|
||||
}
|
||||
Src::SrcSet(set) => {
|
||||
if set.namespace.is_empty() {
|
||||
return Err(Status::invalid_argument("src.namespace must be set"));
|
||||
}
|
||||
if set.id.is_empty() {
|
||||
return Err(Status::invalid_argument("src.id must be set"));
|
||||
}
|
||||
if set.relation.is_empty() {
|
||||
let src: ObjectOrSet = if let Some(req_src_relation) = req_src.relation.as_deref() {
|
||||
if req_src_relation.is_empty() {
|
||||
return Err(Status::invalid_argument("src.relation must be set"));
|
||||
}
|
||||
|
||||
Ok((&*set.namespace, &*set.id, &*set.relation).into())
|
||||
}
|
||||
(&*req_src.namespace, &*req_src.id, req_src_relation).into()
|
||||
} else {
|
||||
(&*req_src.namespace, &*req_src.id).into()
|
||||
};
|
||||
let src = src?;
|
||||
|
||||
let exists = graph.has(src, req_rel.as_str(), (&*req_dst.namespace, &*req_dst.id));
|
||||
|
||||
Ok(Response::new(ExistsResponse { exists }))
|
||||
Ok(Response::new(RelationExistsRes { exists }))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -247,8 +229,8 @@ impl RelationService for GraphService {
|
|||
impl QueryService for GraphService {
|
||||
async fn is_related_to(
|
||||
&self,
|
||||
request: Request<Relation>,
|
||||
) -> Result<Response<IsRelatedToResponse>, Status> {
|
||||
request: Request<QueryIsRelatedToReq>,
|
||||
) -> Result<Response<QueryIsRelatedToRes>, Status> {
|
||||
let graph = self.graph.lock().await;
|
||||
|
||||
let api_key = api_key_from_req(request.metadata(), &self.api_keys).await?;
|
||||
|
@ -276,9 +258,9 @@ impl QueryService for GraphService {
|
|||
}
|
||||
|
||||
if !graph.has_recursive(
|
||||
("themis_key", &*api_key),
|
||||
(API_KEY_NS, &*api_key),
|
||||
"read",
|
||||
("themis_ns", &*req_dst.namespace),
|
||||
(NAMESPACE_NS, &*req_dst.namespace),
|
||||
u32::MAX,
|
||||
) {
|
||||
return Err(Status::permission_denied(
|
||||
|
@ -286,32 +268,21 @@ impl QueryService for GraphService {
|
|||
))?;
|
||||
}
|
||||
|
||||
let src: Result<ObjectOrSet, Status> = match req_src {
|
||||
Src::SrcObj(obj) => {
|
||||
if obj.namespace.is_empty() {
|
||||
if req_src.namespace.is_empty() {
|
||||
return Err(Status::invalid_argument("src.namespace must be set"));
|
||||
}
|
||||
if obj.id.is_empty() {
|
||||
if req_src.id.is_empty() {
|
||||
return Err(Status::invalid_argument("src.id must be set"));
|
||||
}
|
||||
|
||||
Ok((&*obj.namespace, &*obj.id).into())
|
||||
}
|
||||
Src::SrcSet(set) => {
|
||||
if set.namespace.is_empty() {
|
||||
return Err(Status::invalid_argument("src.namespace must be set"));
|
||||
}
|
||||
if set.id.is_empty() {
|
||||
return Err(Status::invalid_argument("src.id must be set"));
|
||||
}
|
||||
if set.relation.is_empty() {
|
||||
let src: ObjectOrSet = if let Some(req_src_relation) = req_src.relation.as_deref() {
|
||||
if req_src_relation.is_empty() {
|
||||
return Err(Status::invalid_argument("src.relation must be set"));
|
||||
}
|
||||
|
||||
Ok((&*set.namespace, &*set.id, &*set.relation).into())
|
||||
}
|
||||
(&*req_src.namespace, &*req_src.id, req_src_relation).into()
|
||||
} else {
|
||||
(&*req_src.namespace, &*req_src.id).into()
|
||||
};
|
||||
let src = src?;
|
||||
|
||||
let related = graph.has_recursive(
|
||||
src,
|
||||
|
@ -320,90 +291,113 @@ impl QueryService for GraphService {
|
|||
u32::MAX,
|
||||
);
|
||||
|
||||
Ok(Response::new(IsRelatedToResponse { related }))
|
||||
Ok(Response::new(QueryIsRelatedToRes { related }))
|
||||
}
|
||||
async fn get_related_to(
|
||||
async fn get_related(
|
||||
&self,
|
||||
request: Request<Set>,
|
||||
) -> Result<Response<GetRelatedToResponse>, Status> {
|
||||
//let graph = self.graph.lock().await;
|
||||
request: Request<QueryGetRelatedReq>,
|
||||
) -> Result<Response<QueryGetRelatedRes>, Status> {
|
||||
let graph = self.graph.lock().await;
|
||||
|
||||
//authenticate(
|
||||
// request.metadata(),
|
||||
// &graph,
|
||||
// &self.api_keys,
|
||||
// &request.get_ref().namespace,
|
||||
// "read",
|
||||
//)
|
||||
//.await?;
|
||||
let api_key = api_key_from_req(request.metadata(), &self.api_keys).await?;
|
||||
|
||||
//let obj = graph
|
||||
// .get_node(&request.get_ref().namespace, &request.get_ref().id)
|
||||
// .ok_or(Status::not_found("object not found"))?;
|
||||
let req_dst = request
|
||||
.get_ref()
|
||||
.dst
|
||||
.as_ref()
|
||||
.ok_or(Status::invalid_argument("dst must be set"))?;
|
||||
let req_rel = &request.get_ref().relation;
|
||||
|
||||
//let rel = graph::Relation::new(&request.get_ref().relation);
|
||||
if req_dst.namespace.is_empty() {
|
||||
return Err(Status::invalid_argument("dst.namespace must be set"));
|
||||
}
|
||||
if req_dst.id.is_empty() {
|
||||
return Err(Status::invalid_argument("dst.id must be set"));
|
||||
}
|
||||
|
||||
//Ok(Response::new(GetRelatedToResponse {
|
||||
// objects: graph
|
||||
// .related_to(obj, rel)
|
||||
// .into_iter()
|
||||
// .map(|x| {
|
||||
// let obj = graph.object_from_ref(&x);
|
||||
// Object {
|
||||
// namespace: obj.namespace.to_string(),
|
||||
// id: obj.id,
|
||||
// }
|
||||
// })
|
||||
// .collect::<Vec<_>>(),
|
||||
//}))
|
||||
todo!()
|
||||
let req_namespace = &request.get_ref().namespace;
|
||||
let req_depth = &request.get_ref().depth;
|
||||
|
||||
if !graph.has_recursive(
|
||||
(API_KEY_NS, &*api_key),
|
||||
"read",
|
||||
(NAMESPACE_NS, &*req_dst.namespace),
|
||||
u32::MAX,
|
||||
) {
|
||||
return Err(Status::permission_denied(
|
||||
"missing dst.namespace read permissions",
|
||||
))?;
|
||||
}
|
||||
|
||||
let dst = (req_dst.namespace.as_ref(), req_dst.id.as_ref());
|
||||
|
||||
let objects = graph
|
||||
.related_to(
|
||||
dst,
|
||||
req_rel.as_deref(),
|
||||
req_namespace.as_deref(),
|
||||
req_depth.unwrap_or(u32::MAX),
|
||||
)
|
||||
.into_iter()
|
||||
.map(|x| QueryGetRelatedItem {
|
||||
src: Some(Object {
|
||||
namespace: x.1.namespace.to_string(),
|
||||
id: x.1.id.to_string(),
|
||||
}),
|
||||
relation: x.0 .0.to_string(),
|
||||
})
|
||||
.collect::<_>();
|
||||
|
||||
Ok(Response::new(QueryGetRelatedRes { objects }))
|
||||
}
|
||||
async fn get_relations(
|
||||
&self,
|
||||
request: Request<GetRelationsRequest>,
|
||||
) -> Result<Response<GetRelationsResponse>, Status> {
|
||||
//let graph = self.graph.lock().await;
|
||||
request: Request<QueryGetRelationsReq>,
|
||||
) -> Result<Response<QueryGetRelationsRes>, Status> {
|
||||
let graph = self.graph.lock().await;
|
||||
|
||||
//if request.get_ref().relation.is_empty() {
|
||||
// return Err(Status::invalid_argument("relation must be set"));
|
||||
//}
|
||||
let api_key = api_key_from_req(request.metadata(), &self.api_keys).await?;
|
||||
|
||||
//let obj = request
|
||||
// .get_ref()
|
||||
// .object
|
||||
// .as_ref()
|
||||
// .ok_or(Status::invalid_argument("object must be set"))?;
|
||||
let req_src = request
|
||||
.get_ref()
|
||||
.src
|
||||
.as_ref()
|
||||
.ok_or(Status::invalid_argument("src must be set"))?;
|
||||
let src = (&*req_src.namespace, &*req_src.id);
|
||||
|
||||
//authenticate(
|
||||
// request.metadata(),
|
||||
// &graph,
|
||||
// &self.api_keys,
|
||||
// &obj.namespace,
|
||||
// "read",
|
||||
//)
|
||||
//.await?;
|
||||
let req_rel = &request.get_ref().relation;
|
||||
let req_namespace = &request.get_ref().namespace;
|
||||
let req_depth = &request.get_ref().depth;
|
||||
|
||||
//let obj = graph
|
||||
// .get_node(&obj.namespace, &obj.id)
|
||||
// .ok_or(Status::not_found("object not found"))?;
|
||||
if !graph.has_recursive(
|
||||
(API_KEY_NS, &*api_key),
|
||||
"read",
|
||||
(NAMESPACE_NS, &*req_src.namespace),
|
||||
u32::MAX,
|
||||
) {
|
||||
return Err(Status::permission_denied(
|
||||
"missing src.namespace read permissions",
|
||||
))?;
|
||||
}
|
||||
|
||||
//Ok(Response::new(GetRelationsResponse {
|
||||
// objects: graph
|
||||
// .relations(ObjectRelation(
|
||||
// obj,
|
||||
// graph::Relation::new(&request.get_ref().relation),
|
||||
// ))
|
||||
// .into_iter()
|
||||
// .map(|x| {
|
||||
// let obj = graph.object_from_ref(&x);
|
||||
// Object {
|
||||
// namespace: obj.namespace.to_string(),
|
||||
// id: obj.id,
|
||||
// }
|
||||
// })
|
||||
// .collect::<Vec<_>>(),
|
||||
//}))
|
||||
todo!()
|
||||
let related = graph
|
||||
.relations(
|
||||
src,
|
||||
req_rel.as_deref(),
|
||||
req_namespace.as_deref(),
|
||||
req_depth.unwrap_or(u32::MAX),
|
||||
)
|
||||
.into_iter()
|
||||
.map(|x| QueryGetRelationsItem {
|
||||
dst: Some(Object {
|
||||
namespace: x.1.namespace.to_string(),
|
||||
id: x.1.id.to_string(),
|
||||
}),
|
||||
relation: x.0 .0.to_string(),
|
||||
})
|
||||
.collect::<_>();
|
||||
|
||||
Ok(Response::new(QueryGetRelationsRes { related }))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,10 +14,10 @@ use tokio::{
|
|||
use tonic::transport::Server;
|
||||
|
||||
pub mod grpc_service;
|
||||
pub mod rebacs_proto;
|
||||
pub mod relation_set;
|
||||
pub mod themis_proto;
|
||||
|
||||
use crate::themis_proto::{
|
||||
use crate::rebacs_proto::{
|
||||
query_service_server::QueryServiceServer, relation_service_server::RelationServiceServer,
|
||||
};
|
||||
|
||||
|
|
1
src/rebacs_proto.rs
Normal file
1
src/rebacs_proto.rs
Normal file
|
@ -0,0 +1 @@
|
|||
tonic::include_proto!("eu.zettoit.rebacs");
|
|
@ -195,6 +195,9 @@ impl RelationSet {
|
|||
|
||||
while let Some(distanced) = q.pop() {
|
||||
let node_dist = distanced.distance() + 1;
|
||||
if node_dist > limit {
|
||||
break;
|
||||
}
|
||||
let node = ObjectOrSet::Set(((*distanced.0).clone(), (*distanced.1).clone()));
|
||||
for (nrel, ndst) in self
|
||||
.src_to_dst
|
||||
|
@ -207,7 +210,7 @@ impl RelationSet {
|
|||
return true;
|
||||
}
|
||||
if let Some(existing_node_dist) = dist.get(&*distanced) {
|
||||
if *existing_node_dist <= node_dist || node_dist >= limit {
|
||||
if *existing_node_dist <= node_dist {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -218,6 +221,139 @@ impl RelationSet {
|
|||
false
|
||||
}
|
||||
|
||||
pub fn related_to(
|
||||
&self,
|
||||
dst: impl Into<D>,
|
||||
rel: Option<impl Into<R>>,
|
||||
namespace: Option<&str>,
|
||||
limit: u32,
|
||||
) -> Vec<(Relation, Object)> {
|
||||
let rel = rel.map(|x| x.into());
|
||||
let dst = dst.into();
|
||||
|
||||
let mut related: Vec<(Relation, Object)> = vec![];
|
||||
|
||||
let mut dist: HashMap<(Arc<Object>, Arc<Relation>), u32> = HashMap::new();
|
||||
let mut q: BinaryHeap<Distanced<(Arc<Object>, Arc<Relation>)>> = BinaryHeap::new();
|
||||
|
||||
for (nrel, ndst) in self
|
||||
.dst_to_src
|
||||
.get(&dst)
|
||||
.iter()
|
||||
.flat_map(|x| x.iter())
|
||||
.flat_map(|(r, d)| d.iter().map(|d| (r.clone(), d.clone())))
|
||||
{
|
||||
match &*ndst {
|
||||
ObjectOrSet::Object(obj) => {
|
||||
if (rel.is_none() || rel.as_ref() == Some(&nrel))
|
||||
&& (namespace.is_none() || namespace == Some(&obj.namespace))
|
||||
{
|
||||
related.push(((*nrel).clone(), obj.clone()));
|
||||
}
|
||||
}
|
||||
ObjectOrSet::Set((obj, rel)) => {
|
||||
let obj = Arc::new(obj.clone());
|
||||
let rel = Arc::new(rel.clone());
|
||||
dist.insert((obj.clone(), rel.clone()), 1);
|
||||
q.push(Distanced::one((obj, rel)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while let Some(distanced) = q.pop() {
|
||||
let node_dist = distanced.distance() + 1;
|
||||
if node_dist > limit {
|
||||
break;
|
||||
}
|
||||
|
||||
for ndst in self
|
||||
.dst_to_src
|
||||
.get(&distanced.0)
|
||||
.and_then(|x| x.get(&distanced.1))
|
||||
.iter()
|
||||
.flat_map(|x| x.iter())
|
||||
{
|
||||
match &**ndst {
|
||||
ObjectOrSet::Object(obj) => {
|
||||
if (rel.is_none() || rel.as_ref() == Some(&distanced.1))
|
||||
&& (namespace.is_none() || namespace == Some(&obj.namespace))
|
||||
{
|
||||
related.push(((*distanced.1).clone(), obj.clone()));
|
||||
}
|
||||
}
|
||||
ObjectOrSet::Set((obj, rel)) => {
|
||||
let obj = Arc::new(obj.clone());
|
||||
let rel = Arc::new(rel.clone());
|
||||
dist.insert((obj.clone(), rel.clone()), node_dist);
|
||||
q.push(Distanced::one((obj, rel)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
related
|
||||
}
|
||||
|
||||
pub fn relations(
|
||||
&self,
|
||||
src: impl Into<S>,
|
||||
rel: Option<impl Into<R>>,
|
||||
namespace: Option<&str>,
|
||||
limit: u32,
|
||||
) -> Vec<(Relation, Object)> {
|
||||
let rel = rel.map(|x| x.into());
|
||||
let src = src.into();
|
||||
|
||||
let mut related: Vec<(Relation, Object)> = vec![];
|
||||
|
||||
let mut dist: HashMap<Arc<ObjectOrSet>, u32> = HashMap::new();
|
||||
let mut q: BinaryHeap<Distanced<Arc<ObjectOrSet>>> = BinaryHeap::new();
|
||||
|
||||
for (nrel, ndst) in self
|
||||
.src_to_dst
|
||||
.get(&src)
|
||||
.iter()
|
||||
.flat_map(|x| x.iter())
|
||||
.flat_map(|(r, d)| d.iter().map(|d| (r.clone(), d.clone())))
|
||||
{
|
||||
if (rel.is_none() || rel.as_ref() == Some(&nrel))
|
||||
&& (namespace.is_none() || namespace == Some(&ndst.namespace))
|
||||
{
|
||||
related.push(((*nrel).clone(), (*ndst).clone()));
|
||||
}
|
||||
let obj = Arc::new(ObjectOrSet::Set(((*ndst).clone(), (*nrel).clone())));
|
||||
dist.insert(obj.clone(), 1);
|
||||
q.push(Distanced::one(obj));
|
||||
}
|
||||
|
||||
while let Some(distanced) = q.pop() {
|
||||
let node_dist = distanced.distance() + 1;
|
||||
if node_dist > limit {
|
||||
break;
|
||||
}
|
||||
|
||||
for (nrel, ndsts) in self
|
||||
.src_to_dst
|
||||
.get(&*distanced)
|
||||
.iter()
|
||||
.flat_map(|x| x.iter())
|
||||
{
|
||||
for ndst in ndsts {
|
||||
if (rel.is_none() || rel.as_ref() == Some(nrel))
|
||||
&& (namespace.is_none() || namespace == Some(&ndst.namespace))
|
||||
{
|
||||
related.push(((**nrel).clone(), (**ndst).clone()));
|
||||
}
|
||||
let obj = Arc::new(ObjectOrSet::Set(((**ndst).clone(), (**nrel).clone())));
|
||||
dist.insert(obj.clone(), node_dist);
|
||||
q.push(Distanced::one(obj));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
related
|
||||
}
|
||||
|
||||
pub async fn to_file(&self, file: &mut File) {
|
||||
for (dst, rels_srcs) in self.dst_to_src.iter() {
|
||||
file.write_all(format!("[{}:{}]\n", &dst.namespace, &dst.id).as_bytes())
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
tonic::include_proto!("eu.zettoit.themis");
|
Loading…
Reference in a new issue