mirror of
https://github.com/pfzetto/rebacs
synced 2024-11-23 19:52:50 +01:00
new backend
This commit is contained in:
parent
dbd581cece
commit
1b53e041e3
7 changed files with 1073 additions and 381 deletions
35
Cargo.lock
generated
35
Cargo.lock
generated
|
@ -145,6 +145,15 @@ version = "1.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
|
||||
|
||||
[[package]]
|
||||
name = "castaway"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a17ed5635fc8536268e5d4de1e22e81ac34419e5f052d4d51f4e01dcc263fcc"
|
||||
dependencies = [
|
||||
"rustversion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.79"
|
||||
|
@ -157,6 +166,19 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "compact_str"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bff0805f79ecb1b35163f3957a6934ea8d04fcd36ef98b52e7316f63e72e73d1"
|
||||
dependencies = [
|
||||
"castaway",
|
||||
"cfg-if",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.7"
|
||||
|
@ -902,6 +924,12 @@ version = "1.0.12"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
|
@ -989,6 +1017,12 @@ version = "0.5.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
|
@ -1043,6 +1077,7 @@ dependencies = [
|
|||
name = "themis"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"compact_str",
|
||||
"dotenvy",
|
||||
"hex",
|
||||
"log",
|
||||
|
|
|
@ -15,6 +15,7 @@ tonic = { version="0.9.2", features=["tls"] }
|
|||
prost = "0.11.9"
|
||||
sha2 = "0.10.6"
|
||||
hex = "0.4.3"
|
||||
compact_str = "0.7.0"
|
||||
|
||||
[build-dependencies]
|
||||
tonic-build = "0.9.2"
|
||||
|
|
|
@ -1,12 +1,6 @@
|
|||
syntax = "proto3";
|
||||
package eu.zettoit.themis;
|
||||
|
||||
service ObjectService{
|
||||
rpc Create(Object) returns (Empty);
|
||||
rpc Delete(Object) returns (Empty);
|
||||
rpc Exists(Object) returns (ExistsResponse);
|
||||
}
|
||||
|
||||
service RelationService {
|
||||
rpc Create(Relation) returns (Empty);
|
||||
rpc Delete(Relation) returns (Empty);
|
||||
|
|
387
src/graph.rs
387
src/graph.rs
|
@ -1,4 +1,5 @@
|
|||
use std::{
|
||||
cmp::Ordering,
|
||||
collections::{
|
||||
hash_map::{Iter, IterMut},
|
||||
BinaryHeap, HashMap, HashSet,
|
||||
|
@ -33,12 +34,12 @@ pub struct ObjectRef(pub u32);
|
|||
|
||||
#[derive(PartialEq, Eq, Hash, Clone, Debug, Deserialize, Serialize)]
|
||||
pub enum ObjectOrSet {
|
||||
Object(ObjectRef),
|
||||
Set((ObjectRef, Relation)),
|
||||
Object(Object),
|
||||
Set((Object, Relation)),
|
||||
}
|
||||
|
||||
#[derive(Hash, PartialEq, Eq, Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Relation(String);
|
||||
pub struct Relation(pub String);
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Hash, Serialize, Deserialize, Debug)]
|
||||
pub struct ObjectRelation(pub ObjectRef, pub Relation);
|
||||
|
@ -53,7 +54,7 @@ impl Object {
|
|||
}
|
||||
|
||||
impl ObjectOrSet {
|
||||
pub fn object_ref(&self) -> &ObjectRef {
|
||||
pub fn object(&self) -> &Object {
|
||||
match self {
|
||||
ObjectOrSet::Object(obj) => obj,
|
||||
ObjectOrSet::Set((obj, _)) => obj,
|
||||
|
@ -67,14 +68,14 @@ impl ObjectOrSet {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<ObjectRef> for ObjectOrSet {
|
||||
fn from(value: ObjectRef) -> Self {
|
||||
impl From<Object> for ObjectOrSet {
|
||||
fn from(value: Object) -> Self {
|
||||
Self::Object(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(ObjectRef, &str)> for ObjectOrSet {
|
||||
fn from(value: (ObjectRef, &str)) -> Self {
|
||||
impl From<(Object, &str)> for ObjectOrSet {
|
||||
fn from(value: (Object, &str)) -> Self {
|
||||
Self::Set((value.0, Relation::new(value.1)))
|
||||
}
|
||||
}
|
||||
|
@ -102,6 +103,27 @@ impl From<(ObjectRef, &str)> for ObjectRelation {
|
|||
Self(value.0, Relation::new(value.1))
|
||||
}
|
||||
}
|
||||
impl From<(&str, &str)> for Object {
|
||||
fn from((namespace, id): (&str, &str)) -> Self {
|
||||
Self {
|
||||
namespace: namespace.to_string(),
|
||||
id: id.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<(&String, &String)> for Object {
|
||||
fn from((namespace, id): (&String, &String)) -> Self {
|
||||
Self {
|
||||
namespace: namespace.to_string(),
|
||||
id: id.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<(String, String)> for Object {
|
||||
fn from((namespace, id): (String, String)) -> Self {
|
||||
Self { namespace, id }
|
||||
}
|
||||
}
|
||||
|
||||
impl Graph {
|
||||
pub fn get_node(&self, namespace: &str, id: &str) -> Option<ObjectRef> {
|
||||
|
@ -110,29 +132,86 @@ impl Graph {
|
|||
pub fn object_from_ref(&self, obj: &ObjectRef) -> Object {
|
||||
self.nodes.get_by_b(obj).unwrap().clone()
|
||||
}
|
||||
pub fn add_node(&mut self, node: Object) -> ObjectRef {
|
||||
pub fn get_or_add_node(&mut self, namespace: &str, id: &str) -> ObjectRef {
|
||||
if let Some(node) = self.get_node(namespace, id) {
|
||||
node
|
||||
} else {
|
||||
self.add_node((namespace, id))
|
||||
}
|
||||
}
|
||||
pub fn add_node(&mut self, node: impl Into<Object>) -> ObjectRef {
|
||||
let obj_ref = ObjectRef(self.counter);
|
||||
self.nodes.insert(node, obj_ref);
|
||||
self.nodes.insert(node.into(), obj_ref);
|
||||
self.counter += 1;
|
||||
obj_ref
|
||||
}
|
||||
pub fn remove_node(&mut self, node: Object) {
|
||||
pub fn remove_node(&mut self, node: impl Into<Object>) {
|
||||
let node = node.into();
|
||||
let index = self.nodes.remove_by_a(&node);
|
||||
if let Some(index) = index {
|
||||
self.edges.remove_by_c(&index);
|
||||
self.edges.get_by_a(&ObjectOrSet::Object(*index));
|
||||
//self.edges.get_by_a(&ObjectOrSet::Object(*index));
|
||||
//TODO: remove edges with ObjectOrSet::Set
|
||||
}
|
||||
}
|
||||
pub fn remove_node_by_ref(&mut self, node: impl Into<ObjectRef>) {
|
||||
let node = node.into();
|
||||
let index = self.nodes.remove_by_b(&node);
|
||||
if index.is_some() {
|
||||
self.edges.remove_by_c(&node);
|
||||
|
||||
//let edges = self
|
||||
// .edges
|
||||
// .left_to_right
|
||||
// .keys()
|
||||
// .filter(|x| *x.object_ref() == node)
|
||||
// .map(|x| (**x).clone())
|
||||
// .collect::<Vec<ObjectOrSet>>();
|
||||
//for edge in edges {
|
||||
// self.edges.remove_by_a(&edge);
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_relation(&self, src: ObjectOrSet, dst: ObjectRelation) -> bool {
|
||||
self.edges.has(&src, &dst.1, &dst.0)
|
||||
}
|
||||
pub fn add_relation(&mut self, src: ObjectOrSet, dst: ObjectRelation) {
|
||||
self.edges.insert(src, dst.1, dst.0);
|
||||
pub fn add_relation(&mut self, src: impl Into<ObjectOrSet>, dst: impl Into<ObjectRelation>) {
|
||||
let dst = dst.into();
|
||||
self.edges.insert(src.into(), dst.1, dst.0);
|
||||
}
|
||||
pub fn remove_relation(&mut self, src: ObjectOrSet, dst: ObjectRelation) {
|
||||
pub fn remove_relation(&mut self, src: impl Into<ObjectOrSet>, dst: impl Into<ObjectRelation>) {
|
||||
let dst = dst.into();
|
||||
self.edges.remove(&src.into(), &dst.1, &dst.0);
|
||||
}
|
||||
pub fn remove_relation_and_residual_node(
|
||||
&mut self,
|
||||
src: impl Into<ObjectOrSet>,
|
||||
dst: impl Into<ObjectRelation>,
|
||||
) {
|
||||
let src = src.into();
|
||||
let dst = dst.into();
|
||||
self.edges.remove(&src, &dst.1, &dst.0);
|
||||
|
||||
//if self.edges.get_by_c(src.object_ref()).is_empty()
|
||||
// && !self
|
||||
// .edges
|
||||
// .left_to_right
|
||||
// .keys()
|
||||
// .any(|x| x.object_ref() == src.object_ref())
|
||||
//{
|
||||
// self.remove_node_by_ref(*src.object_ref());
|
||||
//}
|
||||
|
||||
//if self.edges.get_by_c(&dst.0).is_empty()
|
||||
// && !self
|
||||
// .edges
|
||||
// .left_to_right
|
||||
// .keys()
|
||||
// .any(|x| *x.object_ref() == dst.0)
|
||||
//{
|
||||
// self.remove_node_by_ref(dst.0);
|
||||
//}
|
||||
}
|
||||
|
||||
pub fn is_related_to(
|
||||
|
@ -158,87 +237,88 @@ impl Graph {
|
|||
q.push(ObjectRelationDist(1, neighbor.clone()));
|
||||
}
|
||||
|
||||
while let Some(ObjectRelationDist(node_dist, node)) = q.pop() {
|
||||
let node_dist = node_dist + 1;
|
||||
let node = ObjectOrSet::Set((node.0, node.1));
|
||||
for neighbor in self
|
||||
.edges
|
||||
.get_by_a(&node)
|
||||
.iter()
|
||||
.flat_map(|(r, m)| m.iter().map(|x| ObjectRelation(**x, (**r).clone())))
|
||||
{
|
||||
if neighbor == dst {
|
||||
return true;
|
||||
}
|
||||
if let Some(existing_node_dist) = dist.get(&neighbor) {
|
||||
if *existing_node_dist < node_dist {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
dist.insert(neighbor.clone(), node_dist);
|
||||
q.push(ObjectRelationDist(node_dist, neighbor.clone()));
|
||||
}
|
||||
}
|
||||
//while let Some(ObjectRelationDist(node_dist, node)) = q.pop() {
|
||||
// let node_dist = node_dist + 1;
|
||||
// let node = ObjectOrSet::Set((node.0, node.1));
|
||||
// for neighbor in self
|
||||
// .edges
|
||||
// .get_by_a(&node)
|
||||
// .iter()
|
||||
// .flat_map(|(r, m)| m.iter().map(|x| ObjectRelation(**x, (**r).clone())))
|
||||
// {
|
||||
// if neighbor == dst {
|
||||
// return true;
|
||||
// }
|
||||
// if let Some(existing_node_dist) = dist.get(&neighbor) {
|
||||
// if *existing_node_dist < node_dist {
|
||||
// continue;
|
||||
// }
|
||||
// }
|
||||
// dist.insert(neighbor.clone(), node_dist);
|
||||
// q.push(ObjectRelationDist(node_dist, neighbor.clone()));
|
||||
// }
|
||||
//}
|
||||
|
||||
false
|
||||
}
|
||||
pub fn related_to(&self, dst: ObjectRef, relation: Relation) -> HashSet<ObjectRef> {
|
||||
let mut relation_sets = vec![];
|
||||
let mut relations: HashSet<ObjectRef> = HashSet::new();
|
||||
for obj in self.edges.get_by_cb(&dst, &relation) {
|
||||
match obj {
|
||||
ObjectOrSet::Object(obj) => {
|
||||
relations.insert(*obj);
|
||||
}
|
||||
ObjectOrSet::Set(set) => relation_sets.push(set),
|
||||
}
|
||||
}
|
||||
while let Some(set) = relation_sets.pop() {
|
||||
for obj in self.edges.get_by_cb(&set.0, &set.1) {
|
||||
match obj {
|
||||
ObjectOrSet::Object(obj) => {
|
||||
relations.insert(*obj);
|
||||
}
|
||||
ObjectOrSet::Set(set) => relation_sets.push(set),
|
||||
}
|
||||
}
|
||||
}
|
||||
relations
|
||||
//let mut relation_sets = vec![];
|
||||
//let mut relations: HashSet<ObjectRef> = HashSet::new();
|
||||
//for obj in self.edges.get_by_cb(&dst, &relation) {
|
||||
// match obj {
|
||||
// ObjectOrSet::Object(obj) => {
|
||||
// relations.insert(*obj);
|
||||
// }
|
||||
// ObjectOrSet::Set(set) => relation_sets.push(set),
|
||||
// }
|
||||
//}
|
||||
//while let Some(set) = relation_sets.pop() {
|
||||
// for obj in self.edges.get_by_cb(&set.0, &set.1) {
|
||||
// match obj {
|
||||
// ObjectOrSet::Object(obj) => {
|
||||
// relations.insert(*obj);
|
||||
// }
|
||||
// ObjectOrSet::Set(set) => relation_sets.push(set),
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//relations
|
||||
todo!()
|
||||
}
|
||||
pub fn relations(&self, src: impl Into<ObjectRelation>) -> HashSet<ObjectRef> {
|
||||
let src: ObjectRelation = src.into();
|
||||
//let src: ObjectRelation = src.into();
|
||||
|
||||
let mut visited = HashSet::new();
|
||||
let mut relation_sets = vec![];
|
||||
let mut relations = HashSet::new();
|
||||
//let mut visited = HashSet::new();
|
||||
//let mut relation_sets = vec![];
|
||||
//let mut relations = HashSet::new();
|
||||
|
||||
for (rel, neighbors) in self.edges.get_by_a(&ObjectOrSet::Object(src.0)) {
|
||||
for neighbor in neighbors {
|
||||
if *rel == src.1 {
|
||||
relations.insert(*neighbor);
|
||||
}
|
||||
relation_sets.push((rel, neighbor));
|
||||
}
|
||||
}
|
||||
//for (rel, neighbors) in self.edges.get_by_a(&ObjectOrSet::Object(src.0)) {
|
||||
// for neighbor in neighbors {
|
||||
// if *rel == src.1 {
|
||||
// relations.insert(*neighbor);
|
||||
// }
|
||||
// relation_sets.push((rel, neighbor));
|
||||
// }
|
||||
//}
|
||||
|
||||
while let Some((rel, obj_ref)) = relation_sets.pop() {
|
||||
if !visited.contains(&(rel, obj_ref)) {
|
||||
for (rel, neighbors) in self
|
||||
.edges
|
||||
.get_by_a(&ObjectOrSet::Set((*obj_ref, (*rel).clone())))
|
||||
{
|
||||
for neighbor in neighbors {
|
||||
if *rel == src.1 {
|
||||
relations.insert(*neighbor);
|
||||
}
|
||||
relation_sets.push((rel, neighbor));
|
||||
}
|
||||
}
|
||||
visited.insert((rel, obj_ref));
|
||||
}
|
||||
}
|
||||
|
||||
relations
|
||||
//while let Some((rel, obj_ref)) = relation_sets.pop() {
|
||||
// if !visited.contains(&(rel, obj_ref)) {
|
||||
// for (rel, neighbors) in self
|
||||
// .edges
|
||||
// .get_by_a(&ObjectOrSet::Set((*obj_ref, (*rel).clone())))
|
||||
// {
|
||||
// for neighbor in neighbors {
|
||||
// if *rel == src.1 {
|
||||
// relations.insert(*neighbor);
|
||||
// }
|
||||
// relation_sets.push((rel, neighbor));
|
||||
// }
|
||||
// }
|
||||
// visited.insert((rel, obj_ref));
|
||||
// }
|
||||
//}
|
||||
//relations
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn to_file(&self, file: &mut File) {
|
||||
|
@ -247,33 +327,33 @@ impl Graph {
|
|||
file.write_all(format!("[{}:{}]\n", &obj.namespace, &obj.id).as_bytes())
|
||||
.await
|
||||
.unwrap();
|
||||
for (rel, arr) in self.edges.get_by_c(obj_ref.as_ref()) {
|
||||
let arr = arr
|
||||
.iter()
|
||||
.filter_map(|x| {
|
||||
let rel_obj_ref = x.object_ref();
|
||||
self.nodes.get_by_b(rel_obj_ref).map(|rel_obj| {
|
||||
let (namespace, id) = (&rel_obj.namespace, &rel_obj.id);
|
||||
//for (rel, arr) in self.edges.get_by_c(obj_ref.as_ref()) {
|
||||
// let arr = arr
|
||||
// .iter()
|
||||
// .filter_map(|x| {
|
||||
// let rel_obj_ref = x.object_ref();
|
||||
// self.nodes.get_by_b(rel_obj_ref).map(|rel_obj| {
|
||||
// let (namespace, id) = (&rel_obj.namespace, &rel_obj.id);
|
||||
|
||||
if *namespace == obj.namespace && *id == obj.id {
|
||||
match x.relation() {
|
||||
None => "self".to_string(),
|
||||
Some(rel) => format!("self#{}", &rel.0),
|
||||
}
|
||||
} else {
|
||||
match x.relation() {
|
||||
None => format!("{}:{}", &namespace, &id),
|
||||
Some(rel) => format!("{}:{}#{}", &namespace, &id, &rel.0),
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
.reduce(|acc, e| acc + ", " + &e)
|
||||
.unwrap_or_default();
|
||||
file.write_all(format!("{} = [{}]\n", &rel.0, &arr).as_bytes())
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
// if *namespace == obj.namespace && *id == obj.id {
|
||||
// match x.relation() {
|
||||
// None => "self".to_string(),
|
||||
// Some(rel) => format!("self#{}", &rel.0),
|
||||
// }
|
||||
// } else {
|
||||
// match x.relation() {
|
||||
// None => format!("{}:{}", &namespace, &id),
|
||||
// Some(rel) => format!("{}:{}#{}", &namespace, &id, &rel.0),
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// })
|
||||
// .reduce(|acc, e| acc + ", " + &e)
|
||||
// .unwrap_or_default();
|
||||
// file.write_all(format!("{} = [{}]\n", &rel.0, &arr).as_bytes())
|
||||
// .await
|
||||
// .unwrap();
|
||||
//}
|
||||
file.write_all("\n".as_bytes()).await.unwrap();
|
||||
}
|
||||
}
|
||||
|
@ -339,19 +419,19 @@ impl Graph {
|
|||
}
|
||||
}
|
||||
|
||||
for relation in relations {
|
||||
let src = match relation.2 {
|
||||
Some(rel) => {
|
||||
let obj = graph.get_node(&relation.0, &relation.1).unwrap();
|
||||
ObjectOrSet::Set((obj, Relation::new(&rel)))
|
||||
}
|
||||
None => {
|
||||
let obj = graph.get_node(&relation.0, &relation.1).unwrap();
|
||||
ObjectOrSet::Object(obj)
|
||||
}
|
||||
};
|
||||
graph.add_relation(src, ObjectRelation(relation.3, Relation(relation.4)));
|
||||
}
|
||||
//for relation in relations {
|
||||
// let src = match relation.2 {
|
||||
// Some(rel) => {
|
||||
// let obj = graph.get_node(&relation.0, &relation.1).unwrap();
|
||||
// ObjectOrSet::Set((obj, Relation::new(&rel)))
|
||||
// }
|
||||
// None => {
|
||||
// let obj = graph.get_node(&relation.0, &relation.1).unwrap();
|
||||
// ObjectOrSet::Object(obj)
|
||||
// }
|
||||
// };
|
||||
// graph.add_relation(src, ObjectRelation(relation.3, Relation(relation.4)));
|
||||
//}
|
||||
|
||||
graph
|
||||
}
|
||||
|
@ -598,3 +678,58 @@ impl<A, B, C> Default for BidThreeMap<A, B, C> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
//impl Ord for ObjectOrSet {
|
||||
// fn cmp(&self, other: &Self) -> Ordering {}
|
||||
//}
|
||||
//
|
||||
//impl Ord for ObjectRef {}cmp
|
||||
|
||||
impl PartialOrd for Relation {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
self.0.partial_cmp(&other.0)
|
||||
}
|
||||
}
|
||||
impl Ord for Relation {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.0.cmp(&other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for ObjectOrSet {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
match (
|
||||
self.object().partial_cmp(other.object()),
|
||||
self.relation(),
|
||||
other.relation(),
|
||||
) {
|
||||
(Some(Ordering::Equal), Some(self_rel), Some(other_rel)) => {
|
||||
self_rel.partial_cmp(other_rel)
|
||||
}
|
||||
(ord, _, _) => ord,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Ord for ObjectOrSet {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.object()
|
||||
.cmp(other.object())
|
||||
.then(self.relation().cmp(&other.relation()))
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Object {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
match self.namespace.partial_cmp(&other.namespace) {
|
||||
Some(core::cmp::Ordering::Equal) => self.id.partial_cmp(&other.id),
|
||||
ord => ord,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Ord for Object {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.namespace
|
||||
.cmp(&other.namespace)
|
||||
.then(self.id.cmp(&other.id))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,125 +8,91 @@ use tokio::sync::Mutex;
|
|||
use tonic::metadata::MetadataMap;
|
||||
use tonic::{Request, Response, Status};
|
||||
|
||||
use crate::graph::{self, Graph, ObjectRelation};
|
||||
use crate::relation_set::{ObjectOrSet, RelationSet};
|
||||
use crate::themis_proto::{
|
||||
object_service_server::ObjectService, query_service_server::QueryService, relation::Src,
|
||||
relation_service_server::RelationService, Empty, ExistsResponse, GetRelatedToResponse,
|
||||
GetRelationsRequest, GetRelationsResponse, IsRelatedToResponse, Object, Relation, Set,
|
||||
query_service_server::QueryService, relation::Src, relation_service_server::RelationService,
|
||||
Empty, ExistsResponse, GetRelatedToResponse, GetRelationsRequest, GetRelationsResponse,
|
||||
IsRelatedToResponse, Relation, Set,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GraphService {
|
||||
pub api_keys: Arc<Mutex<HashMap<String, String>>>,
|
||||
pub graph: Arc<Mutex<Graph>>,
|
||||
pub graph: Arc<Mutex<RelationSet>>,
|
||||
pub save_trigger: Sender<()>,
|
||||
}
|
||||
|
||||
#[tonic::async_trait]
|
||||
impl ObjectService for GraphService {
|
||||
async fn create(&self, request: Request<Object>) -> Result<Response<Empty>, Status> {
|
||||
let mut graph = self.graph.lock().await;
|
||||
|
||||
authenticate(
|
||||
request.metadata(),
|
||||
&graph,
|
||||
&self.api_keys,
|
||||
&request.get_ref().namespace,
|
||||
"write",
|
||||
)
|
||||
.await?;
|
||||
|
||||
if request.get_ref().namespace.is_empty() || request.get_ref().id.is_empty() {
|
||||
return Err(Status::invalid_argument("namespace and id must be set"));
|
||||
}
|
||||
|
||||
graph.add_node(graph::Object::new(
|
||||
&request.get_ref().namespace,
|
||||
&request.get_ref().id,
|
||||
));
|
||||
|
||||
info!(
|
||||
"created object {}:{}",
|
||||
&request.get_ref().namespace,
|
||||
&request.get_ref().id
|
||||
);
|
||||
|
||||
self.save_trigger.send(()).await.unwrap();
|
||||
|
||||
Ok(Response::new(Empty {}))
|
||||
}
|
||||
async fn delete(&self, request: Request<Object>) -> Result<Response<Empty>, Status> {
|
||||
let mut graph = self.graph.lock().await;
|
||||
|
||||
authenticate(
|
||||
request.metadata(),
|
||||
&graph,
|
||||
&self.api_keys,
|
||||
&request.get_ref().namespace,
|
||||
"write",
|
||||
)
|
||||
.await?;
|
||||
|
||||
if request.get_ref().namespace.is_empty() || request.get_ref().id.is_empty() {
|
||||
return Err(Status::invalid_argument("namespace and id must be set"));
|
||||
}
|
||||
|
||||
graph.remove_node(graph::Object::new(
|
||||
&request.get_ref().namespace,
|
||||
&request.get_ref().id,
|
||||
));
|
||||
|
||||
info!(
|
||||
"removed object {}:{}",
|
||||
&request.get_ref().namespace,
|
||||
&request.get_ref().id
|
||||
);
|
||||
|
||||
self.save_trigger.send(()).await.unwrap();
|
||||
|
||||
Ok(Response::new(Empty {}))
|
||||
}
|
||||
async fn exists(&self, request: Request<Object>) -> Result<Response<ExistsResponse>, Status> {
|
||||
let graph = self.graph.lock().await;
|
||||
|
||||
authenticate(
|
||||
request.metadata(),
|
||||
&graph,
|
||||
&self.api_keys,
|
||||
&request.get_ref().namespace,
|
||||
"read",
|
||||
)
|
||||
.await?;
|
||||
|
||||
if request.get_ref().namespace.is_empty() || request.get_ref().id.is_empty() {
|
||||
return Err(Status::invalid_argument("namespace and id must be set"));
|
||||
}
|
||||
|
||||
let exists = graph
|
||||
.get_node(&request.get_ref().namespace, &request.get_ref().id)
|
||||
.is_some();
|
||||
|
||||
Ok(Response::new(ExistsResponse { exists }))
|
||||
}
|
||||
}
|
||||
|
||||
#[tonic::async_trait]
|
||||
impl RelationService for GraphService {
|
||||
async fn create(&self, request: Request<Relation>) -> Result<Response<Empty>, Status> {
|
||||
let mut graph = self.graph.lock().await;
|
||||
|
||||
let (src, dst, dst_namespace) = transform_relation(request.get_ref(), &graph)?;
|
||||
let api_key = api_key_from_req(request.metadata(), &self.api_keys).await?;
|
||||
|
||||
authenticate(
|
||||
request.metadata(),
|
||||
&graph,
|
||||
&self.api_keys,
|
||||
&dst_namespace,
|
||||
let req_src = request
|
||||
.get_ref()
|
||||
.src
|
||||
.as_ref()
|
||||
.ok_or(Status::invalid_argument("src must be set"))?;
|
||||
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;
|
||||
|
||||
if req_rel.is_empty() {
|
||||
return Err(Status::invalid_argument("relation must be set"));
|
||||
}
|
||||
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"));
|
||||
}
|
||||
|
||||
if !graph.has(
|
||||
("themis_key", &*api_key),
|
||||
"write",
|
||||
)
|
||||
.await?;
|
||||
("themis_ns", &*req_dst.namespace),
|
||||
) {
|
||||
return Err(Status::permission_denied(
|
||||
"missing dst.namespace write permissions",
|
||||
))?;
|
||||
}
|
||||
|
||||
graph.add_relation(src, dst);
|
||||
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() {
|
||||
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())
|
||||
}
|
||||
};
|
||||
let src = src?;
|
||||
|
||||
graph.insert(
|
||||
src.clone(),
|
||||
req_rel.clone(),
|
||||
(req_dst.namespace.clone(), req_dst.id.clone()),
|
||||
);
|
||||
|
||||
info!("created relation");
|
||||
|
||||
|
@ -137,20 +103,69 @@ impl RelationService for GraphService {
|
|||
async fn delete(&self, request: Request<Relation>) -> Result<Response<Empty>, Status> {
|
||||
let mut graph = self.graph.lock().await;
|
||||
|
||||
let (src, dst, dst_namespace) = transform_relation(request.get_ref(), &graph)?;
|
||||
let api_key = api_key_from_req(request.metadata(), &self.api_keys).await?;
|
||||
|
||||
authenticate(
|
||||
request.metadata(),
|
||||
&graph,
|
||||
&self.api_keys,
|
||||
&dst_namespace,
|
||||
let req_src = request
|
||||
.get_ref()
|
||||
.src
|
||||
.as_ref()
|
||||
.ok_or(Status::invalid_argument("src must be set"))?;
|
||||
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;
|
||||
|
||||
if req_rel.is_empty() {
|
||||
return Err(Status::invalid_argument("relation must be set"));
|
||||
}
|
||||
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"));
|
||||
}
|
||||
|
||||
if !graph.has(
|
||||
("themis_key", &*api_key),
|
||||
"write",
|
||||
)
|
||||
.await?;
|
||||
("themis_ns", &*req_dst.namespace),
|
||||
) {
|
||||
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"));
|
||||
}
|
||||
|
||||
graph.remove_relation(src, dst);
|
||||
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"));
|
||||
}
|
||||
|
||||
info!("removed relation relation");
|
||||
Ok((&*set.namespace, &*set.id, &*set.relation).into())
|
||||
}
|
||||
};
|
||||
let src = src?;
|
||||
|
||||
graph.remove(src, req_rel.as_str(), (&*req_dst.namespace, &*req_dst.id));
|
||||
|
||||
info!("deleted relation");
|
||||
|
||||
self.save_trigger.send(()).await.unwrap();
|
||||
|
||||
|
@ -159,18 +174,67 @@ impl RelationService for GraphService {
|
|||
async fn exists(&self, request: Request<Relation>) -> Result<Response<ExistsResponse>, Status> {
|
||||
let graph = self.graph.lock().await;
|
||||
|
||||
let (src, dst, dst_namespace) = transform_relation(request.get_ref(), &graph)?;
|
||||
let api_key = api_key_from_req(request.metadata(), &self.api_keys).await?;
|
||||
|
||||
authenticate(
|
||||
request.metadata(),
|
||||
&graph,
|
||||
&self.api_keys,
|
||||
&dst_namespace,
|
||||
let req_src = request
|
||||
.get_ref()
|
||||
.src
|
||||
.as_ref()
|
||||
.ok_or(Status::invalid_argument("src must be set"))?;
|
||||
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;
|
||||
|
||||
if req_rel.is_empty() {
|
||||
return Err(Status::invalid_argument("relation must be set"));
|
||||
}
|
||||
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"));
|
||||
}
|
||||
|
||||
if !graph.has(
|
||||
("themis_key", &*api_key),
|
||||
"read",
|
||||
)
|
||||
.await?;
|
||||
("themis_ns", &*req_dst.namespace),
|
||||
) {
|
||||
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"));
|
||||
}
|
||||
|
||||
let exists = graph.has_relation(src, dst);
|
||||
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())
|
||||
}
|
||||
};
|
||||
let src = src?;
|
||||
|
||||
let exists = graph.has(src, req_rel.as_str(), (&*req_dst.namespace, &*req_dst.id));
|
||||
|
||||
Ok(Response::new(ExistsResponse { exists }))
|
||||
}
|
||||
|
@ -184,11 +248,73 @@ impl QueryService for GraphService {
|
|||
) -> Result<Response<IsRelatedToResponse>, Status> {
|
||||
let graph = self.graph.lock().await;
|
||||
|
||||
let related = if let Ok((src, dst, _)) = transform_relation(request.get_ref(), &graph) {
|
||||
graph.is_related_to(src, dst)
|
||||
} else {
|
||||
false
|
||||
let api_key = api_key_from_req(request.metadata(), &self.api_keys).await?;
|
||||
|
||||
let req_src = request
|
||||
.get_ref()
|
||||
.src
|
||||
.as_ref()
|
||||
.ok_or(Status::invalid_argument("src must be set"))?;
|
||||
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;
|
||||
|
||||
if req_rel.is_empty() {
|
||||
return Err(Status::invalid_argument("relation must be set"));
|
||||
}
|
||||
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"));
|
||||
}
|
||||
|
||||
if !graph.has(
|
||||
("themis_key", &*api_key),
|
||||
"read",
|
||||
("themis_ns", &*req_dst.namespace),
|
||||
) {
|
||||
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() {
|
||||
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())
|
||||
}
|
||||
};
|
||||
let src = src?;
|
||||
|
||||
let related = graph.has_recursive(
|
||||
src,
|
||||
req_rel.as_str(),
|
||||
(&*req_dst.namespace, &*req_dst.id),
|
||||
u32::MAX,
|
||||
);
|
||||
|
||||
Ok(Response::new(IsRelatedToResponse { related }))
|
||||
}
|
||||
|
@ -196,127 +322,91 @@ impl QueryService for GraphService {
|
|||
&self,
|
||||
request: Request<Set>,
|
||||
) -> Result<Response<GetRelatedToResponse>, Status> {
|
||||
let graph = self.graph.lock().await;
|
||||
//let graph = self.graph.lock().await;
|
||||
|
||||
authenticate(
|
||||
request.metadata(),
|
||||
&graph,
|
||||
&self.api_keys,
|
||||
&request.get_ref().namespace,
|
||||
"read",
|
||||
)
|
||||
.await?;
|
||||
//authenticate(
|
||||
// request.metadata(),
|
||||
// &graph,
|
||||
// &self.api_keys,
|
||||
// &request.get_ref().namespace,
|
||||
// "read",
|
||||
//)
|
||||
//.await?;
|
||||
|
||||
let obj = graph
|
||||
.get_node(&request.get_ref().namespace, &request.get_ref().id)
|
||||
.ok_or(Status::not_found("object not found"))?;
|
||||
//let obj = graph
|
||||
// .get_node(&request.get_ref().namespace, &request.get_ref().id)
|
||||
// .ok_or(Status::not_found("object not found"))?;
|
||||
|
||||
let rel = graph::Relation::new(&request.get_ref().relation);
|
||||
//let rel = graph::Relation::new(&request.get_ref().relation);
|
||||
|
||||
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<_>>(),
|
||||
}))
|
||||
//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!()
|
||||
}
|
||||
async fn get_relations(
|
||||
&self,
|
||||
request: Request<GetRelationsRequest>,
|
||||
) -> Result<Response<GetRelationsResponse>, Status> {
|
||||
let graph = self.graph.lock().await;
|
||||
//let graph = self.graph.lock().await;
|
||||
|
||||
if request.get_ref().relation.is_empty() {
|
||||
return Err(Status::invalid_argument("relation must be set"));
|
||||
}
|
||||
//if request.get_ref().relation.is_empty() {
|
||||
// return Err(Status::invalid_argument("relation must be set"));
|
||||
//}
|
||||
|
||||
let obj = request
|
||||
.get_ref()
|
||||
.object
|
||||
.as_ref()
|
||||
.ok_or(Status::invalid_argument("object must be set"))?;
|
||||
//let obj = request
|
||||
// .get_ref()
|
||||
// .object
|
||||
// .as_ref()
|
||||
// .ok_or(Status::invalid_argument("object must be set"))?;
|
||||
|
||||
authenticate(
|
||||
request.metadata(),
|
||||
&graph,
|
||||
&self.api_keys,
|
||||
&obj.namespace,
|
||||
"read",
|
||||
)
|
||||
.await?;
|
||||
//authenticate(
|
||||
// request.metadata(),
|
||||
// &graph,
|
||||
// &self.api_keys,
|
||||
// &obj.namespace,
|
||||
// "read",
|
||||
//)
|
||||
//.await?;
|
||||
|
||||
let obj = graph
|
||||
.get_node(&obj.namespace, &obj.id)
|
||||
.ok_or(Status::not_found("object not found"))?;
|
||||
//let obj = graph
|
||||
// .get_node(&obj.namespace, &obj.id)
|
||||
// .ok_or(Status::not_found("object not found"))?;
|
||||
|
||||
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<_>>(),
|
||||
}))
|
||||
//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!()
|
||||
}
|
||||
}
|
||||
|
||||
fn transform_relation(
|
||||
rel: &Relation,
|
||||
graph: &Graph,
|
||||
) -> Result<(graph::ObjectOrSet, graph::ObjectRelation, String), Status> {
|
||||
let src = match rel
|
||||
.src
|
||||
.as_ref()
|
||||
.ok_or(Status::invalid_argument("src must be set"))?
|
||||
{
|
||||
Src::SrcObj(object) => graph::ObjectOrSet::Object(
|
||||
graph
|
||||
.get_node(&object.namespace, &object.id)
|
||||
.ok_or(Status::not_found("src object could not be found"))?,
|
||||
),
|
||||
Src::SrcSet(set) => graph::ObjectOrSet::Set((
|
||||
graph
|
||||
.get_node(&set.namespace, &set.id)
|
||||
.ok_or(Status::not_found("src object could not be found"))?,
|
||||
graph::Relation::new(&set.relation),
|
||||
)),
|
||||
};
|
||||
|
||||
let dst = rel
|
||||
.dst
|
||||
.as_ref()
|
||||
.ok_or(Status::invalid_argument("dst must be set"))?;
|
||||
let dst_namespace = dst.namespace.to_string();
|
||||
let dst = graph
|
||||
.get_node(&dst.namespace, &dst.id)
|
||||
.ok_or(Status::not_found("dst object could not be found"))?;
|
||||
let dst = ObjectRelation(dst, graph::Relation::new(&rel.relation));
|
||||
|
||||
Ok((src, dst, dst_namespace))
|
||||
}
|
||||
|
||||
async fn authenticate(
|
||||
async fn api_key_from_req(
|
||||
metadata: &MetadataMap,
|
||||
graph: &Graph,
|
||||
api_keys: &Arc<Mutex<HashMap<String, String>>>,
|
||||
namespace: &str,
|
||||
relation: &str,
|
||||
) -> Result<(), Status> {
|
||||
) -> Result<String, Status> {
|
||||
let api_key = metadata
|
||||
.get("x-api-key")
|
||||
.map(|x| x.to_str().unwrap())
|
||||
|
@ -329,18 +419,5 @@ async fn authenticate(
|
|||
let api_key = api_keys
|
||||
.get(&api_key)
|
||||
.ok_or(Status::unauthenticated("api-key invalid"))?;
|
||||
|
||||
let api_key = graph
|
||||
.get_node("themis_key", api_key)
|
||||
.ok_or(Status::unauthenticated("api-key invalid"))?;
|
||||
|
||||
let ns_ref = graph
|
||||
.get_node("themis_ns", namespace)
|
||||
.ok_or(Status::permission_denied("no permission for namespace"))?;
|
||||
|
||||
if !graph.is_related_to(api_key, (ns_ref, graph::Relation::new(relation))) {
|
||||
Err(Status::permission_denied("no permission for namespace"))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
Ok(api_key.to_string())
|
||||
}
|
||||
|
|
17
src/main.rs
17
src/main.rs
|
@ -1,7 +1,10 @@
|
|||
#![feature(btree_cursors)]
|
||||
|
||||
use std::{collections::HashMap, sync::Arc, time::Duration};
|
||||
|
||||
use graph::Graph;
|
||||
use grpc_service::GraphService;
|
||||
use relation_set::RelationSet;
|
||||
//use grpc_service::GraphService;
|
||||
use tokio::{
|
||||
fs::{self, File},
|
||||
io::{AsyncBufReadExt, BufReader},
|
||||
|
@ -10,13 +13,12 @@ use tokio::{
|
|||
};
|
||||
use tonic::transport::Server;
|
||||
|
||||
pub mod graph;
|
||||
pub mod grpc_service;
|
||||
pub mod relation_set;
|
||||
pub mod themis_proto;
|
||||
|
||||
use crate::themis_proto::{
|
||||
object_service_server::ObjectServiceServer, query_service_server::QueryServiceServer,
|
||||
relation_service_server::RelationServiceServer,
|
||||
query_service_server::QueryServiceServer, relation_service_server::RelationServiceServer,
|
||||
};
|
||||
|
||||
#[tokio::main]
|
||||
|
@ -38,9 +40,9 @@ async fn main() {
|
|||
}
|
||||
|
||||
let graph = if let Ok(mut file) = File::open("graph.dat").await {
|
||||
Graph::from_file(&mut file).await
|
||||
RelationSet::from_file(&mut file).await
|
||||
} else {
|
||||
Graph::default()
|
||||
RelationSet::new()
|
||||
};
|
||||
|
||||
let graph = Arc::new(Mutex::new(graph));
|
||||
|
@ -68,10 +70,9 @@ async fn main() {
|
|||
};
|
||||
|
||||
Server::builder()
|
||||
.add_service(ObjectServiceServer::new(graph_service.clone()))
|
||||
.add_service(RelationServiceServer::new(graph_service.clone()))
|
||||
.add_service(QueryServiceServer::new(graph_service))
|
||||
.serve("0.0.0.0:50051".parse().unwrap())
|
||||
.serve("[::]:50051".parse().unwrap())
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
|
449
src/relation_set.rs
Normal file
449
src/relation_set.rs
Normal file
|
@ -0,0 +1,449 @@
|
|||
use std::{
|
||||
cmp::Ordering,
|
||||
collections::{BTreeMap, BinaryHeap, HashMap, HashSet},
|
||||
ops::{Bound, Deref},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use compact_str::CompactString;
|
||||
use tokio::{
|
||||
fs::File,
|
||||
io::{AsyncBufReadExt, AsyncWriteExt, BufReader},
|
||||
};
|
||||
|
||||
#[derive(Hash, PartialEq, Eq, Clone, Debug)]
|
||||
pub struct Object {
|
||||
pub namespace: CompactString,
|
||||
pub id: CompactString,
|
||||
}
|
||||
|
||||
#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug)]
|
||||
pub struct ObjectRef<'a> {
|
||||
pub namespace: &'a str,
|
||||
pub id: &'a str,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
|
||||
pub enum ObjectOrSet {
|
||||
Object(Object),
|
||||
Set((Object, Relation)),
|
||||
}
|
||||
#[derive(Hash, PartialEq, Eq, Clone, Debug)]
|
||||
pub struct Relation(pub CompactString);
|
||||
|
||||
type S = ObjectOrSet;
|
||||
type R = Relation;
|
||||
type D = Object;
|
||||
|
||||
pub struct RelationSet {
|
||||
src_to_dst: BTreeMap<Arc<S>, HashMap<Arc<R>, HashSet<Arc<D>>>>,
|
||||
dst_to_src: BTreeMap<Arc<D>, HashMap<Arc<R>, HashSet<Arc<S>>>>,
|
||||
}
|
||||
|
||||
impl RelationSet {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
src_to_dst: BTreeMap::new(),
|
||||
dst_to_src: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, src: impl Into<S>, rel: impl Into<R>, dst: impl Into<D>) {
|
||||
let src = Arc::new(src.into());
|
||||
let rel = Arc::new(rel.into());
|
||||
let dst = Arc::new(dst.into());
|
||||
|
||||
if let Some(rels_dsts) = self.src_to_dst.get_mut(&src) {
|
||||
if let Some(dsts) = rels_dsts.get_mut(&rel) {
|
||||
dsts.insert(dst.clone());
|
||||
} else {
|
||||
let mut dsts = HashSet::new();
|
||||
dsts.insert(dst.clone());
|
||||
rels_dsts.insert(rel.clone(), dsts);
|
||||
}
|
||||
} else {
|
||||
let mut rels_dsts = HashMap::new();
|
||||
let mut dsts = HashSet::new();
|
||||
dsts.insert(dst.clone());
|
||||
rels_dsts.insert(rel.clone(), dsts);
|
||||
self.src_to_dst.insert(src.clone(), rels_dsts);
|
||||
}
|
||||
|
||||
if let Some(rels_srcs) = self.dst_to_src.get_mut(&dst) {
|
||||
if let Some(srcs) = rels_srcs.get_mut(&rel) {
|
||||
srcs.insert(src.clone());
|
||||
} else {
|
||||
let mut srcs = HashSet::new();
|
||||
srcs.insert(src.clone());
|
||||
rels_srcs.insert(rel.clone(), srcs);
|
||||
}
|
||||
} else {
|
||||
let mut rels_srcs = HashMap::new();
|
||||
let mut srcs = HashSet::new();
|
||||
srcs.insert(src.clone());
|
||||
rels_srcs.insert(rel.clone(), srcs);
|
||||
self.dst_to_src.insert(dst.clone(), rels_srcs);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, src: impl Into<S>, rel: impl Into<R>, dst: impl Into<D>) {
|
||||
let src = src.into();
|
||||
let rel = rel.into();
|
||||
let dst = dst.into();
|
||||
|
||||
if let Some(dsts) = self
|
||||
.src_to_dst
|
||||
.get_mut(&src)
|
||||
.and_then(|rels_dsts| rels_dsts.get_mut(&rel))
|
||||
{
|
||||
dsts.remove(&dst);
|
||||
}
|
||||
|
||||
if let Some(srcs) = self
|
||||
.dst_to_src
|
||||
.get_mut(&dst)
|
||||
.and_then(|rels_srcs| rels_srcs.get_mut(&rel))
|
||||
{
|
||||
srcs.remove(&src);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_by_src(&mut self, src: &S) {
|
||||
for (rel, dsts) in self.src_to_dst.remove(src).iter().flat_map(|x| x.iter()) {
|
||||
for dst in dsts {
|
||||
if let Some(srcs) = self
|
||||
.dst_to_src
|
||||
.get_mut(dst)
|
||||
.and_then(|rels_srcs| rels_srcs.get_mut(rel))
|
||||
{
|
||||
srcs.remove(src);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_by_dst(&mut self, dst: &D) {
|
||||
for (rel, srcs) in self.dst_to_src.remove(dst).iter().flat_map(|x| x.iter()) {
|
||||
for src in srcs {
|
||||
if let Some(dsts) = self
|
||||
.src_to_dst
|
||||
.get_mut(src)
|
||||
.and_then(|rels_dsts| rels_dsts.get_mut(rel))
|
||||
{
|
||||
dsts.remove(dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has(&self, src: impl Into<S>, rel: impl Into<R>, dst: impl Into<D>) -> bool {
|
||||
let src = src.into();
|
||||
let rel = rel.into();
|
||||
let dst = dst.into();
|
||||
|
||||
self.src_to_dst
|
||||
.get(&src)
|
||||
.and_then(|rels_dsts| rels_dsts.get(&rel))
|
||||
.and_then(|dsts| dsts.get(&dst))
|
||||
.is_some()
|
||||
}
|
||||
|
||||
pub fn has_object<'a>(&self, obj: impl Into<&'a Object>) -> bool {
|
||||
let obj = obj.into();
|
||||
let has_dst_obj = self.dst_to_src.contains_key(obj);
|
||||
|
||||
let cursor = self
|
||||
.src_to_dst
|
||||
.lower_bound(Bound::Included(&ObjectOrSet::Object(obj.clone())));
|
||||
|
||||
let has_src_obj = if let Some(key) = cursor.key() {
|
||||
obj.namespace == key.object().namespace && obj.id == key.object().id
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
has_dst_obj || has_src_obj
|
||||
}
|
||||
|
||||
pub fn has_recursive(
|
||||
&self,
|
||||
src: impl Into<S>,
|
||||
rel: impl Into<R>,
|
||||
dst: impl Into<D>,
|
||||
limit: u32,
|
||||
) -> bool {
|
||||
let src = src.into();
|
||||
let rel = rel.into();
|
||||
let dst = dst.into();
|
||||
|
||||
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
|
||||
.src_to_dst
|
||||
.get(&src)
|
||||
.iter()
|
||||
.flat_map(|x| x.iter())
|
||||
.flat_map(|(r, d)| d.iter().map(|d| (r.clone(), d.clone())))
|
||||
{
|
||||
if *nrel == rel && *ndst == dst {
|
||||
return true;
|
||||
}
|
||||
dist.insert((ndst.clone(), nrel.clone()), 1);
|
||||
q.push(Distanced::one((ndst, nrel)));
|
||||
}
|
||||
|
||||
while let Some(distanced) = q.pop() {
|
||||
let node_dist = distanced.distance() + 1;
|
||||
let node = ObjectOrSet::Set(((*distanced.0).clone(), (*distanced.1).clone()));
|
||||
for (nrel, ndst) in self
|
||||
.src_to_dst
|
||||
.get(&node)
|
||||
.iter()
|
||||
.flat_map(|x| x.iter())
|
||||
.flat_map(|(r, d)| d.iter().map(|d| (r.clone(), d.clone())))
|
||||
{
|
||||
if *nrel == rel && *ndst == dst {
|
||||
return true;
|
||||
}
|
||||
if let Some(existing_node_dist) = dist.get(&*distanced) {
|
||||
if *existing_node_dist <= node_dist || node_dist >= limit {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
dist.insert((ndst.clone(), nrel.clone()), node_dist);
|
||||
q.push(Distanced::one((ndst, nrel)));
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
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())
|
||||
.await
|
||||
.unwrap();
|
||||
for (rel, srcs) in rels_srcs.iter() {
|
||||
let srcs = srcs
|
||||
.iter()
|
||||
.map(|src| {
|
||||
let src_obj = src.object();
|
||||
let src_str = if src_obj.namespace == dst.namespace && src_obj.id == dst.id
|
||||
{
|
||||
"self".to_string()
|
||||
} else {
|
||||
format!("{}:{}", src_obj.namespace, src_obj.id)
|
||||
};
|
||||
match &**src {
|
||||
ObjectOrSet::Object(_) => src_str,
|
||||
ObjectOrSet::Set(set) => {
|
||||
format!("{}#{}", src_str, set.1 .0)
|
||||
}
|
||||
}
|
||||
})
|
||||
.reduce(|acc, x| acc + ", " + &x)
|
||||
.unwrap_or_default();
|
||||
|
||||
file.write_all(format!("{} = [{}]\n", &rel.0, &srcs).as_bytes())
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
file.write_all("\n".as_bytes()).await.unwrap();
|
||||
}
|
||||
}
|
||||
pub async fn from_file(file: &mut File) -> Self {
|
||||
let reader = BufReader::new(file);
|
||||
let mut lines = reader.lines();
|
||||
let mut graph = Self::new();
|
||||
let mut node: Option<(String, String)> = None;
|
||||
while let Ok(Some(line)) = lines.next_line().await {
|
||||
if line.starts_with('[') && line.ends_with(']') {
|
||||
let line = &mut line[1..line.len() - 1].split(':');
|
||||
let namespace = line.next().unwrap();
|
||||
let id = line.next().unwrap();
|
||||
node = Some((namespace.to_string(), id.to_string()));
|
||||
} else if line.contains('=') && line.contains('[') && line.contains(']') {
|
||||
if let Some(dst) = &node {
|
||||
let equals_pos = line.find('=').unwrap();
|
||||
let arr_start = line.find('[').unwrap();
|
||||
let arr_stop = line.find(']').unwrap();
|
||||
|
||||
let rel = line[..equals_pos].trim();
|
||||
let arr = line[arr_start + 1..arr_stop].split(", ");
|
||||
|
||||
for obj in arr {
|
||||
let src: ObjectOrSet = if obj.contains('#') {
|
||||
let sep_1 = obj.find(':');
|
||||
let sep_2 = obj.find('#').unwrap();
|
||||
|
||||
let (namespace, id) = if let Some(sep_1) = sep_1 {
|
||||
(&obj[..sep_1], &obj[sep_1 + 1..sep_2])
|
||||
} else {
|
||||
(dst.0.as_str(), dst.1.as_str())
|
||||
};
|
||||
|
||||
let rel = &obj[sep_2 + 1..];
|
||||
|
||||
(namespace, id, rel).into()
|
||||
} else {
|
||||
let sep_1 = obj.find(':');
|
||||
|
||||
let (namespace, id) = if let Some(sep_1) = sep_1 {
|
||||
(&obj[..sep_1], &obj[sep_1 + 1..])
|
||||
} else {
|
||||
(dst.0.as_str(), dst.1.as_str())
|
||||
};
|
||||
(namespace, id).into()
|
||||
};
|
||||
|
||||
graph.insert(src, rel, dst.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
graph
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
struct Distanced<T> {
|
||||
distance: u32,
|
||||
data: T,
|
||||
}
|
||||
|
||||
impl<T> Distanced<T> {
|
||||
pub fn one(data: T) -> Self {
|
||||
Self { distance: 1, data }
|
||||
}
|
||||
pub fn distance(&self) -> u32 {
|
||||
self.distance
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for Distanced<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialEq> PartialOrd for Distanced<T> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
self.distance.partial_cmp(&other.distance)
|
||||
}
|
||||
}
|
||||
impl<T: Eq> Ord for Distanced<T> {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.distance.cmp(&other.distance)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Relation {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
self.0.partial_cmp(&other.0)
|
||||
}
|
||||
}
|
||||
impl Ord for Relation {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.0.cmp(&other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for ObjectOrSet {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
match (
|
||||
self.object().partial_cmp(other.object()),
|
||||
self.relation(),
|
||||
other.relation(),
|
||||
) {
|
||||
(Some(Ordering::Equal), self_rel, other_rel) => self_rel.partial_cmp(&other_rel),
|
||||
(ord, _, _) => ord,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Ord for ObjectOrSet {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.object()
|
||||
.cmp(other.object())
|
||||
.then(self.relation().cmp(&other.relation()))
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Object {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
match self.namespace.partial_cmp(&other.namespace) {
|
||||
Some(core::cmp::Ordering::Equal) => self.id.partial_cmp(&other.id),
|
||||
ord => ord,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Ord for Object {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.namespace
|
||||
.cmp(&other.namespace)
|
||||
.then(self.id.cmp(&other.id))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(&str, &str)> for ObjectOrSet {
|
||||
fn from((namespace, id): (&str, &str)) -> Self {
|
||||
ObjectOrSet::Object(Object {
|
||||
namespace: namespace.into(),
|
||||
id: id.into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
impl From<(&str, &str, &str)> for ObjectOrSet {
|
||||
fn from((namespace, id, rel): (&str, &str, &str)) -> Self {
|
||||
ObjectOrSet::Set(((namespace, id).into(), Relation(rel.into())))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(&str, &str)> for Object {
|
||||
fn from((namespace, id): (&str, &str)) -> Self {
|
||||
Self {
|
||||
namespace: namespace.into(),
|
||||
id: id.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<(String, String)> for Object {
|
||||
fn from((namespace, id): (String, String)) -> Self {
|
||||
Self {
|
||||
namespace: namespace.into(),
|
||||
id: id.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Relation {
|
||||
fn from(value: &str) -> Self {
|
||||
Relation(value.into())
|
||||
}
|
||||
}
|
||||
impl From<String> for Relation {
|
||||
fn from(value: String) -> Self {
|
||||
Relation(value.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectOrSet {
|
||||
pub fn object(&self) -> &Object {
|
||||
match self {
|
||||
ObjectOrSet::Object(obj) => obj,
|
||||
ObjectOrSet::Set((obj, _)) => obj,
|
||||
}
|
||||
}
|
||||
pub fn relation(&self) -> Option<&Relation> {
|
||||
match self {
|
||||
ObjectOrSet::Object(_) => None,
|
||||
ObjectOrSet::Set((_, rel)) => Some(rel),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Relation {
|
||||
pub fn new(relation: &str) -> Self {
|
||||
Self(relation.into())
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue