Skip to content

Commit ee67b1a

Browse files
committed
Lazy load auth for users
1 parent c55be20 commit ee67b1a

File tree

7 files changed

+183
-189
lines changed

7 files changed

+183
-189
lines changed

server/src/handlers/http.rs

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,16 @@ use std::fs::File;
2020
use std::io::BufReader;
2121

2222
use actix_cors::Cors;
23-
use actix_web::dev::ServiceRequest;
24-
use actix_web::{web, App, HttpMessage, HttpServer, Route};
25-
use actix_web_httpauth::extractors::basic::BasicAuth;
26-
use actix_web_httpauth::middleware::HttpAuthentication;
23+
use actix_web::{web, App, HttpServer, Route};
2724
use actix_web_prometheus::PrometheusMetrics;
2825
use actix_web_static_files::ResourceFiles;
2926
use rustls::{Certificate, PrivateKey, ServerConfig};
3027
use rustls_pemfile::{certs, pkcs8_private_keys};
3128

3229
use crate::option::CONFIG;
3330
use crate::rbac::role::Action;
34-
use crate::rbac::Users;
3531

36-
use self::middleware::{Authorization, DisAllowRootUser};
32+
use self::middleware::{Auth, DisAllowRootUser};
3733

3834
mod health_check;
3935
mod ingest;
@@ -65,21 +61,6 @@ macro_rules! create_app {
6561
};
6662
}
6763

68-
async fn authenticate(
69-
req: ServiceRequest,
70-
credentials: BasicAuth,
71-
) -> Result<ServiceRequest, (actix_web::Error, ServiceRequest)> {
72-
let username = credentials.user_id().trim().to_owned();
73-
let password = credentials.password().unwrap().trim();
74-
75-
if Users.authenticate(&username, password) {
76-
req.extensions_mut().insert(username);
77-
Ok(req)
78-
} else {
79-
Err((actix_web::error::ErrorUnauthorized("Unauthorized"), req))
80-
}
81-
}
82-
8364
pub async fn run_http(prometheus: PrometheusMetrics) -> anyhow::Result<()> {
8465
let ssl_acceptor = match (
8566
&CONFIG.parseable.tls_cert_path,
@@ -266,8 +247,7 @@ pub fn configure_routes(cfg: &mut web::ServiceConfig) {
266247
logstream_api,
267248
),
268249
)
269-
.service(user_api)
270-
.wrap(HttpAuthentication::basic(authenticate)),
250+
.service(user_api),
271251
)
272252
// GET "/" ==> Serve the static frontend directory
273253
.service(ResourceFiles::new("/", generated));
@@ -288,14 +268,14 @@ trait RouteExt {
288268

289269
impl RouteExt for Route {
290270
fn authorize(self, action: Action) -> Self {
291-
self.wrap(Authorization {
271+
self.wrap(Auth {
292272
action,
293273
stream: false,
294274
})
295275
}
296276

297277
fn authorize_for_stream(self, action: Action) -> Self {
298-
self.wrap(Authorization {
278+
self.wrap(Auth {
299279
action,
300280
stream: true,
301281
})

server/src/handlers/http/middleware.rs

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,22 @@ use std::future::{ready, Ready};
2222
use actix_web::{
2323
dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform},
2424
error::{ErrorBadRequest, ErrorUnauthorized},
25-
Error, HttpMessage,
25+
Error,
2626
};
27+
use actix_web_httpauth::extractors::basic::BasicAuth;
2728
use futures_util::future::LocalBoxFuture;
2829

2930
use crate::{
3031
option::CONFIG,
3132
rbac::{role::Action, Users},
3233
};
3334

34-
pub struct Authorization {
35+
pub struct Auth {
3536
pub action: Action,
3637
pub stream: bool,
3738
}
3839

39-
impl<S, B> Transform<S, ServiceRequest> for Authorization
40+
impl<S, B> Transform<S, ServiceRequest> for Auth
4041
where
4142
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
4243
S::Future: 'static,
@@ -45,25 +46,25 @@ where
4546
type Response = ServiceResponse<B>;
4647
type Error = Error;
4748
type InitError = ();
48-
type Transform = AuthorizationMiddleware<S>;
49+
type Transform = AuthMiddleware<S>;
4950
type Future = Ready<Result<Self::Transform, Self::InitError>>;
5051

5152
fn new_transform(&self, service: S) -> Self::Future {
52-
ready(Ok(AuthorizationMiddleware {
53+
ready(Ok(AuthMiddleware {
5354
action: self.action,
5455
match_stream: self.stream,
5556
service,
5657
}))
5758
}
5859
}
5960

60-
pub struct AuthorizationMiddleware<S> {
61+
pub struct AuthMiddleware<S> {
6162
action: Action,
6263
match_stream: bool,
6364
service: S,
6465
}
6566

66-
impl<S, B> Service<ServiceRequest> for AuthorizationMiddleware<S>
67+
impl<S, B> Service<ServiceRequest> for AuthMiddleware<S>
6768
where
6869
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
6970
S::Future: 'static,
@@ -75,25 +76,33 @@ where
7576

7677
forward_ready!(service);
7778

78-
fn call(&self, req: ServiceRequest) -> Self::Future {
79+
fn call(&self, mut req: ServiceRequest) -> Self::Future {
80+
let creds = req.extract::<BasicAuth>().into_inner();
81+
let creds: Result<(String, String), Error> = creds.map_err(Into::into).and_then(|creds| {
82+
let username = creds.user_id().trim().to_owned();
83+
if let Some(password) = creds.password() {
84+
Ok((username, password.trim().to_owned()))
85+
} else {
86+
Err(ErrorBadRequest("Password is required in basic auth"))
87+
}
88+
});
7989
let stream = if self.match_stream {
8090
req.match_info().get("logstream")
8191
} else {
8292
None
8393
};
84-
let extensions = req.extensions();
85-
let username = extensions
86-
.get::<String>()
87-
.expect("authentication layer verified username");
88-
let is_auth = Users.check_permission(username, self.action, stream);
89-
drop(extensions);
94+
let is_auth = creds.map(|creds| {
95+
let (username, password) = creds;
96+
Users.authenticate(username, password, self.action, stream)
97+
});
9098

9199
let fut = self.service.call(req);
92100

93101
Box::pin(async move {
94-
if !is_auth {
102+
if !is_auth? {
95103
return Err(ErrorUnauthorized("Not authorized"));
96104
}
105+
97106
fut.await
98107
})
99108
}

server/src/handlers/http/rbac.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,6 @@ pub async fn put_role(
116116
let role = role.into_inner();
117117
let role: Vec<DefaultPrivilege> = serde_json::from_value(role)?;
118118

119-
let permissions;
120119
if !Users.contains(&username) {
121120
return Err(RBACError::UserDoesNotExist);
122121
};
@@ -127,16 +126,15 @@ pub async fn put_role(
127126
.iter_mut()
128127
.find(|user| user.username == username)
129128
{
130-
user.role = role;
131-
permissions = user.permissions()
129+
user.role.clone_from(&role);
132130
} else {
133131
// should be unreachable given state is always consistent
134132
return Err(RBACError::UserDoesNotExist);
135133
}
136134

137135
put_metadata(&metadata).await?;
138136
// update in mem table
139-
Users.put_permissions(&username, &permissions);
137+
Users.put_role(&username, role);
140138
Ok(format!("Roles updated successfully for {}", username))
141139
}
142140

0 commit comments

Comments
 (0)