blob: 504780c9d91d231370149e0c261d0c7bf13f6757 [file]
// Copyright 2022 The casbin Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
constants,
model::{db::Pool, response::ResponseBody},
utils::token_utils,
};
use axum::{
body::{self, BoxBody},
extract::Extension,
http::{
header::{HeaderName, HeaderValue},
Method,
},
response::{IntoResponse, Response},
BoxError, Json,
};
use axum_casbin_auth::CasbinVals;
use bytes::Bytes;
use futures::future::BoxFuture;
use http::{self, Request};
use http_body::Body as HttpBody;
use std::{
boxed::Box,
convert::Infallible,
task::{Context, Poll},
};
use tower::{Layer, Service};
#[derive(Clone)]
pub struct AuthLayer;
impl<S> Layer<S> for AuthLayer {
type Service = AuthMiddleware<S>;
fn layer(&self, inner: S) -> Self::Service {
AuthMiddleware { inner }
}
}
#[derive(Clone)]
pub struct AuthMiddleware<S> {
inner: S,
}
impl<S, ReqBody, ResBody> Service<Request<ReqBody>> for AuthMiddleware<S>
where
S: Service<Request<ReqBody>, Response = Response<ResBody>, Error = Infallible>
+ Clone
+ Send
+ 'static,
S::Future: Send + 'static,
ReqBody: Send + 'static,
Infallible: From<<S as Service<Request<ReqBody>>>::Error>,
ResBody: HttpBody<Data = Bytes> + Send + 'static,
ResBody::Error: Into<BoxError>,
{
type Response = Response<BoxBody>;
type Error = Infallible;
// `BoxFuture` is a type alias for `Pin<Box<dyn Future + Send + 'a>>`
type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.inner.poll_ready(cx)
}
fn call(&mut self, mut req: Request<ReqBody>) -> Self::Future {
let not_ready_inner = self.inner.clone();
let mut inner = std::mem::replace(&mut self.inner, not_ready_inner);
let mut authenticate_pass: bool = false;
let mut authenticate_username: String = String::from("");
// Bypass account routes
let headers = req.headers_mut();
headers.append(
HeaderName::from_static("content-length"),
HeaderValue::from_static("true"),
);
if Method::OPTIONS == *req.method() {
authenticate_pass = true;
} else {
for ignore_route in constants::IGNORE_ROUTES.iter() {
if req.uri().path().starts_with(ignore_route) {
authenticate_pass = true;
break;
}
}
if !authenticate_pass {
if let Some(pool) = req.extensions().get::<Extension<Pool>>() {
info!("Connecting to database...");
if let Some(auth_header) = req.headers().get(constants::AUTHORIZATION) {
info!("Parsing authorization header...");
if let Ok(auth_str) = auth_header.to_str() {
if auth_str.starts_with("bearer") || auth_str.starts_with("Bearer") {
info!("Parsing token...");
let token = auth_str[6..auth_str.len()].trim();
if let Ok(token_data) = token_utils::decode_token(token.to_string())
{
info!("Decoding token...");
if token_utils::validate_token(&token_data, pool).is_ok() {
info!("Valid token");
authenticate_username = token_data.claims.user_name;
authenticate_pass = true;
} else {
error!("Invalid token");
}
}
}
}
}
}
}
}
if authenticate_pass {
let vals = CasbinVals {
subject: authenticate_username,
domain: None,
};
req.extensions_mut().insert(vals);
Box::pin(async move { Ok(inner.call(req).await?.map(body::boxed)) })
} else {
Box::pin(async move {
Ok(Json(ResponseBody::new(
constants::MESSAGE_INVALID_TOKEN,
constants::EMPTY,
))
.into_response())
})
}
}
}