forked from ariel-os/ariel-os
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(coap): add configuration with configured scopes
Closes: ariel-os#338
- Loading branch information
Showing
8 changed files
with
692 additions
and
41 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
use serde::Deserialize; | ||
use std::fmt::Write; | ||
|
||
/// Second-level item for deserializing a `peers.yml` | ||
/// | ||
/// (The top level is a list thereof). | ||
#[derive(Deserialize)] | ||
struct Peer { | ||
kccs: String, | ||
scope: Scope, | ||
} | ||
|
||
#[derive(Debug, Deserialize)] | ||
#[serde(untagged)] | ||
enum Scope { | ||
String(String), | ||
Aif(std::collections::HashMap<String, Permission>), | ||
} | ||
|
||
#[derive(Debug, Deserialize)] | ||
#[serde(untagged)] | ||
enum Permission { | ||
Set(Vec<SinglePermission>), | ||
Single(SinglePermission), | ||
} | ||
|
||
#[derive(Debug, Deserialize, Copy, Clone)] | ||
#[allow(clippy::upper_case_acronyms, reason = "used to guide serde values")] | ||
#[repr(u8)] | ||
enum SinglePermission { | ||
GET = coap_numbers::code::GET, | ||
POST = coap_numbers::code::POST, | ||
PUT = coap_numbers::code::PUT, | ||
DELETE = coap_numbers::code::DELETE, | ||
FETCH = coap_numbers::code::FETCH, | ||
PATCH = coap_numbers::code::PATCH, | ||
#[allow(non_camel_case_types, reason = "that's how that code is named")] | ||
iPATCH = coap_numbers::code::IPATCH, | ||
} | ||
|
||
impl Permission { | ||
fn mask(&self) -> u32 { | ||
match self { | ||
Permission::Set(p) => p.iter().fold(0, |old, value| old | value.mask()), | ||
Permission::Single(p) => p.mask(), | ||
} | ||
} | ||
} | ||
|
||
impl SinglePermission { | ||
/// The `Tperm` unsigned integer representation of the REST-specific AIF model described in | ||
/// RFC9237. | ||
fn mask(&self) -> u32 { | ||
1 << (*self as u8 - 1) | ||
} | ||
} | ||
|
||
fn main() { | ||
if !build::cargo_feature("coap-server-config-storage") { | ||
return; | ||
} | ||
|
||
build::rerun_if_env_changed("PEERS_YML"); | ||
let peers_yml = std::path::PathBuf::from(std::env::var("PEERS_YML").unwrap()); | ||
|
||
build::rerun_if_changed(&peers_yml); | ||
let peers_file = | ||
std::fs::File::open(peers_yml).expect("no peers.yml usable in specified location"); | ||
|
||
let peers: Vec<Peer> = serde_yml::from_reader(peers_file).expect("failed to parse peers.yml"); | ||
|
||
let mut chain_once_per_kccs = String::new(); | ||
for peer in peers { | ||
let kccs = cbor_edn::StandaloneItem::parse(&peer.kccs) | ||
.expect("data in kccs is not valid CBOR Diagnostic Notation (EDN)") | ||
.to_cbor() | ||
.expect("CBOR Diagnostic Notation (EDN) is not expressible in CBOR"); | ||
// FIXME: Should we pre-parse the KCCS and have the parsed credentials as const in flash? Or | ||
// just parsed enough that there is no CBOR parsing but credential and material point to | ||
// overlapping slices? | ||
let scope = match peer.scope { | ||
Scope::String(s) if s == "allow-all" => { | ||
"coapcore::scope::UnionScope::AllowAll".to_string() | ||
} | ||
Scope::Aif(aif) => { | ||
let data: Vec<_> = aif | ||
.into_iter() | ||
.map(|(toid, tperm)| (toid, tperm.mask())) | ||
.collect(); | ||
let mut bytes = vec![]; | ||
minicbor::encode(data, &mut bytes).unwrap(); | ||
format!("coapcore::scope::UnionScope::AifValue(coapcore::scope::AifValue::parse(&{bytes:?}).unwrap())") | ||
} | ||
e => panic!("Scope configuration {e:?} is not recognized"), | ||
}; | ||
write!( | ||
chain_once_per_kccs, | ||
".chain(core::iter::once((lakers::Credential::parse_ccs( | ||
&{kccs:?}).unwrap(), | ||
{scope}, | ||
)))" | ||
) | ||
.expect("writing to String is infallible"); | ||
} | ||
|
||
let peers_data = format!( | ||
" | ||
pub(super) fn kccs() -> impl Iterator<Item=(lakers::Credential, coapcore::scope::UnionScope)> {{ | ||
core::iter::empty() | ||
{chain_once_per_kccs} | ||
}} | ||
"); | ||
|
||
let peers_file = build::out_dir().join("peers.rs"); | ||
std::fs::write(peers_file, peers_data).unwrap(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# The format of this may yet change (especially since it should eventually | ||
# source the user's public keys from well-known locations as per | ||
# <https://ariel-os.github.io/ariel-os/dev/docs/book/tooling/coap.html#outlook-interacting-with-an-ariel-os-coap-server-from-the-host>); | ||
# the general gist is that it lists who may do what on the device. | ||
|
||
- kccs: | | ||
# The CWT Claims Set that needs to be used (by value or by reference) by | ||
# the client to gain access to the device. | ||
# | ||
# It is expressed in CBOR diagnostic notation (which at the YAML level is | ||
# just a string), and compatible with aiocoap's credentials. | ||
{2: "42-50-31-FF-EF-37-32-39", 8: {1: {1: 2, 2: h'2b', -1: 1, -2: h'ac75e9ece3e50bfc8ed60399889522405c47bf16df96660a41298cb4307f7eb6', -3: h'6e5de611388a4b8a8211334ac7d37ecb52a387d257e6db3c2a93df21ff3affc8'}}} | ||
scope: | ||
# Authorizations assigned to clients authenticating with the credential | ||
# above. Keys are paths on the device, values are single or lists of CoAP | ||
# methods that may be performed. | ||
# | ||
# Instead of a dictionary, the scope can also be a single string | ||
# "allow-all". | ||
/stdout: [GET, FETCH] | ||
/.well-known/core: GET | ||
/poem: GET |