mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-15 01:12:56 +03:00
[ENG-643] Support array-valued session variables (#1221)
<!-- The PR description should answer 2 important questions: --> ### What Allows JSON arrays in session variables. ### How If the type of a session variable isn't special (custom scalar, string, or ID), then we parse the header as JSON, and switch into type-_checking_ mode, returning the JSON wholesale if the type matches. V3_GIT_ORIGIN_REV_ID: 386558898a188c06b73270a90020a20ff963f5a0
This commit is contained in:
parent
a0b998ad7c
commit
16118760fd
@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- Support array values in session variables
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
@ -23,7 +23,7 @@ use std::{
|
|||||||
// Session variable and role are defined as part of OpenDD
|
// Session variable and role are defined as part of OpenDD
|
||||||
pub use open_dds::{
|
pub use open_dds::{
|
||||||
permissions::Role,
|
permissions::Role,
|
||||||
session_variables::{SessionVariable, SESSION_VARIABLE_ROLE},
|
session_variables::{SessionVariableName, SessionVariableReference, SESSION_VARIABLE_ROLE},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, JsonSchema)]
|
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, JsonSchema)]
|
||||||
@ -37,10 +37,10 @@ impl SessionVariableValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)]
|
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)]
|
||||||
pub struct SessionVariables(HashMap<SessionVariable, SessionVariableValue>);
|
pub struct SessionVariables(HashMap<SessionVariableName, SessionVariableValue>);
|
||||||
|
|
||||||
impl SessionVariables {
|
impl SessionVariables {
|
||||||
pub fn get(&self, session_variable: &SessionVariable) -> Option<&SessionVariableValue> {
|
pub fn get(&self, session_variable: &SessionVariableName) -> Option<&SessionVariableValue> {
|
||||||
self.0.get(session_variable)
|
self.0.get(session_variable)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -56,14 +56,14 @@ pub struct Session {
|
|||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub struct RoleAuthorization {
|
pub struct RoleAuthorization {
|
||||||
pub role: Role,
|
pub role: Role,
|
||||||
pub session_variables: HashMap<SessionVariable, SessionVariableValue>,
|
pub session_variables: HashMap<SessionVariableName, SessionVariableValue>,
|
||||||
pub allowed_session_variables_from_request: SessionVariableList,
|
pub allowed_session_variables_from_request: SessionVariableList,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RoleAuthorization {
|
impl RoleAuthorization {
|
||||||
pub fn build_session(
|
pub fn build_session(
|
||||||
&self,
|
&self,
|
||||||
mut variables: HashMap<SessionVariable, SessionVariableValue>,
|
mut variables: HashMap<SessionVariableName, SessionVariableValue>,
|
||||||
) -> Session {
|
) -> Session {
|
||||||
let allowed_client_session_variables = match &self.allowed_session_variables_from_request {
|
let allowed_client_session_variables = match &self.allowed_session_variables_from_request {
|
||||||
SessionVariableList::All => variables,
|
SessionVariableList::All => variables,
|
||||||
@ -87,7 +87,7 @@ pub enum SessionVariableList {
|
|||||||
// like * in Select * from ...
|
// like * in Select * from ...
|
||||||
All,
|
All,
|
||||||
// An explicit list
|
// An explicit list
|
||||||
Some(HashSet<SessionVariable>),
|
Some(HashSet<SessionVariableName>),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Privileges of the current user
|
// Privileges of the current user
|
||||||
@ -183,7 +183,7 @@ pub fn authorize_identity(
|
|||||||
let mut role = None;
|
let mut role = None;
|
||||||
// traverse through the headers and collect role and session variables
|
// traverse through the headers and collect role and session variables
|
||||||
for (header_name, header_value) in headers {
|
for (header_name, header_value) in headers {
|
||||||
if let Ok(session_variable) = SessionVariable::from_str(header_name.as_str()) {
|
if let Ok(session_variable) = SessionVariableName::from_str(header_name.as_str()) {
|
||||||
let variable_value = match header_value.to_str() {
|
let variable_value = match header_value.to_str() {
|
||||||
Err(e) => Err(SessionError::InvalidHeaderValue {
|
Err(e) => Err(SessionError::InvalidHeaderValue {
|
||||||
header_name: header_name.to_string(),
|
header_name: header_name.to_string(),
|
||||||
@ -220,16 +220,16 @@ mod tests {
|
|||||||
let mut authenticated_session_variables = HashMap::new();
|
let mut authenticated_session_variables = HashMap::new();
|
||||||
|
|
||||||
authenticated_session_variables.insert(
|
authenticated_session_variables.insert(
|
||||||
SessionVariable::from_str("x-hasura-user-id").unwrap(),
|
SessionVariableName::from_str("x-hasura-user-id").unwrap(),
|
||||||
SessionVariableValue::new("1"),
|
SessionVariableValue::new("1"),
|
||||||
);
|
);
|
||||||
|
|
||||||
client_session_variables.insert(
|
client_session_variables.insert(
|
||||||
SessionVariable::from_str("x-hasura-custom").unwrap(),
|
SessionVariableName::from_str("x-hasura-custom").unwrap(),
|
||||||
SessionVariableValue::new("test"),
|
SessionVariableValue::new("test"),
|
||||||
);
|
);
|
||||||
client_session_variables.insert(
|
client_session_variables.insert(
|
||||||
SessionVariable::from_str("x-hasura-custom-claim").unwrap(),
|
SessionVariableName::from_str("x-hasura-custom-claim").unwrap(),
|
||||||
SessionVariableValue::new("claim-value"),
|
SessionVariableValue::new("claim-value"),
|
||||||
);
|
);
|
||||||
let role_authorization = RoleAuthorization {
|
let role_authorization = RoleAuthorization {
|
||||||
@ -243,7 +243,7 @@ mod tests {
|
|||||||
let mut expected_session_variables = client_session_variables.clone();
|
let mut expected_session_variables = client_session_variables.clone();
|
||||||
|
|
||||||
expected_session_variables.insert(
|
expected_session_variables.insert(
|
||||||
SessionVariable::from_str("x-hasura-user-id").unwrap(),
|
SessionVariableName::from_str("x-hasura-user-id").unwrap(),
|
||||||
SessionVariableValue::new("1"),
|
SessionVariableValue::new("1"),
|
||||||
);
|
);
|
||||||
pa::assert_eq!(
|
pa::assert_eq!(
|
||||||
@ -262,23 +262,23 @@ mod tests {
|
|||||||
let mut authenticated_session_variables = HashMap::new();
|
let mut authenticated_session_variables = HashMap::new();
|
||||||
|
|
||||||
authenticated_session_variables.insert(
|
authenticated_session_variables.insert(
|
||||||
SessionVariable::from_str("x-hasura-user-id").unwrap(),
|
SessionVariableName::from_str("x-hasura-user-id").unwrap(),
|
||||||
SessionVariableValue::new("1"),
|
SessionVariableValue::new("1"),
|
||||||
);
|
);
|
||||||
|
|
||||||
client_session_variables.insert(
|
client_session_variables.insert(
|
||||||
SessionVariable::from_str("x-hasura-custom").unwrap(),
|
SessionVariableName::from_str("x-hasura-custom").unwrap(),
|
||||||
SessionVariableValue::new("test"),
|
SessionVariableValue::new("test"),
|
||||||
);
|
);
|
||||||
client_session_variables.insert(
|
client_session_variables.insert(
|
||||||
SessionVariable::from_str("x-hasura-custom-claim").unwrap(),
|
SessionVariableName::from_str("x-hasura-custom-claim").unwrap(),
|
||||||
SessionVariableValue::new("claim-value"),
|
SessionVariableValue::new("claim-value"),
|
||||||
);
|
);
|
||||||
let mut allowed_sesion_variables_from_request = HashSet::new();
|
let mut allowed_sesion_variables_from_request = HashSet::new();
|
||||||
allowed_sesion_variables_from_request
|
allowed_sesion_variables_from_request
|
||||||
.insert(SessionVariable::from_str("x-hasura-custom").unwrap());
|
.insert(SessionVariableName::from_str("x-hasura-custom").unwrap());
|
||||||
allowed_sesion_variables_from_request
|
allowed_sesion_variables_from_request
|
||||||
.insert(SessionVariable::from_str("x-hasura-custom-author-id").unwrap());
|
.insert(SessionVariableName::from_str("x-hasura-custom-author-id").unwrap());
|
||||||
let role_authorization = RoleAuthorization {
|
let role_authorization = RoleAuthorization {
|
||||||
role: Role::new("test-role"),
|
role: Role::new("test-role"),
|
||||||
session_variables: authenticated_session_variables,
|
session_variables: authenticated_session_variables,
|
||||||
@ -291,10 +291,10 @@ mod tests {
|
|||||||
|
|
||||||
let mut expected_session_variables = client_session_variables;
|
let mut expected_session_variables = client_session_variables;
|
||||||
expected_session_variables
|
expected_session_variables
|
||||||
.remove(&SessionVariable::from_str("x-hasura-custom-claim").unwrap());
|
.remove(&SessionVariableName::from_str("x-hasura-custom-claim").unwrap());
|
||||||
|
|
||||||
expected_session_variables.insert(
|
expected_session_variables.insert(
|
||||||
SessionVariable::from_str("x-hasura-user-id").unwrap(),
|
SessionVariableName::from_str("x-hasura-user-id").unwrap(),
|
||||||
SessionVariableValue::new("1"),
|
SessionVariableValue::new("1"),
|
||||||
);
|
);
|
||||||
pa::assert_eq!(
|
pa::assert_eq!(
|
||||||
@ -313,16 +313,16 @@ mod tests {
|
|||||||
let mut authenticated_session_variables = HashMap::new();
|
let mut authenticated_session_variables = HashMap::new();
|
||||||
|
|
||||||
authenticated_session_variables.insert(
|
authenticated_session_variables.insert(
|
||||||
SessionVariable::from_str("x-hasura-user-id").unwrap(),
|
SessionVariableName::from_str("x-hasura-user-id").unwrap(),
|
||||||
SessionVariableValue::new("1"),
|
SessionVariableValue::new("1"),
|
||||||
);
|
);
|
||||||
|
|
||||||
client_session_variables.insert(
|
client_session_variables.insert(
|
||||||
SessionVariable::from_str("x-hasura-custom").unwrap(),
|
SessionVariableName::from_str("x-hasura-custom").unwrap(),
|
||||||
SessionVariableValue::new("test"),
|
SessionVariableValue::new("test"),
|
||||||
);
|
);
|
||||||
client_session_variables.insert(
|
client_session_variables.insert(
|
||||||
SessionVariable::from_str("x-hasura-custom-claim").unwrap(),
|
SessionVariableName::from_str("x-hasura-custom-claim").unwrap(),
|
||||||
SessionVariableValue::new("claim-value"),
|
SessionVariableValue::new("claim-value"),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -337,7 +337,7 @@ mod tests {
|
|||||||
let mut expected_session_variables = HashMap::new();
|
let mut expected_session_variables = HashMap::new();
|
||||||
|
|
||||||
expected_session_variables.insert(
|
expected_session_variables.insert(
|
||||||
SessionVariable::from_str("x-hasura-user-id").unwrap(),
|
SessionVariableName::from_str("x-hasura-user-id").unwrap(),
|
||||||
SessionVariableValue::new("1"),
|
SessionVariableValue::new("1"),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -110,10 +110,11 @@ pub async fn authenticate_request(
|
|||||||
mod tests {
|
mod tests {
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use auth_base::{RoleAuthorization, SessionVariable, SessionVariableValue};
|
use auth_base::{RoleAuthorization, SessionVariableValue};
|
||||||
use jsonwebtoken as jwt;
|
use jsonwebtoken as jwt;
|
||||||
use jsonwebtoken::Algorithm;
|
use jsonwebtoken::Algorithm;
|
||||||
use jwt::{encode, EncodingKey};
|
use jwt::{encode, EncodingKey};
|
||||||
|
use open_dds::session_variables::SessionVariableName;
|
||||||
use reqwest::header::AUTHORIZATION;
|
use reqwest::header::AUTHORIZATION;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use tokio;
|
use tokio;
|
||||||
@ -166,7 +167,7 @@ mod tests {
|
|||||||
fn get_default_hasura_claims() -> HasuraClaims {
|
fn get_default_hasura_claims() -> HasuraClaims {
|
||||||
let mut hasura_custom_claims = HashMap::new();
|
let mut hasura_custom_claims = HashMap::new();
|
||||||
hasura_custom_claims.insert(
|
hasura_custom_claims.insert(
|
||||||
SessionVariable::from_str("x-hasura-user-id").unwrap(),
|
SessionVariableName::from_str("x-hasura-user-id").unwrap(),
|
||||||
SessionVariableValue("1".to_string()),
|
SessionVariableValue("1".to_string()),
|
||||||
);
|
);
|
||||||
HasuraClaims {
|
HasuraClaims {
|
||||||
@ -223,7 +224,7 @@ mod tests {
|
|||||||
let mut role_authorization_session_variables = HashMap::new();
|
let mut role_authorization_session_variables = HashMap::new();
|
||||||
|
|
||||||
role_authorization_session_variables.insert(
|
role_authorization_session_variables.insert(
|
||||||
SessionVariable::from_str("x-hasura-user-id").unwrap(),
|
SessionVariableName::from_str("x-hasura-user-id").unwrap(),
|
||||||
SessionVariableValue::new("1"),
|
SessionVariableValue::new("1"),
|
||||||
);
|
);
|
||||||
expected_allowed_roles.insert(
|
expected_allowed_roles.insert(
|
||||||
@ -272,7 +273,7 @@ mod tests {
|
|||||||
async fn test_successful_role_emulation() -> anyhow::Result<()> {
|
async fn test_successful_role_emulation() -> anyhow::Result<()> {
|
||||||
let mut hasura_claims = get_default_hasura_claims();
|
let mut hasura_claims = get_default_hasura_claims();
|
||||||
hasura_claims.custom_claims.insert(
|
hasura_claims.custom_claims.insert(
|
||||||
SessionVariable::from_str("x-hasura-role").unwrap(),
|
SessionVariableName::from_str("x-hasura-role").unwrap(),
|
||||||
SessionVariableValue::new("admin"),
|
SessionVariableValue::new("admin"),
|
||||||
);
|
);
|
||||||
let encoded_claims = get_encoded_claims(Algorithm::HS256, &hasura_claims)?;
|
let encoded_claims = get_encoded_claims(Algorithm::HS256, &hasura_claims)?;
|
||||||
|
@ -4,10 +4,11 @@ use std::time::Duration;
|
|||||||
use axum::http::{HeaderMap, HeaderValue};
|
use axum::http::{HeaderMap, HeaderValue};
|
||||||
use axum::response::IntoResponse;
|
use axum::response::IntoResponse;
|
||||||
use cookie::{self, Cookie};
|
use cookie::{self, Cookie};
|
||||||
use hasura_authn_core::{Role, SessionVariable, SessionVariableValue};
|
use hasura_authn_core::{Role, SessionVariableValue};
|
||||||
use jsonptr::Pointer;
|
use jsonptr::Pointer;
|
||||||
use jsonwebtoken::{self as jwt, decode, DecodingKey, Validation};
|
use jsonwebtoken::{self as jwt, decode, DecodingKey, Validation};
|
||||||
use jwt::decode_header;
|
use jwt::decode_header;
|
||||||
|
use open_dds::session_variables::SessionVariableName;
|
||||||
use reqwest::header::{AUTHORIZATION, COOKIE};
|
use reqwest::header::{AUTHORIZATION, COOKIE};
|
||||||
use reqwest::StatusCode;
|
use reqwest::StatusCode;
|
||||||
use schemars::gen::SchemaGenerator;
|
use schemars::gen::SchemaGenerator;
|
||||||
@ -253,7 +254,7 @@ pub struct JWTClaimsMap {
|
|||||||
/// A dictionary of the custom claims, where the key is the name of the claim and the value
|
/// A dictionary of the custom claims, where the key is the name of the claim and the value
|
||||||
/// is the JSON pointer to lookup the custom claims within the decoded JWT.
|
/// is the JSON pointer to lookup the custom claims within the decoded JWT.
|
||||||
pub custom_claims:
|
pub custom_claims:
|
||||||
Option<HashMap<SessionVariable, JWTClaimsMappingEntry<SessionVariableValue>>>,
|
Option<HashMap<SessionVariableName, JWTClaimsMappingEntry<SessionVariableValue>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, PartialEq, Clone, JsonSchema, Debug)]
|
#[derive(Serialize, Deserialize, PartialEq, Clone, JsonSchema, Debug)]
|
||||||
@ -390,7 +391,7 @@ pub struct HasuraClaims {
|
|||||||
/// as per the user's defined permissions.
|
/// as per the user's defined permissions.
|
||||||
/// For example, things like `x-hasura-user-id` can go here.
|
/// For example, things like `x-hasura-user-id` can go here.
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub custom_claims: HashMap<SessionVariable, SessionVariableValue>,
|
pub custom_claims: HashMap<SessionVariableName, SessionVariableValue>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
@ -733,7 +734,7 @@ mod tests {
|
|||||||
fn get_default_hasura_claims() -> HasuraClaims {
|
fn get_default_hasura_claims() -> HasuraClaims {
|
||||||
let mut hasura_custom_claims = HashMap::new();
|
let mut hasura_custom_claims = HashMap::new();
|
||||||
hasura_custom_claims.insert(
|
hasura_custom_claims.insert(
|
||||||
SessionVariable::from_str("x-hasura-user-id").unwrap(),
|
SessionVariableName::from_str("x-hasura-user-id").unwrap(),
|
||||||
SessionVariableValue("1".to_string()),
|
SessionVariableValue("1".to_string()),
|
||||||
);
|
);
|
||||||
HasuraClaims {
|
HasuraClaims {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use hasura_authn_core::{Role, SessionVariable, SessionVariableValue};
|
use hasura_authn_core::{Role, SessionVariableName, SessionVariableValue};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
@ -15,7 +15,7 @@ pub struct NoAuthConfig {
|
|||||||
pub role: Role,
|
pub role: Role,
|
||||||
/// static session variables to use whilst running the engine
|
/// static session variables to use whilst running the engine
|
||||||
#[schemars(title = "SessionVariables")]
|
#[schemars(title = "SessionVariables")]
|
||||||
pub session_variables: HashMap<SessionVariable, SessionVariableValue>,
|
pub session_variables: HashMap<SessionVariableName, SessionVariableValue>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NoAuthConfig {
|
impl NoAuthConfig {
|
||||||
|
@ -3,7 +3,7 @@ use std::str::FromStr;
|
|||||||
use std::sync::OnceLock;
|
use std::sync::OnceLock;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use auth_base::{Identity, Role, RoleAuthorization, SessionVariable, SessionVariableValue};
|
use auth_base::{Identity, Role, RoleAuthorization, SessionVariableName, SessionVariableValue};
|
||||||
use axum::{
|
use axum::{
|
||||||
http::{HeaderMap, HeaderName, StatusCode},
|
http::{HeaderMap, HeaderName, StatusCode},
|
||||||
response::IntoResponse,
|
response::IntoResponse,
|
||||||
@ -206,7 +206,7 @@ async fn make_auth_hook_request(
|
|||||||
response.json().await.map_err(InternalError::ReqwestError)?;
|
response.json().await.map_err(InternalError::ReqwestError)?;
|
||||||
let mut session_variables = HashMap::new();
|
let mut session_variables = HashMap::new();
|
||||||
for (k, v) in &auth_hook_response {
|
for (k, v) in &auth_hook_response {
|
||||||
match SessionVariable::from_str(k) {
|
match SessionVariableName::from_str(k) {
|
||||||
Ok(session_variable) => {
|
Ok(session_variable) => {
|
||||||
session_variables
|
session_variables
|
||||||
.insert(session_variable, SessionVariableValue(v.to_string()));
|
.insert(session_variable, SessionVariableValue(v.to_string()));
|
||||||
@ -366,11 +366,11 @@ mod tests {
|
|||||||
let mut expected_allowed_roles = HashMap::new();
|
let mut expected_allowed_roles = HashMap::new();
|
||||||
let mut role_authorization_session_variables = HashMap::new();
|
let mut role_authorization_session_variables = HashMap::new();
|
||||||
role_authorization_session_variables.insert(
|
role_authorization_session_variables.insert(
|
||||||
SessionVariable::from_str("x-hasura-role").unwrap(),
|
SessionVariableName::from_str("x-hasura-role").unwrap(),
|
||||||
SessionVariableValue::new("test-role"),
|
SessionVariableValue::new("test-role"),
|
||||||
);
|
);
|
||||||
role_authorization_session_variables.insert(
|
role_authorization_session_variables.insert(
|
||||||
SessionVariable::from_str("x-hasura-test-role-id").unwrap(),
|
SessionVariableName::from_str("x-hasura-test-role-id").unwrap(),
|
||||||
SessionVariableValue::new("1"),
|
SessionVariableValue::new("1"),
|
||||||
);
|
);
|
||||||
expected_allowed_roles.insert(
|
expected_allowed_roles.insert(
|
||||||
@ -437,11 +437,11 @@ mod tests {
|
|||||||
let mut expected_allowed_roles = HashMap::new();
|
let mut expected_allowed_roles = HashMap::new();
|
||||||
let mut role_authorization_session_variables = HashMap::new();
|
let mut role_authorization_session_variables = HashMap::new();
|
||||||
role_authorization_session_variables.insert(
|
role_authorization_session_variables.insert(
|
||||||
SessionVariable::from_str("x-hasura-role").unwrap(),
|
SessionVariableName::from_str("x-hasura-role").unwrap(),
|
||||||
SessionVariableValue::new("test-role"),
|
SessionVariableValue::new("test-role"),
|
||||||
);
|
);
|
||||||
role_authorization_session_variables.insert(
|
role_authorization_session_variables.insert(
|
||||||
SessionVariable::from_str("x-hasura-test-role-id").unwrap(),
|
SessionVariableName::from_str("x-hasura-test-role-id").unwrap(),
|
||||||
SessionVariableValue::new("1"),
|
SessionVariableValue::new("1"),
|
||||||
);
|
);
|
||||||
expected_allowed_roles.insert(
|
expected_allowed_roles.insert(
|
||||||
@ -509,15 +509,15 @@ mod tests {
|
|||||||
let mut expected_allowed_roles = HashMap::new();
|
let mut expected_allowed_roles = HashMap::new();
|
||||||
let mut role_authorization_session_variables = HashMap::new();
|
let mut role_authorization_session_variables = HashMap::new();
|
||||||
role_authorization_session_variables.insert(
|
role_authorization_session_variables.insert(
|
||||||
SessionVariable::from_str("x-hasura-role").unwrap(),
|
SessionVariableName::from_str("x-hasura-role").unwrap(),
|
||||||
SessionVariableValue::new("test-role"),
|
SessionVariableValue::new("test-role"),
|
||||||
);
|
);
|
||||||
role_authorization_session_variables.insert(
|
role_authorization_session_variables.insert(
|
||||||
SessionVariable::from_str("x-hasura-test-role-id").unwrap(),
|
SessionVariableName::from_str("x-hasura-test-role-id").unwrap(),
|
||||||
SessionVariableValue::new("1"),
|
SessionVariableValue::new("1"),
|
||||||
);
|
);
|
||||||
role_authorization_session_variables.insert(
|
role_authorization_session_variables.insert(
|
||||||
SessionVariable::from_str("status").unwrap(),
|
SessionVariableName::from_str("status").unwrap(),
|
||||||
SessionVariableValue::new("true"),
|
SessionVariableValue::new("true"),
|
||||||
);
|
);
|
||||||
expected_allowed_roles.insert(
|
expected_allowed_roles.insert(
|
||||||
@ -637,11 +637,11 @@ mod tests {
|
|||||||
let mut expected_allowed_roles = HashMap::new();
|
let mut expected_allowed_roles = HashMap::new();
|
||||||
let mut role_authorization_session_variables = HashMap::new();
|
let mut role_authorization_session_variables = HashMap::new();
|
||||||
role_authorization_session_variables.insert(
|
role_authorization_session_variables.insert(
|
||||||
SessionVariable::from_str("x-hasura-role").unwrap(),
|
SessionVariableName::from_str("x-hasura-role").unwrap(),
|
||||||
SessionVariableValue::new("user"),
|
SessionVariableValue::new("user"),
|
||||||
);
|
);
|
||||||
role_authorization_session_variables.insert(
|
role_authorization_session_variables.insert(
|
||||||
SessionVariable::from_str("x-hasura-user-id").unwrap(),
|
SessionVariableName::from_str("x-hasura-user-id").unwrap(),
|
||||||
SessionVariableValue::new("1"),
|
SessionVariableValue::new("1"),
|
||||||
);
|
);
|
||||||
expected_allowed_roles.insert(
|
expected_allowed_roles.insert(
|
||||||
|
@ -7,7 +7,7 @@ use hasura_authn_core::{Identity, Role, Session, SessionError, SessionVariableVa
|
|||||||
use lang_graphql::ast::common as ast;
|
use lang_graphql::ast::common as ast;
|
||||||
use lang_graphql::{http::RawRequest, schema::Schema};
|
use lang_graphql::{http::RawRequest, schema::Schema};
|
||||||
use metadata_resolve::{data_connectors::NdcVersion, LifecyclePluginConfigs};
|
use metadata_resolve::{data_connectors::NdcVersion, LifecyclePluginConfigs};
|
||||||
use open_dds::session_variables::{SessionVariable, SESSION_VARIABLE_ROLE};
|
use open_dds::session_variables::{SessionVariableName, SESSION_VARIABLE_ROLE};
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use serde_json as json;
|
use serde_json as json;
|
||||||
use sql::execute::SqlRequest;
|
use sql::execute::SqlRequest;
|
||||||
@ -41,7 +41,7 @@ pub fn setup(test_dir: &Path) -> GoldenTestContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn resolve_session(
|
pub(crate) fn resolve_session(
|
||||||
session_variables: HashMap<SessionVariable, SessionVariableValue>,
|
session_variables: HashMap<SessionVariableName, SessionVariableValue>,
|
||||||
) -> Result<Session, SessionError> {
|
) -> Result<Session, SessionError> {
|
||||||
//return an arbitrary identity with role emulation enabled
|
//return an arbitrary identity with role emulation enabled
|
||||||
let authorization = Identity::admin(Role::new("admin"));
|
let authorization = Identity::admin(Role::new("admin"));
|
||||||
@ -107,7 +107,7 @@ pub(crate) fn test_introspection_expectation(
|
|||||||
|
|
||||||
let request_headers = reqwest::header::HeaderMap::new();
|
let request_headers = reqwest::header::HeaderMap::new();
|
||||||
let session_vars_path = &test_path.join("session_variables.json");
|
let session_vars_path = &test_path.join("session_variables.json");
|
||||||
let sessions: Vec<HashMap<SessionVariable, SessionVariableValue>> =
|
let sessions: Vec<HashMap<SessionVariableName, SessionVariableValue>> =
|
||||||
json::from_str(read_to_string(session_vars_path)?.as_ref())?;
|
json::from_str(read_to_string(session_vars_path)?.as_ref())?;
|
||||||
let sessions: Vec<Session> = sessions
|
let sessions: Vec<Session> = sessions
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -266,7 +266,7 @@ pub fn test_execution_expectation_for_multiple_ndc_versions(
|
|||||||
|
|
||||||
let request_headers = reqwest::header::HeaderMap::new();
|
let request_headers = reqwest::header::HeaderMap::new();
|
||||||
let session_vars_path = &test_path.join("session_variables.json");
|
let session_vars_path = &test_path.join("session_variables.json");
|
||||||
let sessions: Vec<HashMap<SessionVariable, SessionVariableValue>> =
|
let sessions: Vec<HashMap<SessionVariableName, SessionVariableValue>> =
|
||||||
json::from_str(read_to_string(session_vars_path)?.as_ref())?;
|
json::from_str(read_to_string(session_vars_path)?.as_ref())?;
|
||||||
let sessions: Vec<Session> = sessions
|
let sessions: Vec<Session> = sessions
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -472,7 +472,7 @@ pub fn test_execute_explain(
|
|||||||
let session_variables_raw = r#"{
|
let session_variables_raw = r#"{
|
||||||
"x-hasura-role": "admin"
|
"x-hasura-role": "admin"
|
||||||
}"#;
|
}"#;
|
||||||
let session_variables: HashMap<SessionVariable, SessionVariableValue> =
|
let session_variables: HashMap<SessionVariableName, SessionVariableValue> =
|
||||||
serde_json::from_str(session_variables_raw)?;
|
serde_json::from_str(session_variables_raw)?;
|
||||||
resolve_session(session_variables)
|
resolve_session(session_variables)
|
||||||
}?;
|
}?;
|
||||||
@ -579,7 +579,7 @@ pub(crate) fn test_sql(test_path_string: &str) -> anyhow::Result<()> {
|
|||||||
|
|
||||||
let session = Arc::new({
|
let session = Arc::new({
|
||||||
let session_vars_path = &test_path.join("session_variables.json");
|
let session_vars_path = &test_path.join("session_variables.json");
|
||||||
let session_variables: HashMap<SessionVariable, SessionVariableValue> =
|
let session_variables: HashMap<SessionVariableName, SessionVariableValue> =
|
||||||
serde_json::from_str(read_to_string(session_vars_path)?.as_ref())?;
|
serde_json::from_str(read_to_string(session_vars_path)?.as_ref())?;
|
||||||
resolve_session(session_variables)
|
resolve_session(session_variables)
|
||||||
}?);
|
}?);
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"AuthorMany": [
|
||||||
|
{
|
||||||
|
"author_id": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
@ -0,0 +1,192 @@
|
|||||||
|
{
|
||||||
|
"version": "v2",
|
||||||
|
"flags": {
|
||||||
|
"json_session_variables": true
|
||||||
|
},
|
||||||
|
"subgraphs": [
|
||||||
|
{
|
||||||
|
"name": "default",
|
||||||
|
"objects": [
|
||||||
|
{
|
||||||
|
"kind": "BooleanExpressionType",
|
||||||
|
"version": "v1",
|
||||||
|
"definition": {
|
||||||
|
"name": "int_bool_exp",
|
||||||
|
"operand": {
|
||||||
|
"scalar": {
|
||||||
|
"type": "Int",
|
||||||
|
"comparisonOperators": [
|
||||||
|
{
|
||||||
|
"name": "_eq",
|
||||||
|
"argumentType": "Int!"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "_in",
|
||||||
|
"argumentType": "[Int!]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dataConnectorOperatorMapping": [
|
||||||
|
{
|
||||||
|
"dataConnectorName": "db",
|
||||||
|
"dataConnectorScalarType": "int4",
|
||||||
|
"operatorMapping": {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"logicalOperators": {
|
||||||
|
"enable": true
|
||||||
|
},
|
||||||
|
"isNull": {
|
||||||
|
"enable": true
|
||||||
|
},
|
||||||
|
"graphql": {
|
||||||
|
"typeName": "Int_Filter"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "ObjectType",
|
||||||
|
"version": "v1",
|
||||||
|
"definition": {
|
||||||
|
"name": "author",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "author_id",
|
||||||
|
"type": "Int!"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "first_name",
|
||||||
|
"type": "String!"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "last_name",
|
||||||
|
"type": "String!"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"graphql": {
|
||||||
|
"typeName": "Author"
|
||||||
|
},
|
||||||
|
"dataConnectorTypeMapping": [
|
||||||
|
{
|
||||||
|
"dataConnectorName": "db",
|
||||||
|
"dataConnectorObjectType": "author",
|
||||||
|
"fieldMapping": {
|
||||||
|
"author_id": {
|
||||||
|
"column": {
|
||||||
|
"name": "id"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"first_name": {
|
||||||
|
"column": {
|
||||||
|
"name": "first_name"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"last_name": {
|
||||||
|
"column": {
|
||||||
|
"name": "last_name"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "BooleanExpressionType",
|
||||||
|
"version": "v1",
|
||||||
|
"definition": {
|
||||||
|
"name": "author_bool_exp",
|
||||||
|
"operand": {
|
||||||
|
"object": {
|
||||||
|
"type": "author",
|
||||||
|
"comparableFields": [
|
||||||
|
{
|
||||||
|
"fieldName": "author_id",
|
||||||
|
"booleanExpressionType": "int_bool_exp"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"comparableRelationships": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"logicalOperators": {
|
||||||
|
"enable": true
|
||||||
|
},
|
||||||
|
"isNull": {
|
||||||
|
"enable": true
|
||||||
|
},
|
||||||
|
"graphql": {
|
||||||
|
"typeName": "Author_Filter"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "Model",
|
||||||
|
"version": "v1",
|
||||||
|
"definition": {
|
||||||
|
"name": "Authors",
|
||||||
|
"objectType": "author",
|
||||||
|
"source": {
|
||||||
|
"dataConnectorName": "db",
|
||||||
|
"collection": "author"
|
||||||
|
},
|
||||||
|
"graphql": {
|
||||||
|
"selectUniques": [],
|
||||||
|
"selectMany": {
|
||||||
|
"queryRootField": "AuthorMany"
|
||||||
|
},
|
||||||
|
"orderByExpressionType": "Author_Order_By"
|
||||||
|
},
|
||||||
|
"filterExpressionType": "author_bool_exp",
|
||||||
|
"orderableFields": [
|
||||||
|
{
|
||||||
|
"fieldName": "author_id",
|
||||||
|
"orderByDirections": {
|
||||||
|
"enableAll": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "TypePermissions",
|
||||||
|
"version": "v1",
|
||||||
|
"definition": {
|
||||||
|
"typeName": "author",
|
||||||
|
"permissions": [
|
||||||
|
{
|
||||||
|
"role": "user",
|
||||||
|
"output": {
|
||||||
|
"allowedFields": ["author_id", "first_name", "last_name"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "ModelPermissions",
|
||||||
|
"version": "v1",
|
||||||
|
"definition": {
|
||||||
|
"modelName": "Authors",
|
||||||
|
"permissions": [
|
||||||
|
{
|
||||||
|
"role": "user",
|
||||||
|
"select": {
|
||||||
|
"filter": {
|
||||||
|
"fieldComparison": {
|
||||||
|
"field": "author_id",
|
||||||
|
"operator": "_in",
|
||||||
|
"value": {
|
||||||
|
"sessionVariable": "x-hasura-allowed-author-ids"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
query MyQuery {
|
||||||
|
AuthorMany {
|
||||||
|
author_id
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"x-hasura-role": "user",
|
||||||
|
"x-hasura-allowed-author-ids": "[1]"
|
||||||
|
}
|
||||||
|
]
|
@ -129,6 +129,17 @@ fn test_model_select_many_empty_select() -> anyhow::Result<()> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_model_select_many_array_session_variable() -> anyhow::Result<()> {
|
||||||
|
let test_path_string = "execute/models/select_many/array_session_variable";
|
||||||
|
let common_metadata_path_string = "execute/common_metadata/postgres_connector_schema.json";
|
||||||
|
common::test_execution_expectation(
|
||||||
|
test_path_string,
|
||||||
|
&[common_metadata_path_string],
|
||||||
|
common::TestOpenDDPipeline::TestNDCResponses,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_model_select_many_field_arguments() -> anyhow::Result<()> {
|
fn test_model_select_many_field_arguments() -> anyhow::Result<()> {
|
||||||
common::test_execution_expectation_for_multiple_ndc_versions(
|
common::test_execution_expectation_for_multiple_ndc_versions(
|
||||||
|
@ -0,0 +1,52 @@
|
|||||||
|
---
|
||||||
|
source: crates/engine/tests/common.rs
|
||||||
|
expression: query_ir
|
||||||
|
---
|
||||||
|
V1(
|
||||||
|
QueryRequestV1 {
|
||||||
|
queries: {
|
||||||
|
Alias(
|
||||||
|
Identifier(
|
||||||
|
"AuthorMany",
|
||||||
|
),
|
||||||
|
): Model(
|
||||||
|
ModelSelection {
|
||||||
|
target: ModelTarget {
|
||||||
|
subgraph: SubgraphName(
|
||||||
|
"default",
|
||||||
|
),
|
||||||
|
model_name: ModelName(
|
||||||
|
Identifier(
|
||||||
|
"Authors",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
arguments: {},
|
||||||
|
filter: None,
|
||||||
|
order_by: [],
|
||||||
|
limit: None,
|
||||||
|
offset: None,
|
||||||
|
},
|
||||||
|
selection: {
|
||||||
|
Alias(
|
||||||
|
Identifier(
|
||||||
|
"author_id",
|
||||||
|
),
|
||||||
|
): Field(
|
||||||
|
ObjectFieldSelection {
|
||||||
|
target: ObjectFieldTarget {
|
||||||
|
field_name: FieldName(
|
||||||
|
Identifier(
|
||||||
|
"author_id",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
arguments: {},
|
||||||
|
},
|
||||||
|
selection: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
@ -0,0 +1,52 @@
|
|||||||
|
---
|
||||||
|
source: crates/engine/tests/common.rs
|
||||||
|
expression: query_ir
|
||||||
|
---
|
||||||
|
V1(
|
||||||
|
QueryRequestV1 {
|
||||||
|
queries: {
|
||||||
|
Alias(
|
||||||
|
Identifier(
|
||||||
|
"AuthorMany",
|
||||||
|
),
|
||||||
|
): Model(
|
||||||
|
ModelSelection {
|
||||||
|
target: ModelTarget {
|
||||||
|
subgraph: SubgraphName(
|
||||||
|
"default",
|
||||||
|
),
|
||||||
|
model_name: ModelName(
|
||||||
|
Identifier(
|
||||||
|
"Authors",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
arguments: {},
|
||||||
|
filter: None,
|
||||||
|
order_by: [],
|
||||||
|
limit: None,
|
||||||
|
offset: None,
|
||||||
|
},
|
||||||
|
selection: {
|
||||||
|
Alias(
|
||||||
|
Identifier(
|
||||||
|
"author_id",
|
||||||
|
),
|
||||||
|
): Field(
|
||||||
|
ObjectFieldSelection {
|
||||||
|
target: ObjectFieldTarget {
|
||||||
|
field_name: FieldName(
|
||||||
|
Identifier(
|
||||||
|
"author_id",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
arguments: {},
|
||||||
|
},
|
||||||
|
selection: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
source: crates/engine/tests/common.rs
|
||||||
|
expression: rowsets
|
||||||
|
---
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"rows": [
|
||||||
|
{
|
||||||
|
"author_id": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
source: crates/engine/tests/common.rs
|
||||||
|
expression: rowsets
|
||||||
|
---
|
||||||
|
{
|
||||||
|
"Ok": [
|
||||||
|
{
|
||||||
|
"rows": [
|
||||||
|
{
|
||||||
|
"author_id": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -21,7 +21,7 @@ mod tests {
|
|||||||
use hasura_authn_core::{Identity, Role, Session, SessionVariableValue};
|
use hasura_authn_core::{Identity, Role, Session, SessionVariableValue};
|
||||||
use lang_graphql::http::Request;
|
use lang_graphql::http::Request;
|
||||||
use lang_graphql::{parser::Parser, validation::normalize_request};
|
use lang_graphql::{parser::Parser, validation::normalize_request};
|
||||||
use open_dds::session_variables::{SessionVariable, SESSION_VARIABLE_ROLE};
|
use open_dds::session_variables::{SessionVariableName, SESSION_VARIABLE_ROLE};
|
||||||
use serde_json as json;
|
use serde_json as json;
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
@ -160,7 +160,7 @@ mod tests {
|
|||||||
// TODO: remove duplication between this function and 'add_session'
|
// TODO: remove duplication between this function and 'add_session'
|
||||||
fn resolve_session(session_vars_path: PathBuf) -> Session {
|
fn resolve_session(session_vars_path: PathBuf) -> Session {
|
||||||
let authorization = Identity::admin(Role::new("admin"));
|
let authorization = Identity::admin(Role::new("admin"));
|
||||||
let session_variables: HashMap<SessionVariable, SessionVariableValue> = {
|
let session_variables: HashMap<SessionVariableName, SessionVariableValue> = {
|
||||||
if session_vars_path.exists() {
|
if session_vars_path.exists() {
|
||||||
json::from_str(fs::read_to_string(session_vars_path).unwrap().as_ref()).unwrap()
|
json::from_str(fs::read_to_string(session_vars_path).unwrap().as_ref()).unwrap()
|
||||||
} else {
|
} else {
|
||||||
|
@ -369,8 +369,8 @@ pub(crate) fn map_argument_value_to_ndc_type(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use hasura_authn_core::{
|
use hasura_authn_core::{
|
||||||
Role, RoleAuthorization, Session, SessionVariable, SessionVariableList,
|
Role, RoleAuthorization, Session, SessionVariableList, SessionVariableName,
|
||||||
SessionVariableValue,
|
SessionVariableReference, SessionVariableValue,
|
||||||
};
|
};
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use metadata_resolve::http::SerializableHeaderName;
|
use metadata_resolve::http::SerializableHeaderName;
|
||||||
@ -380,7 +380,7 @@ mod test {
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
fn make_test_session(
|
fn make_test_session(
|
||||||
client_session_variables: HashMap<SessionVariable, SessionVariableValue>,
|
client_session_variables: HashMap<SessionVariableName, SessionVariableValue>,
|
||||||
) -> Session {
|
) -> Session {
|
||||||
let authenticated_session_variables = HashMap::new();
|
let authenticated_session_variables = HashMap::new();
|
||||||
|
|
||||||
@ -459,7 +459,10 @@ mod test {
|
|||||||
let mut additional = IndexMap::new();
|
let mut additional = IndexMap::new();
|
||||||
additional.insert(
|
additional.insert(
|
||||||
SerializableHeaderName::new("name".into()).unwrap(),
|
SerializableHeaderName::new("name".into()).unwrap(),
|
||||||
ValueExpression::SessionVariable(SessionVariable::from_str("x-name").unwrap()),
|
ValueExpression::SessionVariable(SessionVariableReference {
|
||||||
|
name: SessionVariableName::from_str("x-name").unwrap(),
|
||||||
|
passed_as_json: false,
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
let http_headers = HttpHeadersPreset {
|
let http_headers = HttpHeadersPreset {
|
||||||
forward: vec![],
|
forward: vec![],
|
||||||
@ -471,7 +474,7 @@ mod test {
|
|||||||
// what session variables do we have?
|
// what session variables do we have?
|
||||||
let mut client_session_variables = HashMap::new();
|
let mut client_session_variables = HashMap::new();
|
||||||
client_session_variables.insert(
|
client_session_variables.insert(
|
||||||
SessionVariable::from_str("x-name").unwrap(),
|
SessionVariableName::from_str("x-name").unwrap(),
|
||||||
SessionVariableValue::new("Mr Horse"),
|
SessionVariableValue::new("Mr Horse"),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ use open_dds::{
|
|||||||
arguments::ArgumentName,
|
arguments::ArgumentName,
|
||||||
data_connector::{DataConnectorColumnName, DataConnectorName},
|
data_connector::{DataConnectorColumnName, DataConnectorName},
|
||||||
relationships::RelationshipName,
|
relationships::RelationshipName,
|
||||||
session_variables::SessionVariable,
|
session_variables::SessionVariableName,
|
||||||
types::{CustomTypeName, FieldName},
|
types::{CustomTypeName, FieldName},
|
||||||
};
|
};
|
||||||
use serde_json as json;
|
use serde_json as json;
|
||||||
@ -118,7 +118,9 @@ pub enum InternalDeveloperError {
|
|||||||
},
|
},
|
||||||
|
|
||||||
#[error("Required session variable not found in the request: {session_variable}")]
|
#[error("Required session variable not found in the request: {session_variable}")]
|
||||||
MissingSessionVariable { session_variable: SessionVariable },
|
MissingSessionVariable {
|
||||||
|
session_variable: SessionVariableName,
|
||||||
|
},
|
||||||
|
|
||||||
#[error("Unable to typecast session variable. Expected: {expected:}, but found: {found:}")]
|
#[error("Unable to typecast session variable. Expected: {expected:}, but found: {found:}")]
|
||||||
VariableTypeCast { expected: String, found: String },
|
VariableTypeCast { expected: String, found: String },
|
||||||
|
@ -223,13 +223,13 @@ pub(crate) fn make_argument_from_value_expression(
|
|||||||
match val_expr {
|
match val_expr {
|
||||||
metadata_resolve::ValueExpression::Literal(val) => Ok(val.clone()),
|
metadata_resolve::ValueExpression::Literal(val) => Ok(val.clone()),
|
||||||
metadata_resolve::ValueExpression::SessionVariable(session_var) => {
|
metadata_resolve::ValueExpression::SessionVariable(session_var) => {
|
||||||
let value = session_variables.get(session_var).ok_or_else(|| {
|
let value = session_variables.get(&session_var.name).ok_or_else(|| {
|
||||||
error::InternalDeveloperError::MissingSessionVariable {
|
error::InternalDeveloperError::MissingSessionVariable {
|
||||||
session_variable: session_var.clone(),
|
session_variable: session_var.name.clone(),
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
typecast_session_variable(value, value_type)
|
typecast_session_variable(session_var.passed_as_json, value, value_type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -247,14 +247,14 @@ pub(crate) fn make_argument_from_value_expression_or_predicate<'s>(
|
|||||||
Ok(Argument::Literal { value: val.clone() })
|
Ok(Argument::Literal { value: val.clone() })
|
||||||
}
|
}
|
||||||
metadata_resolve::ValueExpressionOrPredicate::SessionVariable(session_var) => {
|
metadata_resolve::ValueExpressionOrPredicate::SessionVariable(session_var) => {
|
||||||
let value = session_variables.get(session_var).ok_or_else(|| {
|
let value = session_variables.get(&session_var.name).ok_or_else(|| {
|
||||||
error::InternalDeveloperError::MissingSessionVariable {
|
error::InternalDeveloperError::MissingSessionVariable {
|
||||||
session_variable: session_var.clone(),
|
session_variable: session_var.name.clone(),
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(Argument::Literal {
|
Ok(Argument::Literal {
|
||||||
value: typecast_session_variable(value, value_type)?,
|
value: typecast_session_variable(session_var.passed_as_json, value, value_type)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
metadata_resolve::ValueExpressionOrPredicate::BooleanExpression(model_predicate) => {
|
metadata_resolve::ValueExpressionOrPredicate::BooleanExpression(model_predicate) => {
|
||||||
@ -274,6 +274,18 @@ pub(crate) fn make_argument_from_value_expression_or_predicate<'s>(
|
|||||||
|
|
||||||
/// Typecast a stringified session variable into a given type, but as a serde_json::Value
|
/// Typecast a stringified session variable into a given type, but as a serde_json::Value
|
||||||
fn typecast_session_variable(
|
fn typecast_session_variable(
|
||||||
|
passed_as_json: bool,
|
||||||
|
session_var_value_wrapped: &SessionVariableValue,
|
||||||
|
to_type: &QualifiedTypeReference,
|
||||||
|
) -> Result<serde_json::Value, error::Error> {
|
||||||
|
if passed_as_json {
|
||||||
|
typecast_session_variable_v2(session_var_value_wrapped, to_type)
|
||||||
|
} else {
|
||||||
|
typecast_session_variable_v1(session_var_value_wrapped, to_type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn typecast_session_variable_v1(
|
||||||
session_var_value_wrapped: &SessionVariableValue,
|
session_var_value_wrapped: &SessionVariableValue,
|
||||||
to_type: &QualifiedTypeReference,
|
to_type: &QualifiedTypeReference,
|
||||||
) -> Result<serde_json::Value, error::Error> {
|
) -> Result<serde_json::Value, error::Error> {
|
||||||
@ -328,3 +340,73 @@ fn typecast_session_variable(
|
|||||||
QualifiedBaseType::List(_) => Err(error::InternalDeveloperError::VariableArrayTypeCast)?,
|
QualifiedBaseType::List(_) => Err(error::InternalDeveloperError::VariableArrayTypeCast)?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn typecast_session_variable_v2(
|
||||||
|
session_var_value: &SessionVariableValue,
|
||||||
|
to_type: &QualifiedTypeReference,
|
||||||
|
) -> Result<serde_json::Value, error::Error> {
|
||||||
|
let value = serde_json::from_str(&session_var_value.0)?;
|
||||||
|
typecheck_session_variable(&value, to_type)?;
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn typecheck_session_variable(
|
||||||
|
value: &serde_json::Value,
|
||||||
|
to_type: &QualifiedTypeReference,
|
||||||
|
) -> Result<(), crate::Error> {
|
||||||
|
match &to_type.underlying_type {
|
||||||
|
QualifiedBaseType::Named(type_name) => match type_name {
|
||||||
|
QualifiedTypeName::Inbuilt(primitive) => match primitive {
|
||||||
|
InbuiltType::Int => {
|
||||||
|
if !value.is_i64() {
|
||||||
|
Err(error::InternalDeveloperError::VariableTypeCast {
|
||||||
|
expected: "int".into(),
|
||||||
|
found: value.to_string(),
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
InbuiltType::Float => {
|
||||||
|
if !value.is_f64() {
|
||||||
|
Err(error::InternalDeveloperError::VariableTypeCast {
|
||||||
|
expected: "float".into(),
|
||||||
|
found: value.to_string(),
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
InbuiltType::Boolean => {
|
||||||
|
if !value.is_boolean() {
|
||||||
|
Err(error::InternalDeveloperError::VariableTypeCast {
|
||||||
|
expected: "true or false".into(),
|
||||||
|
found: value.to_string(),
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
InbuiltType::ID | InbuiltType::String => {
|
||||||
|
if !value.is_string() {
|
||||||
|
Err(error::InternalDeveloperError::VariableTypeCast {
|
||||||
|
expected: "string".into(),
|
||||||
|
found: value.to_string(),
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
QualifiedTypeName::Custom(_) => Ok(()),
|
||||||
|
},
|
||||||
|
QualifiedBaseType::List(element_type) => {
|
||||||
|
let elements = value.as_array().ok_or_else(|| {
|
||||||
|
error::InternalDeveloperError::VariableTypeCast {
|
||||||
|
expected: "array".into(),
|
||||||
|
found: value.to_string(),
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
for element in elements {
|
||||||
|
typecheck_session_variable(element, element_type)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -253,6 +253,7 @@ pub fn get_argument_mappings<'a>(
|
|||||||
/// type to validate it against to ensure the fields it refers to
|
/// type to validate it against to ensure the fields it refers to
|
||||||
/// exist etc
|
/// exist etc
|
||||||
pub(crate) fn resolve_value_expression_for_argument(
|
pub(crate) fn resolve_value_expression_for_argument(
|
||||||
|
flags: &open_dds::flags::Flags,
|
||||||
argument_name: &open_dds::arguments::ArgumentName,
|
argument_name: &open_dds::arguments::ArgumentName,
|
||||||
value_expression: &open_dds::permissions::ValueExpressionOrPredicate,
|
value_expression: &open_dds::permissions::ValueExpressionOrPredicate,
|
||||||
argument_type: &QualifiedTypeReference,
|
argument_type: &QualifiedTypeReference,
|
||||||
@ -278,7 +279,10 @@ pub(crate) fn resolve_value_expression_for_argument(
|
|||||||
match value_expression {
|
match value_expression {
|
||||||
open_dds::permissions::ValueExpressionOrPredicate::SessionVariable(session_variable) => {
|
open_dds::permissions::ValueExpressionOrPredicate::SessionVariable(session_variable) => {
|
||||||
Ok::<ValueExpressionOrPredicate, Error>(ValueExpressionOrPredicate::SessionVariable(
|
Ok::<ValueExpressionOrPredicate, Error>(ValueExpressionOrPredicate::SessionVariable(
|
||||||
session_variable.clone(),
|
hasura_authn_core::SessionVariableReference {
|
||||||
|
name: session_variable.clone(),
|
||||||
|
passed_as_json: flags.json_session_variables,
|
||||||
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
open_dds::permissions::ValueExpressionOrPredicate::Literal(json_value) => {
|
open_dds::permissions::ValueExpressionOrPredicate::Literal(json_value) => {
|
||||||
@ -365,6 +369,7 @@ pub(crate) fn resolve_value_expression_for_argument(
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
let resolved_model_predicate = model_permissions::resolve_model_predicate_with_type(
|
let resolved_model_predicate = model_permissions::resolve_model_predicate_with_type(
|
||||||
|
flags,
|
||||||
bool_exp,
|
bool_exp,
|
||||||
base_type,
|
base_type,
|
||||||
object_type_representation,
|
object_type_representation,
|
||||||
|
@ -304,17 +304,7 @@ fn build_preset_map_from_input_object_type_permission(
|
|||||||
field_path: new_field_path,
|
field_path: new_field_path,
|
||||||
};
|
};
|
||||||
|
|
||||||
let value = (
|
let value = (type_reference.clone(), preset.value.clone());
|
||||||
type_reference.clone(),
|
|
||||||
match &preset.value {
|
|
||||||
open_dds::permissions::ValueExpression::Literal(literal) => {
|
|
||||||
ValueExpressionOrPredicate::Literal(literal.clone())
|
|
||||||
}
|
|
||||||
open_dds::permissions::ValueExpression::SessionVariable(session_variable) => {
|
|
||||||
ValueExpressionOrPredicate::SessionVariable(session_variable.clone())
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok((key, value))
|
Ok((key, value))
|
||||||
})
|
})
|
||||||
|
@ -38,6 +38,7 @@ fn get_command_source_argument<'a>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve_command_permissions(
|
pub fn resolve_command_permissions(
|
||||||
|
flags: &open_dds::flags::Flags,
|
||||||
command: &commands::Command,
|
command: &commands::Command,
|
||||||
permissions: &CommandPermissionsV1,
|
permissions: &CommandPermissionsV1,
|
||||||
object_types: &BTreeMap<
|
object_types: &BTreeMap<
|
||||||
@ -97,6 +98,7 @@ pub fn resolve_command_permissions(
|
|||||||
match command.arguments.get(&argument_preset.argument) {
|
match command.arguments.get(&argument_preset.argument) {
|
||||||
Some(argument) => {
|
Some(argument) => {
|
||||||
let value_expression = resolve_value_expression_for_argument(
|
let value_expression = resolve_value_expression_for_argument(
|
||||||
|
flags,
|
||||||
&argument_preset.argument,
|
&argument_preset.argument,
|
||||||
&argument_preset.value,
|
&argument_preset.value,
|
||||||
&argument.argument_type,
|
&argument.argument_type,
|
||||||
|
@ -67,6 +67,7 @@ pub fn resolve(
|
|||||||
})?;
|
})?;
|
||||||
if command.permissions.is_empty() {
|
if command.permissions.is_empty() {
|
||||||
command.permissions = command_permission::resolve_command_permissions(
|
command.permissions = command_permission::resolve_command_permissions(
|
||||||
|
&metadata_accessor.flags,
|
||||||
&command.command,
|
&command.command,
|
||||||
command_permissions,
|
command_permissions,
|
||||||
object_types,
|
object_types,
|
||||||
|
@ -29,12 +29,15 @@ pub fn resolve<'a>(
|
|||||||
let qualified_data_connector_name =
|
let qualified_data_connector_name =
|
||||||
Qualified::new(subgraph.clone(), data_connector.name.clone());
|
Qualified::new(subgraph.clone(), data_connector.name.clone());
|
||||||
|
|
||||||
let (data_connector_context, connector_issues) =
|
let (data_connector_context, connector_issues) = types::DataConnectorContext::new(
|
||||||
types::DataConnectorContext::new(data_connector, &configuration.unstable_features)
|
metadata_accessor,
|
||||||
.map_err(|error| NamedDataConnectorError {
|
data_connector,
|
||||||
data_connector_name: qualified_data_connector_name.clone(),
|
&configuration.unstable_features,
|
||||||
error,
|
)
|
||||||
})?;
|
.map_err(|error| NamedDataConnectorError {
|
||||||
|
data_connector_name: qualified_data_connector_name.clone(),
|
||||||
|
error,
|
||||||
|
})?;
|
||||||
|
|
||||||
issues.extend(
|
issues.extend(
|
||||||
connector_issues
|
connector_issues
|
||||||
|
@ -12,6 +12,7 @@ use crate::types::subgraph::Qualified;
|
|||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use lang_graphql::ast::common::OperationType;
|
use lang_graphql::ast::common::OperationType;
|
||||||
use ndc_models;
|
use ndc_models;
|
||||||
|
use open_dds::accessor::MetadataAccessor;
|
||||||
use open_dds::data_connector::DataConnectorColumnName;
|
use open_dds::data_connector::DataConnectorColumnName;
|
||||||
use open_dds::types::DataConnectorArgumentName;
|
use open_dds::types::DataConnectorArgumentName;
|
||||||
use open_dds::{
|
use open_dds::{
|
||||||
@ -63,6 +64,7 @@ pub struct DataConnectorContext<'a> {
|
|||||||
|
|
||||||
impl<'a> DataConnectorContext<'a> {
|
impl<'a> DataConnectorContext<'a> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
metadata_accessor: &MetadataAccessor,
|
||||||
data_connector: &'a data_connector::DataConnectorLinkV1,
|
data_connector: &'a data_connector::DataConnectorLinkV1,
|
||||||
unstable_features: &UnstableFeatures,
|
unstable_features: &UnstableFeatures,
|
||||||
) -> Result<(Self, Vec<DataConnectorIssue>), DataConnectorError> {
|
) -> Result<(Self, Vec<DataConnectorIssue>), DataConnectorError> {
|
||||||
@ -102,7 +104,8 @@ impl<'a> DataConnectorContext<'a> {
|
|||||||
.argument_presets
|
.argument_presets
|
||||||
.iter()
|
.iter()
|
||||||
.map(|argument_preset| -> Result<_, DataConnectorError> {
|
.map(|argument_preset| -> Result<_, DataConnectorError> {
|
||||||
let header_presets = HttpHeadersPreset::new(&argument_preset.value.http_headers)?;
|
let header_presets =
|
||||||
|
HttpHeadersPreset::new(metadata_accessor, &argument_preset.value.http_headers)?;
|
||||||
Ok(ArgumentPreset {
|
Ok(ArgumentPreset {
|
||||||
name: argument_preset.argument.clone(),
|
name: argument_preset.argument.clone(),
|
||||||
value: ArgumentPresetValue {
|
value: ArgumentPresetValue {
|
||||||
@ -376,6 +379,7 @@ pub struct HttpHeadersPreset {
|
|||||||
|
|
||||||
impl HttpHeadersPreset {
|
impl HttpHeadersPreset {
|
||||||
fn new(
|
fn new(
|
||||||
|
metadata_accessor: &MetadataAccessor,
|
||||||
headers_preset: &open_dds::data_connector::HttpHeadersPreset,
|
headers_preset: &open_dds::data_connector::HttpHeadersPreset,
|
||||||
) -> Result<Self, DataConnectorError> {
|
) -> Result<Self, DataConnectorError> {
|
||||||
let forward = headers_preset
|
let forward = headers_preset
|
||||||
@ -389,7 +393,7 @@ impl HttpHeadersPreset {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|(header_name, header_val)| {
|
.map(|(header_name, header_val)| {
|
||||||
let key = SerializableHeaderName::new(header_name.to_string()).map_err(to_error)?;
|
let key = SerializableHeaderName::new(header_name.to_string()).map_err(to_error)?;
|
||||||
let val = resolve_value_expression(header_val.clone());
|
let val = resolve_value_expression(metadata_accessor, header_val.clone());
|
||||||
Ok((key, val))
|
Ok((key, val))
|
||||||
})
|
})
|
||||||
.collect::<Result<IndexMap<_, _>, DataConnectorError>>()?;
|
.collect::<Result<IndexMap<_, _>, DataConnectorError>>()?;
|
||||||
@ -402,11 +406,15 @@ impl HttpHeadersPreset {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_value_expression(
|
fn resolve_value_expression(
|
||||||
|
metadata_accessor: &MetadataAccessor,
|
||||||
value_expression_input: open_dds::permissions::ValueExpression,
|
value_expression_input: open_dds::permissions::ValueExpression,
|
||||||
) -> ValueExpression {
|
) -> ValueExpression {
|
||||||
match value_expression_input {
|
match value_expression_input {
|
||||||
open_dds::permissions::ValueExpression::SessionVariable(session_variable) => {
|
open_dds::permissions::ValueExpression::SessionVariable(session_variable) => {
|
||||||
ValueExpression::SessionVariable(session_variable)
|
ValueExpression::SessionVariable(hasura_authn_core::SessionVariableReference {
|
||||||
|
name: session_variable,
|
||||||
|
passed_as_json: metadata_accessor.flags.json_session_variables,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
open_dds::permissions::ValueExpression::Literal(json_value) => {
|
open_dds::permissions::ValueExpression::Literal(json_value) => {
|
||||||
ValueExpression::Literal(json_value)
|
ValueExpression::Literal(json_value)
|
||||||
@ -465,7 +473,7 @@ pub struct DataConnectorCapabilities {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use ndc_models;
|
use ndc_models;
|
||||||
use open_dds::data_connector::DataConnectorLinkV1;
|
use open_dds::{accessor::MetadataAccessor, data_connector::DataConnectorLinkV1, Metadata};
|
||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -525,9 +533,13 @@ mod tests {
|
|||||||
enable_ndc_v02_support: true,
|
enable_ndc_v02_support: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let (context, issues) =
|
let metadata_accessor = MetadataAccessor::new(Metadata::WithoutNamespaces(vec![]));
|
||||||
DataConnectorContext::new(&data_connector_with_capabilities, &unstable_features)
|
let (context, issues) = DataConnectorContext::new(
|
||||||
.unwrap();
|
&metadata_accessor,
|
||||||
|
&data_connector_with_capabilities,
|
||||||
|
&unstable_features,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
assert_eq!(context.capabilities, explicit_capabilities);
|
assert_eq!(context.capabilities, explicit_capabilities);
|
||||||
assert_eq!(context.supported_ndc_version, NdcVersion::V01);
|
assert_eq!(context.supported_ndc_version, NdcVersion::V01);
|
||||||
assert_eq!(issues.len(), 0, "Issues: {issues:#?}");
|
assert_eq!(issues.len(), 0, "Issues: {issues:#?}");
|
||||||
@ -566,9 +578,13 @@ mod tests {
|
|||||||
enable_ndc_v02_support: true,
|
enable_ndc_v02_support: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let (context, issues) =
|
let metadata_accessor = MetadataAccessor::new(Metadata::WithoutNamespaces(vec![]));
|
||||||
DataConnectorContext::new(&data_connector_with_capabilities, &unstable_features)
|
let (context, issues) = DataConnectorContext::new(
|
||||||
.unwrap();
|
&metadata_accessor,
|
||||||
|
&data_connector_with_capabilities,
|
||||||
|
&unstable_features,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
assert_eq!(context.capabilities, explicit_capabilities);
|
assert_eq!(context.capabilities, explicit_capabilities);
|
||||||
assert_eq!(context.supported_ndc_version, NdcVersion::V02);
|
assert_eq!(context.supported_ndc_version, NdcVersion::V02);
|
||||||
assert_eq!(issues.len(), 0, "Issues: {issues:#?}");
|
assert_eq!(issues.len(), 0, "Issues: {issues:#?}");
|
||||||
|
@ -81,6 +81,7 @@ pub fn resolve(
|
|||||||
.and_then(|bool_exp| bool_exp.graphql.as_ref());
|
.and_then(|bool_exp| bool_exp.graphql.as_ref());
|
||||||
|
|
||||||
let select_permissions = model_permission::resolve_model_select_permissions(
|
let select_permissions = model_permission::resolve_model_select_permissions(
|
||||||
|
&metadata_accessor.flags,
|
||||||
&model.model,
|
&model.model,
|
||||||
subgraph,
|
subgraph,
|
||||||
permissions,
|
permissions,
|
||||||
|
@ -33,6 +33,7 @@ use ref_cast::RefCast;
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
fn resolve_model_predicate_with_model(
|
fn resolve_model_predicate_with_model(
|
||||||
|
flags: &open_dds::flags::Flags,
|
||||||
model_predicate: &open_dds::permissions::ModelPredicate,
|
model_predicate: &open_dds::permissions::ModelPredicate,
|
||||||
model: &models::Model,
|
model: &models::Model,
|
||||||
subgraph: &SubgraphName,
|
subgraph: &SubgraphName,
|
||||||
@ -102,6 +103,7 @@ fn resolve_model_predicate_with_model(
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
resolve_model_predicate_with_type(
|
resolve_model_predicate_with_type(
|
||||||
|
flags,
|
||||||
model_predicate,
|
model_predicate,
|
||||||
&model.data_type,
|
&model.data_type,
|
||||||
object_type_representation,
|
object_type_representation,
|
||||||
@ -136,6 +138,7 @@ pub fn get_model_source_argument<'a>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve_model_select_permissions(
|
pub fn resolve_model_select_permissions(
|
||||||
|
flags: &open_dds::flags::Flags,
|
||||||
model: &models::Model,
|
model: &models::Model,
|
||||||
subgraph: &SubgraphName,
|
subgraph: &SubgraphName,
|
||||||
model_permissions: &ModelPermissionsV1,
|
model_permissions: &ModelPermissionsV1,
|
||||||
@ -163,6 +166,7 @@ pub fn resolve_model_select_permissions(
|
|||||||
let resolved_predicate = match &select.filter {
|
let resolved_predicate = match &select.filter {
|
||||||
NullableModelPredicate::NotNull(model_predicate) => {
|
NullableModelPredicate::NotNull(model_predicate) => {
|
||||||
resolve_model_predicate_with_model(
|
resolve_model_predicate_with_model(
|
||||||
|
flags,
|
||||||
model_predicate,
|
model_predicate,
|
||||||
model,
|
model,
|
||||||
subgraph,
|
subgraph,
|
||||||
@ -210,6 +214,7 @@ pub fn resolve_model_select_permissions(
|
|||||||
match model.arguments.get(&argument_preset.argument) {
|
match model.arguments.get(&argument_preset.argument) {
|
||||||
Some(argument) => {
|
Some(argument) => {
|
||||||
let value_expression = resolve_value_expression_for_argument(
|
let value_expression = resolve_value_expression_for_argument(
|
||||||
|
flags,
|
||||||
&argument_preset.argument,
|
&argument_preset.argument,
|
||||||
&argument_preset.value,
|
&argument_preset.value,
|
||||||
&argument.argument_type,
|
&argument.argument_type,
|
||||||
@ -269,6 +274,7 @@ pub fn resolve_model_select_permissions(
|
|||||||
/// re-add in future. Because this function takes the `data_connector_field_mappings` as an input,
|
/// re-add in future. Because this function takes the `data_connector_field_mappings` as an input,
|
||||||
/// many of the errors thrown in `resolve_model_predicate` are pushed out.
|
/// many of the errors thrown in `resolve_model_predicate` are pushed out.
|
||||||
pub(crate) fn resolve_model_predicate_with_type(
|
pub(crate) fn resolve_model_predicate_with_type(
|
||||||
|
flags: &open_dds::flags::Flags,
|
||||||
model_predicate: &open_dds::permissions::ModelPredicate,
|
model_predicate: &open_dds::permissions::ModelPredicate,
|
||||||
type_name: &Qualified<CustomTypeName>,
|
type_name: &Qualified<CustomTypeName>,
|
||||||
object_type_representation: &object_relationships::ObjectTypeWithRelationships,
|
object_type_representation: &object_relationships::ObjectTypeWithRelationships,
|
||||||
@ -385,7 +391,10 @@ pub(crate) fn resolve_model_predicate_with_type(
|
|||||||
ValueExpression::Literal(json_value.clone())
|
ValueExpression::Literal(json_value.clone())
|
||||||
}
|
}
|
||||||
open_dds::permissions::ValueExpression::SessionVariable(session_variable) => {
|
open_dds::permissions::ValueExpression::SessionVariable(session_variable) => {
|
||||||
ValueExpression::SessionVariable(session_variable.clone())
|
ValueExpression::SessionVariable(hasura_authn_core::SessionVariableReference {
|
||||||
|
name: session_variable.clone(),
|
||||||
|
passed_as_json: flags.json_session_variables,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -641,6 +650,7 @@ pub(crate) fn resolve_model_predicate_with_type(
|
|||||||
}?;
|
}?;
|
||||||
|
|
||||||
let target_model_predicate = resolve_model_predicate_with_type(
|
let target_model_predicate = resolve_model_predicate_with_type(
|
||||||
|
flags,
|
||||||
nested_predicate,
|
nested_predicate,
|
||||||
&target_model.inner.data_type,
|
&target_model.inner.data_type,
|
||||||
target_object_type,
|
target_object_type,
|
||||||
@ -682,6 +692,7 @@ pub(crate) fn resolve_model_predicate_with_type(
|
|||||||
|
|
||||||
open_dds::permissions::ModelPredicate::Not(predicate) => {
|
open_dds::permissions::ModelPredicate::Not(predicate) => {
|
||||||
let resolved_predicate = resolve_model_predicate_with_type(
|
let resolved_predicate = resolve_model_predicate_with_type(
|
||||||
|
flags,
|
||||||
predicate,
|
predicate,
|
||||||
type_name,
|
type_name,
|
||||||
object_type_representation,
|
object_type_representation,
|
||||||
@ -702,6 +713,7 @@ pub(crate) fn resolve_model_predicate_with_type(
|
|||||||
let mut resolved_predicates = Vec::new();
|
let mut resolved_predicates = Vec::new();
|
||||||
for predicate in predicates {
|
for predicate in predicates {
|
||||||
resolved_predicates.push(resolve_model_predicate_with_type(
|
resolved_predicates.push(resolve_model_predicate_with_type(
|
||||||
|
flags,
|
||||||
predicate,
|
predicate,
|
||||||
type_name,
|
type_name,
|
||||||
object_type_representation,
|
object_type_representation,
|
||||||
@ -723,6 +735,7 @@ pub(crate) fn resolve_model_predicate_with_type(
|
|||||||
let mut resolved_predicates = Vec::new();
|
let mut resolved_predicates = Vec::new();
|
||||||
for predicate in predicates {
|
for predicate in predicates {
|
||||||
resolved_predicates.push(resolve_model_predicate_with_type(
|
resolved_predicates.push(resolve_model_predicate_with_type(
|
||||||
|
flags,
|
||||||
predicate,
|
predicate,
|
||||||
type_name,
|
type_name,
|
||||||
object_type_representation,
|
object_type_representation,
|
||||||
|
@ -12,6 +12,7 @@ use crate::types::subgraph::Qualified;
|
|||||||
|
|
||||||
use crate::helpers::typecheck;
|
use crate::helpers::typecheck;
|
||||||
use crate::stages::object_types;
|
use crate::stages::object_types;
|
||||||
|
use crate::ValueExpressionOrPredicate;
|
||||||
|
|
||||||
/// resolve type permissions
|
/// resolve type permissions
|
||||||
pub fn resolve(
|
pub fn resolve(
|
||||||
@ -54,6 +55,7 @@ pub fn resolve(
|
|||||||
output_type_permission,
|
output_type_permission,
|
||||||
)?;
|
)?;
|
||||||
object_type.type_input_permissions = resolve_input_type_permission(
|
object_type.type_input_permissions = resolve_input_type_permission(
|
||||||
|
&metadata_accessor.flags,
|
||||||
&object_type.object_type,
|
&object_type.object_type,
|
||||||
output_type_permission,
|
output_type_permission,
|
||||||
)?;
|
)?;
|
||||||
@ -97,6 +99,7 @@ pub fn resolve_output_type_permission(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn resolve_input_type_permission(
|
pub(crate) fn resolve_input_type_permission(
|
||||||
|
flags: &open_dds::flags::Flags,
|
||||||
object_type_representation: &object_types::ObjectTypeRepresentation,
|
object_type_representation: &object_types::ObjectTypeRepresentation,
|
||||||
type_permissions: &TypePermissionsV1,
|
type_permissions: &TypePermissionsV1,
|
||||||
) -> Result<BTreeMap<Role, TypeInputPermission>, TypeInputPermissionError> {
|
) -> Result<BTreeMap<Role, TypeInputPermission>, TypeInputPermissionError> {
|
||||||
@ -133,10 +136,23 @@ pub(crate) fn resolve_input_type_permission(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
let resolved_value = match &value {
|
||||||
|
open_dds::permissions::ValueExpression::Literal(literal) => {
|
||||||
|
ValueExpressionOrPredicate::Literal(literal.clone())
|
||||||
|
}
|
||||||
|
open_dds::permissions::ValueExpression::SessionVariable(session_variable) => {
|
||||||
|
ValueExpressionOrPredicate::SessionVariable(
|
||||||
|
hasura_authn_core::SessionVariableReference {
|
||||||
|
name: session_variable.clone(),
|
||||||
|
passed_as_json: flags.json_session_variables,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
resolved_field_presets.insert(
|
resolved_field_presets.insert(
|
||||||
field_name.clone(),
|
field_name.clone(),
|
||||||
FieldPresetInfo {
|
FieldPresetInfo {
|
||||||
value: value.clone(),
|
value: resolved_value,
|
||||||
deprecated: field_definition.deprecated.clone(),
|
deprecated: field_definition.deprecated.clone(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use open_dds::{
|
use open_dds::{
|
||||||
permissions::{Role, TypeOutputPermission, ValueExpression},
|
permissions::{Role, TypeOutputPermission},
|
||||||
types::Deprecated,
|
types::Deprecated,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::stages::object_types;
|
|
||||||
use crate::Qualified;
|
use crate::Qualified;
|
||||||
|
use crate::{stages::object_types, ValueExpressionOrPredicate};
|
||||||
use open_dds::types::{CustomTypeName, FieldName};
|
use open_dds::types::{CustomTypeName, FieldName};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
@ -43,7 +43,7 @@ pub struct TypeInputPermission {
|
|||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct FieldPresetInfo {
|
pub struct FieldPresetInfo {
|
||||||
pub value: ValueExpression,
|
pub value: ValueExpressionOrPredicate,
|
||||||
pub deprecated: Option<Deprecated>,
|
pub deprecated: Option<Deprecated>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,12 +4,12 @@ use serde::{Deserialize, Serialize};
|
|||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum ValueExpression {
|
pub enum ValueExpression {
|
||||||
Literal(serde_json::Value),
|
Literal(serde_json::Value),
|
||||||
SessionVariable(open_dds::session_variables::SessionVariable),
|
SessionVariable(open_dds::session_variables::SessionVariableReference),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum ValueExpressionOrPredicate {
|
pub enum ValueExpressionOrPredicate {
|
||||||
Literal(serde_json::Value),
|
Literal(serde_json::Value),
|
||||||
SessionVariable(open_dds::session_variables::SessionVariable),
|
SessionVariable(open_dds::session_variables::SessionVariableReference),
|
||||||
BooleanExpression(Box<model_permissions::ModelPredicate>),
|
BooleanExpression(Box<model_permissions::ModelPredicate>),
|
||||||
}
|
}
|
||||||
|
@ -829,9 +829,12 @@ input_file: crates/metadata-resolve/tests/passing/boolean_expression_type/partia
|
|||||||
nullable: true,
|
nullable: true,
|
||||||
},
|
},
|
||||||
value: SessionVariable(
|
value: SessionVariable(
|
||||||
SessionVariable(
|
SessionVariableReference {
|
||||||
"x-hasura-user-id",
|
name: SessionVariableName(
|
||||||
),
|
"x-hasura-user-id",
|
||||||
|
),
|
||||||
|
passed_as_json: false,
|
||||||
|
},
|
||||||
),
|
),
|
||||||
deprecated: None,
|
deprecated: None,
|
||||||
},
|
},
|
||||||
|
@ -818,9 +818,12 @@ input_file: crates/metadata-resolve/tests/passing/missing_subgraph_when_ignoring
|
|||||||
nullable: false,
|
nullable: false,
|
||||||
},
|
},
|
||||||
value: SessionVariable(
|
value: SessionVariable(
|
||||||
SessionVariable(
|
SessionVariableReference {
|
||||||
"x-hasura-user-id",
|
name: SessionVariableName(
|
||||||
),
|
"x-hasura-user-id",
|
||||||
|
),
|
||||||
|
passed_as_json: false,
|
||||||
|
},
|
||||||
),
|
),
|
||||||
deprecated: None,
|
deprecated: None,
|
||||||
},
|
},
|
||||||
|
@ -3254,6 +3254,10 @@
|
|||||||
"allow_partial_supergraph": {
|
"allow_partial_supergraph": {
|
||||||
"default": false,
|
"default": false,
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"json_session_variables": {
|
||||||
|
"default": false,
|
||||||
|
"type": "boolean"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
@ -3294,7 +3298,8 @@
|
|||||||
"disallow_scalar_type_names_conflicting_with_inbuilt_types": false,
|
"disallow_scalar_type_names_conflicting_with_inbuilt_types": false,
|
||||||
"propagate_boolean_expression_deprecation_status": false,
|
"propagate_boolean_expression_deprecation_status": false,
|
||||||
"require_unique_command_graphql_names": false,
|
"require_unique_command_graphql_names": false,
|
||||||
"allow_partial_supergraph": false
|
"allow_partial_supergraph": false,
|
||||||
|
"json_session_variables": false
|
||||||
},
|
},
|
||||||
"allOf": [
|
"allOf": [
|
||||||
{
|
{
|
||||||
@ -3346,7 +3351,8 @@
|
|||||||
"disallow_scalar_type_names_conflicting_with_inbuilt_types": false,
|
"disallow_scalar_type_names_conflicting_with_inbuilt_types": false,
|
||||||
"propagate_boolean_expression_deprecation_status": false,
|
"propagate_boolean_expression_deprecation_status": false,
|
||||||
"require_unique_command_graphql_names": false,
|
"require_unique_command_graphql_names": false,
|
||||||
"allow_partial_supergraph": false
|
"allow_partial_supergraph": false,
|
||||||
|
"json_session_variables": false
|
||||||
},
|
},
|
||||||
"allOf": [
|
"allOf": [
|
||||||
{
|
{
|
||||||
@ -3388,7 +3394,8 @@
|
|||||||
"disallow_scalar_type_names_conflicting_with_inbuilt_types": false,
|
"disallow_scalar_type_names_conflicting_with_inbuilt_types": false,
|
||||||
"propagate_boolean_expression_deprecation_status": false,
|
"propagate_boolean_expression_deprecation_status": false,
|
||||||
"require_unique_command_graphql_names": false,
|
"require_unique_command_graphql_names": false,
|
||||||
"allow_partial_supergraph": false
|
"allow_partial_supergraph": false,
|
||||||
|
"json_session_variables": false
|
||||||
},
|
},
|
||||||
"allOf": [
|
"allOf": [
|
||||||
{
|
{
|
||||||
|
@ -32,6 +32,9 @@ pub struct Flags {
|
|||||||
|
|
||||||
#[opendd(default, rename = "allow_partial_supergraph")]
|
#[opendd(default, rename = "allow_partial_supergraph")]
|
||||||
pub allow_partial_supergraph: bool,
|
pub allow_partial_supergraph: bool,
|
||||||
|
|
||||||
|
#[opendd(default, rename = "json_session_variables")]
|
||||||
|
pub json_session_variables: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Flags {
|
impl Flags {
|
||||||
@ -46,6 +49,7 @@ impl Flags {
|
|||||||
propagate_boolean_expression_deprecation_status: false,
|
propagate_boolean_expression_deprecation_status: false,
|
||||||
require_unique_command_graphql_names: false,
|
require_unique_command_graphql_names: false,
|
||||||
allow_partial_supergraph: false,
|
allow_partial_supergraph: false,
|
||||||
|
json_session_variables: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ use crate::{
|
|||||||
commands::CommandName,
|
commands::CommandName,
|
||||||
models::ModelName,
|
models::ModelName,
|
||||||
relationships::RelationshipName,
|
relationships::RelationshipName,
|
||||||
session_variables::SessionVariable,
|
session_variables::SessionVariableName,
|
||||||
traits,
|
traits,
|
||||||
types::{CustomTypeName, FieldName, OperatorName},
|
types::{CustomTypeName, FieldName, OperatorName},
|
||||||
};
|
};
|
||||||
@ -649,7 +649,7 @@ pub enum ValueExpression {
|
|||||||
#[schemars(title = "Literal")]
|
#[schemars(title = "Literal")]
|
||||||
Literal(JsonValue),
|
Literal(JsonValue),
|
||||||
#[schemars(title = "SessionVariable")]
|
#[schemars(title = "SessionVariable")]
|
||||||
SessionVariable(SessionVariable),
|
SessionVariable(SessionVariableName),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
@ -663,7 +663,7 @@ pub enum ValueExpressionOrPredicate {
|
|||||||
#[schemars(title = "Literal")]
|
#[schemars(title = "Literal")]
|
||||||
Literal(JsonValue),
|
Literal(JsonValue),
|
||||||
#[schemars(title = "SessionVariable")]
|
#[schemars(title = "SessionVariable")]
|
||||||
SessionVariable(SessionVariable),
|
SessionVariable(SessionVariableName),
|
||||||
#[schemars(title = "BooleanExpression")]
|
#[schemars(title = "BooleanExpression")]
|
||||||
BooleanExpression(Box<ModelPredicate>),
|
BooleanExpression(Box<ModelPredicate>),
|
||||||
}
|
}
|
||||||
|
@ -7,23 +7,33 @@ use std::str::FromStr;
|
|||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
/// Used to represent a reference to a session variable,
|
||||||
|
/// where one is required in the IR
|
||||||
|
#[derive(Debug, Clone, Hash, PartialEq, Eq, JsonSchema, Serialize, Deserialize)]
|
||||||
|
#[schemars(rename = "OpenDdSessionVariableReference")]
|
||||||
|
pub struct SessionVariableReference {
|
||||||
|
pub name: SessionVariableName,
|
||||||
|
pub passed_as_json: bool,
|
||||||
|
}
|
||||||
|
|
||||||
/// Used to represent the name of a session variable, like
|
/// Used to represent the name of a session variable, like
|
||||||
/// "x-hasura-role".
|
/// "x-hasura-role".
|
||||||
#[derive(Debug, Clone, Hash, PartialEq, Eq, JsonSchema, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Hash, PartialEq, Eq, JsonSchema, Serialize, Deserialize)]
|
||||||
#[schemars(rename = "OpenDdSessionVariable")]
|
#[schemars(rename = "OpenDdSessionVariable")]
|
||||||
pub struct SessionVariable(Cow<'static, str>);
|
pub struct SessionVariableName(Cow<'static, str>);
|
||||||
|
|
||||||
pub const SESSION_VARIABLE_ROLE: SessionVariable = SessionVariable(Cow::Borrowed("x-hasura-role"));
|
pub const SESSION_VARIABLE_ROLE: SessionVariableName =
|
||||||
|
SessionVariableName(Cow::Borrowed("x-hasura-role"));
|
||||||
|
|
||||||
impl FromStr for SessionVariable {
|
impl FromStr for SessionVariableName {
|
||||||
type Err = Infallible;
|
type Err = Infallible;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
Ok(SessionVariable(s.trim().to_lowercase().into()))
|
Ok(SessionVariableName(s.trim().to_lowercase().into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for SessionVariable {
|
impl std::fmt::Display for SessionVariableName {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "{}", self.0)
|
write!(f, "{}", self.0)
|
||||||
}
|
}
|
||||||
@ -54,12 +64,12 @@ mod tests {
|
|||||||
fn serialize_and_deserialize_session_variable() {
|
fn serialize_and_deserialize_session_variable() {
|
||||||
let mut session_variables = HashMap::new();
|
let mut session_variables = HashMap::new();
|
||||||
session_variables.insert(
|
session_variables.insert(
|
||||||
SessionVariable("test-role".into()),
|
SessionVariableName("test-role".into()),
|
||||||
SessionVariableValue("test-role".into()),
|
SessionVariableValue("test-role".into()),
|
||||||
);
|
);
|
||||||
let json_str = serde_json::to_string(&session_variables).unwrap();
|
let json_str = serde_json::to_string(&session_variables).unwrap();
|
||||||
|
|
||||||
let parsed_from_string: HashMap<SessionVariable, SessionVariableValue> =
|
let parsed_from_string: HashMap<SessionVariableName, SessionVariableValue> =
|
||||||
serde_json::from_str(json_str.trim()).unwrap();
|
serde_json::from_str(json_str.trim()).unwrap();
|
||||||
|
|
||||||
assert_eq!(parsed_from_string, session_variables);
|
assert_eq!(parsed_from_string, session_variables);
|
||||||
|
Loading…
Reference in New Issue
Block a user