Skip to content

Commit 4b52328

Browse files
committed
adding stub authorization
1 parent 0873660 commit 4b52328

8 files changed

Lines changed: 87 additions & 39 deletions

File tree

src/authorization/mod.rs

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,28 @@
11
use mockall::*;
2+
use serde::{Deserialize, Serialize};
23
use thiserror::Error;
34

4-
pub mod oso;
5-
65
#[derive(Error, Debug)]
76
pub enum AuthorizationError {
87
#[error("authorization error")]
98
Error(anyhow::Error),
109
}
1110

11+
pub struct User {
12+
pub user_id: String,
13+
pub role: String,
14+
}
15+
1216
#[automock]
13-
pub trait AuthorizationClient<T> {
14-
fn allow_user_action_field(
15-
&self,
16-
actor: T,
17-
action: String,
18-
resource: T,
19-
field: String,
20-
) -> Result<bool, AuthorizationError>;
17+
pub trait IAuthorization: Send + Sync {
18+
fn is_action_allowed(&self, actor: User, action: String) -> Result<bool, AuthorizationError>;
19+
}
20+
21+
#[derive(Clone, Serialize, Deserialize)]
22+
pub struct Authorization;
23+
24+
impl IAuthorization for Authorization {
25+
fn is_action_allowed(&self, _actor: User, _action: String) -> Result<bool, AuthorizationError> {
26+
Ok(true)
27+
}
2128
}

src/errors.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ pub enum ServerError {
2424
UnauthenticatedReason(anyhow::Error),
2525
#[error("unauthenticated")]
2626
Unauthenticated,
27+
#[error("unauthorized")]
28+
Unauthorized,
2729
}
2830

2931
impl ServerError {
@@ -36,6 +38,7 @@ impl ServerError {
3638
Self::RequiredBodyParameter => "required_body_param".to_owned(),
3739
Self::UnauthenticatedReason(_) => "unauthenticated".to_owned(),
3840
Self::Unauthenticated => "unauthenticated".to_owned(),
41+
Self::Unauthorized => "unauthorized".to_owned(),
3942
}
4043
}
4144
pub fn message(&self) -> String {
@@ -47,6 +50,7 @@ impl ServerError {
4750
Self::RequiredBodyParameter => "required body parameter".to_owned(),
4851
Self::UnauthenticatedReason(_) => "unauthenticated".to_owned(),
4952
Self::Unauthenticated => "unauthenticated".to_owned(),
53+
Self::Unauthorized => "unauthorized".to_owned(),
5054
}
5155
}
5256
}
@@ -71,6 +75,7 @@ impl IntoResponse for ServerError {
7175
Self::BadReqest => StatusCode::BAD_REQUEST,
7276
Self::RequiredBodyParameter => StatusCode::BAD_REQUEST,
7377
Self::Unauthenticated => StatusCode::UNAUTHORIZED,
78+
Self::Unauthorized => StatusCode::UNAUTHORIZED,
7479
Self::UnauthenticatedReason(_) => StatusCode::UNAUTHORIZED,
7580
};
7681

src/handlers.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
mod authentication;
2+
mod authorization;
23
mod health;
34
mod routes;
45
mod users;

src/handlers/authorization.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
use anyhow::anyhow;
2+
use axum::{body::Body, extract::Request, extract::State, http::Response, middleware::Next};
3+
4+
use crate::{authorization::User, errors};
5+
6+
use super::AppState;
7+
8+
pub async fn is_user_action_allowed(
9+
State(AppState { authorization, .. }): State<AppState>,
10+
req: Request,
11+
next: Next,
12+
) -> Result<Response<Body>, errors::ServerError> {
13+
let is_authorized = authorization
14+
.is_action_allowed(
15+
User {
16+
user_id: "random".to_owned(),
17+
role: "random".to_owned(),
18+
},
19+
"list_users".to_owned(),
20+
)
21+
.map_err(|err| errors::ServerError::Internal(anyhow!(err)))?;
22+
23+
if is_authorized {
24+
return Ok(next.run(req).await);
25+
}
26+
Err(errors::ServerError::Unauthorized)
27+
}

