fixup! cargo fmt
Some checks failed
Build and push Rust service Docker images / rust docker build (push) Has been cancelled
rust checks / cargo fmt (push) Has been cancelled

This commit is contained in:
Iris System 2025-08-18 22:33:03 +12:00
parent be218c89cc
commit 3b2c1332c2
10 changed files with 99 additions and 72 deletions

View file

@ -29,7 +29,7 @@ jobs:
- uses: docker/setup-buildx-action@v1
# main docker build
- run: echo "BRANCH_NAME=${GITHUB_REF#refs/heads/}" >> $GITHUB_ENV
- run: echo "BRANCH_NAME=${GITHUB_REF#refs/heads/}" | sed 's|/|-|g' >> $GITHUB_ENV
- uses: docker/build-push-action@v2
with:
# https://github.com/docker/build-push-action/issues/378

View file

@ -28,18 +28,24 @@ impl AccessLevel {
pub struct AuthState {
system_id: Option<i32>,
app_id: Option<Uuid>,
api_key_id: Option<Uuid>,
access_level: AccessLevel,
api_key_id: Option<Uuid>,
access_level: AccessLevel,
internal: bool,
}
impl AuthState {
pub fn new(system_id: Option<i32>, app_id: Option<Uuid>, api_key_id: Option<Uuid>, access_level: AccessLevel, internal: bool) -> Self {
pub fn new(
system_id: Option<i32>,
app_id: Option<Uuid>,
api_key_id: Option<Uuid>,
access_level: AccessLevel,
internal: bool,
) -> Self {
Self {
system_id,
app_id,
api_key_id,
access_level,
api_key_id,
access_level,
internal,
}
}
@ -56,9 +62,9 @@ impl AuthState {
self.api_key_id
}
pub fn access_level(&self) -> AccessLevel {
self.access_level.clone()
}
pub fn access_level(&self) -> AccessLevel {
self.access_level.clone()
}
pub fn internal(&self) -> bool {
self.internal

View file

@ -1,12 +1,12 @@
use crate::{util::json_err, AuthState, ApiContext};
use pluralkit_models::{ApiKeyType, PKApiKey, PKSystem, SystemId};
use crate::{util::json_err, ApiContext, AuthState};
use pk_macros::api_internal_endpoint;
use pluralkit_models::{ApiKeyType, PKApiKey, PKSystem, SystemId};
use axum::{
extract::State,
http::StatusCode,
response::{IntoResponse, Json, Response},
Extension,
Extension,
};
use sqlx::Postgres;
@ -23,7 +23,7 @@ pub struct NewApiKeyRequestData {
#[api_internal_endpoint]
pub async fn create_api_key_user(
State(ctx): State<ApiContext>,
Extension(auth): Extension<AuthState>,
Extension(auth): Extension<AuthState>,
Json(req): Json<NewApiKeyRequestData>,
) -> Response {
let system: Option<PKSystem> = sqlx::query_as("select * from systems where id = $1")
@ -76,7 +76,8 @@ pub async fn create_api_key_user(
"valid": true,
}))
.expect("should not error"),
).into_response());
)
.into_response());
}
let token: PKApiKey = sqlx::query_as(
@ -110,5 +111,6 @@ pub async fn create_api_key_user(
"token": token,
}))
.expect("should not error"),
).into_response())
)
.into_response())
}

View file

@ -1,6 +1,6 @@
use crate::{util::json_err, ApiContext};
use libpk::config;
use pluralkit_models::{PrivacyLevel, PKApiKey, PKSystem, PKSystemConfig};
use pluralkit_models::{PKApiKey, PKSystem, PKSystemConfig, PrivacyLevel};
use axum::{
extract::{self, State},
@ -201,5 +201,6 @@ pub async fn discord_callback(
"token": token,
}))
.expect("should not error"),
).into_response())
)
.into_response())
}

View file

