Add simple documentation generator

Signed-off-by: trivernis <trivernis@protonmail.com>
pull/13/head
trivernis 4 years ago
parent 314da98c17
commit a0e6e4fb07
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

223
Cargo.lock generated

@ -1,5 +1,11 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
[[package]]
name = "adler"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e"
[[package]] [[package]]
name = "adler32" name = "adler32"
version = "1.2.0" version = "1.2.0"
@ -101,6 +107,16 @@ dependencies = [
"getrandom", "getrandom",
] ]
[[package]]
name = "bincode"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f30d3a39baa26f9651f17b375061f3233dde33424a8b72b0dbe93a68a0bc896d"
dependencies = [
"byteorder",
"serde",
]
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.2.1" version = "1.2.1"
@ -349,6 +365,12 @@ version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
[[package]]
name = "dyn-clone"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d55796afa1b20c2945ca8eabfc421839f2b766619209f1ede813cf2484f31804"
[[package]] [[package]]
name = "env_logger" name = "env_logger"
version = "0.7.1" version = "0.7.1"
@ -380,6 +402,18 @@ dependencies = [
"winapi 0.3.9", "winapi 0.3.9",
] ]
[[package]]
name = "flate2"
version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7411863d55df97a419aa64cb4d2f167103ea9d767e2c54a1868b7ac3f6b47129"
dependencies = [
"cfg-if 1.0.0",
"crc32fast",
"libc",
"miniz_oxide",
]
[[package]] [[package]]
name = "flotte-user-management" name = "flotte-user-management"
version = "0.1.0" version = "0.1.0"
@ -407,12 +441,20 @@ dependencies = [
"rmp-serde", "rmp-serde",
"rouille", "rouille",
"scheduled-thread-pool", "scheduled-thread-pool",
"schemars",
"serde", "serde",
"serde_json", "serde_json",
"serde_postgres", "serde_postgres",
"syntect",
"zeroize", "zeroize",
] ]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]] [[package]]
name = "fuchsia-cprng" name = "fuchsia-cprng"
version = "0.1.1" version = "0.1.1"
@ -560,6 +602,12 @@ dependencies = [
"crc32fast", "crc32fast",
] ]
[[package]]
name = "hashbrown"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
[[package]] [[package]]
name = "hermit-abi" name = "hermit-abi"
version = "0.1.17" version = "0.1.17"
@ -605,6 +653,16 @@ dependencies = [
"unicode-normalization", "unicode-normalization",
] ]
[[package]]
name = "indexmap"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2"
dependencies = [
"autocfg 1.0.1",
"hashbrown",
]
[[package]] [[package]]
name = "instant" name = "instant"
version = "0.1.8" version = "0.1.8"
@ -645,12 +703,33 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "lazycell"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.80" version = "0.2.80"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614" checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614"
[[package]]
name = "line-wrap"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9"
dependencies = [
"safemem",
]
[[package]]
name = "linked-hash-map"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a"
[[package]] [[package]]
name = "lock_api" name = "lock_api"
version = "0.4.1" version = "0.4.1"
@ -723,6 +802,16 @@ dependencies = [
"unicase", "unicase",
] ]
[[package]]
name = "miniz_oxide"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d"
dependencies = [
"adler",
"autocfg 1.0.1",
]
[[package]] [[package]]
name = "mio" name = "mio"
version = "0.6.22" version = "0.6.22"
@ -846,6 +935,28 @@ version = "1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0"
[[package]]
name = "onig"
version = "6.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a155d13862da85473665694f4c05d77fb96598bdceeaf696433c84ea9567e20"
dependencies = [
"bitflags",
"lazy_static",
"libc",
"onig_sys",
]
[[package]]
name = "onig_sys"
version = "69.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bff06597a6b17855040955cae613af000fc0bfc8ad49ea68b9479a74e59292d"
dependencies = [
"cc",
"pkg-config",
]
[[package]] [[package]]
name = "opaque-debug" name = "opaque-debug"
version = "0.3.0" version = "0.3.0"
@ -979,6 +1090,26 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
[[package]]
name = "plist"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b336d94e8e4ce29bf15bba393164629764744c567e8ad306cc1fdd0119967fd"
dependencies = [
"base64 0.12.3",
"chrono",
"indexmap",
"line-wrap",
"serde",
"xml-rs",
]
[[package]] [[package]]
name = "postgres" name = "postgres"
version = "0.17.5" version = "0.17.5"
@ -1381,6 +1512,15 @@ version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]] [[package]]
name = "scheduled-thread-pool" name = "scheduled-thread-pool"
version = "0.2.5" version = "0.2.5"
@ -1390,6 +1530,30 @@ dependencies = [
"parking_lot", "parking_lot",
] ]
[[package]]
name = "schemars"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "763f667253711994847f7e73befe859d6fff7bea2b7a7f01669d2c5b60765c37"
dependencies = [
"dyn-clone",
"schemars_derive",
"serde",
"serde_json",
]
[[package]]
name = "schemars_derive"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1d457e2e37415f32b7628ddc5a7fea06ef63bd029ed180d65166e87ca25ce21"
dependencies = [
"proc-macro2",
"quote",
"serde_derive_internals",
"syn",
]
[[package]] [[package]]
name = "scopeguard" name = "scopeguard"
version = "1.1.0" version = "1.1.0"
@ -1416,6 +1580,17 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "serde_derive_internals"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dbab34ca63057a1f15280bdf3c39f2b1eb1b54c17e98360e511637aef7418c6"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.59" version = "1.0.59"
@ -1519,6 +1694,28 @@ dependencies = [
"unicode-xid", "unicode-xid",
] ]
[[package]]
name = "syntect"
version = "4.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e3978df05b5850c839a6b352d3c35ce0478944a4be689be826b53cf75363e88"
dependencies = [
"bincode",
"bitflags",
"flate2",
"fnv",
"lazy_static",
"lazycell",
"onig",
"plist",
"regex-syntax",
"serde",
"serde_derive",
"serde_json",
"walkdir",
"yaml-rust",
]
[[package]] [[package]]
name = "tempdir" name = "tempdir"
version = "0.3.7" version = "0.3.7"
@ -1731,6 +1928,17 @@ version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
[[package]]
name = "walkdir"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d"
dependencies = [
"same-file",
"winapi 0.3.9",
"winapi-util",
]
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.9.0+wasi-snapshot-preview1" version = "0.9.0+wasi-snapshot-preview1"
@ -1796,6 +2004,21 @@ dependencies = [
"winapi-build", "winapi-build",
] ]
[[package]]
name = "xml-rs"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b07db065a5cf61a7e4ba64f29e67db906fb1787316516c4e6e5ff0fea1efcd8a"
[[package]]
name = "yaml-rust"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39f0c922f1a334134dc2f7a8b67dc5d25f0735263feec974345ff706bcf20b0d"
dependencies = [
"linked-hash-map",
]
[[package]] [[package]]
name = "zeroize" name = "zeroize"
version = "1.1.1" version = "1.1.1"

