mirror of
https://github.com/pfzetto/rebacs
synced 2024-11-22 11:12:49 +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]
|
[package]
|
||||||
name = "themis"
|
name = "rebacs"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
|
2
build.rs
2
build.rs
|
@ -1,6 +1,6 @@
|
||||||
fn main() {
|
fn main() {
|
||||||
tonic_build::configure()
|
tonic_build::configure()
|
||||||
.build_server(true)
|
.build_server(true)
|
||||||
.compile(&["proto/themis.proto"], &["proto"])
|
.compile(&["proto/rebacs.proto"], &["proto"])
|
||||||
.unwrap();
|
.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::metadata::MetadataMap;
|
||||||
use tonic::{Request, Response, Status};
|
use tonic::{Request, Response, Status};
|
||||||
|
|
||||||
use crate::relation_set::{ObjectOrSet, RelationSet};
|
use crate::rebacs_proto::{
|
||||||
use crate::themis_proto::{
|
query_service_server::QueryService, relation_service_server::RelationService, Object,
|
||||||
query_service_server::QueryService, relation::Src, relation_service_server::RelationService,
|
QueryGetRelatedItem, QueryGetRelatedReq, QueryGetRelatedRes, QueryGetRelationsItem,
|
||||||
Empty, ExistsResponse, GetRelatedToResponse, GetRelationsRequest, GetRelationsResponse,
|
QueryGetRelationsReq, QueryGetRelationsRes, QueryIsRelatedToReq, QueryIsRelatedToRes,
|
||||||
IsRelatedToResponse, Relation, Set,
|
RelationCreateReq, RelationCreateRes, RelationDeleteReq, RelationDeleteRes, RelationExistsReq,
|
||||||
|
RelationExistsRes,
|
||||||
};
|
};
|
||||||
|
use crate::relation_set::{ObjectOrSet, RelationSet};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct GraphService {
|
pub struct GraphService {
|
||||||
|
@ -22,9 +24,15 @@ pub struct GraphService {
|
||||||
pub save_trigger: Sender<()>,
|
pub save_trigger: Sender<()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const API_KEY_NS: &str = "rebacs_key";
|
||||||
|
const NAMESPACE_NS: &str = "rebacs_ns";
|
||||||
|
|
||||||
#[tonic::async_trait]
|
#[tonic::async_trait]
|
||||||
impl RelationService for GraphService {
|
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 mut graph = self.graph.lock().await;
|
||||||
|
|
||||||
let api_key = api_key_from_req(request.metadata(), &self.api_keys).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(
|
if !graph.has_recursive(
|
||||||
("themis_key", &*api_key),
|
(API_KEY_NS, &*api_key),
|
||||||
"write",
|
"write",
|
||||||
("themis_ns", &*req_dst.namespace),
|
(NAMESPACE_NS, &*req_dst.namespace),
|
||||||
u32::MAX,
|
u32::MAX,
|
||||||
) {
|
) {
|
||||||
return Err(Status::permission_denied(
|
return Err(Status::permission_denied(
|
||||||
|
@ -62,32 +70,21 @@ impl RelationService for GraphService {
|
||||||
))?;
|
))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let src: Result<ObjectOrSet, Status> = match req_src {
|
if req_src.namespace.is_empty() {
|
||||||
Src::SrcObj(obj) => {
|
return Err(Status::invalid_argument("src.namespace must be set"));
|
||||||
if obj.namespace.is_empty() {
|
}
|
||||||
return Err(Status::invalid_argument("src.namespace must be set"));
|
if req_src.id.is_empty() {
|
||||||
}
|
return Err(Status::invalid_argument("src.id must be set"));
|
||||||
if obj.id.is_empty() {
|
}
|
||||||
return Err(Status::invalid_argument("src.id must be set"));
|
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((&*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() {
|
|
||||||
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(
|
graph.insert(
|
||||||
src.clone(),
|
src.clone(),
|
||||||
|
@ -99,9 +96,12 @@ impl RelationService for GraphService {
|
||||||
|
|
||||||
self.save_trigger.send(()).await.unwrap();
|
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 mut graph = self.graph.lock().await;
|
||||||
|
|
||||||
let api_key = api_key_from_req(request.metadata(), &self.api_keys).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(
|
if !graph.has_recursive(
|
||||||
("themis_key", &*api_key),
|
(API_KEY_NS, &*api_key),
|
||||||
"write",
|
"write",
|
||||||
("themis_ns", &*req_dst.namespace),
|
(NAMESPACE_NS, &*req_dst.namespace),
|
||||||
u32::MAX,
|
u32::MAX,
|
||||||
) {
|
) {
|
||||||
return Err(Status::permission_denied(
|
return Err(Status::permission_denied(
|
||||||
"missing dst.namespace write permissions",
|
"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())
|
if req_src.namespace.is_empty() {
|
||||||
|
return Err(Status::invalid_argument("src.namespace must be set"));
|
||||||
|
}
|
||||||
|
if req_src.id.is_empty() {
|
||||||
|
return Err(Status::invalid_argument("src.id must be set"));
|
||||||
|
}
|
||||||
|
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"));
|
||||||
}
|
}
|
||||||
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() {
|
|
||||||
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));
|
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();
|
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 graph = self.graph.lock().await;
|
||||||
|
|
||||||
let api_key = api_key_from_req(request.metadata(), &self.api_keys).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(
|
if !graph.has_recursive(
|
||||||
("themis_key", &*api_key),
|
(API_KEY_NS, &*api_key),
|
||||||
"read",
|
"read",
|
||||||
("themis_ns", &*req_dst.namespace),
|
(NAMESPACE_NS, &*req_dst.namespace),
|
||||||
u32::MAX,
|
u32::MAX,
|
||||||
) {
|
) {
|
||||||
return Err(Status::permission_denied(
|
return Err(Status::permission_denied(
|
||||||
"missing dst.namespace write permissions",
|
"missing dst.namespace write permissions",
|
||||||
))?;
|
))?;
|
||||||
}
|
}
|
||||||
let src: Result<ObjectOrSet, Status> = match req_src {
|
if req_src.namespace.is_empty() {
|
||||||
Src::SrcObj(obj) => {
|
return Err(Status::invalid_argument("src.namespace must be set"));
|
||||||
if obj.namespace.is_empty() {
|
}
|
||||||
return Err(Status::invalid_argument("src.namespace must be set"));
|
if req_src.id.is_empty() {
|
||||||
}
|
return Err(Status::invalid_argument("src.id must be set"));
|
||||||
if obj.id.is_empty() {
|
}
|
||||||
return Err(Status::invalid_argument("src.id must be set"));
|
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((&*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() {
|
|
||||||
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));
|
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 {
|
impl QueryService for GraphService {
|
||||||
async fn is_related_to(
|
async fn is_related_to(
|
||||||
&self,
|
&self,
|
||||||
request: Request<Relation>,
|
request: Request<QueryIsRelatedToReq>,
|
||||||
) -> Result<Response<IsRelatedToResponse>, Status> {
|
) -> Result<Response<QueryIsRelatedToRes>, Status> {
|
||||||
let graph = self.graph.lock().await;
|
let graph = self.graph.lock().await;
|
||||||
|
|
||||||
let api_key = api_key_from_req(request.metadata(), &self.api_keys).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(
|
if !graph.has_recursive(
|
||||||
("themis_key", &*api_key),
|
(API_KEY_NS, &*api_key),
|
||||||
"read",
|
"read",
|
||||||
("themis_ns", &*req_dst.namespace),
|
(NAMESPACE_NS, &*req_dst.namespace),
|
||||||
u32::MAX,
|
u32::MAX,
|
||||||
) {
|
) {
|
||||||
return Err(Status::permission_denied(
|
return Err(Status::permission_denied(
|
||||||
|
@ -286,32 +268,21 @@ impl QueryService for GraphService {
|
||||||
))?;
|
))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let src: Result<ObjectOrSet, Status> = match req_src {
|
if req_src.namespace.is_empty() {
|
||||||
Src::SrcObj(obj) => {
|
return Err(Status::invalid_argument("src.namespace must be set"));
|
||||||
if obj.namespace.is_empty() {
|
}
|
||||||
return Err(Status::invalid_argument("src.namespace must be set"));
|
if req_src.id.is_empty() {
|
||||||
}
|
return Err(Status::invalid_argument("src.id must be set"));
|
||||||
if obj.id.is_empty() {
|
}
|
||||||
return Err(Status::invalid_argument("src.id must be set"));
|
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((&*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() {
|
|
||||||
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(
|
let related = graph.has_recursive(
|
||||||
src,
|
src,
|
||||||
|
@ -320,90 +291,113 @@ impl QueryService for GraphService {
|
||||||
u32::MAX,
|
u32::MAX,
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(Response::new(IsRelatedToResponse { related }))
|
Ok(Response::new(QueryIsRelatedToRes { related }))
|
||||||
}
|
}
|
||||||
async fn get_related_to(
|
async fn get_related(
|
||||||
&self,
|
&self,
|
||||||
request: Request<Set>,
|
request: Request<QueryGetRelatedReq>,
|
||||||
) -> Result<Response<GetRelatedToResponse>, Status> {
|
) -> Result<Response<QueryGetRelatedRes>, Status> {
|
||||||
//let graph = self.graph.lock().await;
|
let graph = self.graph.lock().await;
|
||||||
|
|
||||||
//authenticate(
|
let api_key = api_key_from_req(request.metadata(), &self.api_keys).await?;
|
||||||
// request.metadata(),
|
|
||||||
// &graph,
|
|
||||||
// &self.api_keys,
|
|
||||||
// &request.get_ref().namespace,
|
|
||||||
// "read",
|
|
||||||
//)
|
|
||||||
//.await?;
|
|
||||||
|
|
||||||
//let obj = graph
|
let req_dst = request
|
||||||
// .get_node(&request.get_ref().namespace, &request.get_ref().id)
|
.get_ref()
|
||||||
// .ok_or(Status::not_found("object not found"))?;
|
.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 {
|
let req_namespace = &request.get_ref().namespace;
|
||||||
// objects: graph
|
let req_depth = &request.get_ref().depth;
|
||||||
// .related_to(obj, rel)
|
|
||||||
// .into_iter()
|
if !graph.has_recursive(
|
||||||
// .map(|x| {
|
(API_KEY_NS, &*api_key),
|
||||||
// let obj = graph.object_from_ref(&x);
|
"read",
|
||||||
// Object {
|
(NAMESPACE_NS, &*req_dst.namespace),
|
||||||
// namespace: obj.namespace.to_string(),
|
u32::MAX,
|
||||||
// id: obj.id,
|
) {
|
||||||
// }
|
return Err(Status::permission_denied(
|
||||||
// })
|
"missing dst.namespace read permissions",
|
||||||
// .collect::<Vec<_>>(),
|
))?;
|
||||||
//}))
|
}
|
||||||
todo!()
|
|
||||||
|
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(
|
async fn get_relations(
|
||||||
&self,
|
&self,
|
||||||
request: Request<GetRelationsRequest>,
|
request: Request<QueryGetRelationsReq>,
|
||||||
) -> Result<Response<GetRelationsResponse>, Status> {
|
) -> Result<Response<QueryGetRelationsRes>, Status> {
|
||||||
//let graph = self.graph.lock().await;
|
let graph = self.graph.lock().await;
|
||||||
|
|
||||||
//if request.get_ref().relation.is_empty() {
|
let api_key = api_key_from_req(request.metadata(), &self.api_keys).await?;
|
||||||
// return Err(Status::invalid_argument("relation must be set"));
|
|
||||||
//}
|
|
||||||
|
|
||||||
//let obj = request
|
let req_src = request
|
||||||
// .get_ref()
|
.get_ref()
|
||||||
// .object
|
.src
|
||||||
// .as_ref()
|
.as_ref()
|
||||||
// .ok_or(Status::invalid_argument("object must be set"))?;
|
.ok_or(Status::invalid_argument("src must be set"))?;
|
||||||
|
let src = (&*req_src.namespace, &*req_src.id);
|
||||||
|
|
||||||
//authenticate(
|
let req_rel = &request.get_ref().relation;
|
||||||
// request.metadata(),
|
let req_namespace = &request.get_ref().namespace;
|
||||||
// &graph,
|
let req_depth = &request.get_ref().depth;
|
||||||
// &self.api_keys,
|
|
||||||
// &obj.namespace,
|
|
||||||
// "read",
|
|
||||||
//)
|
|
||||||
//.await?;
|
|
||||||
|
|
||||||
//let obj = graph
|
if !graph.has_recursive(
|
||||||
// .get_node(&obj.namespace, &obj.id)
|
(API_KEY_NS, &*api_key),
|
||||||
// .ok_or(Status::not_found("object not found"))?;
|
"read",
|
||||||
|
(NAMESPACE_NS, &*req_src.namespace),
|
||||||
|
u32::MAX,
|
||||||
|
) {
|
||||||
|
return Err(Status::permission_denied(
|
||||||
|
"missing src.namespace read permissions",
|
||||||
|
))?;
|
||||||
|
}
|
||||||
|
|
||||||
//Ok(Response::new(GetRelationsResponse {
|
let related = graph
|
||||||
// objects: graph
|
.relations(
|
||||||
// .relations(ObjectRelation(
|
src,
|
||||||
// obj,
|
req_rel.as_deref(),
|
||||||
// graph::Relation::new(&request.get_ref().relation),
|
req_namespace.as_deref(),
|
||||||
// ))
|
req_depth.unwrap_or(u32::MAX),
|
||||||
// .into_iter()
|
)
|
||||||
// .map(|x| {
|
.into_iter()
|
||||||
// let obj = graph.object_from_ref(&x);
|
.map(|x| QueryGetRelationsItem {
|
||||||
// Object {
|
dst: Some(Object {
|
||||||
// namespace: obj.namespace.to_string(),
|
namespace: x.1.namespace.to_string(),
|
||||||
// id: obj.id,
|
id: x.1.id.to_string(),
|
||||||
// }
|
}),
|
||||||
// })
|
relation: x.0 .0.to_string(),
|
||||||
// .collect::<Vec<_>>(),
|
})
|
||||||
//}))
|
.collect::<_>();
|
||||||
todo!()
|
|
||||||
|
Ok(Response::new(QueryGetRelationsRes { related }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,10 +14,10 @@ use tokio::{
|
||||||
use tonic::transport::Server;
|
use tonic::transport::Server;
|
||||||
|
|
||||||
pub mod grpc_service;
|
pub mod grpc_service;
|
||||||
|
pub mod rebacs_proto;
|
||||||
pub mod relation_set;
|
pub mod relation_set;
|
||||||
pub mod themis_proto;
|
|
||||||
|
|
||||||
use crate::themis_proto::{
|
use crate::rebacs_proto::{
|
||||||
query_service_server::QueryServiceServer, relation_service_server::RelationServiceServer,
|
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() {
|
while let Some(distanced) = q.pop() {
|
||||||
let node_dist = distanced.distance() + 1;
|
let node_dist = distanced.distance() + 1;
|
||||||
|
if node_dist > limit {
|
||||||
|
break;
|
||||||
|
}
|
||||||
let node = ObjectOrSet::Set(((*distanced.0).clone(), (*distanced.1).clone()));
|
let node = ObjectOrSet::Set(((*distanced.0).clone(), (*distanced.1).clone()));
|
||||||
for (nrel, ndst) in self
|
for (nrel, ndst) in self
|
||||||
.src_to_dst
|
.src_to_dst
|
||||||
|
@ -207,7 +210,7 @@ impl RelationSet {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if let Some(existing_node_dist) = dist.get(&*distanced) {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -218,6 +221,139 @@ impl RelationSet {
|
||||||
false
|
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) {
|
pub async fn to_file(&self, file: &mut File) {
|
||||||
for (dst, rels_srcs) in self.dst_to_src.iter() {
|
for (dst, rels_srcs) in self.dst_to_src.iter() {
|
||||||
file.write_all(format!("[{}:{}]\n", &dst.namespace, &dst.id).as_bytes())
|
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