src/handlers/health_test.rs

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,25 @@
1-
use crate::authentication;
2-
use crate::handlers::router;
3-
use crate::handlers::{health::HealthResponse, AppState};
4-
use axum::{
5-
body::Body,
6-
http::{Request, StatusCode},
7-
};
8-
use http_body_util::BodyExt;
9-
use sea_orm::{DatabaseBackend, MockDatabase};
10-
use tower::ServiceExt;
11-
121
#[cfg(test)]
132
#[tokio::test]
143
async fn test_health() {
4+
use crate::authentication;
5+
use crate::authorization;
6+
use crate::handlers::router;
7+
use crate::handlers::{health::HealthResponse, AppState};
8+
use axum::{
9+
body::Body,
10+
http::{Request, StatusCode},
11+
};
12+
use http_body_util::BodyExt;
13+
use sea_orm::{DatabaseBackend, MockDatabase};
1514
use std::sync::Arc;
15+
use tower::ServiceExt;
1616

1717
let conn = MockDatabase::new(DatabaseBackend::Postgres).into_connection();
1818

1919
let my_router = router(AppState {
2020
conn: Arc::new(conn),
2121
authentication: Arc::new(authentication::MockIAuthentication::new()),
22+
authorization: Arc::new(authorization::MockIAuthorization::new()),
2223
});
2324

2425
let response = my_router

src/handlers/routes.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ use axum::{middleware, Router};
88
use sea_orm::DatabaseConnection;
99

1010
use crate::authentication;
11+
use crate::authorization;
1112
use tower::ServiceBuilder;
1213

1314
use super::authentication as authentication_middleware;
15+
use super::authorization as authorization_middleware;
1416

1517
use super::health;
1618

@@ -20,12 +22,19 @@ use super::users;
2022
pub struct AppState {
2123
pub conn: Arc<DatabaseConnection>,
2224
pub authentication: Arc<dyn authentication::IAuthentication>,
25+
pub authorization: Arc<dyn authorization::IAuthorization>,
2326
}
2427

2528
pub fn router(app_state: AppState) -> Router {
2629
Router::new().route("/", get(health::get_health)).merge(
2730
Router::new()
28-
.route("/users", get(users::list_users))
31+
.route(
32+
"/users",
33+
get(users::list_users).layer(middleware::from_fn_with_state(
34+
app_state.clone(),
35+
authorization_middleware::is_user_action_allowed,
36+
)),
37+
)
2938
.route("/users/{user_id}", get(users::get_user))
3039
.route("/users", post(users::create_user))
3140
.route("/users/{user_id}", put(users::modify_user))

src/handlers/users_test.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ mod tests {
1313
use uuid::Uuid;
1414

1515
use crate::{
16+
authorization,
1617
handlers::{router, users::UserResponse, AppState},
1718
models, test_utils,
1819
};
@@ -35,13 +36,15 @@ mod tests {
3536
.into_connection();
3637

3738
let auth = test_utils::get_default_auth();
39+
let authz = authorization::Authorization {};
3840

3941
let (default_auth_header, default_auth_header_value) =
4042
test_utils::get_default_auth_header();
4143

4244
let router = router(AppState {
4345
conn: Arc::new(conn),
4446
authentication: Arc::from(auth),
47+
authorization: Arc::from(authz),
4548
});
4649

4750
let response = router
@@ -94,13 +97,15 @@ mod tests {
9497
.into_connection();
9598

9699
let auth = test_utils::get_default_auth();
100+
let authz = authorization::Authorization {};
97101

98102
let (default_auth_header, default_auth_header_value) =
99103
test_utils::get_default_auth_header();
100104

101105
let router = router(AppState {
102106
conn: Arc::new(conn),
103107
authentication: Arc::from(auth),
108+
authorization: Arc::from(authz),
104109
});
105110

106111
let response = router
@@ -158,13 +163,15 @@ mod tests {
158163
.into_connection();
159164

160165
let auth = test_utils::get_default_auth();
166+
let authz = authorization::Authorization {};
161167

162168
let (default_auth_header, default_auth_header_value) =
163169
test_utils::get_default_auth_header();
164170

165171
let router = router(AppState {
166172
conn: Arc::new(conn),
167173
authentication: Arc::from(auth),
174+
authorization: Arc::from(authz),
168175
});
169176

170177
let body = serde_json::json!({
@@ -231,13 +238,15 @@ mod tests {
231238
.into_connection();
232239

233240
let auth = test_utils::get_default_auth();
241+
let authz = authorization::Authorization {};
234242

235243
let (default_auth_header, default_auth_header_value) =
236244
test_utils::get_default_auth_header();
237245

238246
let router = router(AppState {
239247
conn: Arc::new(conn),
240248
authentication: Arc::from(auth),
249+
authorization: Arc::from(authz),
241250
});
242251

243252
let body = serde_json::json!({
@@ -286,13 +295,15 @@ mod tests {
286295
.into_connection();
287296

288297
let auth = test_utils::get_default_auth();
298+
let authz = authorization::Authorization {};
289299

290300
let (default_auth_header, default_auth_header_value) =
291301
test_utils::get_default_auth_header();
292302

293303
let router = router(AppState {
294304
conn: Arc::new(conn),
295305
authentication: Arc::from(auth),
306+
authorization: Arc::from(authz),
296307
});
297308

298309
let response = router

src/main.rs

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,8 @@ use tower_http::trace::TraceLayer;
77

88
mod auth0;
99
mod authentication;
10-
// mod authorization;
10+
mod authorization;
1111
mod errors;
12-
// mod extractors;
1312
mod handlers;
1413
mod models;
1514

@@ -49,13 +48,16 @@ async fn main() -> anyhow::Result<(), anyhow::Error> {
4948
domain: auth0_domain,
5049
};
5150

51+
let authz = authorization::Authorization {};
52+
5253
let port = std::env::var("PORT")
5354
.unwrap_or_else(|_| "7000".to_owned())
5455
.parse::<u16>()
5556
.expect("PORT must be a number");
5657

5758
let app_state = AppState {
5859
authentication: Arc::new(auth),
60+
authorization: Arc::new(authz),
5961
conn: Arc::new(conn),
6062
};
6163

@@ -73,19 +75,4 @@ async fn main() -> anyhow::Result<(), anyhow::Error> {
7375
)
7476
.await
7577
.map_err(|err| anyhow!(err))
76-
77-
// HttpServer::new(move || {
78-
// let cors = Cors::permissive();
79-
80-
// App::new()
81-
// .app_data(state.clone())
82-
// .wrap(middleware::Logger::default())
83-
// .wrap(cors)
84-
// .service(handlers::routes())
85-
// .service(handlers::health::get_health)
86-
// })
87-
// .bind(("0.0.0.0", port))?
88-
// .run()
89-
// .await
90-
// .map_err(|err| anyhow!(err))
9178
}

0 commit comments

Comments
 (0)