mirror of
https://github.com/pfzetto/rebacs
synced 2024-12-03 15:57:14 +01:00
added authentication
This commit is contained in:
parent
f368a85317
commit
dbd581cece
6 changed files with 251 additions and 26 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -2,3 +2,4 @@
|
|||
.env
|
||||
graph.dat
|
||||
graph.dat.bak
|
||||
api_keys.dat
|
||||
|
|
79
Cargo.lock
generated
79
Cargo.lock
generated
|
@ -124,6 +124,15 @@ version = "1.3.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.12.1"
|
||||
|
@ -148,6 +157,35 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"crypto-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dotenvy"
|
||||
version = "0.15.7"
|
||||
|
@ -254,6 +292,16 @@ dependencies = [
|
|||
"pin-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.9"
|
||||
|
@ -320,6 +368,12 @@ version = "0.3.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "0.2.9"
|
||||
|
@ -884,6 +938,17 @@ dependencies = [
|
|||
"syn 2.0.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.1"
|
||||
|
@ -979,10 +1044,12 @@ name = "themis"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"dotenvy",
|
||||
"hex",
|
||||
"log",
|
||||
"pretty_env_logger",
|
||||
"prost",
|
||||
"serde",
|
||||
"sha2",
|
||||
"tokio",
|
||||
"tonic",
|
||||
"tonic-build",
|
||||
|
@ -1177,6 +1244,12 @@ version = "0.2.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.8"
|
||||
|
@ -1189,6 +1262,12 @@ version = "0.7.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "want"
|
||||
version = "0.3.0"
|
||||
|
|
|
@ -13,6 +13,8 @@ pretty_env_logger = "0.4.0"
|
|||
dotenvy = "0.15.7"
|
||||
tonic = { version="0.9.2", features=["tls"] }
|
||||
prost = "0.11.9"
|
||||
sha2 = "0.10.6"
|
||||
hex = "0.4.3"
|
||||
|
||||
[build-dependencies]
|
||||
tonic-build = "0.9.2"
|
||||
|
|
52
src/graph.rs
52
src/graph.rs
|
@ -251,13 +251,20 @@ impl Graph {
|
|||
let arr = arr
|
||||
.iter()
|
||||
.filter_map(|x| {
|
||||
let obj_ref = x.object_ref();
|
||||
self.nodes.get_by_b(obj_ref).map(|obj| {
|
||||
let (namespace, id) = (&obj.namespace, &obj.id);
|
||||
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);
|
||||
|
||||
match x.relation() {
|
||||
None => format!("{}:{}", &namespace, &id),
|
||||
Some(rel) => format!("{}:{}#{}", &namespace, &id, &rel.0),
|
||||
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),
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
@ -277,16 +284,17 @@ impl Graph {
|
|||
let mut lines = reader.lines();
|
||||
let mut graph = Graph::default();
|
||||
|
||||
let mut node: Option<ObjectRef> = None;
|
||||
let mut node: Option<(ObjectRef, String, String)> = None;
|
||||
let mut relations = vec![];
|
||||
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 obj_ref =
|
||||
graph.add_node(Object::new(line.next().unwrap(), line.next().unwrap()));
|
||||
node = Some(obj_ref);
|
||||
let namespace = line.next().unwrap();
|
||||
let id = line.next().unwrap();
|
||||
let obj_ref = graph.add_node(Object::new(namespace, id));
|
||||
node = Some((obj_ref, namespace.to_string(), id.to_string()));
|
||||
} else if line.contains('=') && line.contains('[') && line.contains(']') {
|
||||
if let Some(dst) = node {
|
||||
if let Some(dst) = &node {
|
||||
let equals_pos = line.find('=').unwrap();
|
||||
let arr_start = line.find('[').unwrap();
|
||||
let arr_stop = line.find(']').unwrap();
|
||||
|
@ -296,20 +304,26 @@ impl Graph {
|
|||
|
||||
for obj in arr {
|
||||
let (src_namespace, src_id, src_rel) = if obj.contains('#') {
|
||||
let sep_1 = obj.find(':').unwrap();
|
||||
let sep_1 = obj.find(':');
|
||||
let sep_2 = obj.find('#').unwrap();
|
||||
|
||||
let namespace = &obj[..sep_1];
|
||||
let id = &obj[sep_1 + 1..sep_2];
|
||||
let (namespace, id) = if let Some(sep_1) = sep_1 {
|
||||
(&obj[..sep_1], &obj[sep_1 + 1..sep_2])
|
||||
} else {
|
||||
(dst.1.as_str(), dst.2.as_str())
|
||||
};
|
||||
|
||||
let rel = &obj[sep_2 + 1..];
|
||||
|
||||
(namespace, id, Some(rel))
|
||||
} else {
|
||||
let sep_1 = obj.find(':').unwrap();
|
||||
|
||||
let namespace = &obj[..sep_1];
|
||||
let id = &obj[sep_1 + 1..];
|
||||
let sep_1 = obj.find(':');
|
||||
|
||||
let (namespace, id) = if let Some(sep_1) = sep_1 {
|
||||
(&obj[..sep_1], &obj[sep_1 + 1..])
|
||||
} else {
|
||||
(dst.1.as_str(), dst.2.as_str())
|
||||
};
|
||||
(namespace, id, None)
|
||||
};
|
||||
|
||||
|
@ -317,7 +331,7 @@ impl Graph {
|
|||
src_namespace.to_string(),
|
||||
src_id.to_string(),
|
||||
src_rel.map(String::from),
|
||||
dst,
|
||||
dst.0,
|
||||
rel.to_string(),
|
||||
));
|
||||
}
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use log::info;
|
||||
use sha2::{Digest, Sha256};
|
||||
use tokio::sync::mpsc::Sender;
|
||||
use tokio::sync::Mutex;
|
||||
use tonic::metadata::MetadataMap;
|
||||
use tonic::{Request, Response, Status};
|
||||
|
||||
use crate::graph::{self, Graph, ObjectRelation};
|
||||
|
@ -14,6 +17,7 @@ use crate::themis_proto::{
|
|||
|
||||
#[derive(Clone)]
|
||||
pub struct GraphService {
|
||||
pub api_keys: Arc<Mutex<HashMap<String, String>>>,
|
||||
pub graph: Arc<Mutex<Graph>>,
|
||||
pub save_trigger: Sender<()>,
|
||||
}
|
||||
|
@ -23,6 +27,15 @@ 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"));
|
||||
}
|
||||
|
@ -45,6 +58,15 @@ impl ObjectService for GraphService {
|
|||
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"));
|
||||
}
|
||||
|
@ -67,6 +89,15 @@ impl ObjectService for GraphService {
|
|||
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"));
|
||||
}
|
||||
|
@ -84,7 +115,16 @@ 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) = transform_relation(request.get_ref(), &graph)?;
|
||||
let (src, dst, dst_namespace) = transform_relation(request.get_ref(), &graph)?;
|
||||
|
||||
authenticate(
|
||||
request.metadata(),
|
||||
&graph,
|
||||
&self.api_keys,
|
||||
&dst_namespace,
|
||||
"write",
|
||||
)
|
||||
.await?;
|
||||
|
||||
graph.add_relation(src, dst);
|
||||
|
||||
|
@ -97,7 +137,16 @@ 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) = transform_relation(request.get_ref(), &graph)?;
|
||||
let (src, dst, dst_namespace) = transform_relation(request.get_ref(), &graph)?;
|
||||
|
||||
authenticate(
|
||||
request.metadata(),
|
||||
&graph,
|
||||
&self.api_keys,
|
||||
&dst_namespace,
|
||||
"write",
|
||||
)
|
||||
.await?;
|
||||
|
||||
graph.remove_relation(src, dst);
|
||||
|
||||
|
@ -110,7 +159,16 @@ impl RelationService for GraphService {
|
|||
async fn exists(&self, request: Request<Relation>) -> Result<Response<ExistsResponse>, Status> {
|
||||
let graph = self.graph.lock().await;
|
||||
|
||||
let (src, dst) = transform_relation(request.get_ref(), &graph)?;
|
||||
let (src, dst, dst_namespace) = transform_relation(request.get_ref(), &graph)?;
|
||||
|
||||
authenticate(
|
||||
request.metadata(),
|
||||
&graph,
|
||||
&self.api_keys,
|
||||
&dst_namespace,
|
||||
"read",
|
||||
)
|
||||
.await?;
|
||||
|
||||
let exists = graph.has_relation(src, dst);
|
||||
|
||||
|
@ -126,7 +184,7 @@ 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) {
|
||||
let related = if let Ok((src, dst, _)) = transform_relation(request.get_ref(), &graph) {
|
||||
graph.is_related_to(src, dst)
|
||||
} else {
|
||||
false
|
||||
|
@ -140,9 +198,19 @@ impl QueryService for GraphService {
|
|||
) -> Result<Response<GetRelatedToResponse>, Status> {
|
||||
let graph = self.graph.lock().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 rel = graph::Relation::new(&request.get_ref().relation);
|
||||
|
||||
Ok(Response::new(GetRelatedToResponse {
|
||||
|
@ -174,6 +242,16 @@ impl QueryService for GraphService {
|
|||
.object
|
||||
.as_ref()
|
||||
.ok_or(Status::invalid_argument("object must be set"))?;
|
||||
|
||||
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"))?;
|
||||
|
@ -200,7 +278,7 @@ impl QueryService for GraphService {
|
|||
fn transform_relation(
|
||||
rel: &Relation,
|
||||
graph: &Graph,
|
||||
) -> Result<(graph::ObjectOrSet, graph::ObjectRelation), Status> {
|
||||
) -> Result<(graph::ObjectOrSet, graph::ObjectRelation, String), Status> {
|
||||
let src = match rel
|
||||
.src
|
||||
.as_ref()
|
||||
|
@ -223,10 +301,46 @@ fn transform_relation(
|
|||
.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))
|
||||
Ok((src, dst, dst_namespace))
|
||||
}
|
||||
|
||||
async fn authenticate(
|
||||
metadata: &MetadataMap,
|
||||
graph: &Graph,
|
||||
api_keys: &Arc<Mutex<HashMap<String, String>>>,
|
||||
namespace: &str,
|
||||
relation: &str,
|
||||
) -> Result<(), Status> {
|
||||
let api_key = metadata
|
||||
.get("x-api-key")
|
||||
.map(|x| x.to_str().unwrap())
|
||||
.ok_or(Status::unauthenticated("x-api-key required"))?;
|
||||
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(api_key);
|
||||
let api_key = hex::encode(hasher.finalize());
|
||||
let api_keys = api_keys.lock().await;
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
|
17
src/main.rs
17
src/main.rs
|
@ -1,9 +1,10 @@
|
|||
use std::{sync::Arc, time::Duration};
|
||||
use std::{collections::HashMap, sync::Arc, time::Duration};
|
||||
|
||||
use graph::Graph;
|
||||
use grpc_service::GraphService;
|
||||
use tokio::{
|
||||
fs::{self, File},
|
||||
io::{AsyncBufReadExt, BufReader},
|
||||
select,
|
||||
sync::{mpsc::channel, Mutex},
|
||||
};
|
||||
|
@ -23,6 +24,19 @@ async fn main() {
|
|||
dotenvy::dotenv().ok();
|
||||
pretty_env_logger::init();
|
||||
|
||||
let mut api_keys = HashMap::new();
|
||||
if let Ok(file) = File::open("api_keys.dat").await {
|
||||
let reader = BufReader::new(file);
|
||||
let mut lines = reader.lines();
|
||||
while let Ok(Some(line)) = lines.next_line().await {
|
||||
let line = line.replace(' ', "");
|
||||
let mut line = line.split('=');
|
||||
let name = line.next().unwrap().to_string();
|
||||
let hash = line.next().unwrap().to_string();
|
||||
api_keys.insert(hash, name);
|
||||
}
|
||||
}
|
||||
|
||||
let graph = if let Ok(mut file) = File::open("graph.dat").await {
|
||||
Graph::from_file(&mut file).await
|
||||
} else {
|
||||
|
@ -48,6 +62,7 @@ async fn main() {
|
|||
});
|
||||
|
||||
let graph_service = GraphService {
|
||||
api_keys: Arc::new(Mutex::new(api_keys)),
|
||||
graph: graph.clone(),
|
||||
save_trigger: save_tx.clone(),
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue