Skip to content

Commit 8ca79e4

Browse files
committed
feat: acl'ed list bucket
1 parent 2319035 commit 8ca79e4

File tree

6 files changed

+163
-134
lines changed

6 files changed

+163
-134
lines changed

src/auth.rs

+39-13
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
//! S3 Authentication
22
33
use std::collections::HashMap;
4+
use std::fmt::Debug;
45
use std::path::{Path, PathBuf};
6+
use std::sync::Arc;
57

68
use crate::errors::S3AuthError;
79
use crate::ops::{ReqContext, S3Operation};
810
use crate::path::S3Path;
9-
use crate::token::database::IndexDB;
11+
use crate::token::database::{IndexDB, PassiveIndexDB};
1012
use crate::utils::metrics::Mesurable;
1113
use crate::S3Storage;
1214
use crate::{dto::S3AuthContext, ops::S3Handler};
@@ -80,30 +82,30 @@ mod authorization {
8082
})
8183
}
8284

85+
pub fn match_listops(&self, bucket: &str) -> bool {
86+
self.operations.contains(&"BucketList".to_string())
87+
&& self.bucket_matcher.is_match(bucket)
88+
}
89+
8390
pub fn matches(&self, operation: &S3Operation, bucket: &str, path: Option<&str>) -> bool {
8491
if !self.operations.contains(&operation.to_string()) {
8592
debug!("not match ops");
8693
return false;
8794
}
8895

89-
if !self.bucket_matcher.is_match(bucket) {
96+
if bucket != "*" && !self.bucket_matcher.is_match(bucket) {
9097
debug!("not match bucket");
9198
return false;
9299
}
100+
93101
if let Some(ref path_matcher) = self.path_matcher {
94102
if let Some(path) = path {
95103
debug!("attempt to path matching");
96104
let matches = path_matcher.matches(&PathBuf::from(path));
97-
matches ^ self.path_matcher_inverted
98-
} else {
99-
// TODO
100-
// if there is no path but requested in a path required endpoint, it should not be possible
101-
// there is no such case yet this is exploitable
102-
true
105+
return matches ^ self.path_matcher_inverted;
103106
}
104-
} else {
105-
true
106-
}
107+
};
108+
true
107109
}
108110
}
109111

@@ -164,14 +166,21 @@ use tracing::debug;
164166
/// S3 Authentication Provider
165167
166168
#[async_trait]
167-
pub trait S3Auth: Mesurable {
169+
pub trait S3Auth: Mesurable + Debug {
168170
/// lookup `secret_access_key` by `access_key_id`
169171
async fn get_secret_access_key(
170172
&self,
171173
context: &mut S3AuthContext<'_>,
172174
access_key_id: &str,
173175
) -> Result<String, S3AuthError>;
174176

177+
async fn get_listops_matchers(
178+
&self,
179+
_context: &S3AuthContext<'_>,
180+
) -> Result<Arc<Vec<Permission>>, S3AuthError> {
181+
Err(S3AuthError::AuthServiceUnavailable)
182+
}
183+
175184
async fn authorize_query(
176185
&self,
177186
_ctx: &'_ ReqContext<'_>,
@@ -252,6 +261,21 @@ impl S3Auth for RwLock<ACLAuth> {
252261
Err(S3AuthError::NotSignedUp)
253262
}
254263

264+
async fn get_listops_matchers(
265+
&self,
266+
context: &S3AuthContext<'_>,
267+
) -> Result<Arc<Vec<Permission>>, S3AuthError> {
268+
if let Some(access_id) = context.access_id {
269+
self.read()
270+
.await
271+
.indexdb
272+
.get_roles_as_permission(&access_id)
273+
.ok_or(S3AuthError::MissingToken)
274+
} else {
275+
Err(S3AuthError::MissingToken)
276+
}
277+
}
278+
255279
async fn authorize_public_query(&self, ctx: &'_ ReqContext<'_>) -> Result<(), S3AuthError> {
256280
let readed = self.read().await;
257281
if let S3Path::Object { bucket, key } = ctx.path {
@@ -318,7 +342,9 @@ impl S3Auth for RwLock<ACLAuth> {
318342
}
319343
let is_valid_origin = {
320344
let read_guard = self.read().await;
321-
(token.origin == bucket) ^ read_guard.indexdb.validate_orign(bucket, &token)
345+
(bucket == "*")
346+
|| (token.origin == bucket)
347+
^ read_guard.indexdb.validate_orign(bucket, &token)
322348
};
323349
if is_valid_origin {
324350
return Ok(());

src/ops.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use crate::errors::S3Result;
2525
use crate::path::S3Path;
2626
use crate::storage::S3Storage;
2727
use crate::streams::multipart::Multipart;
28-
use crate::{async_trait, Body, BoxStdError, Mime, Request, Response};
28+
use crate::{async_trait, Body, BoxStdError, Mime, Request, Response, S3Auth};
2929

3030
use std::fmt::Debug;
3131
use std::mem;
@@ -124,6 +124,8 @@ pub struct ReqContext<'a> {
124124
pub multipart: Option<Multipart>,
125125
/// auth
126126
pub auth: &'a mut S3AuthContext<'a>,
127+
/// auth engine
128+
pub auth_engine: Option<&'a Box<dyn S3Auth + Send + Sync>>,
127129
}
128130

129131
impl<'a> ReqContext<'a> {

src/ops/list_buckets.rs

+29-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
//! [`ListBuckets`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListBuckets.html)
22
3+
use std::ops::Deref;
4+
35
use super::{wrap_internal_error, ReqContext, S3Handler};
46

57
use crate::dto::{ListBucketsError, ListBucketsOutput, ListBucketsRequest};
6-
use crate::errors::{S3Error, S3Result};
8+
use crate::errors::{S3Error, S3Result, S3StorageError};
79
use crate::output::S3Output;
810
use crate::storage::S3Storage;
911
use crate::utils::{ResponseExt, XmlWriterExt};
@@ -27,8 +29,32 @@ impl S3Handler for Handler {
2729
storage: &(dyn S3Storage + Send + Sync),
2830
) -> S3Result<Response> {
2931
let input = extract(ctx)?;
30-
let output = storage.list_buckets(input).await;
31-
output.try_into_response()
32+
if let Some(auth_engine) = ctx.auth_engine {
33+
if let Ok(matchers) = auth_engine
34+
.as_ref()
35+
.get_listops_matchers(ctx.auth.deref())
36+
.await
37+
{
38+
let output = storage
39+
.list_buckets(input)
40+
.await
41+
.map(|f| ListBucketsOutput {
42+
buckets: f.buckets.map(|b| {
43+
b.iter()
44+
.filter(|bn| {
45+
matchers.iter().any(|m| {
46+
m.match_listops(bn.name.as_ref().unwrap_or(&String::new()))
47+
})
48+
})
49+
.map(|f| f.clone())
50+
.collect::<Vec<_>>()
51+
}),
52+
..f
53+
});
54+
return output.try_into_response();
55+
}
56+
}
57+
ListBucketsOutput::default().try_into_response()
3258
}
3359
}
3460

src/service.rs

+3
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,8 @@ impl S3Service {
190190
let query_strings = extract_qs(&req)?;
191191
let mime = extract_mime(&headers)?;
192192

193+
let refed = self.auth.as_ref();
194+
193195
let mut ctx: ReqContext<'_> = ReqContext {
194196
req: &req,
195197
headers,
@@ -199,6 +201,7 @@ impl S3Service {
199201
mime,
200202
multipart: None,
201203
auth: &mut context,
204+
auth_engine: self.auth.as_ref(),
202205
};
203206

204207
debug!("authenticated");

src/storages/fs.rs

+3
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,19 @@ use crate::errors::{S3StorageError, S3StorageResult};
2121
use crate::headers::{AmzCopySource, Range};
2222
use crate::path::S3Path;
2323
use crate::storage::S3Storage;
24+
use crate::token::database::PassiveIndexDB;
2425
use crate::utils::{crypto, time, Apply};
2526

2627
use std::collections::{HashMap, HashSet};
2728
use std::convert::TryInto;
2829
use std::env;
30+
use std::fmt::Debug;
2931
use std::io::{self, SeekFrom};
3032
use std::path::{Path, PathBuf};
3133

3234
use futures::io::{AsyncReadExt, AsyncSeekExt, AsyncWrite, AsyncWriteExt, BufWriter};
3335
use futures::stream::{Stream, StreamExt, TryStreamExt};
36+
use hex_simd::AsOut;
3437
use hyper::body::Bytes;
3538
use md5::{Digest, Md5};
3639
use path_absolutize::Absolutize;

0 commit comments

Comments
 (0)