Are we JSON yet? Kinda. But we're closer. (#1967)

This commit is contained in:
James Hodgkinson 2023-08-14 08:51:44 +10:00 committed by GitHub
parent c24d214463
commit cc79f7eba1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 407 additions and 1008 deletions

1
Cargo.lock generated
View file

@ -2439,6 +2439,7 @@ dependencies = [
"kanidmd_lib",
"oauth2",
"reqwest",
"serde",
"serde_json",
"sketching",
"testkit-macros",

View file

@ -24,6 +24,7 @@ use std::os::unix::fs::MetadataExt;
use std::path::Path;
use std::time::Duration;
use kanidm_proto::constants::APPLICATION_JSON;
use kanidm_proto::v1::*;
use reqwest::header::CONTENT_TYPE;
pub use reqwest::StatusCode;
@ -44,7 +45,6 @@ mod service_account;
mod sync_account;
mod system;
pub const APPLICATION_JSON: &str = "application/json";
pub const KOPID: &str = "X-KANIDM-OPID";
pub const KSESSIONID: &str = "X-KANIDM-AUTH-SESSION-ID";
@ -621,7 +621,7 @@ impl KanidmClient {
.map_err(|e| ClientError::JsonDecode(e, opid))
}
async fn perform_post_request<R: Serialize, T: DeserializeOwned>(
pub async fn perform_post_request<R: Serialize, T: DeserializeOwned>(
&self,
dest: &str,
request: R,

View file

@ -1,5 +1,7 @@
/// Because consistency is great!
pub const APPLICATION_JSON: &str = "application/json";
/// The "system" path for Kanidm client config
pub const DEFAULT_CLIENT_CONFIG_PATH: &str = "/etc/kanidm/config";
/// The user-owned path for Kanidm client config

View file

@ -341,7 +341,7 @@ pub struct OidcDiscoveryResponse {
}
#[skip_serializing_none]
#[derive(Serialize, Deserialize, Debug)]
#[derive(Serialize, Deserialize, Debug, Default)]
pub struct ErrorResponse {
pub error: String,
pub error_description: Option<String>,

View file

@ -611,8 +611,12 @@ impl fmt::Display for UnixUserToken {
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(deny_unknown_fields)]
pub struct AccountUnixExtend {
pub gidnumber: Option<u32>,
// TODO: rename shell to loginshell everywhere we can find
/// The internal attribute is "loginshell" but we use shell in the API currently
#[serde(alias = "loginshell")]
pub shell: Option<String>,
}

View file

@ -3,6 +3,7 @@
use axum::extract::State;
use axum::response::{IntoResponse, Response};
use axum::Extension;
use http::header::CONTENT_TYPE;
use http::HeaderValue;
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
@ -163,7 +164,7 @@ pub(crate) async fn manifest(
let mut res = Response::new(manifest_string);
res.headers_mut()
.insert("Content-Type", HeaderValue::from_static(MIME_TYPE_MANIFEST));
.insert(CONTENT_TYPE, HeaderValue::from_static(MIME_TYPE_MANIFEST));
res
}

View file

@ -10,10 +10,10 @@ pub async fn strict_transport_security_layer<B>(request: Request<B>, next: Next<
let mut response = next.run(request).await;
// add the header
let headers = response.headers_mut();
let hsts_header = HeaderValue::from_static(HSTS_HEADER);
headers.insert(http::header::STRICT_TRANSPORT_SECURITY, hsts_header);
response.headers_mut().insert(
http::header::STRICT_TRANSPORT_SECURITY,
HeaderValue::from_static(HSTS_HEADER),
);
response
}

View file

@ -5,6 +5,8 @@ use axum::{
response::Response,
TypedHeader,
};
#[cfg(debug_assertions)]
use http::header::CONTENT_TYPE;
use http::HeaderValue;
use uuid::Uuid;
@ -19,8 +21,9 @@ const KANIDM_VERSION: &str = env!("CARGO_PKG_VERSION");
/// Injects a header into the response with "X-KANIDM-VERSION" matching the version of the package.
pub async fn version_middleware<B>(request: Request<B>, next: Next<B>) -> Response {
let mut response = next.run(request).await;
let headers = response.headers_mut();
headers.insert("X-KANIDM-VERSION", HeaderValue::from_static(KANIDM_VERSION));
response
.headers_mut()
.insert("X-KANIDM-VERSION", HeaderValue::from_static(KANIDM_VERSION));
response
}
@ -31,6 +34,41 @@ pub struct KOpId {
pub uat: Option<String>,
}
/// Ensure the status code is 200..=299
#[cfg(debug_assertions)]
fn from_200_to_299(status: http::StatusCode) -> bool {
status.as_u16() >= 200 && status.as_u16() <= 299
}
#[test]
fn test_from_200_to_299() {
assert!(from_200_to_299(http::StatusCode::OK));
assert!(from_200_to_299(http::StatusCode::IM_USED));
assert!(!from_200_to_299(http::StatusCode::BAD_REQUEST));
assert!(!from_200_to_299(http::StatusCode::INTERNAL_SERVER_ERROR));
}
#[cfg(debug_assertions)]
/// This is a debug middleware to ensure that /v1/ endpoints only return JSON
#[instrument(name = "are_we_json_yet", skip_all)]
pub async fn are_we_json_yet<B>(request: Request<B>, next: Next<B>) -> Response {
let uri = request.uri().path().to_string();
let response = next.run(request).await;
if uri.starts_with("/v1") && from_200_to_299(response.status()) {
let headers = response.headers();
assert!(headers.contains_key(CONTENT_TYPE));
dbg!(headers.get(CONTENT_TYPE));
assert!(
headers.get(CONTENT_TYPE)
== Some(&HeaderValue::from_static(crate::https::APPLICATION_JSON))
);
}
response
}
/// This runs at the start of the request, adding an extension with `KOpId` which has useful things inside it.
#[instrument(name = "request", skip_all)]
pub async fn kopid_middleware<B>(

View file

@ -26,32 +26,26 @@ pub async fn security_headers_layer<B>(
//
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
// https://scotthelme.co.uk/hardening-your-http-response-headers/#x-content-type-options
#[allow(clippy::expect_used)]
headers.insert(
X_CONTENT_TYPE_OPTIONS,
HeaderValue::from_str(X_CONTENT_TYPE_OPTIONS_VALUE)
.expect("Failed to generate security header X-Content-Type-Options"),
HeaderValue::from_static(X_CONTENT_TYPE_OPTIONS_VALUE),
);
// Permissions policy defines access to platform services like geolocation, fullscreen etc.
//
// https://www.w3.org/TR/permissions-policy-1/
#[allow(clippy::expect_used)]
headers.insert(
"Permissions-Policy",
HeaderValue::from_str(PERMISSIONS_POLICY_VALUE)
.expect("Failed to generate security header Permissions-Policy"),
HeaderValue::from_static(PERMISSIONS_POLICY_VALUE),
);
// Don't send a referrer header when the user is navigating to a non-HTTPS URL
// Ref:
// https://scotthelme.co.uk/a-new-security-header-referrer-policy/
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy
#[allow(clippy::expect_used)]
headers.insert(
http::header::REFERRER_POLICY,
HeaderValue::from_str("no-referrer-when-downgrade")
.expect("Failed to generate Referer-Policy header"),
HeaderValue::from_static("no-referrer-when-downgrade"),
);
response

View file

@ -14,21 +14,24 @@ use crate::actors::v1_write::QueryServerWriteV1;
use crate::config::{Configuration, ServerRole, TlsConfiguration};
use axum::extract::connect_info::{IntoMakeServiceWithConnectInfo, ResponseFuture};
use axum::middleware::{from_fn, from_fn_with_state};
use axum::response::{Redirect, Response};
use axum::response::{IntoResponse, Redirect, Response};
use axum::routing::*;
use axum::Router;
use axum_csp::{CspDirectiveType, CspValue};
use axum_macros::FromRef;
use compact_jwt::{Jws, JwsSigner, JwsUnverified};
use generic::*;
use http::{HeaderMap, HeaderValue};
use http::header::{ACCESS_CONTROL_ALLOW_ORIGIN, CONTENT_TYPE};
use http::{HeaderMap, HeaderValue, StatusCode};
use hyper::server::accept::Accept;
use hyper::server::conn::{AddrStream, Http};
use hyper::Body;
use javascript::*;
use kanidm_proto::constants::APPLICATION_JSON;
use kanidm_proto::v1::OperationError;
use kanidmd_lib::status::StatusActor;
use openssl::ssl::{Ssl, SslAcceptor, SslFiletype, SslMethod};
use sketching::*;
use tokio_openssl::SslStream;
use futures_util::future::poll_fn;
@ -247,7 +250,13 @@ pub async fn create_https_server(
// This is because the last middleware here is the first to be entered and the last
// to be exited, and this middleware sets up ids' and other bits for for logging
// coherence to be maintained.
.layer(from_fn(middleware::kopid_middleware))
.layer(from_fn(middleware::kopid_middleware));
// layer which checks the responses have a content-type of JSON when we're in debug mode
#[cfg(debug_assertions)]
let app = app.layer(from_fn(middleware::are_we_json_yet));
let app = app
.with_state(state)
// the connect_info bit here lets us pick up the remote address of the client
.into_make_service_with_connect_info::<SocketAddr>();
@ -369,8 +378,8 @@ async fn handle_conn(
std::io::Error::from(ErrorKind::ConnectionAborted)
})
}
Err(_error) => {
// trace!("Failed to handle connection: {:?}", error);
Err(error) => {
trace!("Failed to handle connection: {:?}", error);
Ok(())
}
}
@ -393,7 +402,10 @@ pub fn to_axum_response<T: Serialize + core::fmt::Debug>(
};
trace!("Response Body: {:?}", body);
#[allow(clippy::unwrap_used)]
Response::builder().body(Body::from(body)).unwrap()
Response::builder()
.header(CONTENT_TYPE, APPLICATION_JSON)
.body(Body::from(body))
.unwrap()
}
Err(e) => {
debug!("OperationError: {:?}", e);
@ -430,3 +442,27 @@ pub fn to_axum_response<T: Serialize + core::fmt::Debug>(
}
}
}
/// Wrapper for the externally-defined error type from the protocol
pub struct HttpOperationError(OperationError);
impl IntoResponse for HttpOperationError {
fn into_response(self) -> Response {
let HttpOperationError(error) = self;
let body = match serde_json::to_string(&error) {
Ok(val) => val,
Err(e) => {
admin_warn!("Failed to serialize error response: original_error=\"{:?}\" serialization_error=\"{:?}\"", error , e);
format!("{:?}", error)
}
};
#[allow(clippy::unwrap_used)]
Response::builder()
.status(StatusCode::BAD_REQUEST)
.header(ACCESS_CONTROL_ALLOW_ORIGIN, "*")
.body(Body::from(body))
.unwrap()
.into_response()
}
}

View file

@ -1,17 +1,20 @@
use super::middleware::KOpId;
use super::v1::{json_rest_event_get, json_rest_event_post};
use super::{to_axum_response, ServerState};
use super::{to_axum_response, HttpOperationError, ServerState};
use axum::extract::{Path, Query, State};
use axum::middleware::from_fn;
use axum::response::{IntoResponse, Response};
use axum::routing::{get, post};
use axum::{Extension, Form, Json, Router};
use axum_macros::debug_handler;
use http::header::{
ACCESS_CONTROL_ALLOW_HEADERS, ACCESS_CONTROL_ALLOW_ORIGIN, AUTHORIZATION, LOCATION,
ACCESS_CONTROL_ALLOW_HEADERS, ACCESS_CONTROL_ALLOW_ORIGIN, AUTHORIZATION, CONTENT_TYPE,
LOCATION, WWW_AUTHENTICATE,
};
use http::{HeaderMap, HeaderValue, StatusCode};
use hyper::Body;
use kanidm_proto::oauth2::AuthorisationResponse;
use kanidm_proto::constants::APPLICATION_JSON;
use kanidm_proto::oauth2::{AuthorisationResponse, OidcDiscoveryResponse};
use kanidm_proto::v1::Entry as ProtoEntry;
use kanidmd_lib::idm::oauth2::{
AccessTokenIntrospectRequest, AccessTokenRequest, AuthorisationRequest, AuthorisePermitSuccess,
@ -22,6 +25,45 @@ use kanidmd_lib::prelude::*;
use kanidmd_lib::value::PartialValue;
use serde::{Deserialize, Serialize};
pub struct HTTPOauth2Error(Oauth2Error);
impl IntoResponse for HTTPOauth2Error {
fn into_response(self) -> Response {
let HTTPOauth2Error(error) = self;
if let Oauth2Error::AuthenticationRequired = error {
#[allow(clippy::unwrap_used)]
Response::builder()
.status(StatusCode::UNAUTHORIZED)
.header(WWW_AUTHENTICATE, "Bearer")
.header(ACCESS_CONTROL_ALLOW_ORIGIN, "*")
.body(Body::empty())
.unwrap()
} else {
let err = ErrorResponse {
error: error.to_string(),
..Default::default()
};
let body = match serde_json::to_string(&err) {
Ok(val) => val,
Err(e) => {
admin_warn!("Failed to serialize error response: original_error=\"{:?}\" serialization_error=\"{:?}\"", err, e);
format!("{:?}", err)
}
};
#[allow(clippy::unwrap_used)]
Response::builder()
.status(StatusCode::BAD_REQUEST)
.header(ACCESS_CONTROL_ALLOW_ORIGIN, "*")
.body(Body::from(body))
.unwrap()
}
.into_response()
}
}
// == Oauth2 Configuration Endpoints ==
/// List all the OAuth2 Resource Servers
@ -73,7 +115,7 @@ pub async fn oauth2_id_get(
State(state): State<ServerState>,
Path(rs_name): Path<String>,
Extension(kopid): Extension<KOpId>,
) -> impl IntoResponse {
) -> Response<Body> {
let filter = oauth2_id(&rs_name);
let res = state
@ -89,7 +131,7 @@ pub async fn oauth2_id_get_basic_secret(
State(state): State<ServerState>,
Extension(kopid): Extension<KOpId>,
Path(rs_name): Path<String>,
) -> impl IntoResponse {
) -> Response<Body> {
let filter = oauth2_id(&rs_name);
let res = state
.qe_r_ref
@ -103,7 +145,7 @@ pub async fn oauth2_id_patch(
Path(rs_name): Path<String>,
Extension(kopid): Extension<KOpId>,
Json(obj): Json<ProtoEntry>,
) -> impl IntoResponse {
) -> Response<Body> {
let filter = oauth2_id(&rs_name);
let res = state
@ -118,7 +160,7 @@ pub async fn oauth2_id_scopemap_post(
Extension(kopid): Extension<KOpId>,
Path((rs_name, group)): Path<(String, String)>,
Json(scopes): Json<Vec<String>>,
) -> impl IntoResponse {
) -> Response<Body> {
let filter = oauth2_id(&rs_name);
let res = state
.qe_w_ref
@ -131,7 +173,7 @@ pub async fn oauth2_id_scopemap_delete(
State(state): State<ServerState>,
Extension(kopid): Extension<KOpId>,
Path((rs_name, group)): Path<(String, String)>,
) -> impl IntoResponse {
) -> Response<Body> {
let filter = oauth2_id(&rs_name);
let res = state
.qe_w_ref
@ -145,7 +187,7 @@ pub async fn oauth2_id_sup_scopemap_post(
Extension(kopid): Extension<KOpId>,
Path((rs_name, group)): Path<(String, String)>,
Json(scopes): Json<Vec<String>>,
) -> impl IntoResponse {
) -> Response<Body> {
let filter = oauth2_id(&rs_name);
let res = state
.qe_w_ref
@ -158,7 +200,7 @@ pub async fn oauth2_id_sup_scopemap_delete(
State(state): State<ServerState>,
Extension(kopid): Extension<KOpId>,
Path((rs_name, group)): Path<(String, String)>,
) -> impl IntoResponse {
) -> Response<Body> {
let filter = oauth2_id(&rs_name);
let res = state
.qe_w_ref
@ -171,7 +213,7 @@ pub async fn oauth2_id_delete(
State(state): State<ServerState>,
Extension(kopid): Extension<KOpId>,
Path(rs_name): Path<String>,
) -> impl IntoResponse {
) -> Response<Body> {
let filter = oauth2_id(&rs_name);
let res = state
.qe_w_ref
@ -337,7 +379,7 @@ async fn oauth2_authorise(
#[allow(clippy::unwrap_used)]
Response::builder()
.status(StatusCode::UNAUTHORIZED)
.header("WWW-Authenticate", HeaderValue::from_str("Bearer").unwrap())
.header(WWW_AUTHENTICATE, HeaderValue::from_static("Bearer"))
.header(ACCESS_CONTROL_ALLOW_ORIGIN, "*")
.body(Body::empty())
.unwrap()
@ -432,9 +474,9 @@ async fn oauth2_authorise_permit(
#[allow(clippy::unwrap_used)]
Response::builder()
.status(StatusCode::FOUND)
.header("Location", redirect_uri.as_str())
.header(LOCATION, redirect_uri.as_str())
.header(
"Access-Control-Allow-Origin",
ACCESS_CONTROL_ALLOW_ORIGIN,
redirect_uri.origin().ascii_serialization(),
)
.body(Body::empty())
@ -464,7 +506,7 @@ pub async fn oauth2_authorise_reject_post(
State(state): State<ServerState>,
Extension(kopid): Extension<KOpId>,
Form(consent_req): Form<ConsentRequestData>,
) -> impl IntoResponse {
) -> Response<Body> {
oauth2_authorise_reject(state, consent_req.token, kopid).await
}
@ -472,7 +514,7 @@ pub async fn oauth2_authorise_reject_get(
State(state): State<ServerState>,
Extension(kopid): Extension<KOpId>,
Query(consent_req): Query<ConsentRequestData>,
) -> impl IntoResponse {
) -> Response<Body> {
oauth2_authorise_reject(state, consent_req.token, kopid).await
}
@ -483,7 +525,7 @@ async fn oauth2_authorise_reject(
state: ServerState,
consent_req: String,
kopid: KOpId,
) -> impl IntoResponse {
) -> Response<Body> {
// Need to go back to the redir_uri
// For this, we'll need to lookup where to go.
@ -525,12 +567,14 @@ async fn oauth2_authorise_reject(
}
}
#[axum_macros::debug_handler]
#[instrument(skip(state, kopid, headers), level = "DEBUG")]
pub async fn oauth2_token_post(
State(state): State<ServerState>,
Extension(kopid): Extension<KOpId>,
headers: HeaderMap, // TODO: make this a typed basic auth header
headers: HeaderMap,
Form(tok_req): Form<AccessTokenRequest>,
) -> impl IntoResponse {
) -> Result<Json<kanidm_proto::oauth2::AccessTokenResponse>, HTTPOauth2Error> {
// This is called directly by the resource server, where we then issue
// the token to the caller.
@ -545,53 +589,13 @@ pub async fn oauth2_token_post(
// grant? Should we cease the delayed/async session update here and just opt
// for a wr txn?
let res = state
match state
.qe_w_ref
.handle_oauth2_token_exchange(client_authz, tok_req, kopid.eventid)
.await;
match res {
Ok(atr) =>
{
#[allow(clippy::unwrap_used)]
Response::builder()
.status(StatusCode::OK)
.header(ACCESS_CONTROL_ALLOW_ORIGIN, "*")
.body(Body::from(serde_json::to_string(&atr).unwrap()))
.unwrap()
}
Err(Oauth2Error::AuthenticationRequired) =>
{
#[allow(clippy::unwrap_used)]
Response::builder()
.status(StatusCode::UNAUTHORIZED)
.header(ACCESS_CONTROL_ALLOW_ORIGIN, "*")
.body(Body::empty())
.unwrap()
}
Err(e) => {
// https://datatracker.ietf.org/doc/html/rfc6749#section-5.2
let err = ErrorResponse {
error: e.to_string(),
error_description: None,
error_uri: None,
};
let body = match serde_json::to_string(&err) {
Ok(val) => val,
Err(e) => {
admin_warn!("Failed to serialize error response: original_error=\"{:?}\" serialization_error=\"{:?}\"", err, e);
format!("{:?}", err)
}
};
#[allow(clippy::unwrap_used)]
Response::builder()
.status(StatusCode::BAD_REQUEST)
.header(ACCESS_CONTROL_ALLOW_ORIGIN, "*")
.body(Body::from(body))
.unwrap()
}
.await
{
Ok(tok_res) => Ok(Json(tok_res)),
Err(e) => Err(HTTPOauth2Error(e)),
}
}
@ -600,7 +604,7 @@ pub async fn oauth2_openid_discovery_get(
State(state): State<ServerState>,
Path(client_id): Path<String>,
Extension(kopid): Extension<KOpId>,
) -> impl IntoResponse {
) -> Result<Json<OidcDiscoveryResponse>, HttpOperationError> {
// let client_id = req.get_url_param("client_id")?;
let res = state
@ -609,51 +613,26 @@ pub async fn oauth2_openid_discovery_get(
.await;
match res {
Ok(dsc) => {
// Humans may look at this so we pretty it.
#[allow(clippy::unwrap_used)]
let body = serde_json::to_string_pretty(&dsc).unwrap();
#[allow(clippy::unwrap_used)]
Response::builder()
.status(StatusCode::OK)
.header(ACCESS_CONTROL_ALLOW_ORIGIN, "*")
.body(Body::from(body))
.unwrap()
}
Ok(dsc) => Ok(Json(dsc)),
Err(e) => {
error!(err = ?e, "Unable to access discovery info");
let body = match serde_json::to_string(&e) {
Ok(val) => val,
Err(e) => {
format!("{:?}", e)
}
};
#[allow(clippy::unwrap_used)]
Response::builder()
.status(StatusCode::BAD_REQUEST)
.header(ACCESS_CONTROL_ALLOW_ORIGIN, "*")
.body(Body::from(body))
.unwrap()
Err(HttpOperationError(e))
}
}
}
#[debug_handler]
pub async fn oauth2_openid_userinfo_get(
State(state): State<ServerState>,
Path(client_id): Path<String>,
Extension(kopid): Extension<KOpId>,
) -> Response<Body> {
) -> impl IntoResponse {
// The token we want to inspect is in the authorisation header.
let client_token = match kopid.uat {
Some(val) => val,
None => {
error!("Bearer Authentication Not Provided");
#[allow(clippy::unwrap_used)]
return Response::builder()
.status(StatusCode::UNAUTHORIZED)
.header(ACCESS_CONTROL_ALLOW_ORIGIN, "*")
.body(Body::from("Invalid Bearer Authorisation"))
.unwrap();
return Err(HTTPOauth2Error(Oauth2Error::AuthenticationRequired));
}
};
@ -663,36 +642,8 @@ pub async fn oauth2_openid_userinfo_get(
.await;
match res {
Ok(uir) => {
#[allow(clippy::unwrap_used)]
let body = serde_json::to_string(&uir).unwrap();
#[allow(clippy::unwrap_used)]
Response::builder()
.status(StatusCode::OK)
.header(ACCESS_CONTROL_ALLOW_ORIGIN, "*")
.body(Body::from(body))
.unwrap()
}
Err(e) => {
let err = ErrorResponse {
error: e.to_string(),
error_description: None,
error_uri: None,
};
let body = match serde_json::to_string(&err) {
Ok(val) => val,
Err(e) => {
format!("{:?}", e)
}
};
#[allow(clippy::unwrap_used)]
Response::builder()
.status(StatusCode::BAD_REQUEST)
.header(ACCESS_CONTROL_ALLOW_ORIGIN, "*")
.body(Body::from(body))
.unwrap()
// https://datatracker.ietf.org/doc/html/rfc6750#section-6.2
}
Ok(uir) => Ok(Json(uir)),
Err(e) => Err(HTTPOauth2Error(e)),
}
}
@ -700,12 +651,13 @@ pub async fn oauth2_openid_publickey_get(
State(state): State<ServerState>,
Path(client_id): Path<String>,
Extension(kopid): Extension<KOpId>,
) -> impl IntoResponse {
let res = state
.qe_r_ref
.handle_oauth2_openid_publickey(client_id, kopid.eventid)
.await;
to_axum_response(res)
) -> Response<Body> {
to_axum_response(
state
.qe_r_ref
.handle_oauth2_openid_publickey(client_id, kopid.eventid)
.await,
)
}
/// This is called directly by the resource server, where we then issue
@ -760,6 +712,7 @@ pub async fn oauth2_token_introspect_post(
#[allow(clippy::unwrap_used)]
Response::builder()
.header(ACCESS_CONTROL_ALLOW_ORIGIN, "*")
.header(CONTENT_TYPE, APPLICATION_JSON)
.body(Body::from(body))
.unwrap()
}
@ -776,8 +729,7 @@ pub async fn oauth2_token_introspect_post(
// https://datatracker.ietf.org/doc/html/rfc6749#section-5.2
let err = ErrorResponse {
error: e.to_string(),
error_description: None,
error_uri: None,
..Default::default()
};
let body = match serde_json::to_string(&err) {
@ -847,21 +799,22 @@ pub async fn oauth2_token_revoke_post(
// https://datatracker.ietf.org/doc/html/rfc6749#section-5.2
let err = ErrorResponse {
error: e.to_string(),
error_description: None,
error_uri: None,
..Default::default()
};
#[allow(clippy::unwrap_used)]
Response::builder()
.status(StatusCode::BAD_REQUEST)
.header(ACCESS_CONTROL_ALLOW_ORIGIN, "*")
.body(Body::from(serde_json::to_string(&err).unwrap()))
.body(Body::from(
serde_json::to_string(&err).unwrap_or("".to_string()),
))
.unwrap()
}
}
}
// Some requests from browsers require preflight so that CORS works.
pub async fn oauth2_preflight_options() -> impl IntoResponse {
pub async fn oauth2_preflight_options() -> Response<Body> {
#[allow(clippy::unwrap_used)]
Response::builder()
.status(StatusCode::OK)

View file

@ -2,6 +2,7 @@ use axum::extract::State;
use axum::http::HeaderValue;
use axum::response::Response;
use axum::Extension;
use http::header::CONTENT_TYPE;
use super::middleware::KOpId;
use super::ServerState;
@ -58,7 +59,7 @@ pub async fn ui_handler(
let mut res = Response::new(body);
res.headers_mut().insert(
"Content-Type",
CONTENT_TYPE,
HeaderValue::from_static("text/html;charset=utf-8"),
);
res

View file

@ -892,6 +892,7 @@ pub async fn account_get_id_radius_token(
res
}
/// Expects an `AccountUnixExtend` object
pub async fn account_post_id_unix(
State(state): State<ServerState>,
Path(id): Path<String>,
@ -1403,27 +1404,26 @@ pub fn router(state: ServerState) -> Router<ServerState> {
)
.route(
"/v1/schema/classtype/:id",
get(schema_classtype_get_id)
.put(|| async { "TODO" })
.patch(|| async { "TODO" }),
get(schema_classtype_get_id), // .put(|| async { "TODO" })
// .patch(|| async { "TODO" }),
)
.route("/v1/self", get(whoami))
.route("/v1/self/_uat", get(whoami_uat))
.route("/v1/self/_attr/:attr", get(|| async { "TODO" }))
.route("/v1/self/_credential", get(|| async { "TODO" }))
.route("/v1/self/_credential/:cid/_lock", get(|| async { "TODO" }))
.route(
"/v1/self/_radius",
get(|| async { "TODO" })
.delete(|| async { "TODO" })
.post(|| async { "TODO" }),
)
.route("/v1/self/_radius/_config", post(|| async { "TODO" }))
.route("/v1/self/_radius/_config/:token", get(|| async { "TODO" }))
.route(
"/v1/self/_radius/_config/:token/apple",
get(|| async { "TODO" }),
)
// .route("/v1/self/_attr/:attr", get(|| async { "TODO" }))
// .route("/v1/self/_credential", get(|| async { "TODO" }))
// .route("/v1/self/_credential/:cid/_lock", get(|| async { "TODO" }))
// .route(
// "/v1/self/_radius",
// get(|| async { "TODO" })
// .delete(|| async { "TODO" })
// .post(|| async { "TODO" }),
// )
// .route("/v1/self/_radius/_config", post(|| async { "TODO" }))
// .route("/v1/self/_radius/_config/:token", get(|| async { "TODO" }))
// .route(
// "/v1/self/_radius/_config/:token/apple",
// get(|| async { "TODO" }),
// )
// Applinks are the list of apps this account can access.
.route("/v1/self/_applinks", get(applinks_get))
// Person routes
@ -1442,16 +1442,16 @@ pub fn router(state: ServerState) -> Router<ServerState> {
.post(account_id_post_attr)
.delete(account_id_delete_attr),
)
.route("/v1/person/:id/_lock", get(|| async { "TODO" }))
.route("/v1/person/:id/_credential", get(|| async { "TODO" }))
// .route("/v1/person/:id/_lock", get(|| async { "TODO" }))
// .route("/v1/person/:id/_credential", get(|| async { "TODO" }))
.route(
"/v1/person/:id/_credential/_status",
get(account_get_id_credential_status),
)
.route(
"/v1/person/:id/_credential/:cid/_lock",
get(|| async { "TODO" }),
)
// .route(
// "/v1/person/:id/_credential/:cid/_lock",
// get(|| async { "TODO" }),
// )
.route(
"/v1/person/:id/_credential/_update",
get(account_get_id_credential_update),
@ -1481,7 +1481,7 @@ pub fn router(state: ServerState) -> Router<ServerState> {
.route(
"/v1/person/:id/_radius/_token",
get(account_get_id_radius_token),
) // TODO: make this cacheable
) // TODO: make radius token cacheable
.route("/v1/person/:id/_unix", post(account_post_id_unix))
.route(
"/v1/person/:id/_unix/_credential",
@ -1507,7 +1507,7 @@ pub fn router(state: ServerState) -> Router<ServerState> {
.post(account_id_post_attr)
.delete(account_id_delete_attr),
)
.route("/v1/service_account/:id/_lock", get(|| async { "TODO" }))
// .route("/v1/service_account/:id/_lock", get(|| async { "TODO" }))
.route(
"/v1/service_account/:id/_into_person",
post(service_account_into_person),
@ -1520,10 +1520,10 @@ pub fn router(state: ServerState) -> Router<ServerState> {
"/v1/service_account/:id/_api_token/:token_id",
delete(service_account_api_token_delete),
)
.route(
"/v1/service_account/:id/_credential",
get(|| async { "TODO" }),
)
// .route(
// "/v1/service_account/:id/_credential",
// get(|| async { "TODO" }),
// )
.route(
"/v1/service_account/:id/_credential/_generate",
get(service_account_credential_generate),
@ -1532,10 +1532,10 @@ pub fn router(state: ServerState) -> Router<ServerState> {
"/v1/service_account/:id/_credential/_status",
get(account_get_id_credential_status),
)
.route(
"/v1/service_account/:id/_credential/:cid/_lock",
get(|| async { "TODO" }),
)
// .route(
// "/v1/service_account/:id/_credential/:cid/_lock",
// get(|| async { "TODO" }),
// )
.route(
"/v1/service_account/:id/_ssh_pubkeys",
get(account_get_id_ssh_pubkeys).post(account_post_id_ssh_pubkey),
@ -1614,12 +1614,12 @@ pub fn router(state: ServerState) -> Router<ServerState> {
"/v1/recycle_bin/:id/_revive",
post(recycle_bin_revive_id_post),
)
.route("/v1/access_profile", get(|| async { "TODO" }))
.route("/v1/access_profile/:id", get(|| async { "TODO" }))
.route(
"/v1/access_profile/:id/_attr/:attr",
get(|| async { "TODO" }),
)
// .route("/v1/access_profile", get(|| async { "TODO" }))
// .route("/v1/access_profile/:id", get(|| async { "TODO" }))
// .route(
// "/v1/access_profile/:id/_attr/:attr",
// get(|| async { "TODO" }),
// )
.route("/v1/auth", post(auth))
.route("/v1/auth/valid", get(auth_valid))
.route("/v1/logout", get(logout))

View file

@ -28,12 +28,12 @@ kanidmd_lib = { workspace = true }
# used for webdriver testing
hyper-tls = { workspace = true }
# used for webdriver testing
fantoccini = { version="0.19.3", optional=true}
fantoccini = { version = "0.19.3", optional = true }
serde = { workspace = true }
url = { workspace = true, features = ["serde"] }
reqwest = { workspace = true, default-features = false, features=["cookies"] }
reqwest = { workspace = true, default-features = false, features = ["cookies"] }
sketching = { workspace = true }
testkit-macros = { workspace = true }
tracing = { workspace = true, features = ["attributes"] }

View file

@ -2,8 +2,9 @@
use std::collections::HashSet;
use kanidm_client::KanidmClient;
use kanidm_proto::constants::APPLICATION_JSON;
use kanidmd_testkit::*;
use reqwest::header::CONTENT_TYPE;
static USER_READABLE_ATTRS: [&str; 9] = [
"name",
@ -607,7 +608,7 @@ async fn test_v1_raw_delete(rsclient: KanidmClient) {
let response = match client
.post(format!("{}/v1/raw/delete", &addr))
.header("Content-Type", "application/json")
.header(CONTENT_TYPE, APPLICATION_JSON)
.body(post_body)
.send()
.await

View file

@ -4,11 +4,13 @@ use std::convert::TryFrom;
use std::str::FromStr;
use compact_jwt::{JwkKeySet, JwsValidator, OidcToken, OidcUnverified};
use kanidm_proto::constants::APPLICATION_JSON;
use kanidm_proto::oauth2::{
AccessTokenIntrospectRequest, AccessTokenIntrospectResponse, AccessTokenRequest,
AccessTokenResponse, AuthorisationResponse, GrantTypeReq, OidcDiscoveryResponse,
};
use oauth2_ext::PkceCodeChallenge;
use reqwest::header::{HeaderValue, CONTENT_TYPE};
use reqwest::StatusCode;
use url::Url;
@ -318,6 +320,9 @@ async fn test_oauth2_openid_basic_flow(rsclient: KanidmClient) {
.expect("Failed to send code exchange request.");
assert!(response.status() == reqwest::StatusCode::OK);
assert!(
response.headers().get(CONTENT_TYPE) == Some(&HeaderValue::from_static(APPLICATION_JSON))
);
assert_no_cache!(response);
// The body is a json AccessTokenResponse
@ -342,6 +347,10 @@ async fn test_oauth2_openid_basic_flow(rsclient: KanidmClient) {
.expect("Failed to send token introspect request.");
assert!(response.status() == reqwest::StatusCode::OK);
dbg!(response.headers());
assert!(
response.headers().get(CONTENT_TYPE) == Some(&HeaderValue::from_static(APPLICATION_JSON))
);
assert_no_cache!(response);
let tir = response
@ -384,6 +393,10 @@ async fn test_oauth2_openid_basic_flow(rsclient: KanidmClient) {
.await
.expect("Failed to send userinfo request.");
dbg!(response.headers());
assert!(
response.headers().get(CONTENT_TYPE) == Some(&HeaderValue::from_static(APPLICATION_JSON))
);
let userinfo = response
.json::<OidcToken>()
.await

View file

@ -1,4 +1,6 @@
use kanidm_client::KanidmClient;
use kanidm_proto::constants::APPLICATION_JSON;
use reqwest::header::CONTENT_TYPE;
/// This literally tests that the thing exists and responds in a way we expect, probably worth testing it better...
#[kanidmd_testkit::test]
@ -14,7 +16,7 @@ async fn test_v1_person_patch(rsclient: KanidmClient) {
let response = match client
.patch(format!("{}/v1/person/foo", &addr))
.header("Content-Type", "application/json")
.header(CONTENT_TYPE, APPLICATION_JSON)
.body(post_body)
.send()
.await

View file

@ -635,6 +635,34 @@ async fn test_server_rest_posix_auth_lifecycle(rsclient: KanidmClient) {
.await
.unwrap();
// test sending a faulty JSON blob to the person unix update endpoint
let bad_json: serde_json::Value = serde_json::json!({
"shell" : "test_value",
"gidnumber" : "5" // this should be a u32, but it's not!
});
let res = rsclient
.perform_post_request::<serde_json::Value, String>(
format!("/v1/person/{}/_unix", "posix_account").as_str(),
bad_json,
)
.await;
dbg!(&res);
assert!(res.is_err());
// test sending a faulty JSON blob to the person unix update endpoint
let bad_json: serde_json::Value = serde_json::json!({
"crab" : "cakes", // this is an invalid field.
"gidnumber" : 5
});
let res = rsclient
.perform_post_request::<serde_json::Value, String>(
format!("/v1/person/{}/_unix", "posix_account").as_str(),
bad_json,
)
.await;
dbg!(&res);
assert!(res.is_err());
// attempt to verify (good, anon-conn)
let r1 = anon_rsclient
.idm_account_unix_cred_verify("posix_account", UNIX_TEST_PASSWORD)

View file

@ -1,681 +0,0 @@
use kanidm_client::KanidmClient;
#[kanidmd_testkit::test]
async fn test_routes(rsclient: KanidmClient) {
let routemap = r#"
[
{
"path": "/",
"method": "GET"
},
{
"path": "/robots.txt",
"method": "GET"
},
{
"path": "/manifest.webmanifest",
"method": "GET"
},
{
"path": "/ui",
"method": "GET"
},
{
"path": "/ui/login",
"method": "GET"
},
{
"path": "/v1/account/:id/_unix/_token",
"method": "GET"
},
{
"path": "/v1/account/:id/_radius/_token",
"method": "GET"
},
{
"path": "/v1/group/:id/_unix/_token",
"method": "GET"
},
{
"path": "/v1/oauth2/:rs_name/_icon",
"method": "GET"
},
{
"path": "/status",
"method": "GET"
},
{
"path": "/oauth2/authorise",
"method": "POST"
},
{
"path": "/oauth2/authorise",
"method": "GET"
},
{
"path": "/oauth2/authorise/permit",
"method": "POST"
},
{
"path": "/oauth2/authorise/permit",
"method": "GET"
},
{
"path": "/oauth2/authorise/reject",
"method": "POST"
},
{
"path": "/oauth2/authorise/reject",
"method": "GET"
},
{
"path": "/oauth2/token",
"method": "POST"
},
{
"path": "/oauth2/token/introspect",
"method": "POST"
},
{
"path": "/oauth2/token/revoke",
"method": "POST"
},
{
"path": "/oauth2/openid/:client_id/.well-known/openid-configuration",
"method": "GET"
},
{
"path": "/oauth2/openid/:client_id/userinfo",
"method": "GET"
},
{
"path": "/oauth2/openid/:client_id/public_key.jwk",
"method": "GET"
},
{
"path": "/scim/v1/Sync",
"method": "POST"
},
{
"path": "/scim/v1/Sync",
"method": "GET"
},
{
"path": "/scim/v1/Sink",
"method": "GET"
},
{
"path": "/v1/sync_account",
"method": "GET"
},
{
"path": "/v1/sync_account",
"method": "POST"
},
{
"path": "/v1/sync_account/:id",
"method": "GET"
},
{
"path": "/v1/sync_account/:id",
"method": "PATCH"
},
{
"path": "/v1/sync_account/:id/_finalise",
"method": "GET"
},
{
"path": "/v1/sync_account/:id/_terminate",
"method": "GET"
},
{
"path": "/v1/sync_account/:id/_sync_token",
"method": "POST"
},
{
"path": "/v1/sync_account/:id/_sync_token",
"method": "DELETE"
},
{
"path": "/v1/raw/create",
"method": "POST"
},
{
"path": "/v1/raw/modify",
"method": "POST"
},
{
"path": "/v1/raw/delete",
"method": "POST"
},
{
"path": "/v1/raw/search",
"method": "POST"
},
{
"path": "/v1/auth",
"method": "POST"
},
{
"path": "/v1/auth/valid",
"method": "GET"
},
{
"path": "/v1/reauth",
"method": "POST"
},
{
"path": "/v1/logout",
"method": "GET"
},
{
"path": "/v1/schema",
"method": "GET"
},
{
"path": "/v1/schema/attributetype",
"method": "GET"
},
{
"path": "/v1/schema/attributetype",
"method": "POST"
},
{
"path": "/v1/schema/attributetype/:id",
"method": "GET"
},
{
"path": "/v1/schema/attributetype/:id",
"method": "PUT"
},
{
"path": "/v1/schema/attributetype/:id",
"method": "PATCH"
},
{
"path": "/v1/schema/classtype",
"method": "GET"
},
{
"path": "/v1/schema/classtype",
"method": "POST"
},
{
"path": "/v1/schema/classtype/:id",
"method": "GET"
},
{
"path": "/v1/schema/classtype/:id",
"method": "PUT"
},
{
"path": "/v1/schema/classtype/:id",
"method": "PATCH"
},
{
"path": "/v1/oauth2",
"method": "GET"
},
{
"path": "/v1/oauth2/_basic",
"method": "POST"
},
{
"path": "/v1/oauth2/:rs_name",
"method": "GET"
},
{
"path": "/v1/oauth2/:rs_name",
"method": "PATCH"
},
{
"path": "/v1/oauth2/:rs_name",
"method": "DELETE"
},
{
"path": "/v1/oauth2/:rs_name/_basic_secret",
"method": "GET"
},
{
"path": "/v1/oauth2/_scopemap/:id/:group",
"method": "POST"
},
{
"path": "/v1/oauth2/_scopemap/:id/:group",
"method": "DELETE"
},
{
"path": "/v1/oauth2/_sup_scopemap/:id/:group",
"method": "POST"
},
{
"path": "/v1/oauth2/_sup_scopemap/:id/:group",
"method": "DELETE"
},
{
"path": "/v1/self",
"method": "GET"
},
{
"path": "/v1/self/_uat",
"method": "GET"
},
{
"path": "/v1/self/_attr/:attr",
"method": "GET"
},
{
"path": "/v1/self/_credential",
"method": "GET"
},
{
"path": "/v1/self/_credential/:cid/_lock",
"method": "GET"
},
{
"path": "/v1/self/_radius",
"method": "GET"
},
{
"path": "/v1/self/_radius",
"method": "DELETE"
},
{
"path": "/v1/self/_radius",
"method": "POST"
},
{
"path": "/v1/self/_radius/_config",
"method": "POST"
},
{
"path": "/v1/self/_radius/_config/:token",
"method": "GET"
},
{
"path": "/v1/self/_radius/_config/:token/apple",
"method": "GET"
},
{
"path": "/v1/self/_applinks",
"method": "GET"
},
{
"path": "/v1/person",
"method": "GET"
},
{
"path": "/v1/person",
"method": "POST"
},
{
"path": "/v1/person/:id",
"method": "GET"
},
{
"path": "/v1/person/:id",
"method": "PATCH"
},
{
"path": "/v1/person/:id",
"method": "DELETE"
},
{
"path": "/v1/person/:id/_attr/:attr",
"method": "GET"
},
{
"path": "/v1/person/:id/_attr/:attr",
"method": "PUT"
},
{
"path": "/v1/person/:id/_attr/:attr",
"method": "POST"
},
{
"path": "/v1/person/:id/_attr/:attr",
"method": "DELETE"
},
{
"path": "/v1/person/:id/_lock",
"method": "GET"
},
{
"path": "/v1/person/:id/_credential",
"method": "GET"
},
{
"path": "/v1/person/:id/_credential/_status",
"method": "GET"
},
{
"path": "/v1/person/:id/_credential/:cid/_lock",
"method": "GET"
},
{
"path": "/v1/person/:id/_credential/_update",
"method": "GET"
},
{
"path": "/v1/person/:id/_credential/_update_intent",
"method": "GET"
},
{
"path": "/v1/person/:id/_credential/_update_intent/:ttl",
"method": "GET"
},
{
"path": "/v1/person/:id/_ssh_pubkeys",
"method": "GET"
},
{
"path": "/v1/person/:id/_ssh_pubkeys",
"method": "POST"
},
{
"path": "/v1/person/:id/_ssh_pubkeys/:tag",
"method": "GET"
},
{
"path": "/v1/person/:id/_ssh_pubkeys/:tag",
"method": "DELETE"
},
{
"path": "/v1/person/:id/_radius",
"method": "GET"
},
{
"path": "/v1/person/:id/_radius",
"method": "POST"
},
{
"path": "/v1/person/:id/_radius",
"method": "DELETE"
},
{
"path": "/v1/person/:id/_unix",
"method": "POST"
},
{
"path": "/v1/person/:id/_unix/_credential",
"method": "PUT"
},
{
"path": "/v1/person/:id/_unix/_credential",
"method": "DELETE"
},
{
"path": "/v1/service_account",
"method": "GET"
},
{
"path": "/v1/service_account",
"method": "POST"
},
{
"path": "/v1/service_account/:id",
"method": "GET"
},
{
"path": "/v1/service_account/:id",
"method": "PATCH"
},
{
"path": "/v1/service_account/:id",
"method": "DELETE"
},
{
"path": "/v1/service_account/:id/_attr/:attr",
"method": "GET"
},
{
"path": "/v1/service_account/:id/_attr/:attr",
"method": "PUT"
},
{
"path": "/v1/service_account/:id/_attr/:attr",
"method": "POST"
},
{
"path": "/v1/service_account/:id/_attr/:attr",
"method": "DELETE"
},
{
"path": "/v1/service_account/:id/_lock",
"method": "GET"
},
{
"path": "/v1/service_account/:id/_into_person",
"method": "POST"
},
{
"path": "/v1/service_account/:id/_api_token",
"method": "POST"
},
{
"path": "/v1/service_account/:id/_api_token",
"method": "GET"
},
{
"path": "/v1/service_account/:id/_api_token/:token_id",
"method": "DELETE"
},
{
"path": "/v1/service_account/:id/_credential",
"method": "GET"
},
{
"path": "/v1/service_account/:id/_credential/_generate",
"method": "GET"
},
{
"path": "/v1/service_account/:id/_credential/_status",
"method": "GET"
},
{
"path": "/v1/service_account/:id/_credential/:cid/_lock",
"method": "GET"
},
{
"path": "/v1/service_account/:id/_ssh_pubkeys",
"method": "GET"
},
{
"path": "/v1/service_account/:id/_ssh_pubkeys",
"method": "POST"
},
{
"path": "/v1/service_account/:id/_ssh_pubkeys/:tag",
"method": "GET"
},
{
"path": "/v1/service_account/:id/_ssh_pubkeys/:tag",
"method": "DELETE"
},
{
"path": "/v1/service_account/:id/_unix",
"method": "POST"
},
{
"path": "/v1/account/:id/_unix/_auth",
"method": "POST"
},
{
"path": "/v1/account/:id/_ssh_pubkeys",
"method": "GET"
},
{
"path": "/v1/account/:id/_ssh_pubkeys/:tag",
"method": "GET"
},
{
"path": "/v1/account/:id/_user_auth_token",
"method": "GET"
},
{
"path": "/v1/account/:id/_user_auth_token/:token_id",
"method": "DELETE"
},
{
"path": "/v1/credential/_exchange_intent",
"method": "POST"
},
{
"path": "/v1/credential/_status",
"method": "POST"
},
{
"path": "/v1/credential/_update",
"method": "POST"
},
{
"path": "/v1/credential/_commit",
"method": "POST"
},
{
"path": "/v1/credential/_cancel",
"method": "POST"
},
{
"path": "/v1/group",
"method": "GET"
},
{
"path": "/v1/group",
"method": "POST"
},
{
"path": "/v1/group/:id",
"method": "GET"
},
{
"path": "/v1/group/:id",
"method": "DELETE"
},
{
"path": "/v1/group/:id/_attr/:attr",
"method": "DELETE"
},
{
"path": "/v1/group/:id/_attr/:attr",
"method": "GET"
},
{
"path": "/v1/group/:id/_attr/:attr",
"method": "PUT"
},
{
"path": "/v1/group/:id/_attr/:attr",
"method": "POST"
},
{
"path": "/v1/group/:id/_unix",
"method": "POST"
},
{
"path": "/v1/domain",
"method": "GET"
},
{
"path": "/v1/domain/_attr/:attr",
"method": "GET"
},
{
"path": "/v1/domain/_attr/:attr",
"method": "PUT"
},
{
"path": "/v1/domain/_attr/:attr",
"method": "DELETE"
},
{
"path": "/v1/system",
"method": "GET"
},
{
"path": "/v1/system/_attr/:attr",
"method": "GET"
},
{
"path": "/v1/system/_attr/:attr",
"method": "POST"
},
{
"path": "/v1/system/_attr/:attr",
"method": "DELETE"
},
{
"path": "/v1/recycle_bin",
"method": "GET"
},
{
"path": "/v1/recycle_bin/:id",
"method": "GET"
},
{
"path": "/v1/recycle_bin/:id/_revive",
"method": "POST"
},
{
"path": "/v1/access_profile",
"method": "GET"
},
{
"path": "/v1/access_profile/:id",
"method": "GET"
},
{
"path": "/v1/access_profile/:id/_attr/:attr",
"method": "GET"
}
]
"#;
// ,{
// "path": "/v1/routemap",
// "method": "GET"
// }
let routelist: Vec<serde_json::Value> = serde_json::from_str(routemap).unwrap();
let client = reqwest::ClientBuilder::new()
.danger_accept_invalid_certs(true)
.danger_accept_invalid_hostnames(true)
.build()
.unwrap();
for route in routelist {
// println!("{:?}", route);
let path: String = route.get("path").unwrap().to_string();
let method: String = route.get("method").unwrap().to_string();
let method = method.replace('"', "");
let method = method.as_str();
println!("'{method}'");
let method = match method {
"GET" => reqwest::Method::GET,
"POST" => reqwest::Method::POST,
"DELETE" => reqwest::Method::DELETE,
"PATCH" => reqwest::Method::PATCH,
"PUT" => reqwest::Method::PUT,
_ => todo!("{}", method),
};
if path.contains(':') {
println!("Can't do this because it has an attribute: {}", path);
continue;
}
let url = format!("{}{}", rsclient.get_url(), path.replace('"', ""));
println!("#### {:?} {} {}", method, path, url);
let res = match client
.request(method, &url)
// .version(http::Version::HTTP_11)
.send()
.await
{
Ok(val) => val,
Err(error) => {
panic!("Failed to query {:?} : {:#?}", url, error);
}
};
if res.status() == 404 {
panic!("Failed to query {:?} : {:#?}", url, res);
}
}
}

View file

@ -234,7 +234,7 @@ function addBorrowedObject(obj) {
}
function __wbg_adapter_48(arg0, arg1, arg2) {
try {
wasm._dyn_core__ops__function__FnMut___A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__he237e4e531e49fe1(arg0, arg1, addBorrowedObject(arg2));
wasm._dyn_core__ops__function__FnMut___A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hc988533207355089(arg0, arg1, addBorrowedObject(arg2));
} finally {
heap[stack_pointer++] = undefined;
}
@ -242,14 +242,14 @@ function __wbg_adapter_48(arg0, arg1, arg2) {
function __wbg_adapter_51(arg0, arg1, arg2) {
try {
wasm._dyn_core__ops__function__FnMut___A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hc1460e7f28360ed3(arg0, arg1, addBorrowedObject(arg2));
wasm._dyn_core__ops__function__FnMut___A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__ha34c6831d0d6d86f(arg0, arg1, addBorrowedObject(arg2));
} finally {
heap[stack_pointer++] = undefined;
}
}
function __wbg_adapter_54(arg0, arg1, arg2) {
wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__ha2ecae7653526986(arg0, arg1, addHeapObject(arg2));
wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h52363d7e83a077de(arg0, arg1, addHeapObject(arg2));
}
/**
@ -356,13 +356,13 @@ function __wbg_get_imports() {
getInt32Memory0()[arg0 / 4 + 1] = len1;
getInt32Memory0()[arg0 / 4 + 0] = ptr1;
};
imports.wbg.__wbg_modalhidebyid_14daee5d362376c0 = function(arg0, arg1) {
modal_hide_by_id(getStringFromWasm0(arg0, arg1));
};
imports.wbg.__wbindgen_object_clone_ref = function(arg0) {
const ret = getObject(arg0);
return addHeapObject(ret);
};
imports.wbg.__wbg_modalhidebyid_a36f33eb8222a059 = function(arg0, arg1) {
modal_hide_by_id(getStringFromWasm0(arg0, arg1));
};
imports.wbg.__wbindgen_error_new = function(arg0, arg1) {
const ret = new Error(getStringFromWasm0(arg0, arg1));
return addHeapObject(ret);
@ -411,11 +411,14 @@ function __wbg_get_imports() {
const ret = getObject(arg0) === undefined;
return ret;
};
imports.wbg.__wbg_cachekey_b61393159c57fd7b = function(arg0, arg1) {
const ret = getObject(arg1).__yew_subtree_cache_key;
imports.wbg.__wbg_listenerid_12315eee21527820 = function(arg0, arg1) {
const ret = getObject(arg1).__yew_listener_id;
getInt32Memory0()[arg0 / 4 + 1] = isLikeNone(ret) ? 0 : ret;
getInt32Memory0()[arg0 / 4 + 0] = !isLikeNone(ret);
};
imports.wbg.__wbg_setlistenerid_3183aae8fa5840fb = function(arg0, arg1) {
getObject(arg0).__yew_listener_id = arg1 >>> 0;
};
imports.wbg.__wbg_subtreeid_e348577f7ef777e3 = function(arg0, arg1) {
const ret = getObject(arg1).__yew_subtree_id;
getInt32Memory0()[arg0 / 4 + 1] = isLikeNone(ret) ? 0 : ret;
@ -424,16 +427,13 @@ function __wbg_get_imports() {
imports.wbg.__wbg_setsubtreeid_d32e6327eef1f7fc = function(arg0, arg1) {
getObject(arg0).__yew_subtree_id = arg1 >>> 0;
};
imports.wbg.__wbg_setcachekey_80183b7cfc421143 = function(arg0, arg1) {
getObject(arg0).__yew_subtree_cache_key = arg1 >>> 0;
};
imports.wbg.__wbg_listenerid_12315eee21527820 = function(arg0, arg1) {
const ret = getObject(arg1).__yew_listener_id;
imports.wbg.__wbg_cachekey_b61393159c57fd7b = function(arg0, arg1) {
const ret = getObject(arg1).__yew_subtree_cache_key;
getInt32Memory0()[arg0 / 4 + 1] = isLikeNone(ret) ? 0 : ret;
getInt32Memory0()[arg0 / 4 + 0] = !isLikeNone(ret);
};
imports.wbg.__wbg_setlistenerid_3183aae8fa5840fb = function(arg0, arg1) {
getObject(arg0).__yew_listener_id = arg1 >>> 0;
imports.wbg.__wbg_setcachekey_80183b7cfc421143 = function(arg0, arg1) {
getObject(arg0).__yew_subtree_cache_key = arg1 >>> 0;
};
imports.wbg.__wbg_new_abda76e883ba8a5f = function() {
const ret = new Error();
@ -461,9 +461,6 @@ function __wbg_get_imports() {
const ret = arg0;
return addHeapObject(ret);
};
imports.wbg.__wbg_set_20cbc34131e76824 = function(arg0, arg1, arg2) {
getObject(arg0)[takeObject(arg1)] = takeObject(arg2);
};
imports.wbg.__wbindgen_jsval_loose_eq = function(arg0, arg1) {
const ret = getObject(arg0) == getObject(arg1);
return ret;
@ -495,6 +492,9 @@ function __wbg_get_imports() {
wasm.__wbindgen_free(arg0, arg1 * 4);
console.warn(...v0);
};
imports.wbg.__wbg_set_20cbc34131e76824 = function(arg0, arg1, arg2) {
getObject(arg0)[takeObject(arg1)] = takeObject(arg2);
};
imports.wbg.__wbg_documentURI_4bff51077cdeeac1 = function() { return handleError(function (arg0, arg1) {
const ret = getObject(arg1).documentURI;
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
@ -564,21 +564,15 @@ function __wbg_get_imports() {
const ret = getObject(arg0).fetch(getObject(arg1));
return addHeapObject(ret);
};
imports.wbg.__wbg_addEventListener_a5963e26cd7b176b = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
getObject(arg0).addEventListener(getStringFromWasm0(arg1, arg2), getObject(arg3), getObject(arg4));
}, arguments) };
imports.wbg.__wbg_removeEventListener_782040b4432709cb = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
getObject(arg0).removeEventListener(getStringFromWasm0(arg1, arg2), getObject(arg3), arg4 !== 0);
}, arguments) };
imports.wbg.__wbg_instanceof_HtmlFormElement_b57527983c7c1ada = function(arg0) {
let result;
try {
result = getObject(arg0) instanceof HTMLFormElement;
} catch {
result = false;
}
const ret = result;
return ret;
imports.wbg.__wbg_value_3c5f08ffc2b7d6f9 = function(arg0, arg1) {
const ret = getObject(arg1).value;
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
getInt32Memory0()[arg0 / 4 + 1] = len1;
getInt32Memory0()[arg0 / 4 + 0] = ptr1;
};
imports.wbg.__wbg_setvalue_0dc100d4b9908028 = function(arg0, arg1, arg2) {
getObject(arg0).value = getStringFromWasm0(arg1, arg2);
};
imports.wbg.__wbg_instanceof_ShadowRoot_b64337370f59fe2d = function(arg0) {
let result;
@ -601,6 +595,13 @@ function __wbg_get_imports() {
imports.wbg.__wbg_pushState_1145414a47c0b629 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4, arg5) {
getObject(arg0).pushState(getObject(arg1), getStringFromWasm0(arg2, arg3), arg4 === 0 ? undefined : getStringFromWasm0(arg4, arg5));
}, arguments) };
imports.wbg.__wbg_href_47b90f0ddf3ddcd7 = function(arg0, arg1) {
const ret = getObject(arg1).href;
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
getInt32Memory0()[arg0 / 4 + 1] = len1;
getInt32Memory0()[arg0 / 4 + 0] = ptr1;
};
imports.wbg.__wbg_getItem_ed8e218e51f1efeb = function() { return handleError(function (arg0, arg1, arg2, arg3) {
const ret = getObject(arg1).getItem(getStringFromWasm0(arg2, arg3));
var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
@ -614,16 +615,6 @@ function __wbg_get_imports() {
imports.wbg.__wbg_setItem_d002ee486462bfff = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
getObject(arg0).setItem(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4));
}, arguments) };
imports.wbg.__wbg_value_3c5f08ffc2b7d6f9 = function(arg0, arg1) {
const ret = getObject(arg1).value;
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
getInt32Memory0()[arg0 / 4 + 1] = len1;
getInt32Memory0()[arg0 / 4 + 0] = ptr1;
};
imports.wbg.__wbg_setvalue_0dc100d4b9908028 = function(arg0, arg1, arg2) {
getObject(arg0).value = getStringFromWasm0(arg1, arg2);
};
imports.wbg.__wbg_instanceof_HtmlInputElement_31b50e0cf542c524 = function(arg0) {
let result;
try {
@ -651,6 +642,12 @@ function __wbg_get_imports() {
imports.wbg.__wbg_setvalue_1f95e61cbc382f7f = function(arg0, arg1, arg2) {
getObject(arg0).value = getStringFromWasm0(arg1, arg2);
};
imports.wbg.__wbg_add_3eafedc4b2a28db0 = function() { return handleError(function (arg0, arg1, arg2) {
getObject(arg0).add(getStringFromWasm0(arg1, arg2));
}, arguments) };
imports.wbg.__wbg_remove_8ae45e50cb58bb66 = function() { return handleError(function (arg0, arg1, arg2) {
getObject(arg0).remove(getStringFromWasm0(arg1, arg2));
}, arguments) };
imports.wbg.__wbg_instanceof_Element_4622f5da1249a3eb = function(arg0) {
let result;
try {
@ -715,20 +712,6 @@ function __wbg_get_imports() {
imports.wbg.__wbg_set_b34caba58723c454 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
getObject(arg0).set(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4));
}, arguments) };
imports.wbg.__wbg_href_47b90f0ddf3ddcd7 = function(arg0, arg1) {
const ret = getObject(arg1).href;
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
getInt32Memory0()[arg0 / 4 + 1] = len1;
getInt32Memory0()[arg0 / 4 + 0] = ptr1;
};
imports.wbg.__wbg_log_1d3ae0273d8f4f8a = function(arg0) {
console.log(getObject(arg0));
};
imports.wbg.__wbg_credentials_66b6baa89eb03c21 = function(arg0) {
const ret = getObject(arg0).credentials;
return addHeapObject(ret);
};
imports.wbg.__wbg_headers_b439dcff02e808e5 = function(arg0) {
const ret = getObject(arg0).headers;
return addHeapObject(ret);
@ -737,12 +720,16 @@ function __wbg_get_imports() {
const ret = new Request(getStringFromWasm0(arg0, arg1), getObject(arg2));
return addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_newwithform_368648c82279d486 = function() { return handleError(function (arg0) {
const ret = new FormData(getObject(arg0));
imports.wbg.__wbg_create_c7e40b6b88186cbf = function() { return handleError(function (arg0, arg1) {
const ret = getObject(arg0).create(getObject(arg1));
return addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_get_4c356dcef81d58a5 = function(arg0, arg1, arg2) {
const ret = getObject(arg0).get(getStringFromWasm0(arg1, arg2));
imports.wbg.__wbg_get_e66794f89dcd7828 = function() { return handleError(function (arg0, arg1) {
const ret = getObject(arg0).get(getObject(arg1));
return addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_credentials_66b6baa89eb03c21 = function(arg0) {
const ret = getObject(arg0).credentials;
return addHeapObject(ret);
};
imports.wbg.__wbg_instanceof_Response_fc4327dbfcdf5ced = function(arg0) {
@ -767,14 +754,9 @@ function __wbg_get_imports() {
const ret = getObject(arg0).json();
return addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_create_c7e40b6b88186cbf = function() { return handleError(function (arg0, arg1) {
const ret = getObject(arg0).create(getObject(arg1));
return addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_get_e66794f89dcd7828 = function() { return handleError(function (arg0, arg1) {
const ret = getObject(arg0).get(getObject(arg1));
return addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_log_1d3ae0273d8f4f8a = function(arg0) {
console.log(getObject(arg0));
};
imports.wbg.__wbg_target_f171e89c61e2bccf = function(arg0) {
const ret = getObject(arg0).target;
return isLikeNone(ret) ? 0 : addHeapObject(ret);
@ -794,6 +776,49 @@ function __wbg_get_imports() {
imports.wbg.__wbg_preventDefault_24104f3f0a54546a = function(arg0) {
getObject(arg0).preventDefault();
};
imports.wbg.__wbg_newwithform_368648c82279d486 = function() { return handleError(function (arg0) {
const ret = new FormData(getObject(arg0));
return addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_get_4c356dcef81d58a5 = function(arg0, arg1, arg2) {
const ret = getObject(arg0).get(getStringFromWasm0(arg1, arg2));
return addHeapObject(ret);
};
imports.wbg.__wbg_href_d62a28e4fc1ab948 = function() { return handleError(function (arg0, arg1) {
const ret = getObject(arg1).href;
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
getInt32Memory0()[arg0 / 4 + 1] = len1;
getInt32Memory0()[arg0 / 4 + 0] = ptr1;
}, arguments) };
imports.wbg.__wbg_pathname_c8fd5c498079312d = function() { return handleError(function (arg0, arg1) {
const ret = getObject(arg1).pathname;
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
getInt32Memory0()[arg0 / 4 + 1] = len1;
getInt32Memory0()[arg0 / 4 + 0] = ptr1;
}, arguments) };
imports.wbg.__wbg_search_6c3c472e076ee010 = function() { return handleError(function (arg0, arg1) {
const ret = getObject(arg1).search;
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
getInt32Memory0()[arg0 / 4 + 1] = len1;
getInt32Memory0()[arg0 / 4 + 0] = ptr1;
}, arguments) };
imports.wbg.__wbg_hash_a1a795b89dda8e3d = function() { return handleError(function (arg0, arg1) {
const ret = getObject(arg1).hash;
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
getInt32Memory0()[arg0 / 4 + 1] = len1;
getInt32Memory0()[arg0 / 4 + 0] = ptr1;
}, arguments) };
imports.wbg.__wbg_replace_5d1d2b7956cafd7b = function() { return handleError(function (arg0, arg1, arg2) {
getObject(arg0).replace(getStringFromWasm0(arg1, arg2));
}, arguments) };
imports.wbg.__wbg_getClientExtensionResults_b9108fbba9f54b38 = function(arg0) {
const ret = getObject(arg0).getClientExtensionResults();
return addHeapObject(ret);
};
imports.wbg.__wbg_parentNode_9e53f8b17eb98c9d = function(arg0) {
const ret = getObject(arg0).parentNode;
return isLikeNone(ret) ? 0 : addHeapObject(ret);
@ -832,47 +857,6 @@ function __wbg_get_imports() {
const ret = getObject(arg0).removeChild(getObject(arg1));
return addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_getClientExtensionResults_b9108fbba9f54b38 = function(arg0) {
const ret = getObject(arg0).getClientExtensionResults();
return addHeapObject(ret);
};
imports.wbg.__wbg_add_3eafedc4b2a28db0 = function() { return handleError(function (arg0, arg1, arg2) {
getObject(arg0).add(getStringFromWasm0(arg1, arg2));
}, arguments) };
imports.wbg.__wbg_remove_8ae45e50cb58bb66 = function() { return handleError(function (arg0, arg1, arg2) {
getObject(arg0).remove(getStringFromWasm0(arg1, arg2));
}, arguments) };
imports.wbg.__wbg_href_d62a28e4fc1ab948 = function() { return handleError(function (arg0, arg1) {
const ret = getObject(arg1).href;
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
getInt32Memory0()[arg0 / 4 + 1] = len1;
getInt32Memory0()[arg0 / 4 + 0] = ptr1;
}, arguments) };
imports.wbg.__wbg_pathname_c8fd5c498079312d = function() { return handleError(function (arg0, arg1) {
const ret = getObject(arg1).pathname;
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
getInt32Memory0()[arg0 / 4 + 1] = len1;
getInt32Memory0()[arg0 / 4 + 0] = ptr1;
}, arguments) };
imports.wbg.__wbg_search_6c3c472e076ee010 = function() { return handleError(function (arg0, arg1) {
const ret = getObject(arg1).search;
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
getInt32Memory0()[arg0 / 4 + 1] = len1;
getInt32Memory0()[arg0 / 4 + 0] = ptr1;
}, arguments) };
imports.wbg.__wbg_hash_a1a795b89dda8e3d = function() { return handleError(function (arg0, arg1) {
const ret = getObject(arg1).hash;
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
const len1 = WASM_VECTOR_LEN;
getInt32Memory0()[arg0 / 4 + 1] = len1;
getInt32Memory0()[arg0 / 4 + 0] = ptr1;
}, arguments) };
imports.wbg.__wbg_replace_5d1d2b7956cafd7b = function() { return handleError(function (arg0, arg1, arg2) {
getObject(arg0).replace(getStringFromWasm0(arg1, arg2));
}, arguments) };
imports.wbg.__wbg_href_17ed54b321396524 = function(arg0, arg1) {
const ret = getObject(arg1).href;
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
@ -915,6 +899,22 @@ function __wbg_get_imports() {
const ret = new URL(getStringFromWasm0(arg0, arg1), getStringFromWasm0(arg2, arg3));
return addHeapObject(ret);
}, arguments) };
imports.wbg.__wbg_addEventListener_a5963e26cd7b176b = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
getObject(arg0).addEventListener(getStringFromWasm0(arg1, arg2), getObject(arg3), getObject(arg4));
}, arguments) };
imports.wbg.__wbg_removeEventListener_782040b4432709cb = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
getObject(arg0).removeEventListener(getStringFromWasm0(arg1, arg2), getObject(arg3), arg4 !== 0);
}, arguments) };
imports.wbg.__wbg_instanceof_HtmlFormElement_b57527983c7c1ada = function(arg0) {
let result;
try {
result = getObject(arg0) instanceof HTMLFormElement;
} catch {
result = false;
}
const ret = result;
return ret;
};
imports.wbg.__wbg_get_44be0491f933a435 = function(arg0, arg1) {
const ret = getObject(arg0)[arg1 >>> 0];
return addHeapObject(ret);
@ -1123,16 +1123,16 @@ function __wbg_get_imports() {
const ret = wasm.memory;
return addHeapObject(ret);
};
imports.wbg.__wbindgen_closure_wrapper2610 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 1223, __wbg_adapter_48);
imports.wbg.__wbindgen_closure_wrapper2586 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 1198, __wbg_adapter_48);
return addHeapObject(ret);
};
imports.wbg.__wbindgen_closure_wrapper3431 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 1533, __wbg_adapter_51);
imports.wbg.__wbindgen_closure_wrapper2813 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 1257, __wbg_adapter_51);
return addHeapObject(ret);
};
imports.wbg.__wbindgen_closure_wrapper4517 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 1592, __wbg_adapter_54);
imports.wbg.__wbindgen_closure_wrapper2986 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 1319, __wbg_adapter_54);
return addHeapObject(ret);
};

View file

@ -1,5 +1,7 @@
//! Constants
pub const CONTENT_TYPE: &str = "content-type";
// CSS classes that get applied to full-page forms
pub const CSS_CLASSES_BODY_FORM: &[&str] = &["flex-column", "d-flex", "h-100"];

View file

@ -14,6 +14,7 @@
#![allow(clippy::disallowed_types)]
use error::FetchError;
use kanidm_proto::constants::APPLICATION_JSON;
use serde::{Deserialize, Serialize};
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::JsFuture;
@ -83,7 +84,7 @@ pub async fn do_request(
let request = Request::new_with_str_and_init(uri, &opts)?;
request
.headers()
.set("content-type", "application/json")
.set(crate::constants::CONTENT_TYPE, APPLICATION_JSON)
.expect_throw("failed to set content-type header");
if let Some(sessionid) = models::pop_auth_session_id() {

View file

@ -1,4 +1,5 @@
use gloo::console;
use kanidm_proto::constants::APPLICATION_JSON;
pub use kanidm_proto::oauth2::{
AccessTokenRequest, AccessTokenResponse, AuthorisationRequest, AuthorisationResponse,
CodeChallengeMethod, ErrorResponse,
@ -146,9 +147,10 @@ impl Oauth2App {
opts.body(Some(&consentreq_jsvalue));
let request = Request::new_with_str_and_init("/oauth2/authorise/permit", &opts)?;
request
.headers()
.set("content-type", "application/json")
.set(crate::constants::CONTENT_TYPE, APPLICATION_JSON)
.expect_throw("failed to set header");
if let Some(bearer_token) = models::get_bearer_token() {

View file

@ -137,6 +137,7 @@ async fn test_fixture(rsclient: KanidmClient) {
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
.await;
debug!("auth_simple_password res: {:?}", res);
dbg!(&res);
assert!(res.is_ok());
// Not recommended in production!
rsclient