mirror of
https://github.com/aljazceru/pubky-core.git
synced 2026-01-04 23:04:23 +01:00
feat(common): serialize capabilities in AuthToken as a string with ',' separator
This commit is contained in:
@@ -5,7 +5,7 @@ use std::sync::{Arc, Mutex};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
capabilities::Capability,
|
||||
capabilities::{Capabilities, Capability},
|
||||
crypto::{Keypair, PublicKey, Signature},
|
||||
namespaces::PUBKY_AUTH,
|
||||
timestamp::Timestamp,
|
||||
@@ -37,11 +37,11 @@ pub struct AuthToken {
|
||||
/// The [PublicKey] of the owner of the resources being accessed by this token.
|
||||
pubky: PublicKey,
|
||||
// Variable length capabilities
|
||||
capabilities: Vec<Capability>,
|
||||
capabilities: Capabilities,
|
||||
}
|
||||
|
||||
impl AuthToken {
|
||||
pub fn sign(keypair: &Keypair, capabilities: Vec<Capability>) -> Self {
|
||||
pub fn sign(keypair: &Keypair, capabilities: impl Into<Capabilities>) -> Self {
|
||||
let timestamp = Timestamp::now();
|
||||
|
||||
let mut token = Self {
|
||||
@@ -50,7 +50,7 @@ impl AuthToken {
|
||||
version: 0,
|
||||
timestamp,
|
||||
pubky: keypair.public_key(),
|
||||
capabilities,
|
||||
capabilities: capabilities.into(),
|
||||
};
|
||||
|
||||
let serialized = token.serialize();
|
||||
@@ -61,7 +61,7 @@ impl AuthToken {
|
||||
}
|
||||
|
||||
pub fn capabilities(&self) -> &[Capability] {
|
||||
&self.capabilities
|
||||
&self.capabilities.0
|
||||
}
|
||||
|
||||
fn verify(bytes: &[u8]) -> Result<Self, Error> {
|
||||
@@ -225,13 +225,13 @@ mod tests {
|
||||
|
||||
verifier.verify(serialized).unwrap();
|
||||
|
||||
assert_eq!(token.capabilities, capabilities);
|
||||
assert_eq!(token.capabilities, capabilities.into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expired() {
|
||||
let signer = Keypair::random();
|
||||
let capabilities = vec![Capability::root()];
|
||||
let capabilities = Capabilities(vec![Capability::root()]);
|
||||
|
||||
let verifier = AuthVerifier::default();
|
||||
|
||||
@@ -272,7 +272,7 @@ mod tests {
|
||||
|
||||
verifier.verify(serialized).unwrap();
|
||||
|
||||
assert_eq!(token.capabilities, capabilities);
|
||||
assert_eq!(token.capabilities, capabilities.into());
|
||||
|
||||
assert_eq!(verifier.verify(serialized), Err(Error::AlreadyUsed));
|
||||
}
|
||||
|
||||
@@ -50,14 +50,6 @@ impl TryFrom<char> for Ability {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for Capability {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: String) -> Result<Self, Error> {
|
||||
value.as_str().try_into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Capability {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
@@ -69,6 +61,14 @@ impl Display for Capability {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for Capability {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: String) -> Result<Self, Error> {
|
||||
value.as_str().try_into()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for Capability {
|
||||
type Error = Error;
|
||||
|
||||
@@ -105,16 +105,6 @@ impl TryFrom<&str> for Capability {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug, PartialEq, Eq)]
|
||||
pub enum Error {
|
||||
#[error("Capability: Invalid resource path: does not start with `/`")]
|
||||
InvalidResource,
|
||||
#[error("Capability: Invalid format should be <resource>:<abilities>")]
|
||||
InvalidFormat,
|
||||
#[error("Capability: Invalid Ability")]
|
||||
InvalidAbility,
|
||||
}
|
||||
|
||||
impl Serialize for Capability {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
@@ -137,6 +127,74 @@ impl<'de> Deserialize<'de> for Capability {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug, PartialEq, Eq)]
|
||||
pub enum Error {
|
||||
#[error("Capability: Invalid resource path: does not start with `/`")]
|
||||
InvalidResource,
|
||||
#[error("Capability: Invalid format should be <resource>:<abilities>")]
|
||||
InvalidFormat,
|
||||
#[error("Capability: Invalid Ability")]
|
||||
InvalidAbility,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug, PartialEq, Eq)]
|
||||
/// A wrapper around `Vec<Capability>` to enable serialization without
|
||||
/// a varint. Useful when [Capabilities] are at the end of a struct.
|
||||
pub struct Capabilities(pub Vec<Capability>);
|
||||
|
||||
impl Capabilities {
|
||||
pub fn contains(&self, capability: &Capability) -> bool {
|
||||
self.0.contains(capability)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<Capability>> for Capabilities {
|
||||
fn from(value: Vec<Capability>) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Capabilities> for Vec<Capability> {
|
||||
fn from(value: Capabilities) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Capabilities {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
let string = self
|
||||
.0
|
||||
.iter()
|
||||
.map(|c| c.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(",");
|
||||
|
||||
string.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Capabilities {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let string: String = Deserialize::deserialize(deserializer)?;
|
||||
|
||||
let mut caps = vec![];
|
||||
|
||||
for s in string.split(',') {
|
||||
if let Ok(cap) = Capability::try_from(s) {
|
||||
caps.push(cap);
|
||||
};
|
||||
}
|
||||
|
||||
Ok(Capabilities(caps))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
Reference in New Issue
Block a user