trifid-api 0.3.0-alpha1 and some work on a tfweb rewrite #6

Merged
core merged 12 commits from api-and-web-rewrite into master 2023-12-28 05:44:29 +00:00
20 changed files with 340 additions and 62 deletions
Showing only changes of commit c1762169b7 - Show all commits

3
Cargo.lock generated
View File

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

View File

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

View File

@ -30,4 +30,5 @@ trifid-pki = { version = "0.1", path = "../trifid-pki", features = ["serde_deriv
chacha20poly1305 = "0.10"
hex = "0.4"
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,
name 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 (
id VARCHAR NOT NULL UNIQUE,
roleId VARCHAR NOT NULL REFERENCES roles(id),
id VARCHAR NOT NULL PRIMARY KEY,
role_id VARCHAR NOT NULL REFERENCES roles(id),
protocol VARCHAR NOT NULL,
description VARCHAR NOT NULL,
allowedRoleId VARCHAR NULL,
allowedTags VARCHAR[],
portRange int4range NULL
allowed_role_id VARCHAR NULL,
allowed_tags text[] NOT NULL,
port_range int4range NULL
);

View File

@ -1,21 +1,21 @@
CREATE TABLE hosts (
id VARCHAR NOT NULL PRIMARY KEY,
organizationId VARCHAR NOT NULL REFERENCES organizations(id),
networkId VARCHAR NOT NULL REFERENCES networks(id),
roleId VARCHAR NULL REFERENCES roles(id),
organization_id VARCHAR NOT NULL REFERENCES organizations(id),
network_id VARCHAR NOT NULL REFERENCES networks(id),
role_id VARCHAR NULL REFERENCES roles(id),
name VARCHAR NOT NULL,
ipAddress VARCHAR NOT NULL,
staticAddresses VARCHAR[] NOT NULL,
listenPort int2 NOT NULL,
isLighthouse BOOLEAN NOT NULL,
isRelay BOOLEAN NOT NULL,
createdAt TIMESTAMP NOT NULL,
isBlocked BOOLEAN NOT NULL,
ip_address VARCHAR NOT NULL,
static_addresses text[] NOT NULL,
listen_port int2 NOT NULL,
is_lighthouse BOOLEAN NOT NULL,
is_relay BOOLEAN NOT NULL,
created_at TIMESTAMP NOT NULL,
is_blocked BOOLEAN NOT NULL,
lastSeenAt TIMESTAMP NULL,
clientVersion VARCHAR NULL,
last_seen_at TIMESTAMP NULL,
client_version 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 (
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,
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 (
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,
client_ed_pub bytea NOT NULL,
client_dh_pub 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)?;
let salt_u24: [u8; 24] = ca
.salt.clone()
.salt
.clone()
.try_into()
.map_err(|_| CryptographyError::InvalidSaltLength)?;

View File

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

View File

@ -1,6 +1,7 @@
use diesel::{Associations, Identifiable, Insertable, Queryable, Selectable};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::ops::{Bound, Range};
use std::time::SystemTime;
#[derive(
@ -206,3 +207,155 @@ pub struct NetworkNormalized {
pub name: String,
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 v2;

View File

@ -1,18 +1,18 @@
use crate::ca::create_signing_ca;
use crate::models::{Network, NetworkNormalized, Organization, SigningCA, User};
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 actix_web::web::{Data, Json};
use actix_web::HttpRequest;
use chrono::{DateTime, SecondsFormat, Utc};
use diesel::{ExpressionMethods, QueryDsl, SelectableHelper};
use diesel_async::RunQueryDsl;
use serde::{Deserialize, Serialize};
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)]
pub struct CreateNetworkReq {
@ -79,21 +79,36 @@ pub async fn create_network_req(
lighthouses_as_relays: true,
};
handle_error!(diesel::insert_into(organizations)
.values(&new_org)
.execute(&mut conn).await);
handle_error!(diesel::insert_into(signing_cas)
.values(&ca_oneyear)
.execute(&mut conn).await);
handle_error!(diesel::insert_into(signing_cas)
.values(&ca_twoyears)
.execute(&mut conn).await);
handle_error!(diesel::insert_into(signing_cas)
.values(&ca_threeyears)
.execute(&mut conn).await);
handle_error!(diesel::insert_into(networks)
.values(&new_network)
.execute(&mut conn).await);
handle_error!(
diesel::insert_into(organizations)
.values(&new_org)
.execute(&mut conn)
.await
);
handle_error!(
diesel::insert_into(signing_cas)
.values(&ca_oneyear)
.execute(&mut conn)
.await
);
handle_error!(
diesel::insert_into(signing_cas)
.values(&ca_twoyears)
.execute(&mut conn)
.await
);
handle_error!(
diesel::insert_into(signing_cas)
.values(&ca_threeyears)
.execute(&mut conn)
.await
);
handle_error!(
diesel::insert_into(networks)
.values(&new_network)
.execute(&mut conn)
.await
);
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! {
magic_links (id) {
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! {
session_tokens (id) {
id -> Varchar,
@ -77,19 +148,33 @@ diesel::table! {
}
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!(networks -> organizations (organization_id));
diesel::joinable!(networks -> signing_cas (signing_ca_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!(signing_cas -> organizations (organization_id));
diesel::joinable!(totp_authenticators -> users (user_id));
diesel::allow_tables_to_appear_in_same_query!(
auth_tokens,
enrollment_codes,
host_keys,
host_overrides,
hosts,
magic_links,
networks,
organizations,
role_firewall_rules,
roles,
session_tokens,
signing_cas,
totp_authenticators,