Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,485 changes: 610 additions & 875 deletions Cargo.lock

Large diffs are not rendered by default.

17 changes: 9 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
[package]
name = "api_actix-web_rust_hello-world"
version = "0.1.0"
version = "0.2.0"
authors = ["Sebastian Estrella <sestrella@stackbuilders.com>"]
edition = "2021"
rust-version = "1.56"
rust-version = "1.71"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
actix-cors = "^0.5.4"
actix-web = { version = "^3.3.2" , features = ["rustls"] }
actix-web-httpauth = "^0.5.1"
actix-cors = "0.6.4"
actix-web = { version = "^4.3.1" , features = ["rustls"] }
actix-web-httpauth = "0.8.0"
derive_more = "^0.99.16"
dotenv = "^0.15.0"
env_logger = "^0.9.0"
env_logger = "0.10.0"
envy = "^0.4.2"
jsonwebtoken = "^8.0.0-beta.6"
serde = "^1.0.130"
jsonwebtoken = "^8.3.0-beta.6"
serde = { version = "1.0.130", features = ["derive"] }
awc = { version = "3.0.0-beta.11", features = [ "openssl" ] }
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ The recommended way to install Rust is via [Rustup](https://rust-lang.github.io/
Install the [toolchain](https://rust-lang.github.io/rustup/concepts/toolchains.html):

```bash
rustup toolchain install 1.56
rustup toolchain install 1.71
```

Install the project dependencies:
Expand Down
4 changes: 2 additions & 2 deletions src/api/messages/handlers.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::types::Message;
use crate::{extractors::Claims, types::ErrorMessage};
use actix_web::{get, web, Responder, HttpResponse};
use actix_web::{get, web, Responder, error};
use std::collections::HashSet;

#[get("/admin")]
Expand All @@ -12,7 +12,7 @@ pub async fn admin(claims: Claims) -> impl Responder {
text: "The secured API requires a valid access token and the read:admin-messages permission to share this admin message.".to_string(),
}))
} else {
Err(HttpResponse::Forbidden().json(ErrorMessage {
Err(error::ErrorForbidden(ErrorMessage {
error: Some("insufficient_permissions".to_string()),
error_description: Some("Requires read:admin-messages".to_string()),
message: "Permission denied".to_string(),
Expand Down
11 changes: 5 additions & 6 deletions src/extractors/claims.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::types::ErrorMessage;
use awc::Client;
use actix_web::{
client::Client,
error::ResponseError,
http::{StatusCode, Uri},
Error, FromRequest, HttpResponse,
Expand Down Expand Up @@ -94,7 +94,6 @@ impl Claims {
impl FromRequest for Claims {
type Error = Error;
type Future = Pin<Box<dyn Future<Output = Result<Self, Self::Error>>>>;
type Config = ();

fn from_request(
req: &actix_web::HttpRequest,
Expand All @@ -120,17 +119,17 @@ impl FromRequest for Claims {
.unwrap(),
)
.send()
.await?
.await.unwrap()
.json()
.await?;
.await.unwrap();
let jwk = jwks
.find(&kid)
.ok_or_else(|| ClientError::NotFound("No JWK found for kid".to_string()))?;
match jwk.clone().algorithm {
AlgorithmParameters::RSA(ref rsa) => {
let mut validation = Validation::new(Algorithm::RS256);
validation.set_audience(&[config.audience]);
validation.set_iss(&[Uri::builder()
validation.set_issuer(&[Uri::builder()
.scheme("https")
.authority(domain)
.path_and_query("/")
Expand All @@ -146,4 +145,4 @@ impl FromRequest for Claims {
}
})
}
}
}
10 changes: 5 additions & 5 deletions src/middlewares/err_handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use crate::types::ErrorMessage;
use actix_web::{
dev::ServiceResponse,
http::StatusCode,
middleware::errhandlers::{ErrorHandlerResponse, ErrorHandlers},
web::HttpResponse,
middleware::{ErrorHandlerResponse, ErrorHandlers},
HttpResponse,
Result,
};

Expand All @@ -20,7 +20,7 @@ fn internal_error<B>(res: ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>>
message: "Internal server error".to_string(),
});
Ok(ErrorHandlerResponse::Response(
res.into_response(http_res.into_body()),
res.into_response(http_res.map_into_right_body()),
))
}

Expand All @@ -31,6 +31,6 @@ fn not_found<B>(res: ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> {
message: "Not Found".to_string(),
});
Ok(ErrorHandlerResponse::Response(
res.into_response(http_res.into_body()),
res.into_response(http_res.map_into_right_body()),
))
}
}
28 changes: 14 additions & 14 deletions src/middlewares/security_headers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,21 @@ use actix_web::{http::header, middleware::DefaultHeaders};

pub fn security_headers() -> DefaultHeaders {
DefaultHeaders::new()
.header(header::X_XSS_PROTECTION, "0")
.header(
.add((header::X_XSS_PROTECTION, "0"))
.add((
header::STRICT_TRANSPORT_SECURITY,
"max-age=31536000 ; includeSubDomains",
"max-age=31536000; includeSubDomains",
))
.add((header::X_FRAME_OPTIONS, "deny"))
.add((header::X_CONTENT_TYPE_OPTIONS, "nosniff"))
.add(
(header::CONTENT_SECURITY_POLICY,
"default-src 'self'; frame-ancestors 'none';",)
)
.header(header::X_FRAME_OPTIONS, "deny")
.header(header::X_CONTENT_TYPE_OPTIONS, "nosniff")
.header(
header::CONTENT_SECURITY_POLICY,
"default-src 'self'; frame-ancestors 'none';",
)
.header(
.add((
header::CACHE_CONTROL,
"no-cache, no-store, max-age=0, must-revalidate",
)
.header(header::PRAGMA, "no-cache")
.header(header::EXPIRES, "0")
}
))
.add((header::PRAGMA, "no-cache"))
.add((header::EXPIRES, "0"))
}
10 changes: 8 additions & 2 deletions src/types.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::fmt::{Formatter, Result};
use serde::{Deserialize, Serialize};

#[derive(Deserialize)]
pub struct Config {
pub port: u16,
Expand All @@ -12,11 +12,17 @@ impl Default for Config {
}
}

#[derive(Serialize)]
#[derive(Serialize, Debug)]
pub struct ErrorMessage {
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error_description: Option<String>,
pub message: String,
}

impl std::fmt::Display for ErrorMessage {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
write!(f, "{}", self.message)
}
}