@ -34,4 +34,6 @@ scheduled-thread-pool = "0.2.5"
num_cpus = "1.13.0" num_cpus = "1.13.0"
parking_lot = "0.11.0" parking_lot = "0.11.0"
regex = "1.4.2" regex = "1.4.2"
lazy_static = "1.4.0" lazy_static = "1.4.0"
schemars = "0.8.0"
syntect = "4.4.0"

@ -31,7 +31,7 @@ impl UserRecord {
/// A row of the permission table that can be serialized and sent /// A row of the permission table that can be serialized and sent
/// via the rcp connection /// via the rcp connection
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
pub struct Permission { pub struct Permission {
pub id: i32, pub id: i32,
pub name: String, pub name: String,
@ -40,7 +40,7 @@ pub struct Permission {
/// A row of the role table that can be serialized and sent /// A row of the role table that can be serialized and sent
/// via the rcp connection /// via the rcp connection
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
pub struct Role { pub struct Role {
pub id: i32, pub id: i32,
pub name: String, pub name: String,

@ -15,7 +15,7 @@ const REQUEST_TOKEN_EXPIRE_SECONDS: u32 = 60 * 10;
const REFRESH_TOKEN_EXPIRE_SECONDS: u32 = 60 * 60 * 24; const REFRESH_TOKEN_EXPIRE_SECONDS: u32 = 60 * 60 * 24;
/// A struct to store session tokens of a user in a API-readable format /// A struct to store session tokens of a user in a API-readable format
#[derive(Clone, Debug, Zeroize, Serialize)] #[derive(Clone, Debug, Zeroize, Serialize, JsonSchema)]
#[zeroize(drop)] #[zeroize(drop)]
pub struct SessionTokens { pub struct SessionTokens {
pub request_token: String, pub request_token: String,

@ -5,6 +5,9 @@
#[macro_use] #[macro_use]
extern crate rouille; extern crate rouille;
#[macro_use]
extern crate schemars;
pub mod database; pub mod database;
pub mod server; pub mod server;
pub mod utils; pub mod utils;

@ -0,0 +1,80 @@
// flotte-user-management server for managing users, roles and permissions
// Copyright (C) 2020 trivernis
// See LICENSE for more information
use schemars::JsonSchema;
use std::collections::HashMap;
use syntect::highlighting::ThemeSet;
use syntect::html::highlighted_html_for_string;
use syntect::parsing::SyntaxSet;
pub struct RESTDocumentation {
paths: HashMap<String, String>,
base_path: String,
}
impl RESTDocumentation {
pub fn new(base_path: &str) -> Self {
Self {
paths: HashMap::new(),
base_path: base_path.to_string(),
}
}
pub fn get(&self, path: String) -> String {
log::trace!("Rendering help for {}.", path);
format!(
"<html><head><style type='text/css'>{}</style></head><body>{}</body></html>",
include_str!("style.css"),
self.paths.get(&path).unwrap_or(&self.landing())
)
}
fn landing(&self) -> String {
let types = self.paths.keys().fold("".to_string(), |a, b| {
format!("{}<br><a href='{}?path={2}'>{2}</a>", a, self.base_path, b)
});
format!("<h1>Paths</h1><br>{}", types)
}
pub fn add_path<I: JsonSchema, O: JsonSchema>(
&mut self,
path: &str,
method: &str,
description: &str,
) -> Result<(), serde_json::error::Error> {
let input_schema = schema_for!(I);
let output_schema = schema_for!(O);
let input_json = highlight_json(serde_json::to_string_pretty(&input_schema)?);
let output_json = highlight_json(serde_json::to_string_pretty(&output_schema)?);
let content = format!(
"\
<a href={}>Back</a>
<h1>{}: {}</h1>
<p>{}</p>
<h2>Input</h2>
<code>{}</code>
<h2>Output</h2>
<code>{}</code>
",
self.base_path, method, path, description, input_json, output_json
);
self.paths.insert(path.to_string(), content);
Ok(())
}
}
fn highlight_json(input: String) -> String {
lazy_static::lazy_static! { static ref PS: SyntaxSet = SyntaxSet::load_defaults_nonewlines(); }
lazy_static::lazy_static! { static ref TS: ThemeSet = ThemeSet::load_defaults(); }
highlighted_html_for_string(
input.as_str(),
&PS,
PS.find_syntax_by_token("json").unwrap(),
&TS.themes["InspiredGitHub"],
)
}

@ -0,0 +1,22 @@
/*
* flotte-user-management server for managing users, roles and permissions
* Copyright (C) 2020 trivernis
* See LICENSE for more information
*/
body {
font-family: "Fira Sans", "Noto Sans", sans-serif;
}
code > pre {
font-family: "Fira Code", "DejaVu Sans Mono", monospace;
max-width: 100%;
padding: 2em;
border: 2px solid #EEE;
overflow: auto;
}
a {
font-weight: bold;
font-size: 1.5em;
}

@ -11,10 +11,13 @@ use rouille::{Request, Response, Server};
use serde::export::Formatter; use serde::export::Formatter;
use serde::Serialize; use serde::Serialize;
use crate::database::models::Role;
use crate::database::permissions::{ use crate::database::permissions::{
CREATE_ROLE_PERMISSION, UPDATE_ROLE_PERMISSION, VIEW_ROLE_PERMISSION, CREATE_ROLE_PERMISSION, UPDATE_ROLE_PERMISSION, VIEW_ROLE_PERMISSION,
}; };
use crate::database::tokens::SessionTokens;
use crate::database::Database; use crate::database::Database;
use crate::server::documentation::RESTDocumentation;
use crate::server::messages::{ use crate::server::messages::{
ErrorMessage, FullRoleData, LoginMessage, LogoutConfirmation, LogoutMessage, ModifyRoleRequest, ErrorMessage, FullRoleData, LoginMessage, LogoutConfirmation, LogoutMessage, ModifyRoleRequest,
RefreshMessage, RefreshMessage,
@ -97,6 +100,9 @@ impl UserHttpServer {
let database = Database::clone(&self.database); let database = Database::clone(&self.database);
let server = Server::new(&listen_address, move |request| { let server = Server::new(&listen_address, move |request| {
let mut response = router!(request, let mut response = router!(request,
(GET) (/info) => {
Self::info(request).unwrap_or_else(HTTPError::into)
},
(POST) (/login) => { (POST) (/login) => {
Self::login(&database, request).unwrap_or_else(HTTPError::into) Self::login(&database, request).unwrap_or_else(HTTPError::into)
}, },
@ -149,6 +155,51 @@ impl UserHttpServer {
server.run() server.run()
} }
fn build_docs() -> Result<RESTDocumentation, serde_json::Error> {
let mut doc = RESTDocumentation::new("/info");
doc.add_path::<LoginMessage, SessionTokens>(
"/login",
"POST",
"Returns request and refresh tokens",
)?;
doc.add_path::<RefreshMessage, SessionTokens>(
"/new-token",
"POST",
"Returns a new request token",
)?;
doc.add_path::<LogoutMessage, LogoutConfirmation>(
"/logout",
"POST",
"Invalidates the refresh and request tokens",
)?;
doc.add_path::<(), FullRoleData>(
"/roles/{name:String}",
"GET",
"Returns the role with the given name",
)?;
doc.add_path::<(), Vec<Role>>("/roles", "GET", "Returns a list of all roles")?;
doc.add_path::<ModifyRoleRequest, FullRoleData>(
"/roles/create",
"POST",
"Creates a new role",
)?;
doc.add_path::<ModifyRoleRequest, FullRoleData>(
"/roles/update",
"POST",
"Updates an existing role",
)?;
Ok(doc)
}
fn info(request: &Request) -> HTTPResult<Response> {
lazy_static::lazy_static! {static ref DOCS: RESTDocumentation = UserHttpServer::build_docs().unwrap();}
Ok(Response::html(
DOCS.get(request.get_param("path").unwrap_or("/".to_string())),
))
}
/// Handles the login part of the REST api /// Handles the login part of the REST api
fn login(database: &Database, request: &Request) -> HTTPResult<Response> { fn login(database: &Database, request: &Request) -> HTTPResult<Response> {
let login_request: LoginMessage = let login_request: LoginMessage =

@ -71,7 +71,7 @@ pub struct GetPermissionsRequest {
pub roles: Vec<i32>, pub roles: Vec<i32>,
} }
#[derive(Deserialize)] #[derive(Deserialize, JsonSchema)]
pub struct ModifyRoleRequest { pub struct ModifyRoleRequest {
pub name: String, pub name: String,
pub description: Option<String>, pub description: Option<String>,
@ -83,31 +83,31 @@ pub struct CreatePermissionsRequest {
pub permissions: Vec<CreatePermissionsEntry>, pub permissions: Vec<CreatePermissionsEntry>,
} }
#[derive(Deserialize, Zeroize)] #[derive(Deserialize, Zeroize, JsonSchema)]
#[zeroize(drop)] #[zeroize(drop)]
pub struct LoginMessage { pub struct LoginMessage {
pub email: String, pub email: String,
pub password: String, pub password: String,
} }
#[derive(Deserialize, Zeroize)] #[derive(Deserialize, Zeroize, JsonSchema)]
#[zeroize(drop)] #[zeroize(drop)]
pub struct RefreshMessage { pub struct RefreshMessage {
pub refresh_token: String, pub refresh_token: String,
} }
#[derive(Deserialize, Zeroize)] #[derive(Deserialize, Zeroize, JsonSchema)]
#[zeroize(drop)] #[zeroize(drop)]
pub struct LogoutMessage { pub struct LogoutMessage {
pub request_token: String, pub request_token: String,
} }
#[derive(Serialize)] #[derive(Serialize, JsonSchema)]
pub struct LogoutConfirmation { pub struct LogoutConfirmation {
pub success: bool, pub success: bool,
} }
#[derive(Serialize)] #[derive(Serialize, JsonSchema)]
pub struct FullRoleData { pub struct FullRoleData {
pub id: i32, pub id: i32,
pub name: String, pub name: String,

@ -2,6 +2,7 @@
// Copyright (C) 2020 trivernis // Copyright (C) 2020 trivernis
// See LICENSE for more information // See LICENSE for more information
pub mod documentation;
pub mod http_server; pub mod http_server;
pub mod messages; pub mod messages;
pub mod rpc_methods; pub mod rpc_methods;

Loading…
Cancel
Save