Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added convenience methods to credentials. #412

Merged
merged 4 commits into from
May 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions libs/gl-client-py/glclient/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,10 @@ def is_running(self) -> bool:

class Scheduler(object):

def __init__(self, node_id: bytes, network: str, creds: Optional[Credentials] = None):
self.node_id = node_id
def __init__(self, network: str, creds: Optional[Credentials] = None):
self.network = network
self.creds = creds if creds is not None else native.Credentials()
self.inner = native.Scheduler(node_id, network, self.creds)
self.inner = native.Scheduler(network, self.creds)

def schedule(self) -> schedpb.NodeInfoResponse:
res = self.inner.schedule()
Expand Down Expand Up @@ -99,7 +98,7 @@ def node(self) -> "Node":
res = self.inner.node()
info = schedpb.NodeInfoResponse.FromString(bytes(res))
return Node(
node_id=self.node_id,
node_id=self.creds.node_id(),
grpc_uri=info.grpc_uri,
creds=self.creds,
)
Expand Down
3 changes: 2 additions & 1 deletion libs/gl-client-py/glclient/glclient.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class Credentials:
def from_path(path: str) -> Credentials: ...
@staticmethod
def from_parts(cert: bytes, key: bytes, ca: bytes, rune: str) -> Credentials: ...
def node_id(self) -> bytes: ...
def upgrade(self, scheduler: Scheduler, signer: Signer) -> Credentials: ...
def to_bytes(self) -> bytes: ...

Expand All @@ -47,7 +48,7 @@ class Signer:


class Scheduler:
def __init__(self, node_id: bytes, network: str, creds: Optional[Credentials]): ...
def __init__(self, network: str, creds: Optional[Credentials]): ...
def register(self, signer: Signer, invite_code: Optional[str]) -> bytes: ...
def recover(self, signer: Signer) -> bytes: ...
def authenticate(self, creds: Credentials): ...
Expand Down
34 changes: 27 additions & 7 deletions libs/gl-client-py/src/credentials.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::runtime::exec;
use crate::scheduler::Scheduler;
use crate::signer::Signer;
use gl_client::credentials::{self, RuneProvider, TlsConfigProvider};
use gl_client::credentials::{self, NodeIdProvider, RuneProvider, TlsConfigProvider};
use pyo3::exceptions::PyValueError;
use pyo3::prelude::*;
use pyo3::types::PyBytes;
Expand All @@ -12,7 +12,7 @@ pub type PyCredentials = UnifiedCredentials<credentials::Nobody, credentials::De
pub enum UnifiedCredentials<T, R>
where
T: TlsConfigProvider,
R: TlsConfigProvider + RuneProvider + Clone,
R: TlsConfigProvider + RuneProvider + NodeIdProvider + Clone,
{
Nobody(T),
Device(R),
Expand All @@ -21,7 +21,7 @@ where
impl<T, R> UnifiedCredentials<T, R>
where
T: TlsConfigProvider,
R: TlsConfigProvider + RuneProvider + Clone,
R: TlsConfigProvider + RuneProvider + NodeIdProvider + Clone,
{
pub fn ensure_nobody(&self) -> Result<()> {
if let Self::Nobody(_) = self {
Expand All @@ -47,7 +47,7 @@ where
impl<T, R> TlsConfigProvider for UnifiedCredentials<T, R>
where
T: TlsConfigProvider,
R: TlsConfigProvider + RuneProvider + Clone,
R: TlsConfigProvider + RuneProvider + NodeIdProvider + Clone,
{
fn tls_config(&self) -> gl_client::tls::TlsConfig {
match self {
Expand All @@ -60,18 +60,33 @@ where
impl<T, R> RuneProvider for UnifiedCredentials<T, R>
where
T: TlsConfigProvider,
R: TlsConfigProvider + RuneProvider + Clone,
R: TlsConfigProvider + RuneProvider + NodeIdProvider + Clone,
{
fn rune(&self) -> String {
match self {
UnifiedCredentials::Nobody(_) => panic!(
"can not provide rune from nobody credentials! something really bad happended."
"can not provide rune from nobody credentials! something really bad happened."
),
UnifiedCredentials::Device(d) => d.rune(),
}
}
}

impl<T, R> NodeIdProvider for UnifiedCredentials<T, R>
where
T: TlsConfigProvider,
R: TlsConfigProvider + RuneProvider + NodeIdProvider + Clone,
{
fn node_id(&self) -> credentials::Result<Vec<u8>> {
match self {
UnifiedCredentials::Nobody(_) => panic!(
"can not provide node_id from nobody credentials! something really bad happened."
),
UnifiedCredentials::Device(d) => d.node_id(),
}
}
}

#[pyclass]
#[derive(Clone)]
pub struct Credentials {
Expand Down Expand Up @@ -110,7 +125,8 @@ impl Credentials {

#[staticmethod]
pub fn from_parts(cert: &[u8], key: &[u8], ca: &[u8], rune: &str) -> Self {
let inner = UnifiedCredentials::Device(gl_client::credentials::Device::with(cert, key, ca, rune));
let inner =
UnifiedCredentials::Device(gl_client::credentials::Device::with(cert, key, ca, rune));
Self { inner }
}

Expand Down Expand Up @@ -150,6 +166,10 @@ impl Credentials {
pub fn ensure_nobody(&self) -> Result<()> {
self.inner.ensure_nobody()
}

pub fn node_id(&self) -> Result<Vec<u8>> {
Ok(self.inner.node_id()?)
}
}

type Result<T, E = ErrorWrapper> = std::result::Result<T, E>;
Expand Down
19 changes: 8 additions & 11 deletions libs/gl-client-py/src/scheduler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::runtime::exec;
use crate::Signer;
use anyhow::{anyhow, Result};
use gl_client::bitcoin::Network;
use gl_client::credentials::RuneProvider;
use gl_client::credentials::{NodeIdProvider, RuneProvider};
use gl_client::credentials::TlsConfigProvider;
use gl_client::pb;
use gl_client::scheduler;
Expand All @@ -15,7 +15,7 @@ use pyo3::prelude::*;
pub enum UnifiedScheduler<T, R>
where
T: TlsConfigProvider,
R: TlsConfigProvider + RuneProvider + Clone,
R: TlsConfigProvider + RuneProvider + NodeIdProvider + Clone,
{
Unauthenticated(scheduler::Scheduler<T>),
Authenticated(scheduler::Scheduler<R>),
Expand All @@ -24,7 +24,7 @@ where
impl<T, R> UnifiedScheduler<T, R>
where
T: TlsConfigProvider,
R: TlsConfigProvider + RuneProvider + Clone,
R: TlsConfigProvider + RuneProvider + NodeIdProvider + Clone,
{
pub fn is_authenticated(&self) -> Result<()> {
if let Self::Authenticated(_) = self {
Expand Down Expand Up @@ -72,7 +72,7 @@ where
impl<T, R> UnifiedScheduler<T, R>
where
T: TlsConfigProvider,
R: TlsConfigProvider + RuneProvider + Clone,
R: TlsConfigProvider + RuneProvider + NodeIdProvider + Clone,
{
async fn export_node(&self) -> Result<pb::scheduler::ExportNodeResponse> {
let s = self.authenticated_scheduler()?;
Expand Down Expand Up @@ -137,41 +137,39 @@ where

#[pyclass]
pub struct Scheduler {
node_id: Vec<u8>,
pub inner: UnifiedScheduler<PyCredentials, PyCredentials>,
}

#[pymethods]
impl Scheduler {
#[new]
fn new(node_id: Vec<u8>, network: &str, creds: Credentials) -> PyResult<Scheduler> {
fn new(network: &str, creds: Credentials) -> PyResult<Scheduler> {
let network: Network = network
.parse()
.map_err(|_| PyValueError::new_err("Error parsing the network"))?;

let id = node_id.clone();
let uri = gl_client::utils::scheduler_uri();

let inner = match creds.inner {
crate::credentials::UnifiedCredentials::Nobody(_) => {
let scheduler = exec(async move {
gl_client::scheduler::Scheduler::with(id, network, creds.inner.clone(), uri)
gl_client::scheduler::Scheduler::with(network, creds.inner.clone(), uri)
.await
})
.map_err(|e| PyValueError::new_err(e.to_string()))?;
UnifiedScheduler::Unauthenticated(scheduler)
}
crate::credentials::UnifiedCredentials::Device(_) => {
let scheduler = exec(async move {
gl_client::scheduler::Scheduler::with(id, network, creds.inner.clone(), uri)
gl_client::scheduler::Scheduler::with(network, creds.inner.clone(), uri)
.await
})
.map_err(|e| PyValueError::new_err(e.to_string()))?;
UnifiedScheduler::Authenticated(scheduler)
}
};

Ok(Scheduler { node_id, inner })
Ok(Scheduler { inner })
}

fn register(&self, signer: &Signer, invite_code: Option<String>) -> PyResult<Vec<u8>> {
Expand All @@ -198,7 +196,6 @@ impl Scheduler {
))
})?;
Ok(Scheduler {
node_id: self.node_id.clone(),
inner: s,
})
}
Expand Down
2 changes: 1 addition & 1 deletion libs/gl-client-py/tests/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def sclient(signer, creds):
registration and recovery, but no mTLS certificate yet.
"""
network = "regtest"
return Scheduler(signer.node_id(), network=network, creds=creds)
return Scheduler(network=network, creds=creds)


@pytest.fixture
Expand Down
2 changes: 1 addition & 1 deletion libs/gl-client-py/tests/test_creds.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def test_upgrade_credentials(scheduler, sclient, signer):

c = Credentials.from_bytes(creds).upgrade(
Scheduler(
node_id=signer.node_id(), network="regtest", creds=screds
network="regtest", creds=screds
).inner,
signer.inner,
)
Expand Down
2 changes: 1 addition & 1 deletion libs/gl-client-py/tests/test_scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ def test_connect(scheduler, creds):
"""Test that we can connect to the scheduler."""
sig = Signer(b"\x00" * 32, network="regtest", creds=creds)
node_id = sig.node_id()
s = Scheduler(node_id, network="regtest", creds=creds)
s = Scheduler(network="regtest", creds=creds)
with pytest.raises(ValueError):
s.recover(sig)

Expand Down
56 changes: 36 additions & 20 deletions libs/gl-client/src/credentials.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::{
scheduler::Scheduler,
signer::Signer,
tls::{self, TlsConfig},
utils::get_node_id_from_tls_config,
};
/// Credentials is a collection of all relevant keys and attestations
/// required to authenticate a device and authorize a command on the node.
Expand Down Expand Up @@ -40,15 +41,20 @@ pub enum Error {
FetchDefaultNobodyCredentials(#[source] anyhow::Error),
}

type Result<T, E = Error> = std::result::Result<T, E>;
pub type Result<T, E = Error> = std::result::Result<T, E>;

pub trait TlsConfigProvider: Send + Sync {
fn tls_config(&self) -> TlsConfig;
}

pub trait RuneProvider {
fn rune(&self) -> String;
}

pub trait NodeIdProvider {
fn node_id(&self) -> Result<Vec<u8>>;
}

/// A helper struct to combine the Tls certificate and the corresponding private
/// key.
#[derive(Clone, Debug)]
Expand Down Expand Up @@ -177,31 +183,30 @@ impl Device {

/// Asynchronously upgrades the credentials using the provided scheduler and
/// signer, potentially involving network operations or other async tasks.
pub async fn upgrade<T>(mut self, scheduler: &Scheduler<T>, signer: &Signer) -> Result<Self>
pub async fn upgrade<T>(mut self, _scheduler: &Scheduler<T>, signer: &Signer) -> Result<Self>
where
T: TlsConfigProvider,
{
use Error::*;

// For now, upgrade is covered by recover
let res = scheduler
.recover(signer)
.await
.map_err(|e| UpgradeCredentialsError(e.to_string()))?;
let mut data = model::Data::try_from(&res.creds[..])
self.version = CRED_VERSION;

if self.rune.is_empty() {
let node_id = self
.node_id()
.map_err(|e| UpgradeCredentialsError(e.to_string()))?;

let alt = runeauth::Alternative::new(
"pubkey".to_string(),
runeauth::Condition::Equal,
hex::encode(node_id),
false,
)
.map_err(|e| UpgradeCredentialsError(e.to_string()))?;
data.version = CRED_VERSION;
if let Some(cert) = data.cert {
self.cert = cert
}
if let Some(key) = data.key {
self.key = key
}
if let Some(ca) = data.ca {
self.ca = ca
}
if let Some(rune) = data.rune {
self.rune = rune

self.rune = signer
.create_rune(None, vec![vec![&alt.encode()]])
.map_err(|e| UpgradeCredentialsError(e.to_string()))?;
};
Ok(self)
}
Expand All @@ -217,6 +222,7 @@ impl TlsConfigProvider for Device {
fn tls_config(&self) -> TlsConfig {
tls::TlsConfig::with(&self.cert, &self.key, &self.ca)
}

}

impl RuneProvider for Device {
Expand All @@ -225,6 +231,16 @@ impl RuneProvider for Device {
}
}

impl NodeIdProvider for Device {
fn node_id(&self) -> Result<Vec<u8>> {
get_node_id_from_tls_config(&self.tls_config()).map_err(|_e| {
Error::GetFromIdentityError(
"node_id could not be retrieved from the certificate".to_string(),
)
})
}
}

impl From<Device> for Vec<u8> {
fn from(value: Device) -> Self {
let data: model::Data = value.into();
Expand Down
8 changes: 1 addition & 7 deletions libs/gl-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,7 @@ pub mod tls;
pub mod export;

/// Tools to interact with a node running on greenlight.
pub mod utils {

pub fn scheduler_uri() -> String {
std::env::var("GL_SCHEDULER_GRPC_URI")
.unwrap_or_else(|_| "https://scheduler.gl.blckstrm.com".to_string())
}
}
pub mod utils;

pub mod credentials;

Expand Down
Loading
Loading