Skip to content

Commit

Permalink
Update dependencies and Rust Docker image. Refactor JWT validation
Browse files Browse the repository at this point in the history
  • Loading branch information
rkudryashov committed Jan 1, 2023
1 parent a03c55a commit e095f22
Show file tree
Hide file tree
Showing 12 changed files with 851 additions and 609 deletions.
1,287 changes: 739 additions & 548 deletions Cargo.lock

Large diffs are not rendered by default.

16 changes: 8 additions & 8 deletions apollo-router/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ edition = "2021"

[dependencies]
common-utils = { path = "../common-utils" }
apollo-router = "1.2.1"
anyhow = "1.0.66"
async-trait = "0.1.58"
apollo-router = "1.7.0"
anyhow = "1.0.68"
async-trait = "0.1.60"
futures = "0.3.25"
schemars = "0.8.11"
jsonwebtoken = "8.1.1"
serde = "1.0.147"
serde_json = "1.0.87"
tokio = { version = "1.21.2", features = ["full"] }
jsonwebtoken = "8.2.0"
serde = "1.0.152"
serde_json = "1.0.91"
tokio = { version = "1.23.0", features = ["full"] }
tower = { version = "0.4.13", features = ["full"] }
tracing = "0.1.34"
tracing = "0.1.37"
http = "0.2.8"
dotenv = "0.15.0"
4 changes: 2 additions & 2 deletions apollo-router/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
FROM rust:1.64 as builder
FROM rust:1.66 as builder

ENV CARGO_TERM_COLOR always
RUN rustup component add rustfmt
RUN apt-get update && apt-get install -y npm
RUN apt-get update && apt-get install -y npm protobuf-compiler

WORKDIR /usr/src/docker-build
# create empty project for caching dependencies
Expand Down
97 changes: 74 additions & 23 deletions apollo-router/src/jwt_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ use apollo_router::{
plugin::{Plugin, PluginInit},
register_plugin,
services::supergraph,
Context,
};
use http::header::AUTHORIZATION;
use http::StatusCode;
use jsonwebtoken::{decode, DecodingKey, TokenData, Validation};
use schemars::JsonSchema;
Expand All @@ -21,7 +23,6 @@ use tracing::debug;
use common_utils::Claims;

const ROLE_CONTEXT_PARAM_NAME: &str = "user_role";
const AUTHORIZATION_HEADER_NAME: &str = "authorization";
const ROLE_HEADER_NAME: &str = "role";

