add request signatures and GET/POST requests
This commit is contained in:
parent
0e71e9dc5f
commit
7a694623e5
24 changed files with 690 additions and 30 deletions
32
identity/.sqlx/query-29b5c01c2511f63be1ae213d8b1999becab8ab1c5f6da712db7f759f30f2bca8.json
generated
Normal file
32
identity/.sqlx/query-29b5c01c2511f63be1ae213d8b1999becab8ab1c5f6da712db7f759f30f2bca8.json
generated
Normal file
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT * FROM instance WHERE id = 1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "public_key",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "private_key",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "29b5c01c2511f63be1ae213d8b1999becab8ab1c5f6da712db7f759f30f2bca8"
|
||||
}
|
62
identity/.sqlx/query-3402be9906900efd5c0e9f7ec10e9303a3068b728142dc90bb66389069608e28.json
generated
Normal file
62
identity/.sqlx/query-3402be9906900efd5c0e9f7ec10e9303a3068b728142dc90bb66389069608e28.json
generated
Normal file
|
@ -0,0 +1,62 @@
|
|||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "select\n a.id, a.username, a.email, a.password, a.role as \"role: Role\", a.avatar\n from accounts a\n join tokens t on t.account_id = a.id\n where t.account_id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "username",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "email",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "password",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "role: Role",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
"name": "account_role",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"user",
|
||||
"admin"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "avatar",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "3402be9906900efd5c0e9f7ec10e9303a3068b728142dc90bb66389069608e28"
|
||||
}
|
31
identity/.sqlx/query-665e75d8304aab441acb50e78793092e5c603089672bceff3a4fa17dd624d933.json
generated
Normal file
31
identity/.sqlx/query-665e75d8304aab441acb50e78793092e5c603089672bceff3a4fa17dd624d933.json
generated
Normal file
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "insert into accounts (id, username, email, password) values ($1, $2, $3, $4) returning id, username",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "username",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "665e75d8304aab441acb50e78793092e5c603089672bceff3a4fa17dd624d933"
|
||||
}
|
62
identity/.sqlx/query-acb98db00095d510a139a8a4fd61edcd04e1570c96869993b6b20d16ad71da3c.json
generated
Normal file
62
identity/.sqlx/query-acb98db00095d510a139a8a4fd61edcd04e1570c96869993b6b20d16ad71da3c.json
generated
Normal file
|
@ -0,0 +1,62 @@
|
|||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "select\n id, username, email, password, role as \"role: Role\", avatar\n from accounts where username = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "username",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "email",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "password",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "role: Role",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
"name": "account_role",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"user",
|
||||
"admin"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "avatar",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "acb98db00095d510a139a8a4fd61edcd04e1570c96869993b6b20d16ad71da3c"
|
||||
}
|
20
identity/.sqlx/query-ad1116f3443ba2c3093c6c7605508d7c7ce717e42f970ea30fd7e828c8c9ef06.json
generated
Normal file
20
identity/.sqlx/query-ad1116f3443ba2c3093c6c7605508d7c7ce717e42f970ea30fd7e828c8c9ef06.json
generated
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "select exists(select * from instance)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "exists",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "ad1116f3443ba2c3093c6c7605508d7c7ce717e42f970ea30fd7e828c8c9ef06"
|
||||
}
|
65
identity/.sqlx/query-bdf6cfaad2176b76f5092898e796580047c45469fc9a867a4cc93a49343a5333.json
generated
Normal file
65
identity/.sqlx/query-bdf6cfaad2176b76f5092898e796580047c45469fc9a867a4cc93a49343a5333.json
generated
Normal file
|
@ -0,0 +1,65 @@
|
|||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "insert into chat_instances\n (id, domain, base_url, public_key) values ($1, $2, $3, $4)\n returning id, domain, base_url, public_key,\n status as \"status: InstanceStatus\", reason",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "domain",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "base_url",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "public_key",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "status: InstanceStatus",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
"name": "instance_status",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"active",
|
||||
"suspended"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "reason",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "bdf6cfaad2176b76f5092898e796580047c45469fc9a867a4cc93a49343a5333"
|
||||
}
|
15
identity/.sqlx/query-d125d3c97a59ac5c4ba04eb02d9a8a35d5dc84b0860ba22a8c105d457cb8b4e6.json
generated
Normal file
15
identity/.sqlx/query-d125d3c97a59ac5c4ba04eb02d9a8a35d5dc84b0860ba22a8c105d457cb8b4e6.json
generated
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "insert into tokens (token, account_id) values ($1, $2)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "d125d3c97a59ac5c4ba04eb02d9a8a35d5dc84b0860ba22a8c105d457cb8b4e6"
|
||||
}
|
62
identity/.sqlx/query-dab570575b63e52a4bee944f139121074794c172310c88bdcdd8bef083df9431.json
generated
Normal file
62
identity/.sqlx/query-dab570575b63e52a4bee944f139121074794c172310c88bdcdd8bef083df9431.json
generated
Normal file
|
@ -0,0 +1,62 @@
|
|||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "select id, domain, base_url, public_key,\n status as \"status: InstanceStatus\", reason\n from chat_instances where domain = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "domain",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "base_url",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "public_key",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "status: InstanceStatus",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
"name": "instance_status",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"active",
|
||||
"suspended"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "reason",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "dab570575b63e52a4bee944f139121074794c172310c88bdcdd8bef083df9431"
|
||||
}
|
15
identity/.sqlx/query-e8680ec00d42254b8fc8240636d8e91cbd34ef31391418828f4a0bb4e9e2a4ce.json
generated
Normal file
15
identity/.sqlx/query-e8680ec00d42254b8fc8240636d8e91cbd34ef31391418828f4a0bb4e9e2a4ce.json
generated
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "insert into instance (public_key, private_key) values ($1, $2)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "e8680ec00d42254b8fc8240636d8e91cbd34ef31391418828f4a0bb4e9e2a4ce"
|
||||
}
|
|
@ -26,3 +26,4 @@ bcrypt = "0.15.0"
|
|||
base64 = "0.21.7"
|
||||
sha256 = "1.5.0"
|
||||
reqwest = { version = "0.11.23", features = ["json", "gzip", "brotli", "multipart"] }
|
||||
chrono = "0.4.31"
|
||||
|
|
|
@ -10,11 +10,9 @@ pub const CONFIG_FILE: &str = "config.identity.toml";
|
|||
pub struct Config {
|
||||
pub database_url: String,
|
||||
pub port: u16,
|
||||
pub domain: String,
|
||||
pub auto_migrate: Option<bool>,
|
||||
pub log_level: Option<String>,
|
||||
/// Whether to try HTTP if a server is not served over HTTPS.
|
||||
/// This should only be set to `true` in development.
|
||||
pub insecure_requests: Option<bool>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
|
@ -43,21 +41,16 @@ impl Config {
|
|||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
/// If true, the server will try to identify remote servers over HTTP if they are not available over HTTPS
|
||||
pub fn should_try_insecure(&self) -> bool {
|
||||
self.insecure_requests.unwrap_or(false)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Config {
|
||||
database_url: env::var("DATABASE_URL").unwrap_or("".into()),
|
||||
domain: "".into(),
|
||||
port: 3000,
|
||||
auto_migrate: None,
|
||||
log_level: None,
|
||||
insecure_requests: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use base64::prelude::{Engine, BASE64_URL_SAFE_NO_PAD};
|
||||
use eyre::{Context, Report, Result};
|
||||
use foxchat::QueryError;
|
||||
use foxchat::FoxError;
|
||||
use rand::RngCore;
|
||||
use sqlx::{PgExecutor, Pool, Postgres};
|
||||
|
||||
|
@ -22,16 +22,16 @@ pub async fn get_user_by_username_and_password(
|
|||
.await
|
||||
.map_err(|e| -> Report {
|
||||
match e {
|
||||
sqlx::Error::RowNotFound => QueryError::NotFound.into(),
|
||||
sqlx::Error::RowNotFound => FoxError::NotFound.into(),
|
||||
_ => e.into(),
|
||||
}
|
||||
})?;
|
||||
|
||||
if bcrypt::verify(password, &account.password).map_err(|_| QueryError::NotFound)? {
|
||||
if bcrypt::verify(password, &account.password).map_err(|_| FoxError::NotFound)? {
|
||||
return Ok(account);
|
||||
}
|
||||
|
||||
Err(QueryError::NotFound.into())
|
||||
Err(FoxError::NotFound.into())
|
||||
}
|
||||
|
||||
pub async fn check_token(pool: &Pool<Postgres>, token: String) -> Result<Account> {
|
||||
|
@ -51,7 +51,7 @@ pub async fn check_token(pool: &Pool<Postgres>, token: String) -> Result<Account
|
|||
|
||||
match account {
|
||||
Some(a) => Ok(a),
|
||||
None => Err(QueryError::NotFound.into()),
|
||||
None => Err(FoxError::NotFound.into()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ pub fn new(pool: Pool<Postgres>, config: Config) -> Router {
|
|||
let app = Router::new()
|
||||
.merge(account::router())
|
||||
.route("/_fox/ident/node", get(node::get_node))
|
||||
.route("/_fox/ident/node/:domain", get(node::get_chat_node))
|
||||
.layer(TraceLayer::new_for_http())
|
||||
.layer(Extension(app_state));
|
||||
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use axum::{Extension, Json};
|
||||
use eyre::{Result, Context};
|
||||
use axum::{Extension, Json, extract::Path};
|
||||
use eyre::{Context, Result};
|
||||
use foxchat::http::ApiError;
|
||||
use rsa::pkcs1::EncodeRsaPublicKey;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{app_state::AppState, model::instance::Instance};
|
||||
use crate::{app_state::AppState, model::{instance::Instance, chat_instance::ChatInstance}};
|
||||
|
||||
pub async fn get_node(Extension(state): Extension<Arc<AppState>>) -> Result<Json<NodeResponse>, ApiError> {
|
||||
pub async fn get_node(
|
||||
Extension(state): Extension<Arc<AppState>>,
|
||||
) -> Result<Json<NodeResponse>, ApiError> {
|
||||
let instance = Instance::get(&state.pool).await?;
|
||||
|
||||
let public_key = instance
|
||||
|
@ -29,3 +31,12 @@ pub struct NodeResponse {
|
|||
pub software: &'static str,
|
||||
pub public_key: String,
|
||||
}
|
||||
|
||||
pub async fn get_chat_node(
|
||||
Extension(state): Extension<Arc<AppState>>,
|
||||
Path(domain): Path<String>,
|
||||
) -> Result<Json<ChatInstance>, ApiError> {
|
||||
let instance = ChatInstance::get(state, domain).await?;
|
||||
|
||||
Ok(Json(instance))
|
||||
}
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use eyre::Result;
|
||||
use foxchat::{fed::{self, request::is_valid_domain}, FoxError};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ulid::Ulid;
|
||||
|
||||
use crate::app_state::AppState;
|
||||
use crate::{app_state::AppState, model::instance::Instance};
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct ChatInstance {
|
||||
pub id: String,
|
||||
pub domain: String,
|
||||
|
@ -23,6 +26,10 @@ pub enum InstanceStatus {
|
|||
|
||||
impl ChatInstance {
|
||||
pub async fn get(state: Arc<AppState>, domain: String) -> Result<Self> {
|
||||
if !is_valid_domain(&domain) {
|
||||
return Err(FoxError::InvalidServer.into());
|
||||
}
|
||||
|
||||
if let Some(instance) = sqlx::query_as!(
|
||||
Self,
|
||||
r#"select id, domain, base_url, public_key,
|
||||
|
@ -36,9 +43,49 @@ impl ChatInstance {
|
|||
return Ok(instance);
|
||||
}
|
||||
|
||||
// TODO: identify server process
|
||||
// only try HTTP if `state.config.should_try_insecure()`
|
||||
let current_instance = Instance::get(&state.pool).await?;
|
||||
|
||||
todo!()
|
||||
let resp: HelloResponse = fed::post(
|
||||
¤t_instance.private_key,
|
||||
state.config.domain.clone(),
|
||||
domain.clone(),
|
||||
"/_fox/chat/hello".into(),
|
||||
None,
|
||||
&HelloRequest {
|
||||
host: state.config.domain.clone(),
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
if resp.host != domain.clone() {
|
||||
return Err(FoxError::InvalidServer.into());
|
||||
}
|
||||
|
||||
let instance = sqlx::query_as!(
|
||||
Self,
|
||||
r#"insert into chat_instances
|
||||
(id, domain, base_url, public_key) values ($1, $2, $3, $4)
|
||||
returning id, domain, base_url, public_key,
|
||||
status as "status: InstanceStatus", reason"#,
|
||||
Ulid::new().to_string(),
|
||||
domain.clone(),
|
||||
format!("https://{domain}"),
|
||||
resp.public_key
|
||||
)
|
||||
.fetch_one(&state.pool)
|
||||
.await?;
|
||||
|
||||
Ok(instance)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
struct HelloRequest {
|
||||
pub host: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct HelloResponse {
|
||||
pub public_key: String,
|
||||
pub host: String,
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue