Skip to content

Commit b5e7c1f

Browse files
committed
Add permissions check in josh-filter
1 parent 3c9ce5e commit b5e7c1f

File tree

9 files changed

+336
-90
lines changed

9 files changed

+336
-90
lines changed

josh-proxy/src/bin/josh-proxy.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ async fn do_filter(
295295

296296
let mut headref = headref;
297297

298-
josh::filter_refs(&transaction, filter, &from_to)?;
298+
josh::filter_refs(&transaction, filter, &from_to, josh::filter::empty())?;
299299
if headref == "HEAD" {
300300
headref = heads_map
301301
.read()?

src/bin/josh-filter.rs

Lines changed: 37 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,18 @@ fn run_filter(args: Vec<String>) -> josh::JoshResult<i32> {
101101
.arg(
102102
clap::Arg::with_name("check-permission")
103103
.long("check-permission")
104-
.short("c")
104+
.short("c"),
105+
)
106+
.arg(
107+
clap::Arg::with_name("whitelist")
108+
.long("whitelist")
109+
.short("w")
110+
.takes_value(true),
111+
)
112+
.arg(
113+
clap::Arg::with_name("blacklist")
114+
.long("blacklist")
115+
.short("b")
105116
.takes_value(true),
106117
)
107118
.arg(clap::Arg::with_name("version").long("version").short("v"))
@@ -183,6 +194,7 @@ fn run_filter(args: Vec<String>) -> josh::JoshResult<i32> {
183194
josh::filter::parse(&i)?,
184195
input_ref,
185196
"refs/JOSH_TMP",
197+
josh::filter::empty(),
186198
)?;
187199
}
188200
}
@@ -193,12 +205,6 @@ fn run_filter(args: Vec<String>) -> josh::JoshResult<i32> {
193205
let target = update_target;
194206

195207
let reverse = args.is_present("reverse");
196-
let check_permissions = args.is_present("check-permission");
197-
198-
if check_permissions {
199-
filterobj = josh::filter::chain(josh::filter::parse(":PATHS")?, filterobj);
200-
filterobj = josh::filter::chain(filterobj, josh::filter::parse(":FOLD")?);
201-
}
202208

203209
let t = if reverse {
204210
"refs/JOSH_TMP".to_owned()
@@ -213,21 +219,32 @@ fn run_filter(args: Vec<String>) -> josh::JoshResult<i32> {
213219
.unwrap()
214220
.to_string();
215221

216-
josh::filter_ref(&transaction, filterobj, &src, &t)?;
217-
218-
let mut all_paths = vec![];
219-
222+
let check_permissions = args.is_present("check-permission");
223+
let mut permissions_filter = josh::filter::empty();
220224
if check_permissions {
221-
let result_tree = repo.find_reference(&t)?.peel_to_tree()?;
225+
let whitelist = match args.value_of("whitelist") {
226+
Some(s) => josh::filter::parse(s)?,
227+
_ => josh::filter::nop(),
228+
};
229+
let blacklist = match args.value_of("blacklist") {
230+
Some(s) => josh::filter::parse(s)?,
231+
_ => josh::filter::empty(),
232+
};
233+
permissions_filter = josh::filter::make_permissions_filter(filterobj, whitelist, blacklist)
234+
}
222235

223-
result_tree.walk(git2::TreeWalkMode::PreOrder, |_, entry| {
224-
let name = entry.name().unwrap();
225-
if name.starts_with("JOSH_ORIG_PATH_") {
226-
let pathname = josh::from_ns(&name.replacen("JOSH_ORIG_PATH_", "", 1));
227-
all_paths.push(pathname);
228-
}
229-
git2::TreeWalkResult::Ok
230-
})?;
236+
let updated_refs = josh::filter_ref(
237+
&transaction,
238+
filterobj,
239+
&src,
240+
&t,
241+
permissions_filter,
242+
)?;
243+
if args.value_of("update") != Some("FILTERED_HEAD") && updated_refs == 0 {
244+
println!(
245+
"Warning: reference {} wasn't updated",
246+
args.value_of("update").unwrap()
247+
);
231248
}
232249

233250
#[cfg(feature = "search")]
@@ -264,39 +281,6 @@ fn run_filter(args: Vec<String>) -> josh::JoshResult<i32> {
264281
/* println!("\n Search took {:?}", duration); */
265282
}
266283

267-
let mut dedup = vec![];
268-
269-
for w in all_paths.as_slice().windows(2) {
270-
if let [a, b, ..] = w {
271-
if !b.starts_with(a) {
272-
dedup.push(a.to_owned());
273-
}
274-
}
275-
}
276-
277-
let dedup = all_paths;
278-
279-
let options = glob::MatchOptions {
280-
case_sensitive: true,
281-
require_literal_separator: true,
282-
require_literal_leading_dot: true,
283-
};
284-
285-
if let Some(cp) = args.value_of("check-permission") {
286-
let pattern = glob::Pattern::new(cp)?;
287-
288-
let mut allowed = !dedup.is_empty();
289-
for d in dedup.iter() {
290-
let d = std::path::PathBuf::from(d);
291-
let m = pattern.matches_path_with(&d, options);
292-
if !m {
293-
allowed = false;
294-
println!("missing permission for: {:?}", &d);
295-
}
296-
}
297-
println!("Allowed = {:?}", allowed);
298-
}
299-
300284
if reverse {
301285
let new = repo.revparse_single(target).unwrap().id();
302286
let old = repo.revparse_single("JOSH_TMP").unwrap().id();

src/filter/mod.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ pub fn nop() -> Filter {
3333
to_filter(Op::Nop)
3434
}
3535

36+
pub fn empty() -> Filter {
37+
to_filter(Op::Empty)
38+
}
39+
3640
fn to_filter(op: Op) -> Filter {
3741
let s = format!("{:?}", op);
3842
let f = Filter(
@@ -767,6 +771,20 @@ fn compute_warnings2<'a>(
767771
warnings
768772
}
769773

774+
pub fn make_permissions_filter(filter: Filter, whitelist: Filter, blacklist: Filter) -> Filter {
775+
rs_tracing::trace_scoped!("make_permissions_filter");
776+
777+
let filter = chain(to_filter(Op::Paths), filter);
778+
let filter = chain(filter, to_filter(Op::Invert));
779+
let filter = chain(
780+
filter,
781+
compose(blacklist, to_filter(Op::Subtract(nop(), whitelist))),
782+
);
783+
let filter = opt::optimize(filter);
784+
785+
return filter;
786+
}
787+
770788
#[cfg(test)]
771789
mod tests {
772790
use super::*;

src/housekeeping.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ pub fn refresh_known_filters(
227227
upstream_repo,
228228
);
229229

230-
updated_count += filter_refs(&t, filter::parse(filter_spec)?, &refs)?;
230+
updated_count += filter_refs(&t, filter::parse(filter_spec)?, &refs, filter::empty())?;
231231
}
232232
info!("updated {} refs for {:?}", updated_count, upstream_repo);
233233
}

src/lib.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,12 +166,34 @@ pub fn filter_ref(
166166
filterobj: filter::Filter,
167167
from_refsname: &str,
168168
to_refname: &str,
169+
permissions: filter::Filter,
169170
) -> JoshResult<usize> {
170171
let mut updated_count = 0;
171172
if let Ok(reference) = transaction.repo().revparse_single(from_refsname) {
172173
let original_commit = reference.peel_to_commit()?;
173174
let oid = original_commit.id();
174175

176+
let perms_commit = if let Some(s) = transaction.get_ref(permissions, oid) {
177+
s
178+
} else {
179+
tracing::trace!("apply_to_commit (permissions)");
180+
181+
filter::apply_to_commit(permissions, &original_commit, &transaction)?
182+
};
183+
184+
if perms_commit != git2::Oid::zero() {
185+
let perms_commit = transaction.repo().find_commit(perms_commit)?;
186+
if !perms_commit.tree()?.is_empty() || perms_commit.parents().len() > 0 {
187+
tracing::event!(
188+
tracing::Level::WARN,
189+
msg = "filter_refs: missing permissions for ref",
190+
warn = true,
191+
reference = from_refsname,
192+
);
193+
return Err(josh_error("missing permissions for ref"));
194+
}
195+
}
196+
175197
let filter_commit = if let Some(s) = transaction.get_ref(filterobj, oid) {
176198
s
177199
} else {
@@ -226,6 +248,7 @@ pub fn filter_refs(
226248
transaction: &cache::Transaction,
227249
filterobj: filter::Filter,
228250
refs: &[(String, String)],
251+
permissions: filter::Filter,
229252
) -> JoshResult<usize> {
230253
rs_tracing::trace_scoped!("filter_refs", "spec": filter::spec(filterobj));
231254
let s = tracing::Span::current();
@@ -235,7 +258,7 @@ pub fn filter_refs(
235258

236259
let mut updated_count = 0;
237260
for (k, v) in refs {
238-
updated_count += ok_or!(filter_ref(transaction, filterobj, k, v), {
261+
updated_count += ok_or!(filter_ref(&transaction, filterobj, &k, &v, permissions), {
239262
tracing::event!(
240263
tracing::Level::WARN,
241264
msg = "filter_refs: Can't filter reference",

tests/filter/empty_head.t

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
$ git commit -m "add file5" 1> /dev/null
3636

3737
$ josh-filter -s :/sub2 master --update refs/josh/filter/master
38+
Warning: reference refs/josh/filter/master wasn't updated
3839
[2] :/sub1
3940
[2] :/sub2
4041
$ git log --graph --pretty=%s josh/filter/master

tests/filter/infofile.t

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
* add file1
3131

3232
$ josh-filter -s c=:/sub1 master --update refs/josh/filter/master
33+
Warning: reference refs/josh/filter/master wasn't updated
3334
[2] :/sub1
3435
[2] :prefix=c
3536
$ git log --graph --pretty=%s josh/filter/master
@@ -48,6 +49,7 @@
4849
$ git commit -m "add file5" 1> /dev/null
4950

5051
$ josh-filter -s c=:/sub2 master --update refs/josh/filter/master
52+
Warning: reference refs/josh/filter/master wasn't updated
5153
[2] :/sub1
5254
[2] :/sub2
5355
[3] :prefix=c

0 commit comments

Comments
 (0)