#[derive(Deserialize, JsonSchema)]
Expand All @@ -47,35 +48,90 @@ impl Plugin for JwtValidation {
fn supergraph_service(&self, service: supergraph::BoxService) -> supergraph::BoxService {
let jwt_secret_key = self.secret_key.clone();

let handler = move |request: supergraph::Request| {
let headers = request.supergraph_request.headers();
let maybe_auth_header_value = headers.get(AUTHORIZATION_HEADER_NAME);
fn failure_message(
context: Context,
message: String,
status: StatusCode,
) -> Result<ControlFlow<supergraph::Response, supergraph::Request>, BoxError> {
let response = supergraph::Response::error_builder()
.error(
graphql::Error::builder()
.message(message)
.extension_code("AUTH_ERROR")
.build(),
)
.status_code(status)
.context(context)
.build()?;
Ok(ControlFlow::Break(response))
}

let jwt = match maybe_auth_header_value {
Some(auth_header_value) => {
let auth_header_value_str = auth_header_value.to_str()?;
get_jwt_from_header_value(auth_header_value_str).to_string()
let handler = move |request: supergraph::Request| {
let auth_header_value_result =
match request.supergraph_request.headers().get(AUTHORIZATION) {
Some(auth_header_value) => auth_header_value.to_str(),
// in this project, I decided to allow the passage of requests without the AUTHORIZATION header. then each subgraph performs authorization based on the ROLE header
// your case may be different. be careful
None => return Ok(ControlFlow::Continue(request)),
};

let auth_header_value_untrimmed = match auth_header_value_result {
Ok(auth_header_value) => auth_header_value,
Err(_not_a_string_error) => {
return failure_message(
request.context,
"AUTHORIZATION' header is not convertible to a string".to_string(),
StatusCode::BAD_REQUEST,
)
}
None => return Ok(ControlFlow::Continue(request)),
};

let auth_header_value = auth_header_value_untrimmed.trim();

if !auth_header_value
.to_uppercase()
.as_str()
.starts_with("BEARER ")
{
return failure_message(
request.context,
format!("'{auth_header_value_untrimmed}' is not correctly formatted"),
StatusCode::BAD_REQUEST,
);
}

let auth_header_value_parts: Vec<&str> = auth_header_value.splitn(2, ' ').collect();
if auth_header_value_parts.len() != 2 {
return failure_message(
request.context,
format!("'{auth_header_value}' is not correctly formatted"),
StatusCode::BAD_REQUEST,
);
}

let jwt = auth_header_value_parts[1].trim_end();

match decode_jwt(&jwt, &jwt_secret_key) {
Ok(token_data) => {
let role = token_data.claims.role;
debug!("User role is: {}", &role);
request.context.insert(ROLE_CONTEXT_PARAM_NAME, role)?;
if let Err(error) = request.context.insert(ROLE_CONTEXT_PARAM_NAME, role) {
return failure_message(
request.context,
format!("Failed to pass a user's role: {}", error),
StatusCode::INTERNAL_SERVER_ERROR,
);
}

Ok(ControlFlow::Continue(request))
}
Err(e) => {
let error_message = format!("JWT is invalid: {}", e);
let response = supergraph::Response::error_builder()
.error(graphql::Error::builder().message(error_message).build())
.status_code(StatusCode::BAD_REQUEST)
.context(request.context)
.build()?;

Ok(ControlFlow::Break(response))
return failure_message(
request.context,
error_message,
StatusCode::BAD_REQUEST,
);
}
}
};
Expand Down Expand Up @@ -106,11 +162,6 @@ impl Plugin for JwtValidation {
}
}

fn get_jwt_from_header_value(auth_header_value: &str) -> &str {
let jwt_start_index = "Bearer ".len();
&auth_header_value[jwt_start_index..auth_header_value.len()]
}

// this also includes JWT validation
fn decode_jwt(jwt: &str, secret_key: &str) -> jsonwebtoken::errors::Result<TokenData<Claims>> {
decode::<Claims>(
Expand Down Expand Up @@ -138,7 +189,7 @@ mod tests {
apollo_router::TestHarness::builder()
.configuration_json(config)
.unwrap()
.build()
.build_supergraph()
.await
.unwrap();
}
Expand Down
14 changes: 7 additions & 7 deletions auth-service/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,23 @@ edition = "2021"

[dependencies]
common-utils = { path = "../common-utils" }
async-graphql = "4.0.16"
async-graphql-actix-web = "4.0.16"
async-graphql = "5.0.4"
async-graphql-actix-web = "5.0.4"
actix-web = "4.2.1"
actix-rt = "2.7.0"
serde = { version = "1.0.147", features = ["derive"] }
serde = { version = "1.0.152", features = ["derive"] }
diesel = { version = "2.0.2", features = ["postgres", "r2d2"] }
diesel_migrations = "2.0.0"
dotenv = "0.15.0"
jsonwebtoken = "8.1.1"
jsonwebtoken = "8.2.0"
argonautica = "0.2.0"
chrono = "0.4.22"
chrono = "0.4.23"
lazy_static = "1.4.0"
strum = "0.24.1"
strum_macros = "0.24.3"

[dev-dependencies]
serde_json = "1.0.87"
serde_json = "1.0.91"
jsonpath_lib = "0.3.0"
base64 = "0.13.1"
base64 = "0.20.0"
testcontainers = "0.14.0"
2 changes: 1 addition & 1 deletion auth-service/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM rust:1.64 as builder
FROM rust:1.66 as builder

ENV CARGO_TERM_COLOR always
RUN apt-get update && apt-get install -y libpq-dev clang
Expand Down
2 changes: 1 addition & 1 deletion common-utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ edition = "2021"

[dependencies]
actix-web = "4.2.1"
serde = { version = "1.0.147", features = ["derive"] }
serde = { version = "1.0.152", features = ["derive"] }
strum = "0.24.1"
strum_macros = "0.24.3"
12 changes: 6 additions & 6 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ services:
ports:
- 8001:8001
planets-db:
image: postgres:13.2-alpine
image: postgres:15.1-alpine
container_name: planets-db
restart: always
environment:
POSTGRES_DB: planets
POSTGRES_PASSWORD: $PLANETS_DB_PASSWORD
healthcheck:
test: [ "CMD-SHELL", "pg_isready -U postgres" ]
test: "pg_isready -U postgres -d planets"
interval: 1m
timeout: 5s
start_period: 10s
Expand All @@ -52,14 +52,14 @@ services:
ports:
- 8002:8002
satellites-db:
image: postgres:13.2-alpine
image: postgres:15.1-alpine
container_name: satellites-db
restart: always
environment:
POSTGRES_DB: satellites
POSTGRES_PASSWORD: $SATELLITES_DB_PASSWORD
healthcheck:
test: [ "CMD-SHELL", "pg_isready -U postgres" ]
test: "pg_isready -U postgres -d satellites"
interval: 1m
timeout: 5s
start_period: 10s
Expand All @@ -85,14 +85,14 @@ services:
ports:
- 8003:8003
users-db:
image: postgres:13.2-alpine
image: postgres:15.1-alpine
container_name: users-db
restart: always
environment:
POSTGRES_DB: users
POSTGRES_PASSWORD: $USERS_DB_PASSWORD
healthcheck:
test: [ "CMD-SHELL", "pg_isready -U postgres" ]
test: "pg_isready -U postgres -d users"
interval: 1m
timeout: 5s
start_period: 10s
Expand Down
12 changes: 6 additions & 6 deletions planets-service/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,22 @@ edition = "2021"

[dependencies]
common-utils = { path = "../common-utils" }
async-graphql = { version = "4.0.16", features = ["dataloader"] }
async-graphql-actix-web = "4.0.16"
async-graphql = { version = "5.0.4", features = ["dataloader"] }
async-graphql-actix-web = "5.0.4"
actix-web = "4.2.1"
actix-rt = "2.7.0"
actix-web-actors = "4.1.0"
futures = "0.3.25"
async-trait = "0.1.58"
async-trait = "0.1.60"
bigdecimal = { version = "0.3.0", features = ["serde"] }
serde = { version = "1.0.147", features = ["derive"] }
serde_json = "1.0.87"
serde = { version = "1.0.152", features = ["derive"] }
serde_json = "1.0.91"
diesel = { version = "2.0.2", features = ["postgres", "r2d2", "numeric"] }
diesel_migrations = "2.0.0"
dotenv = "0.15.0"
strum = "0.24.1"
strum_macros = "0.24.3"
rdkafka = { version = "0.28.0", features = ["cmake-build"] }
rdkafka = { version = "0.29.0", features = ["cmake-build"] }
async-stream = "0.3.3"
lazy_static = "1.4.0"

Expand Down
2 changes: 1 addition & 1 deletion planets-service/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM rust:1.64 as builder
FROM rust:1.66 as builder

ENV CARGO_TERM_COLOR always
RUN apt-get update && apt-get install -y libpq-dev cmake
Expand Down
10 changes: 5 additions & 5 deletions satellites-service/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,19 @@ authors = ["Roman Kudryashov <rskudryashov@gmail.com>"]
edition = "2021"

[dependencies]
async-graphql = { version = "4.0.16", features = ["chrono"] }
async-graphql-actix-web = "4.0.16"
async-graphql = { version = "5.0.4", features = ["chrono"] }
async-graphql-actix-web = "5.0.4"
actix-web = "4.2.1"
actix-rt = "2.7.0"
serde = { version = "1.0.147", features = ["derive"] }
serde = { version = "1.0.152", features = ["derive"] }
diesel = { version = "2.0.2", features = ["postgres", "r2d2", "chrono"] }
diesel_migrations = "2.0.0"
chrono = { version = "0.4.22", features = ["serde"] }
chrono = { version = "0.4.23", features = ["serde"] }
dotenv = "0.15.0"
strum = "0.24.1"
strum_macros = "0.24.3"

[dev-dependencies]
serde_json = "1.0.87"
serde_json = "1.0.91"
jsonpath_lib = "0.3.0"
testcontainers = "0.14.0"
2 changes: 1 addition & 1 deletion satellites-service/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM rust:1.64 as builder
FROM rust:1.66 as builder

ENV CARGO_TERM_COLOR always
RUN apt-get update && apt-get install -y libpq-dev
Expand Down

0 comments on commit e095f22

Please sign in to comment.