models for new db changes, update dnapi-rs, add debug to enroll messages for compat with JsonAPIResponse

This commit is contained in:
core 2023-12-25 18:45:02 -05:00
parent bda0ac2a52
commit c1762169b7
Signed by: core
GPG Key ID: FDBF740DADDCEECF
20 changed files with 340 additions and 62 deletions

3
Cargo.lock generated
View File

@ -1025,7 +1025,7 @@ dependencies = [
[[package]] [[package]]
name = "dnapi-rs" name = "dnapi-rs"
version = "0.2.1" version = "0.2.2"
dependencies = [ dependencies = [
"base64 0.21.5", "base64 0.21.5",
"base64-serde", "base64-serde",
@ -3085,6 +3085,7 @@ dependencies = [
"diesel", "diesel",
"diesel-async", "diesel-async",
"diesel_migrations", "diesel_migrations",
"dnapi-rs",
"env_logger", "env_logger",
"hex", "hex",
"log", "log",

View File

@ -1,6 +1,6 @@
[package] [package]
name = "dnapi-rs" name = "dnapi-rs"
version = "0.2.1" version = "0.2.2"
edition = "2021" edition = "2021"
description = "A rust client for the Defined Networking API" description = "A rust client for the Defined Networking API"
license = "AGPL-3.0-or-later" license = "AGPL-3.0-or-later"

View File

@ -13,7 +13,7 @@ pub const DO_UPDATE: &str = "DoUpdate";
base64_serde_type!(Base64Standard, base64::engine::general_purpose::STANDARD); base64_serde_type!(Base64Standard, base64::engine::general_purpose::STANDARD);
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize, Debug)]
/// `RequestV1` is the version 1 `DNClient` request message. /// `RequestV1` is the version 1 `DNClient` request message.
pub struct RequestV1 { pub struct RequestV1 {
/// Version is always 1 /// Version is always 1
@ -30,7 +30,7 @@ pub struct RequestV1 {
pub signature: Vec<u8>, pub signature: Vec<u8>,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize, Debug)]
/// `RequestWrapper` wraps a `DNClient` request message. It consists of a /// `RequestWrapper` wraps a `DNClient` request message. It consists of a
/// type and value, with the type string indicating how to interpret the value blob. /// type and value, with the type string indicating how to interpret the value blob.
pub struct RequestWrapper { pub struct RequestWrapper {
@ -48,14 +48,14 @@ pub struct RequestWrapper {
pub timestamp: String, pub timestamp: String,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize, Debug)]
/// `SignedResponseWrapper` contains a response message and a signature to validate inside `data`. /// `SignedResponseWrapper` contains a response message and a signature to validate inside `data`.
pub struct SignedResponseWrapper { pub struct SignedResponseWrapper {
/// The response data contained in this message /// The response data contained in this message
pub data: SignedResponse, pub data: SignedResponse,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize, Debug)]
/// `SignedResponse` contains a response message and a signature to validate. /// `SignedResponse` contains a response message and a signature to validate.
pub struct SignedResponse { pub struct SignedResponse {
/// The API version - always 1 /// The API version - always 1
@ -68,14 +68,14 @@ pub struct SignedResponse {
pub signature: Vec<u8>, pub signature: Vec<u8>,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize, Debug)]
/// `CheckForUpdateResponseWrapper` contains a response to `CheckForUpdate` inside "data." /// `CheckForUpdateResponseWrapper` contains a response to `CheckForUpdate` inside "data."
pub struct CheckForUpdateResponseWrapper { pub struct CheckForUpdateResponseWrapper {
/// The response data contained in this message /// The response data contained in this message
pub data: CheckForUpdateResponse, pub data: CheckForUpdateResponse,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize, Debug)]
/// `CheckForUpdateResponse` is the response generated for a `CheckForUpdate` request. /// `CheckForUpdateResponse` is the response generated for a `CheckForUpdate` request.
pub struct CheckForUpdateResponse { pub struct CheckForUpdateResponse {
#[serde(rename = "updateAvailable")] #[serde(rename = "updateAvailable")]
@ -83,7 +83,7 @@ pub struct CheckForUpdateResponse {
pub update_available: bool, pub update_available: bool,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize, Debug)]
/// `DoUpdateRequest` is the request sent for a `DoUpdate` request. /// `DoUpdateRequest` is the request sent for a `DoUpdate` request.
pub struct DoUpdateRequest { pub struct DoUpdateRequest {
#[serde(rename = "edPubkeyPEM")] #[serde(rename = "edPubkeyPEM")]
@ -100,7 +100,7 @@ pub struct DoUpdateRequest {
pub nonce: Vec<u8>, pub nonce: Vec<u8>,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize, Debug)]
/// A server response to a `DoUpdateRequest`, with the updated config and key information /// A server response to a `DoUpdateRequest`, with the updated config and key information
pub struct DoUpdateResponse { pub struct DoUpdateResponse {
#[serde(with = "Base64Standard")] #[serde(with = "Base64Standard")]
@ -141,7 +141,7 @@ pub struct EnrollRequest {
pub timestamp: String, pub timestamp: String,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize, Debug)]
#[serde(untagged)] #[serde(untagged)]
/// The response to an `EnrollRequest` /// The response to an `EnrollRequest`
pub enum EnrollResponse { pub enum EnrollResponse {
@ -157,7 +157,7 @@ pub enum EnrollResponse {
}, },
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize, Debug)]
/// The data included in an successful enrollment. /// The data included in an successful enrollment.
pub struct EnrollResponseData { pub struct EnrollResponseData {
#[serde(with = "Base64Standard")] #[serde(with = "Base64Standard")]
@ -176,7 +176,7 @@ pub struct EnrollResponseData {
pub organization: EnrollResponseDataOrg, pub organization: EnrollResponseDataOrg,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize, Debug)]
/// The organization data that this node is now a part of /// The organization data that this node is now a part of
pub struct EnrollResponseDataOrg { pub struct EnrollResponseDataOrg {
/// The organization ID that this node is now a part of /// The organization ID that this node is now a part of
@ -185,7 +185,7 @@ pub struct EnrollResponseDataOrg {
pub name: String, pub name: String,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize, Debug)]
/// `APIError` represents a single error returned in an API error response. /// `APIError` represents a single error returned in an API error response.
pub struct APIError { pub struct APIError {
/// The error code /// The error code

View File

@ -31,3 +31,4 @@ chacha20poly1305 = "0.10"
hex = "0.4" hex = "0.4"
thiserror = "1" thiserror = "1"
chrono = "0.4" chrono = "0.4"
dnapi-rs = { version = "0.2", path = "../dnapi-rs" }

View File

@ -2,5 +2,5 @@ CREATE TABLE roles (
id VARCHAR NOT NULL PRIMARY KEY, id VARCHAR NOT NULL PRIMARY KEY,
name VARCHAR NOT NULL, name VARCHAR NOT NULL,
description VARCHAR NOT NULL, description VARCHAR NOT NULL,
organizationId VARCHAR NOT NULL REFERENCES organizations(id) organization_id VARCHAR NOT NULL REFERENCES organizations(id)
); );

View File

@ -1,9 +1,9 @@
CREATE TABLE role_firewall_rules ( CREATE TABLE role_firewall_rules (
id VARCHAR NOT NULL UNIQUE, id VARCHAR NOT NULL PRIMARY KEY,
roleId VARCHAR NOT NULL REFERENCES roles(id), role_id VARCHAR NOT NULL REFERENCES roles(id),
protocol VARCHAR NOT NULL, protocol VARCHAR NOT NULL,
description VARCHAR NOT NULL, description VARCHAR NOT NULL,
allowedRoleId VARCHAR NULL, allowed_role_id VARCHAR NULL,
allowedTags VARCHAR[], allowed_tags text[] NOT NULL,
portRange int4range NULL port_range int4range NULL
); );

View File

@ -1,21 +1,21 @@
CREATE TABLE hosts ( CREATE TABLE hosts (
id VARCHAR NOT NULL PRIMARY KEY, id VARCHAR NOT NULL PRIMARY KEY,
organizationId VARCHAR NOT NULL REFERENCES organizations(id), organization_id VARCHAR NOT NULL REFERENCES organizations(id),
networkId VARCHAR NOT NULL REFERENCES networks(id), network_id VARCHAR NOT NULL REFERENCES networks(id),
roleId VARCHAR NULL REFERENCES roles(id), role_id VARCHAR NULL REFERENCES roles(id),
name VARCHAR NOT NULL, name VARCHAR NOT NULL,
ipAddress VARCHAR NOT NULL, ip_address VARCHAR NOT NULL,
staticAddresses VARCHAR[] NOT NULL, static_addresses text[] NOT NULL,
listenPort int2 NOT NULL, listen_port int2 NOT NULL,
isLighthouse BOOLEAN NOT NULL, is_lighthouse BOOLEAN NOT NULL,
isRelay BOOLEAN NOT NULL, is_relay BOOLEAN NOT NULL,
createdAt TIMESTAMP NOT NULL, created_at TIMESTAMP NOT NULL,
isBlocked BOOLEAN NOT NULL, is_blocked BOOLEAN NOT NULL,
lastSeenAt TIMESTAMP NULL, last_seen_at TIMESTAMP NULL,
clientVersion VARCHAR NULL, client_version VARCHAR NULL,
platform VARCHAR NULL, platform VARCHAR NULL,
updateAvailable BOOLEAN NULL, update_available BOOLEAN NULL,
tags VARCHAR[] NOT NULL tags text[] NOT NULL
); );

View File

@ -1,6 +1,6 @@
CREATE TABLE host_overrides ( CREATE TABLE host_overrides (
id VARCHAR NOT NULL PRIMARY KEY, id VARCHAR NOT NULL PRIMARY KEY,
hostId VARCHAR NOT NULL REFERENCES hosts(id), host_id VARCHAR NOT NULL REFERENCES hosts(id),
key VARCHAR NOT NULL, key VARCHAR NOT NULL,
value jsonb NOT NULL value jsonb NOT NULL
); );

View File

@ -1 +1 @@
-- This file should undo anything in `up.sql` DROP TABLE host_keys;

View File

@ -1,11 +1,11 @@
CREATE TABLE host_keys ( CREATE TABLE host_keys (
id VARCHAR NOT NULL PRIMARY KEY, id VARCHAR NOT NULL PRIMARY KEY,
hostId VARCHAR NOT NULL REFERENCES hosts(id), host_id VARCHAR NOT NULL REFERENCES hosts(id),
counter INT NOT NULL, counter INT NOT NULL,
client_ed_pub bytea NOT NULL, client_ed_pub bytea NOT NULL,
client_dh_pub bytea NOT NULL, client_dh_pub bytea NOT NULL,
client_cert bytea NOT NULL, client_cert bytea NOT NULL,
UNIQUE (hostId, counter) UNIQUE (host_id, counter)
); );

View File

@ -0,0 +1 @@
DROP TABLE enrollment_codes;

View File

@ -0,0 +1,5 @@
CREATE TABLE enrollment_codes (
id VARCHAR NOT NULL PRIMARY KEY,
host_id VARCHAR NOT NULL REFERENCES hosts(id),
expires TIMESTAMP NOT NULL
);

View File

@ -112,7 +112,8 @@ pub fn sign_cert_with_ca(
.map_err(|_| CryptographyError::InvalidKeyLength)?; .map_err(|_| CryptographyError::InvalidKeyLength)?;
let salt_u24: [u8; 24] = ca let salt_u24: [u8; 24] = ca
.salt.clone() .salt
.clone()
.try_into() .try_into()
.map_err(|_| CryptographyError::InvalidSaltLength)?; .map_err(|_| CryptographyError::InvalidSaltLength)?;

View File

@ -31,10 +31,11 @@ macro_rules! randid {
} }
pub fn random_with_charset(len: u32, charset: &[u8]) -> String { pub fn random_with_charset(len: u32, charset: &[u8]) -> String {
(0..(len-2)) (0..(len - 2))
.map(|_| { .map(|_| {
let idx = rand::thread_rng().gen_range(0..charset.len()); let idx = rand::thread_rng().gen_range(0..charset.len());
charset[idx] as char charset[idx] as char
}) })
.collect::<String>() + "tf" .collect::<String>()
+ "tf"
} }

View File

@ -1,6 +1,7 @@
use diesel::{Associations, Identifiable, Insertable, Queryable, Selectable}; use diesel::{Associations, Identifiable, Insertable, Queryable, Selectable};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::Value; use serde_json::Value;
use std::ops::{Bound, Range};
use std::time::SystemTime; use std::time::SystemTime;
#[derive( #[derive(
@ -206,3 +207,155 @@ pub struct NetworkNormalized {
pub name: String, pub name: String,
pub lighthouses_as_relays: bool, pub lighthouses_as_relays: bool,
} }
#[derive(
Queryable,
Selectable,
Insertable,
Identifiable,
Associations,
Debug,
PartialEq,
Clone,
Serialize,
Deserialize,
)]
#[diesel(belongs_to(Organization))]
#[diesel(table_name = crate::schema::roles)]
#[diesel(check_for_backend(diesel::pg::Pg))]
pub struct Role {
pub id: String,
pub name: String,
pub description: String,
pub organization_id: String,
}
#[derive(
Queryable,
Selectable,
Insertable,
Identifiable,
Associations,
Debug,
PartialEq,
Clone,
Serialize,
Deserialize,
)]
#[diesel(belongs_to(Role))]
#[diesel(table_name = crate::schema::role_firewall_rules)]
#[diesel(check_for_backend(diesel::pg::Pg))]
pub struct RoleFirewallRule {
pub id: String,
pub role_id: String,
pub protocol: String,
pub description: String,
pub allowed_role_id: Option<String>,
pub allowed_tags: Vec<Option<String>>, // postgres WHYYY
pub port_range: Option<(Bound<i32>, Bound<i32>)>,
}
#[derive(
Queryable,
Selectable,
Insertable,
Identifiable,
Associations,
Debug,
PartialEq,
Clone,
Serialize,
Deserialize,
)]
#[diesel(belongs_to(Organization))]
#[diesel(belongs_to(Network))]
#[diesel(belongs_to(Role))]
#[diesel(table_name = crate::schema::hosts)]
#[diesel(check_for_backend(diesel::pg::Pg))]
pub struct Host {
pub id: String,
pub organization_id: String,
pub network_id: String,
pub role_id: Option<String>,
pub name: String,
pub ip_address: String,
pub static_addresses: Vec<Option<String>>,
pub listen_port: i16,
pub is_lighthouse: bool,
pub is_relay: bool,
pub created_at: SystemTime,
pub is_blocked: bool,
pub last_seen_at: Option<SystemTime>,
pub client_version: Option<String>,
pub platform: Option<String>,
pub update_available: Option<bool>,
pub tags: Vec<Option<String>>,
}
#[derive(
Queryable,
Selectable,
Insertable,
Identifiable,
Associations,
Debug,
PartialEq,
Clone,
Serialize,
Deserialize,
)]
#[diesel(belongs_to(Host))]
#[diesel(table_name = crate::schema::host_overrides)]
#[diesel(check_for_backend(diesel::pg::Pg))]
pub struct HostOverride {
pub id: String,
pub host_id: String,
pub key: String,
pub value: Value,
}
#[derive(
Queryable,
Selectable,
Insertable,
Identifiable,
Associations,
Debug,
PartialEq,
Clone,
Serialize,
Deserialize,
)]
#[diesel(belongs_to(Host))]
#[diesel(table_name = crate::schema::host_keys)]
#[diesel(check_for_backend(diesel::pg::Pg))]
pub struct HostKey {
pub id: String,
pub host_id: String,
pub counter: i32,
pub client_ed_pub: Vec<u8>,
pub client_dh_pub: Vec<u8>,
pub client_cert: Vec<u8>,
}
#[derive(
Queryable,
Selectable,
Insertable,
Identifiable,
Associations,
Debug,
PartialEq,
Clone,
Serialize,
Deserialize,
)]
#[diesel(belongs_to(Host))]
#[diesel(table_name = crate::schema::enrollment_codes)]
#[diesel(check_for_backend(diesel::pg::Pg))]
pub struct EnrollmentCode {
pub id: String,
pub host_id: String,
pub expires: SystemTime,
}

