mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-15 09:22:43 +03:00
introduce AuthConfig v2, which removes role emulation (#891)
<!-- The PR description should answer 2 (maybe 3) important questions: --> ### What We have decided to remove the role emulation feature from engine altogether. More details in the RFC - https://docs.google.com/document/d/1tlS9pqRzLEotLXN_dhjFOeIgbH6zmejOdZTbkkPD-aM/edit ### How <!-- How is it trying to accomplish it (what are the implementation steps)? --> V3_GIT_ORIGIN_REV_ID: e7cb765df5afac6c6d6a05a572a832ce9910cc0b
This commit is contained in:
parent
a8d1002175
commit
5b23ed53bc
@ -12,6 +12,9 @@
|
|||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
- Introduced `AuthConfig` `v2`. This new version removes role emulation in
|
||||||
|
engine (`allowRoleEmulationBy`) field.
|
||||||
|
|
||||||
## [v2024.07.25]
|
## [v2024.07.25]
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
@ -36,8 +36,8 @@ fn build_allowed_roles(
|
|||||||
pub async fn authenticate_request(
|
pub async fn authenticate_request(
|
||||||
http_client: &reqwest::Client,
|
http_client: &reqwest::Client,
|
||||||
jwt_config: JWTConfig,
|
jwt_config: JWTConfig,
|
||||||
allow_role_emulation_for: Option<&Role>,
|
|
||||||
headers: &HeaderMap,
|
headers: &HeaderMap,
|
||||||
|
allow_role_emulation_for: Option<&Role>,
|
||||||
) -> Result<Identity, Error> {
|
) -> Result<Identity, Error> {
|
||||||
let tracer = tracing_util::global_tracer();
|
let tracer = tracing_util::global_tracer();
|
||||||
tracer
|
tracer
|
||||||
@ -213,8 +213,8 @@ mod tests {
|
|||||||
let authenticated_identity = authenticate_request(
|
let authenticated_identity = authenticate_request(
|
||||||
&http_client,
|
&http_client,
|
||||||
jwt_config,
|
jwt_config,
|
||||||
Some(&Role::new("admin")),
|
|
||||||
&header_map,
|
&header_map,
|
||||||
|
Some(&Role::new("admin")),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
@ -310,8 +310,8 @@ mod tests {
|
|||||||
let authenticated_identity = authenticate_request(
|
let authenticated_identity = authenticate_request(
|
||||||
&http_client,
|
&http_client,
|
||||||
jwt_config,
|
jwt_config,
|
||||||
Some(&Role::new("admin")),
|
|
||||||
&header_map,
|
&header_map,
|
||||||
|
Some(&Role::new("admin")),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
use std::hash;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
use std::net;
|
use std::net;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@ -12,29 +14,18 @@ use axum::{
|
|||||||
routing::{get, post},
|
routing::{get, post},
|
||||||
Extension, Json, Router,
|
Extension, Json, Router,
|
||||||
};
|
};
|
||||||
|
use base64::engine::Engine;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use pre_execution_plugin::{
|
|
||||||
configuration::PrePluginConfig, execute::pre_execution_plugins_handler,
|
|
||||||
};
|
|
||||||
use reqwest::header::CONTENT_TYPE;
|
use reqwest::header::CONTENT_TYPE;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use tower_http::cors::CorsLayer;
|
use tower_http::cors::CorsLayer;
|
||||||
use tower_http::trace::TraceLayer;
|
use tower_http::trace::TraceLayer;
|
||||||
use tracing_util::{
|
|
||||||
add_event_on_active_span, set_attribute_on_active_span, set_status_on_current_span,
|
|
||||||
ErrorVisibility, SpanVisibility, TraceableError, TraceableHttpResponse,
|
|
||||||
};
|
|
||||||
|
|
||||||
use base64::engine::Engine;
|
|
||||||
use engine::internal_flags::{resolve_unstable_features, UnstableFeature};
|
|
||||||
use engine::VERSION;
|
|
||||||
use engine::{
|
use engine::{
|
||||||
authentication::{
|
authentication::{resolve_auth_config, AuthConfig, AuthModeConfig},
|
||||||
AuthConfig::{self, V1 as V1AuthConfig},
|
internal_flags::{resolve_unstable_features, UnstableFeature},
|
||||||
AuthModeConfig,
|
|
||||||
},
|
|
||||||
plugins::read_pre_execution_plugins_config,
|
plugins::read_pre_execution_plugins_config,
|
||||||
|
VERSION,
|
||||||
};
|
};
|
||||||
use execute::HttpContext;
|
use execute::HttpContext;
|
||||||
use hasura_authn_core::Session;
|
use hasura_authn_core::Session;
|
||||||
@ -43,9 +34,14 @@ use hasura_authn_jwt::jwt;
|
|||||||
use hasura_authn_noauth as noauth;
|
use hasura_authn_noauth as noauth;
|
||||||
use hasura_authn_webhook::webhook;
|
use hasura_authn_webhook::webhook;
|
||||||
use lang_graphql as gql;
|
use lang_graphql as gql;
|
||||||
|
use pre_execution_plugin::{
|
||||||
|
configuration::PrePluginConfig, execute::pre_execution_plugins_handler,
|
||||||
|
};
|
||||||
use schema::GDS;
|
use schema::GDS;
|
||||||
use std::hash;
|
use tracing_util::{
|
||||||
use std::hash::{Hash, Hasher};
|
add_event_on_active_span, set_attribute_on_active_span, set_status_on_current_span,
|
||||||
|
ErrorVisibility, SpanVisibility, TraceableError, TraceableHttpResponse,
|
||||||
|
};
|
||||||
|
|
||||||
mod cors;
|
mod cors;
|
||||||
|
|
||||||
@ -547,33 +543,36 @@ where
|
|||||||
SpanVisibility::Internal,
|
SpanVisibility::Internal,
|
||||||
|| {
|
|| {
|
||||||
Box::pin(async {
|
Box::pin(async {
|
||||||
match &engine_state.auth_config {
|
// We are still supporting AuthConfig::V1, hence we need to
|
||||||
V1AuthConfig(auth_config) => match &auth_config.mode {
|
// support role emulation
|
||||||
AuthModeConfig::NoAuth(no_auth_config) => {
|
let (auth_mode, allow_role_emulation_by) = match &engine_state.auth_config {
|
||||||
Ok(noauth::identity_from_config(no_auth_config))
|
AuthConfig::V1(auth_config) => (
|
||||||
}
|
&auth_config.mode,
|
||||||
|
auth_config.allow_role_emulation_by.as_ref(),
|
||||||
AuthModeConfig::Webhook(webhook_config) => {
|
),
|
||||||
webhook::authenticate_request(
|
// There is no role emulation in AuthConfig::V2
|
||||||
&engine_state.http_context.client,
|
AuthConfig::V2(auth_config) => (&auth_config.mode, None),
|
||||||
webhook_config,
|
};
|
||||||
&headers_map,
|
match auth_mode {
|
||||||
auth_config.allow_role_emulation_by.as_ref(),
|
AuthModeConfig::NoAuth(no_auth_config) => {
|
||||||
)
|
Ok(noauth::identity_from_config(no_auth_config))
|
||||||
.await
|
}
|
||||||
.map_err(AuthError::from)
|
AuthModeConfig::Webhook(webhook_config) => webhook::authenticate_request(
|
||||||
}
|
&engine_state.http_context.client,
|
||||||
AuthModeConfig::Jwt(jwt_secret_config) => {
|
webhook_config,
|
||||||
jwt_auth::authenticate_request(
|
&headers_map,
|
||||||
&engine_state.http_context.client,
|
allow_role_emulation_by,
|
||||||
*jwt_secret_config.clone(),
|
)
|
||||||
auth_config.allow_role_emulation_by.as_ref(),
|
.await
|
||||||
&headers_map,
|
.map_err(AuthError::from),
|
||||||
)
|
AuthModeConfig::Jwt(jwt_secret_config) => jwt_auth::authenticate_request(
|
||||||
.await
|
&engine_state.http_context.client,
|
||||||
.map_err(AuthError::from)
|
*jwt_secret_config.clone(),
|
||||||
}
|
&headers_map,
|
||||||
},
|
allow_role_emulation_by,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(AuthError::from),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@ -728,7 +727,7 @@ async fn handle_sql_request(
|
|||||||
|
|
||||||
#[allow(clippy::print_stdout)]
|
#[allow(clippy::print_stdout)]
|
||||||
/// Print any build warnings to stdout
|
/// Print any build warnings to stdout
|
||||||
fn print_warnings(warnings: Vec<metadata_resolve::Warning>) {
|
fn print_warnings<T: Display>(warnings: Vec<T>) {
|
||||||
for warning in warnings {
|
for warning in warnings {
|
||||||
println!("Warning: {warning}");
|
println!("Warning: {warning}");
|
||||||
}
|
}
|
||||||
@ -742,15 +741,23 @@ fn build_state(
|
|||||||
pre_execution_plugins_path: &Option<PathBuf>,
|
pre_execution_plugins_path: &Option<PathBuf>,
|
||||||
metadata_resolve_configuration: metadata_resolve::configuration::Configuration,
|
metadata_resolve_configuration: metadata_resolve::configuration::Configuration,
|
||||||
) -> Result<Arc<EngineState>, anyhow::Error> {
|
) -> Result<Arc<EngineState>, anyhow::Error> {
|
||||||
let auth_config = read_auth_config(authn_config_path).map_err(StartupError::ReadAuth)?;
|
// Auth Config
|
||||||
|
let raw_auth_config = std::fs::read_to_string(authn_config_path)?;
|
||||||
|
let (auth_config, auth_warnings) =
|
||||||
|
resolve_auth_config(&raw_auth_config).map_err(StartupError::ReadAuth)?;
|
||||||
|
|
||||||
|
// Plugins
|
||||||
let pre_execution_plugins_config =
|
let pre_execution_plugins_config =
|
||||||
read_pre_execution_plugins_config(pre_execution_plugins_path)
|
read_pre_execution_plugins_config(pre_execution_plugins_path)
|
||||||
.map_err(StartupError::ReadPrePlugin)?;
|
.map_err(StartupError::ReadPrePlugin)?;
|
||||||
|
|
||||||
|
// Metadata
|
||||||
let raw_metadata = std::fs::read_to_string(metadata_path)?;
|
let raw_metadata = std::fs::read_to_string(metadata_path)?;
|
||||||
let metadata = open_dds::Metadata::from_json_str(&raw_metadata)?;
|
let metadata = open_dds::Metadata::from_json_str(&raw_metadata)?;
|
||||||
let (resolved_metadata, warnings) =
|
let (resolved_metadata, warnings) =
|
||||||
metadata_resolve::resolve(metadata, metadata_resolve_configuration)?;
|
metadata_resolve::resolve(metadata, metadata_resolve_configuration)?;
|
||||||
|
|
||||||
|
print_warnings(auth_warnings);
|
||||||
print_warnings(warnings);
|
print_warnings(warnings);
|
||||||
|
|
||||||
let http_context = HttpContext {
|
let http_context = HttpContext {
|
||||||
@ -772,10 +779,3 @@ fn build_state(
|
|||||||
});
|
});
|
||||||
Ok(state)
|
Ok(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_auth_config(path: &PathBuf) -> Result<AuthConfig, anyhow::Error> {
|
|
||||||
let raw_auth_config = std::fs::read_to_string(path)?;
|
|
||||||
Ok(open_dds::traits::OpenDd::deserialize(
|
|
||||||
serde_json::from_str(&raw_auth_config)?,
|
|
||||||
)?)
|
|
||||||
}
|
|
||||||
|
@ -25,6 +25,42 @@ pub enum AuthModeConfig {
|
|||||||
/// Definition of the authentication configuration used by the API server.
|
/// Definition of the authentication configuration used by the API server.
|
||||||
pub enum AuthConfig {
|
pub enum AuthConfig {
|
||||||
V1(AuthConfigV1),
|
V1(AuthConfigV1),
|
||||||
|
V2(AuthConfigV2),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AuthConfig {
|
||||||
|
pub fn upgrade(self) -> AuthConfigV2 {
|
||||||
|
match self {
|
||||||
|
AuthConfig::V1(v1) => AuthConfigV2 { mode: v1.mode },
|
||||||
|
AuthConfig::V2(v2) => v2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Debug, Clone, JsonSchema, PartialEq, opendds_derive::OpenDd, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
#[schemars(title = "AuthConfigV2")]
|
||||||
|
#[schemars(example = "AuthConfigV2::example")]
|
||||||
|
/// Definition of the authentication configuration used by the API server.
|
||||||
|
pub struct AuthConfigV2 {
|
||||||
|
pub mode: AuthModeConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AuthConfigV2 {
|
||||||
|
fn example() -> Self {
|
||||||
|
open_dds::traits::OpenDd::deserialize(serde_json::json!(
|
||||||
|
{
|
||||||
|
"mode": {
|
||||||
|
"webhook": {
|
||||||
|
"url": "http://auth_hook:3050/validate-request",
|
||||||
|
"method": "Post"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
))
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Debug, Clone, JsonSchema, PartialEq, opendds_derive::OpenDd, Deserialize)]
|
#[derive(Serialize, Debug, Clone, JsonSchema, PartialEq, opendds_derive::OpenDd, Deserialize)]
|
||||||
@ -55,6 +91,29 @@ impl AuthConfigV1 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Warnings for the user raised during auth config generation
|
||||||
|
/// These are things that don't break the build, but may do so in future
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum Warning {
|
||||||
|
#[error("AuthConfig v1 is deprecated. `allowRoleEmulationBy` has been removed. Please consider upgrading to AuthConfig v2.")]
|
||||||
|
PleaseUpgradeToV2,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolve `AuthConfig` which is not part of metadata. Hence we resolve/build
|
||||||
|
/// it separately. This also emits warnings.
|
||||||
|
pub fn resolve_auth_config(
|
||||||
|
raw_auth_config: &str,
|
||||||
|
) -> Result<(AuthConfig, Vec<Warning>), anyhow::Error> {
|
||||||
|
let mut warnings = vec![];
|
||||||
|
let auth_config: AuthConfig =
|
||||||
|
open_dds::traits::OpenDd::deserialize(serde_json::from_str(raw_auth_config)?)?;
|
||||||
|
match &auth_config {
|
||||||
|
AuthConfig::V1(_) => warnings.push(Warning::PleaseUpgradeToV2),
|
||||||
|
AuthConfig::V2(_) => (),
|
||||||
|
}
|
||||||
|
Ok((auth_config, warnings))
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use goldenfile::Mint;
|
use goldenfile::Mint;
|
||||||
|
@ -22,6 +22,25 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"definition",
|
||||||
|
"version"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"version": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"v2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"definition": {
|
||||||
|
"$ref": "#/definitions/AuthConfigV2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"definitions": {
|
"definitions": {
|
||||||
@ -319,39 +338,17 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"NoAuthConfig": {
|
"AuthConfigV2": {
|
||||||
"$id": "https://hasura.io/jsonschemas/metadata/NoAuthConfig",
|
"$id": "https://hasura.io/jsonschemas/metadata/AuthConfigV2",
|
||||||
"title": "NoAuthConfig",
|
"title": "AuthConfigV2",
|
||||||
"description": "Configuration used when running engine without authentication",
|
"description": "Definition of the authentication configuration used by the API server.",
|
||||||
"examples": [
|
|
||||||
{
|
|
||||||
"role": "admin",
|
|
||||||
"sessionVariables": {
|
|
||||||
"x-hasura-user-id": "100"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
"role",
|
"mode"
|
||||||
"sessionVariables"
|
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"role": {
|
"mode": {
|
||||||
"description": "role to assume whilst running the engine",
|
"$ref": "#/definitions/AuthModeConfig"
|
||||||
"allOf": [
|
|
||||||
{
|
|
||||||
"$ref": "#/definitions/Role"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"sessionVariables": {
|
|
||||||
"title": "SessionVariables",
|
|
||||||
"description": "static session variables to use whilst running the engine",
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": {
|
|
||||||
"$ref": "#/definitions/SessionVariableValue"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
@ -803,6 +800,43 @@
|
|||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"NoAuthConfig": {
|
||||||
|
"$id": "https://hasura.io/jsonschemas/metadata/NoAuthConfig",
|
||||||
|
"title": "NoAuthConfig",
|
||||||
|
"description": "Configuration used when running engine without authentication",
|
||||||
|
"examples": [
|
||||||
|
{
|
||||||
|
"role": "admin",
|
||||||
|
"sessionVariables": {
|
||||||
|
"x-hasura-user-id": "100"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"role",
|
||||||
|
"sessionVariables"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"role": {
|
||||||
|
"description": "role to assume whilst running the engine",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/Role"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"sessionVariables": {
|
||||||
|
"title": "SessionVariables",
|
||||||
|
"description": "static session variables to use whilst running the engine",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"$ref": "#/definitions/SessionVariableValue"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user