Skip to content

Commit

Permalink
Provide backward compatibility for the dot-local hack
Browse files Browse the repository at this point in the history
Broker sets both 'Access' and 'AccessLevel' RpcMessage meta
attributes or pass them as is from a parent broker in order
to keep backward compatibility with older brokers that read
'dot-local' in the 'Access' string to expose their '.local'
dir.

The AccessLevel is treated as plain i32, so broker can evaluate
access also for values that are not defined in the enum.

(#14)
  • Loading branch information
j4r0u53k committed Mar 16, 2024
1 parent 50ab39a commit d09d68a
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 18 deletions.
25 changes: 17 additions & 8 deletions src/broker/broker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use log::{debug, error, info, log, warn};
use crate::broker::{BrokerToPeerMessage, Device, Mount, ParsedAccessRule, Peer, PeerKind, BrokerCommand, PendingRpcCall, SubscribePath, node};
use crate::broker::config::{AccessControl, Password};
use crate::rpcframe::RpcFrame;
use crate::rpcmessage::{CliId, RpcError, RpcErrorCode};
use crate::rpcmessage::{CliId, RpcError, RpcErrorCode, Tag};
use crate::{List, RpcMessage, RpcMessageMetaTags, RpcValue, rpcvalue, shvnode, util};
use crate::rpc::{Subscription, SubscriptionPattern};
use crate::shvnode::{DIR_APP, AppNode, find_longest_prefix, METH_DIR, METH_LS, process_local_dir_ls, ShvNode};
Expand Down Expand Up @@ -77,7 +77,7 @@ impl Broker {
let method = frame.method().unwrap_or_default().to_string();
let response_meta= RpcFrame::prepare_response_meta(&frame.meta)?;
// println!("response meta: {:?}", response_meta);
let grant = match self.grant_for_request(peer_id, &frame) {
let (grant_access_level, grant_access) = match self.grant_for_request(peer_id, &frame) {
Ok(grant) => { grant }
Err(err) => {
self.command_sender.send(BrokerCommand::SendResponse { peer_id, meta: response_meta, result: Err(err) }).await?;
Expand All @@ -95,14 +95,16 @@ impl Broker {
let mut frame = frame;
frame.push_caller_id(peer_id);
frame.set_shvpath(node_path);
frame.set_access_level(grant);
frame.set_tag(Tag::AccessLevel as i32, grant_access_level.map(RpcValue::from));
frame.set_tag(Tag::Access as i32, grant_access.map(RpcValue::from));
let device_peer_id = device.peer_id;
self.peers.get(&device_peer_id).ok_or("client ID must exist")?.sender.send(BrokerToPeerMessage::SendFrame(frame)).await?;
}
Mount::Node(node) => {
let mut frame = frame;
frame.set_shvpath(node_path);
frame.set_access_level(grant);
frame.set_tag(Tag::AccessLevel as i32, grant_access_level.map(RpcValue::from));
frame.set_tag(Tag::Access as i32, grant_access.map(RpcValue::from));
if let Some(method) = node.is_request_granted(&frame) {
let ctx = NodeRequestContext {
peer_id,
Expand Down Expand Up @@ -440,7 +442,7 @@ impl Broker {
None
}
}
fn grant_for_request(&self, client_id: CliId, frame: &RpcFrame) -> Result<AccessLevel, RpcError> {
fn grant_for_request(&self, client_id: CliId, frame: &RpcFrame) -> Result<(Option<i32>, Option<String>), RpcError> {
log!(target: "Acc", Level::Debug, "======================= grant_for_request {}", &frame);
let shv_path = frame.shv_path().unwrap_or_default();
let method = frame.method().unwrap_or("");
Expand All @@ -459,16 +461,23 @@ impl Broker {
log!(target: "Acc", Level::Debug, "\trule: {}", rule.path_method);
if rule.path_method.match_shv_method(shv_path, method) {
log!(target: "Acc", Level::Debug, "\t\t HIT");
return Ok(rule.grant);
return Ok((Some(rule.grant_lvl as i32), Some(rule.grant_str.clone())));
}
}
}
}
}
Err(RpcError::new(RpcErrorCode::PermissionDenied, format!("Access denied for user: {}", peer.user)))
}
PeerKind::ParentBroker =>
frame.access_level().ok_or_else(|| RpcError::new(RpcErrorCode::PermissionDenied, ""))
PeerKind::ParentBroker => {
let access = frame.tag(Tag::Access as i32).map(RpcValue::as_str).map(String::from);
let access_level = frame.tag(Tag::AccessLevel as i32).map(RpcValue::as_i32);
if access_level.is_some() || access.is_some() {
Ok((access_level, access))
} else {
Err(RpcError::new(RpcErrorCode::PermissionDenied, ""))
}
}
}
}
}
Expand Down
8 changes: 6 additions & 2 deletions src/broker/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,10 @@ enum Mount {

struct ParsedAccessRule {
path_method: SubscriptionPattern,
grant: AccessLevel,
// Needed in order to pass 'dot-local' in 'Access' meta-attribute
// to support the dot-local hack on older brokers
grant_str: String,
grant_lvl: AccessLevel,
}

impl ParsedAccessRule {
Expand All @@ -163,7 +166,8 @@ impl ParsedAccessRule {
Ok(path) => {
Ok(Self {
path_method: SubscriptionPattern { paths: path, methods: method },
grant: grant
grant_str: grant.to_string(),
grant_lvl: grant
.split(',')
.find_map(AccessLevel::from_str)
.unwrap_or_else(|| panic!("Invalid access grant `{grant}`")),
Expand Down
11 changes: 9 additions & 2 deletions src/broker/peer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ use log::{debug, error, info};
use rand::distributions::{Alphanumeric, DistString};
use url::Url;
use crate::metamethod::AccessLevel;
use crate::rpcmessage::Tag;
use crate::{client, RpcMessage, RpcMessageMetaTags, RpcValue};
use crate::client::LoginParams;
use crate::rpcframe::RpcFrame;
use crate::shvnode::{DOT_LOCAL_DIR, DOT_LOCAL_HACK, METH_PING};
use crate::shvnode::{DOT_LOCAL_DIR, DOT_LOCAL_HACK, METH_PING, DOT_LOCAL_GRANT};
use crate::util::{join_path, login_from_url, sha1_hash};
use crate::broker::{BrokerCommand, BrokerToPeerMessage, PeerKind};
use crate::broker::config::ParentBrokerConfig;
Expand Down Expand Up @@ -264,7 +265,13 @@ async fn parent_broker_peer_loop(client_id: i32, config: ParentBrokerConfig, bro
Ok(mut frame) => {
if frame.is_request() {
fn is_dot_local_granted(frame: &RpcFrame) -> bool {
frame.access_level().is_some_and(|access| access >= AccessLevel::Superuser)
frame.access_level()
.is_some_and(|access| access == AccessLevel::Superuser as i32)
||
frame.tag(Tag::Access as i32)
.map(RpcValue::as_str)
.is_some_and(|s| s.split(',')
.any(|access| access == DOT_LOCAL_GRANT))
}
fn is_dot_local_request(frame: &RpcFrame) -> bool {
let shv_path = frame.shv_path().unwrap_or_default();
Expand Down
11 changes: 6 additions & 5 deletions src/rpcmessage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,13 +219,14 @@ pub trait RpcMessageMetaTags {
fn set_method(&mut self, method: &str) -> &mut Self::Target {
self.set_tag(Tag::Method as i32, Some(RpcValue::from(method)))
}
fn access_level(&self) -> Option<AccessLevel> {
fn access_level(&self) -> Option<i32> {
self.tag(Tag::AccessLevel as i32)
.map(RpcValue::as_i32)
.and_then(|v| v.try_into().ok())
.or_else(|| self.tag(Tag::Access as i32)
.map(RpcValue::as_str)
.and_then(|s| s.split(',').find_map(AccessLevel::from_str)))
.and_then(|s| s.split(',')
.find_map(AccessLevel::from_str)
.map(|v| v as i32)))
}
fn set_access_level(&mut self, grant: AccessLevel) -> &mut Self::Target {
self.set_tag(Tag::Access as i32, Some(RpcValue::from(grant.as_str())));
Expand Down Expand Up @@ -480,13 +481,13 @@ mod test {
fn rpc_msg_access_level_some() {
let mut rq = RpcMessage::new_request("foo/bar", "baz", None);
rq.set_access_level(AccessLevel::Read);
assert_eq!(rq.access_level(), Some(AccessLevel::Read));
assert_eq!(rq.access_level(), Some(AccessLevel::Read as i32));
}

#[test]
fn rpc_msg_access_level_compat() {
let mut rq = RpcMessage::new_request("foo/bar", "baz", None);
rq.set_tag(Tag::Access as i32, Some(RpcValue::from("srv")));
assert_eq!(rq.access_level(), Some(AccessLevel::Service));
assert_eq!(rq.access_level(), Some(AccessLevel::Service as i32));
}
}
2 changes: 1 addition & 1 deletion src/shvnode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ impl ShvNode {
let method = rq.method().unwrap_or_default();
for mm in &self.methods {
if mm.name == method {
return if rq_access >= mm.access { Some(*mm) } else { None }
return if rq_access >= mm.access as i32 { Some(*mm) } else { None }
}
}
}
Expand Down

0 comments on commit d09d68a

Please sign in to comment.