View File

@ -1 +1,2 @@
pub mod v1; pub mod v1;
pub mod v2;

View File

@ -1,18 +1,18 @@
use crate::ca::create_signing_ca; use crate::ca::create_signing_ca;
use crate::models::{Network, NetworkNormalized, Organization, SigningCA, User}; use crate::models::{Network, NetworkNormalized, Organization, SigningCA, User};
use crate::response::JsonAPIResponse; use crate::response::JsonAPIResponse;
use crate::schema::{users}; use crate::schema::networks::dsl::networks;
use crate::schema::organizations::dsl::organizations;
use crate::schema::signing_cas::dsl::signing_cas;
use crate::schema::users;
use crate::{auth, enforce, randid, AppState}; use crate::{auth, enforce, randid, AppState};
use actix_web::web::{Data, Json}; use actix_web::web::{Data, Json};
use actix_web::HttpRequest; use actix_web::HttpRequest;
use chrono::{DateTime, SecondsFormat, Utc};
use diesel::{ExpressionMethods, QueryDsl, SelectableHelper}; use diesel::{ExpressionMethods, QueryDsl, SelectableHelper};
use diesel_async::RunQueryDsl; use diesel_async::RunQueryDsl;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::time::{Duration, SystemTime}; use std::time::{Duration, SystemTime};
use chrono::{DateTime, SecondsFormat, Utc};
use crate::schema::networks::dsl::networks;
use crate::schema::organizations::dsl::organizations;
use crate::schema::signing_cas::dsl::signing_cas;
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
pub struct CreateNetworkReq { pub struct CreateNetworkReq {
@ -79,21 +79,36 @@ pub async fn create_network_req(
lighthouses_as_relays: true, lighthouses_as_relays: true,
}; };
handle_error!(diesel::insert_into(organizations) handle_error!(
diesel::insert_into(organizations)
.values(&new_org) .values(&new_org)
.execute(&mut conn).await); .execute(&mut conn)
handle_error!(diesel::insert_into(signing_cas) .await
);
handle_error!(
diesel::insert_into(signing_cas)
.values(&ca_oneyear) .values(&ca_oneyear)
.execute(&mut conn).await); .execute(&mut conn)
handle_error!(diesel::insert_into(signing_cas) .await
);
handle_error!(
diesel::insert_into(signing_cas)
.values(&ca_twoyears) .values(&ca_twoyears)
.execute(&mut conn).await); .execute(&mut conn)
handle_error!(diesel::insert_into(signing_cas) .await
);
handle_error!(
diesel::insert_into(signing_cas)
.values(&ca_threeyears) .values(&ca_threeyears)
.execute(&mut conn).await); .execute(&mut conn)
handle_error!(diesel::insert_into(networks) .await
);
handle_error!(
diesel::insert_into(networks)
.values(&new_network) .values(&new_network)
.execute(&mut conn).await); .execute(&mut conn)
.await
);
let cdt: DateTime<Utc> = DateTime::from(new_network.created_at); let cdt: DateTime<Utc> = DateTime::from(new_network.created_at);

View File

@ -0,0 +1,13 @@
use crate::response::JsonAPIResponse;
use crate::AppState;
use actix_web::web::{Data, Json};
use dnapi_rs::message::{EnrollRequest, EnrollResponse};
pub async fn enroll_req(
req: Json<EnrollRequest>,
state: Data<AppState>,
) -> JsonAPIResponse<EnrollResponse> {
let mut conn = handle_error!(state.pool.get().await);
todo!()
}

View File

@ -0,0 +1 @@
pub mod enroll;

View File

@ -8,6 +8,56 @@ diesel::table! {
} }
} }
diesel::table! {
enrollment_codes (id) {
id -> Varchar,
host_id -> Varchar,
expires -> Timestamp,
}
}
diesel::table! {
host_keys (id) {
id -> Varchar,
host_id -> Varchar,
counter -> Int4,
client_ed_pub -> Bytea,
client_dh_pub -> Bytea,
client_cert -> Bytea,
}
}
diesel::table! {
host_overrides (id) {
id -> Varchar,
host_id -> Varchar,
key -> Varchar,
value -> Jsonb,
}
}
diesel::table! {
hosts (id) {
id -> Varchar,
organization_id -> Varchar,
network_id -> Varchar,
role_id -> Nullable<Varchar>,
name -> Varchar,
ip_address -> Varchar,
static_addresses -> Array<Nullable<Text>>,
listen_port -> Int2,
is_lighthouse -> Bool,
is_relay -> Bool,
created_at -> Timestamp,
is_blocked -> Bool,
last_seen_at -> Nullable<Timestamp>,
client_version -> Nullable<Varchar>,
platform -> Nullable<Varchar>,
update_available -> Nullable<Bool>,
tags -> Array<Nullable<Text>>,
}
}
diesel::table! { diesel::table! {
magic_links (id) { magic_links (id) {
id -> Varchar, id -> Varchar,
@ -36,6 +86,27 @@ diesel::table! {
} }
} }
diesel::table! {
role_firewall_rules (id) {
id -> Varchar,
role_id -> Varchar,
protocol -> Varchar,
description -> Varchar,
allowed_role_id -> Nullable<Varchar>,
allowed_tags -> Array<Nullable<Text>>,
port_range -> Nullable<Int4range>,
}
}
diesel::table! {
roles (id) {
id -> Varchar,
name -> Varchar,
description -> Varchar,
organization_id -> Varchar,
}
}
diesel::table! { diesel::table! {
session_tokens (id) { session_tokens (id) {
id -> Varchar, id -> Varchar,
@ -77,19 +148,33 @@ diesel::table! {
} }
diesel::joinable!(auth_tokens -> users (user_id)); diesel::joinable!(auth_tokens -> users (user_id));
diesel::joinable!(enrollment_codes -> hosts (host_id));
diesel::joinable!(host_keys -> hosts (host_id));
diesel::joinable!(host_overrides -> hosts (host_id));
diesel::joinable!(hosts -> networks (network_id));
diesel::joinable!(hosts -> organizations (organization_id));
diesel::joinable!(hosts -> roles (role_id));
diesel::joinable!(magic_links -> users (user_id)); diesel::joinable!(magic_links -> users (user_id));
diesel::joinable!(networks -> organizations (organization_id)); diesel::joinable!(networks -> organizations (organization_id));
diesel::joinable!(networks -> signing_cas (signing_ca_id)); diesel::joinable!(networks -> signing_cas (signing_ca_id));
diesel::joinable!(organizations -> users (owner_id)); diesel::joinable!(organizations -> users (owner_id));
diesel::joinable!(role_firewall_rules -> roles (role_id));
diesel::joinable!(roles -> organizations (organization_id));
diesel::joinable!(session_tokens -> users (user_id)); diesel::joinable!(session_tokens -> users (user_id));
diesel::joinable!(signing_cas -> organizations (organization_id)); diesel::joinable!(signing_cas -> organizations (organization_id));
diesel::joinable!(totp_authenticators -> users (user_id)); diesel::joinable!(totp_authenticators -> users (user_id));
diesel::allow_tables_to_appear_in_same_query!( diesel::allow_tables_to_appear_in_same_query!(
auth_tokens, auth_tokens,
enrollment_codes,
host_keys,
host_overrides,
hosts,
magic_links, magic_links,
networks, networks,
organizations, organizations,
role_firewall_rules,
roles,
session_tokens, session_tokens,
signing_cas, signing_cas,
totp_authenticators, totp_authenticators,