@ -1,6 +1,9 @@
#![feature(let_chains)]
use auth::{AuthState, INTERNAL_APPID_HEADER, INTERNAL_SYSTEMID_HEADER, INTERNAL_TOKENID_HEADER, INTERNAL_PRIVACYLEVEL_HEADER};
use auth::{
AuthState, INTERNAL_APPID_HEADER, INTERNAL_PRIVACYLEVEL_HEADER, INTERNAL_SYSTEMID_HEADER,
INTERNAL_TOKENID_HEADER,
};
use axum::{
body::Body,
extract::{Request as ExtractRequest, State},
@ -15,8 +18,8 @@ use hyper_util::{
};
use jsonwebtoken::{DecodingKey, EncodingKey};
use tracing::{error, info};
use pk_macros::api_endpoint;
use tracing::{error, info};
mod auth;
mod endpoints;
@ -57,21 +60,30 @@ async fn rproxy(
headers.remove(INTERNAL_SYSTEMID_HEADER);
headers.remove(INTERNAL_APPID_HEADER);
headers.remove(INTERNAL_TOKENID_HEADER);
headers.remove(INTERNAL_PRIVACYLEVEL_HEADER);
headers.remove(INTERNAL_TOKENID_HEADER);
headers.remove(INTERNAL_PRIVACYLEVEL_HEADER);
if let Some(sid) = auth.system_id() {
headers.append(INTERNAL_SYSTEMID_HEADER, sid.into());
headers.append(INTERNAL_PRIVACYLEVEL_HEADER, HeaderValue::from_str(&auth.access_level().privacy_level().to_string())?);
headers.append(
INTERNAL_PRIVACYLEVEL_HEADER,
HeaderValue::from_str(&auth.access_level().privacy_level().to_string())?,
);
}
if let Some(aid) = auth.app_id() {
headers.append(INTERNAL_APPID_HEADER, HeaderValue::from_str(&format!("{}", aid))?);
headers.append(
INTERNAL_APPID_HEADER,
HeaderValue::from_str(&format!("{}", aid))?,
);
}
if let Some(tid) = auth.api_key_id() {
headers.append(
INTERNAL_TOKENID_HEADER,
HeaderValue::from_str(&format!("{}", tid))?,
);
}
if let Some(tid) = auth.api_key_id() {
headers.append(INTERNAL_TOKENID_HEADER, HeaderValue::from_str(&format!("{}", tid))?);
}
Ok(ctx.rproxy_client.request(req).await?.into_response())
}

View file

@ -1,19 +1,19 @@
use axum::{
extract::{Request, State, MatchedPath},
extract::{MatchedPath, Request, State},
http::StatusCode,
middleware::Next,
response::Response,
};
use uuid::Uuid;
use subtle::ConstantTimeEq;
use uuid::Uuid;
use tracing::error;
use sqlx::Postgres;
use tracing::error;
use pluralkit_models::{ApiKeyType, PKApiKey};
use crate::auth::{AccessLevel, AuthState};
use crate::{util::json_err, ApiContext};
use pluralkit_models::{ApiKeyType, PKApiKey};
pub fn is_part_path<'a, 'b>(part: &'a str, endpoint: &'b str) -> bool {
if !endpoint.starts_with("/v2/") {
@ -113,7 +113,7 @@ pub fn apikey_can_access(token: &PKApiKey, method: String, endpoint: String) ->
}
pub async fn auth(State(ctx): State<ApiContext>, mut req: Request, next: Next) -> Response {
let endpoint = req
let endpoint = req
.extensions()
.get::<MatchedPath>()
.cloned()
@ -122,8 +122,8 @@ pub async fn auth(State(ctx): State<ApiContext>, mut req: Request, next: Next) -
let mut authed_system_id: Option<i32> = None;
let mut authed_app_id: Option<Uuid> = None;
let mut authed_api_key_id: Option<Uuid> = None;
let mut access_level = AccessLevel::None;
let mut authed_api_key_id: Option<Uuid> = None;
let mut access_level = AccessLevel::None;
// fetch user authorization
if let Some(system_auth_header) = req
@ -131,10 +131,12 @@ pub async fn auth(State(ctx): State<ApiContext>, mut req: Request, next: Next) -
.get("authorization")
.map(|h| h.to_str().ok())
.flatten()
{
if system_auth_header.starts_with("Bearer ")
&& let Some(tid) =
PKApiKey::parse_header_str(system_auth_header[7..].to_string(), &ctx.token_publickey)
{
if system_auth_header.starts_with("Bearer ")
&& let Some(tid) = PKApiKey::parse_header_str(
system_auth_header[7..].to_string(),
&ctx.token_publickey,
)
&& let Some(token) =
sqlx::query_as::<Postgres, PKApiKey>("select * from api_keys where id = $1")
.bind(&tid)
@ -142,11 +144,10 @@ pub async fn auth(State(ctx): State<ApiContext>, mut req: Request, next: Next) -
.await
.expect("failed to query apitoken in postgres")
{
authed_system_id = Some(token.system);
authed_api_key_id = Some(tid);
access_level = apikey_can_access(&token, req.method().to_string(), endpoint.clone());
}
else if let Some(system_id) =
authed_system_id = Some(token.system);
authed_api_key_id = Some(tid);
access_level = apikey_can_access(&token, req.method().to_string(), endpoint.clone());
} else if let Some(system_id) =
match libpk::db::repository::legacy_token_auth(&ctx.db, system_auth_header).await {
Ok(val) => val,
Err(err) => {
@ -157,11 +158,11 @@ pub async fn auth(State(ctx): State<ApiContext>, mut req: Request, next: Next) -
);
}
}
{
authed_system_id = Some(system_id);
access_level = AccessLevel::Full;
}
}
{
authed_system_id = Some(system_id);
access_level = AccessLevel::Full;
}
}
// fetch app authorization
if let Some(app_auth_header) = req
@ -170,7 +171,7 @@ pub async fn auth(State(ctx): State<ApiContext>, mut req: Request, next: Next) -
.map(|h| h.to_str().ok())
.flatten()
&& let Some(app_id) =
match libpk::db::repository::app_token_auth(&ctx.db, app_auth_header).await {
match libpk::db::repository::app_token_auth(&ctx.db, app_auth_header).await {
Ok(val) => val,
Err(err) => {
error!(?err, "failed to query authorization token in postgres");
@ -199,8 +200,13 @@ pub async fn auth(State(ctx): State<ApiContext>, mut req: Request, next: Next) -
false
};
req.extensions_mut()
.insert(AuthState::new(authed_system_id, authed_app_id, authed_api_key_id, access_level, internal));
req.extensions_mut().insert(AuthState::new(
authed_system_id,
authed_app_id,
authed_api_key_id,
access_level,
internal,
));
next.run(req).await
}

View file

@ -12,9 +12,9 @@ use sqlx::Postgres;
use tracing::{debug, error, info, warn};
use crate::{
ApiContext,
auth::AuthState,
util::{header_or_unknown, json_err},
ApiContext,
};
use pluralkit_models::PKExternalApp;

View file

@ -22,10 +22,10 @@ struct LegacyTokenDbResponse {
}
pub async fn app_token_auth(
pool: &sqlx::postgres::PgPool,
token: &str,
pool: &sqlx::postgres::PgPool,
token: &str,
) -> anyhow::Result<Option<Uuid>> {
let mut app: Vec<AppTokenDbResponse> =
let mut app: Vec<AppTokenDbResponse> =
sqlx::query_as("select id from external_apps where api_rl_token = $1")
.bind(token)
.fetch_all(pool)

View file

@ -9,7 +9,7 @@ fn pretty_print(ts: &proc_macro2::TokenStream) -> String {
pub fn macro_impl(
_args: proc_macro::TokenStream,
input: proc_macro::TokenStream,
is_internal: bool,
is_internal: bool,
) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as ItemFn);
@ -35,15 +35,15 @@ pub fn macro_impl(
})
.collect();
let internal_res = if is_internal {
quote! {
if !auth.internal() {
return crate::error::FORBIDDEN_INTERNAL_ROUTE.into_response();
}
}
} else {
quote!()
};
let internal_res = if is_internal {
quote! {
if !auth.internal() {
return crate::error::FORBIDDEN_INTERNAL_ROUTE.into_response();
}
}
} else {
quote!()
};
let res = quote! {
#[allow(unused_mut)]
@ -52,7 +52,7 @@ pub fn macro_impl(
#fn_body
}
#internal_res
#internal_res
match inner(#(#pms),*).await {
Ok(res) => res.into_response(),
Err(err) => err.into_response(),

View file

@ -21,12 +21,12 @@ impl From<i32> for PrivacyLevel {
}
impl PrivacyLevel {
pub fn to_string(&self) -> String {
match self {
PrivacyLevel::Public => "public".into(),
PrivacyLevel::Private => "private".into(),
}
}
pub fn to_string(&self) -> String {
match self {
PrivacyLevel::Public => "public".into(),
PrivacyLevel::Private => "private".into(),
}
}
}
macro_rules! model {