add client certificate api

This commit is contained in:
yggverse 2024-11-27 18:03:22 +02:00
parent 9d240c4c37
commit 4e712260ff
6 changed files with 129 additions and 5 deletions

View file

@ -1,10 +1,12 @@
//! High-level client API to interact with Gemini Socket Server:
//! * https://geminiprotocol.net/docs/protocol-specification.gmi
pub mod certificate;
pub mod connection;
pub mod error;
pub mod response;
pub use certificate::Certificate;
pub use connection::Connection;
pub use error::Error;
pub use response::Response;

View file

@ -8,15 +8,20 @@ use gio::{prelude::TlsCertificateExt, TlsCertificate};
use glib::DateTime;
pub struct Certificate {
tls_certificate: TlsCertificate,
pub scope: Scope,
pub tls_certificate: TlsCertificate,
}
impl Certificate {
// Constructors
/// Create new `Self`
pub fn from_pem(pem: &str) -> Result<Self, Error> {
pub fn from_pem(pem: &str, scope_url: &str) -> Result<Self, Error> {
Ok(Self {
scope: match Scope::from_url(scope_url) {
Ok(scope) => scope,
Err(reason) => return Err(Error::Scope(reason)),
},
tls_certificate: match TlsCertificate::from_pem(&pem) {
Ok(tls_certificate) => {
// Validate expiration time

View file

@ -0,0 +1,55 @@
use std::fmt::{Display, Formatter, Result};
use glib::gformat;
#[derive(Debug)]
pub enum Error {
DateTime,
Decode(glib::Error),
Expired(glib::DateTime),
Inactive(glib::DateTime),
Scope(crate::client::certificate::scope::Error),
ValidAfter,
ValidBefore,
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter) -> Result {
match self {
Self::DateTime => {
write!(f, "Could not parse local `DateTime`")
}
Self::Decode(reason) => {
write!(
f,
"Could not decode TLS certificate from PEM string: {reason}"
)
}
Self::Expired(not_valid_after) => {
write!(
f,
"Certificate expired after: {}",
match not_valid_after.format_iso8601() {
Ok(value) => value,
Err(_) => gformat!("unknown"),
}
)
}
Self::Inactive(not_valid_before) => {
write!(
f,
"Certificate inactive before: {}",
match not_valid_before.format_iso8601() {
Ok(value) => value,
Err(_) => gformat!("unknown"),
}
)
}
Self::Scope(reason) => {
write!(f, "Certificate inactive before: {reason}")
}
Self::ValidAfter => write!(f, "Could not get `not_valid_after` value"),
Self::ValidBefore => write!(f, "Could not get `not_valid_before` value"),
}
}
}

View file

@ -0,0 +1,41 @@
pub mod error;
pub use error::Error;
use glib::{GString, Uri, UriFlags, UriHideFlags};
/// Scope implement path prefix to apply TLS authorization for
/// * https://geminiprotocol.net/docs/protocol-specification.gmi#status-60
pub struct Scope {
uri: Uri,
}
impl Scope {
// Constructors
/// Create new `Self` for given `url` string
/// * check URI parts required for valid `Scope` build
pub fn from_url(url: &str) -> Result<Self, Error> {
match Uri::parse(url, UriFlags::NONE) {
Ok(uri) => {
if !uri.scheme().to_lowercase().contains("gemini") {
return Err(Error::Scheme);
}
if uri.host().is_none() {
return Err(Error::Host);
}
Ok(Self { uri })
}
Err(reason) => Err(Error::Uri(reason)),
}
}
// Getters
/// Get `Scope` string match [Specification](https://geminiprotocol.net/docs/protocol-specification.gmi#status-60)
pub fn to_string(&self) -> GString {
self.uri
.to_string_partial(UriHideFlags::QUERY | UriHideFlags::FRAGMENT)
}
}

View file

@ -0,0 +1,24 @@
use std::fmt::{Display, Formatter, Result};
#[derive(Debug)]
pub enum Error {
Host,
Scheme,
Uri(glib::Error),
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter) -> Result {
match self {
Self::Host => {
write!(f, "Host required")
}
Self::Scheme => {
write!(f, "Scope does not match `gemini`")
}
Self::Uri(reason) => {
write!(f, "Could not parse URI: {reason}")
}
}
}
}

View file

@ -1,7 +1,4 @@
pub mod certificate;
pub mod error;
pub use certificate::Certificate;
pub use error::Error;
use gio::{