mirror of
https://github.com/PluralKit/PluralKit.git
synced 2026-02-04 04:56:49 +00:00
feat(api): allow unauthed requests to /systems/:id/settings
This commit is contained in:
parent
0406c32f6b
commit
0610701252
14 changed files with 334 additions and 70 deletions
|
|
@ -1,3 +1,5 @@
|
|||
use pluralkit_models::{PKSystem, PrivacyLevel, SystemId};
|
||||
|
||||
pub const INTERNAL_SYSTEMID_HEADER: &'static str = "x-pluralkit-systemid";
|
||||
pub const INTERNAL_APPID_HEADER: &'static str = "x-pluralkit-appid";
|
||||
|
||||
|
|
@ -19,4 +21,28 @@ impl AuthState {
|
|||
pub fn app_id(&self) -> Option<i32> {
|
||||
self.app_id
|
||||
}
|
||||
|
||||
pub fn access_level_for(&self, a: &impl Authable) -> PrivacyLevel {
|
||||
if self
|
||||
.system_id
|
||||
.map(|id| id == a.authable_system_id())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
PrivacyLevel::Private
|
||||
} else {
|
||||
PrivacyLevel::Public
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// authable trait/impls
|
||||
|
||||
pub trait Authable {
|
||||
fn authable_system_id(&self) -> SystemId;
|
||||
}
|
||||
|
||||
impl Authable for PKSystem {
|
||||
fn authable_system_id(&self) -> SystemId {
|
||||
self.id
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1,2 @@
|
|||
pub mod private;
|
||||
pub mod system;
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ use axum::{
|
|||
};
|
||||
use hyper::StatusCode;
|
||||
use libpk::config;
|
||||
use pluralkit_models::{PKSystem, PKSystemConfig};
|
||||
use pluralkit_models::{PKSystem, PKSystemConfig, PrivacyLevel};
|
||||
use reqwest::ClientBuilder;
|
||||
|
||||
#[derive(serde::Deserialize, Debug)]
|
||||
|
|
@ -151,14 +151,12 @@ pub async fn discord_callback(
|
|||
.await
|
||||
.expect("failed to query");
|
||||
|
||||
if system.is_none() {
|
||||
let Some(system) = system else {
|
||||
return json_err(
|
||||
StatusCode::BAD_REQUEST,
|
||||
"user does not have a system registered".to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
let system = system.unwrap();
|
||||
};
|
||||
|
||||
let system_config: Option<PKSystemConfig> = sqlx::query_as(
|
||||
r#"
|
||||
|
|
@ -179,7 +177,7 @@ pub async fn discord_callback(
|
|||
(
|
||||
StatusCode::OK,
|
||||
serde_json::to_string(&serde_json::json!({
|
||||
"system": system.to_json(),
|
||||
"system": system.to_json(PrivacyLevel::Private),
|
||||
"config": system_config.to_json(),
|
||||
"user": user,
|
||||
"token": token,
|
||||
|
|
|
|||
68
crates/api/src/endpoints/system.rs
Normal file
68
crates/api/src/endpoints/system.rs
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
use axum::{
|
||||
extract::State,
|
||||
http::StatusCode,
|
||||
response::{IntoResponse, Response},
|
||||
Extension,
|
||||
};
|
||||
use serde_json::json;
|
||||
use sqlx::Postgres;
|
||||
use tracing::error;
|
||||
|
||||
use pluralkit_models::{PKSystem, PKSystemConfig, PrivacyLevel};
|
||||
|
||||
use crate::{auth::AuthState, util::json_err, ApiContext};
|
||||
|
||||
pub async fn get_system_settings(
|
||||
Extension(auth): Extension<AuthState>,
|
||||
Extension(system): Extension<PKSystem>,
|
||||
State(ctx): State<ApiContext>,
|
||||
) -> Response {
|
||||
let access_level = auth.access_level_for(&system);
|
||||
|
||||
let config = match sqlx::query_as::<Postgres, PKSystemConfig>(
|
||||
"select * from system_config where system = $1",
|
||||
)
|
||||
.bind(system.id)
|
||||
.fetch_optional(&ctx.db)
|
||||
.await
|
||||
{
|
||||
Ok(Some(config)) => config,
|
||||
Ok(None) => {
|
||||
error!(
|
||||
system = system.id,
|
||||
"failed to find system config for existing system"
|
||||
);
|
||||
return json_err(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
r#"{"message": "500: Internal Server Error", "code": 0}"#.to_string(),
|
||||
);
|
||||
}
|
||||
Err(err) => {
|
||||
error!(?err, "failed to query system config");
|
||||
return json_err(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
r#"{"message": "500: Internal Server Error", "code": 0}"#.to_string(),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
(
|
||||
StatusCode::OK,
|
||||
serde_json::to_string(&match access_level {
|
||||
PrivacyLevel::Private => config.to_json(),
|
||||
PrivacyLevel::Public => json!({
|
||||
"pings_enabled": config.pings_enabled,
|
||||
"latch_timeout": config.latch_timeout,
|
||||
"case_sensitive_proxy_tags": config.case_sensitive_proxy_tags,
|
||||
"proxy_error_message_enabled": config.proxy_error_message_enabled,
|
||||
"hid_display_split": config.hid_display_split,
|
||||
"hid_display_caps": config.hid_display_caps,
|
||||
"hid_list_padding": config.hid_list_padding,
|
||||
"proxy_switch": config.proxy_switch,
|
||||
"name_format": config.name_format,
|
||||
}),
|
||||
})
|
||||
.unwrap(),
|
||||
)
|
||||
.into_response()
|
||||
}
|
||||
|
|
@ -77,7 +77,7 @@ fn router(ctx: ApiContext) -> Router {
|
|||
Router::new()
|
||||
.route("/v2/systems/{system_id}", get(rproxy))
|
||||
.route("/v2/systems/{system_id}", patch(rproxy))
|
||||
.route("/v2/systems/{system_id}/settings", get(rproxy))
|
||||
.route("/v2/systems/{system_id}/settings", get(endpoints::system::get_system_settings))
|
||||
.route("/v2/systems/{system_id}/settings", patch(rproxy))
|
||||
|
||||
.route("/v2/systems/{system_id}/members", get(rproxy))
|
||||
|
|
@ -134,10 +134,12 @@ fn router(ctx: ApiContext) -> Router {
|
|||
.route("/v2/groups/{group_id}/oembed.json", get(rproxy))
|
||||
|
||||
.layer(middleware::ratelimit::ratelimiter(middleware::ratelimit::do_request_ratelimited)) // this sucks
|
||||
.layer(axum::middleware::from_fn(middleware::ignore_invalid_routes))
|
||||
.layer(axum::middleware::from_fn(middleware::cors))
|
||||
.layer(axum::middleware::from_fn(middleware::logger))
|
||||
|
||||
.layer(axum::middleware::from_fn(middleware::ignore_invalid_routes::ignore_invalid_routes))
|
||||
.layer(axum::middleware::from_fn(middleware::cors::cors))
|
||||
.layer(axum::middleware::from_fn(middleware::logger::logger))
|
||||
|
||||
.layer(axum::middleware::from_fn_with_state(ctx.clone(), middleware::params::params))
|
||||
.layer(axum::middleware::from_fn_with_state(ctx.clone(), middleware::auth::auth))
|
||||
|
||||
.layer(tower_http::catch_panic::CatchPanicLayer::custom(util::handle_panic))
|
||||
|
|
|
|||
|
|
@ -1,12 +1,6 @@
|
|||
mod cors;
|
||||
pub use cors::cors;
|
||||
|
||||
mod logger;
|
||||
pub use logger::logger;
|
||||
|
||||
mod ignore_invalid_routes;
|
||||
pub use ignore_invalid_routes::ignore_invalid_routes;
|
||||
|
||||
pub mod ratelimit;
|
||||
|
||||
pub mod auth;
|
||||
pub mod cors;
|
||||
pub mod ignore_invalid_routes;
|
||||
pub mod logger;
|
||||
pub mod params;
|
||||
pub mod ratelimit;
|
||||
|
|
|
|||
123
crates/api/src/middleware/params.rs
Normal file
123
crates/api/src/middleware/params.rs
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
use axum::{
|
||||
extract::{Request, State},
|
||||
http::StatusCode,
|
||||
middleware::Next,
|
||||
response::Response,
|
||||
routing::url_params::UrlParams,
|
||||
};
|
||||
|
||||
use sqlx::{types::Uuid, Postgres};
|
||||
use tracing::error;
|
||||
|
||||
use crate::auth::AuthState;
|
||||
use crate::{util::json_err, ApiContext};
|
||||
use pluralkit_models::PKSystem;
|
||||
|
||||
pub async fn params(State(ctx): State<ApiContext>, mut req: Request, next: Next) -> Response {
|
||||
let pms = match req.extensions().get::<UrlParams>() {
|
||||
None => Vec::new(),
|
||||
Some(UrlParams::Params(pms)) => pms.clone(),
|
||||
_ => {
|
||||
return json_err(
|
||||
StatusCode::BAD_REQUEST,
|
||||
r#"{"error": "400: Bad Request", "code": 0}"#.to_string(),
|
||||
)
|
||||
.into()
|
||||
}
|
||||
};
|
||||
|
||||
for (key, value) in pms {
|
||||
match key.as_ref() {
|
||||
"system_id" => match value.as_str() {
|
||||
"@me" => {
|
||||
let Some(system_id) = req
|
||||
.extensions()
|
||||
.get::<AuthState>()
|
||||
.expect("missing auth state")
|
||||
.system_id()
|
||||
else {
|
||||
return json_err(
|
||||
StatusCode::UNAUTHORIZED,
|
||||
r#"{"error": "401: Missing or invalid Authorization header", "code": 0}"#.to_string(),
|
||||
)
|
||||
.into();
|
||||
};
|
||||
|
||||
match sqlx::query_as::<Postgres, PKSystem>(
|
||||
"select * from systems where id = $1",
|
||||
)
|
||||
.bind(system_id)
|
||||
.fetch_optional(&ctx.db)
|
||||
.await
|
||||
{
|
||||
Ok(Some(system)) => {
|
||||
req.extensions_mut().insert(system);
|
||||
}
|
||||
Ok(None) => {
|
||||
error!(
|
||||
?system_id,
|
||||
"could not find previously authenticated system in db"
|
||||
);
|
||||
return json_err(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
r#"{"message": "500: Internal Server Error", "code": 0}"#
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
Err(err) => {
|
||||
error!(
|
||||
?err,
|
||||
"failed to query previously authenticated system in db"
|
||||
);
|
||||
return json_err(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
r#"{"message": "500: Internal Server Error", "code": 0}"#
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
id => {
|
||||
match match Uuid::parse_str(id) {
|
||||
Ok(uuid) => sqlx::query_as::<Postgres, PKSystem>(
|
||||
"select * from systems where uuid = $1",
|
||||
)
|
||||
.bind(uuid),
|
||||
Err(_) => sqlx::query_as::<Postgres, PKSystem>(
|
||||
"select * from systems where hid = $1",
|
||||
)
|
||||
.bind(id),
|
||||
}
|
||||
.fetch_optional(&ctx.db)
|
||||
.await
|
||||
{
|
||||
Ok(Some(system)) => {
|
||||
req.extensions_mut().insert(system);
|
||||
}
|
||||
Ok(None) => {
|
||||
return json_err(
|
||||
StatusCode::NOT_FOUND,
|
||||
r#"{"message":"System not found.","code":20001}"#.to_string(),
|
||||
)
|
||||
}
|
||||
Err(err) => {
|
||||
error!(?err, ?id, "failed to query system from path in db");
|
||||
return json_err(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
r#"{"message": "500: Internal Server Error", "code": 0}"#
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"member_id" => {}
|
||||
"group_id" => {}
|
||||
"switch_id" => {}
|
||||
"guild_id" => {}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
next.run(req).await
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue