Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add token field for exact app matches #77

Merged
merged 2 commits into from
Feb 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ pub struct TokenSubsetArgs {
scope: Vec<ClaimsScope>,
duration: i64,
prefixes: Option<Vec<String>>,
apps: Option<Vec<String>>,
repos: Option<Vec<String>>,
name: String,
}
Expand Down Expand Up @@ -135,6 +136,13 @@ pub fn prefix_is_subset(
}
}

pub fn apps_is_subset(maybe_subset_apps: Option<&[String]>, claimed_apps: &[String]) -> bool {
match maybe_subset_apps {
Some(subset_apps) => subset_apps.iter().all(|s| claimed_apps.contains(s)),
None => true,
}
}

pub fn token_subset(
args: Json<TokenSubsetArgs>,
config: Data<Config>,
Expand All @@ -148,19 +156,27 @@ pub fn token_subset(
&& tokens::sub_has_prefix(&args.sub, &claims.sub)
&& args.scope.iter().all(|s| claims.scope.contains(s))
&& prefix_is_subset(&args.prefixes, &claims.prefixes)
&& apps_is_subset(args.apps.as_deref(), &claims.apps)
&& repos_is_subset(&args.repos, &claims.repos)
{
let new_claims = Claims {
sub: args.sub.clone(),
scope: args.scope.clone(),
name: Some(claims.name.unwrap_or_else(|| "".to_string()) + "/" + &args.name),
name: Some(claims.name.unwrap_or_default() + "/" + &args.name),
prefixes: {
if let Some(ref prefixes) = args.prefixes {
prefixes.clone()
} else {
claims.prefixes.clone()
}
},
apps: {
if let Some(ref apps) = args.apps {
apps.clone()
} else {
claims.apps.clone()
}
},
jameswestman marked this conversation as resolved.
Show resolved Hide resolved
repos: {
if let Some(ref repos) = args.repos {
repos.clone()
Expand Down Expand Up @@ -620,7 +636,7 @@ fn filename_parse_delta(name: &str) -> Option<path::PathBuf> {
path::Path::new("deltas")
.join(&v[0][..2])
.join(&v[0][2..])
.join(&v[1]),
.join(v[1]),
)
}

Expand Down Expand Up @@ -664,7 +680,7 @@ fn start_save(
let absolute_path = state.repo_path.join(subpath);

if let Some(parent) = absolute_path.parent() {
fs::create_dir_all(&parent)?;
fs::create_dir_all(parent)?;
}

let tmp_dir = state.repo_path.join("tmp");
Expand Down
21 changes: 11 additions & 10 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ where
{
use serde::de::Error;
String::deserialize(deserializer)
.and_then(|string| base64::decode(&string).map_err(|err| Error::custom(err.to_string())))
.and_then(|string| base64::decode(string).map_err(|err| Error::custom(err.to_string())))
}

fn from_opt_base64<'de, D>(deserializer: D) -> Result<Option<Vec<u8>>, D::Error>
Expand All @@ -74,7 +74,7 @@ where
{
use serde::de::Error;
String::deserialize(deserializer)
.and_then(|string| base64::decode(&string).map_err(|err| Error::custom(err.to_string())))
.and_then(|string| base64::decode(string).map_err(|err| Error::custom(err.to_string())))
.map(Some)
}

Expand All @@ -94,15 +94,15 @@ fn match_glob(glob: &str, s: &str) -> bool {
let glob_after_star = glob_chars.as_str();

/* Consume at least one, fail if none */
if s_chars.next() == None {
if s_chars.next().is_none() {
return false;
}

loop {
if match_glob(glob_after_star, s_chars.as_str()) {
return true;
}
if s_chars.next() == None {
if s_chars.next().is_none() {
break;
}
}
Expand Down Expand Up @@ -175,12 +175,13 @@ pub struct Claims {
pub sub: String, // "build", "build/N", or user id for repo tokens
pub exp: i64,

// Below are optional, and not used for repo tokens
#[serde(default)]
pub scope: Vec<ClaimsScope>,
#[serde(default)]
pub prefixes: Vec<String>, // [''] => all, ['org.foo'] => org.foo + org.foo.bar (but not org.foobar)
#[serde(default)]
pub apps: Vec<String>, // like prefixes, but only exact matches
#[serde(default)]
pub repos: Vec<String>, // list of repo names or a '' for match all
pub name: Option<String>, // for debug/logs only
}
Expand Down Expand Up @@ -421,7 +422,7 @@ async fn handle_build_repo_async(
let relpath = canonicalize_path(params.tail.trim_start_matches('/'))?;
let realid = canonicalize_path(&params.id.to_string())?;
let path = Path::new(&config.build_repo_base)
.join(&realid)
.join(realid)
.join(&relpath);
if path.is_dir() {
return Err(ErrorNotFound("Ignoring directory"));
Expand All @@ -430,7 +431,7 @@ async fn handle_build_repo_async(
NamedFile::open(path)
.or_else(|_e| {
let fallback_path = Path::new(&config.build_repo_base)
.join(&params.id.to_string())
.join(params.id.to_string())
.join("parent")
.join(&relpath);
if fallback_path.is_dir() {
Expand Down Expand Up @@ -521,9 +522,9 @@ fn handle_repo(config: Data<Config>, req: HttpRequest) -> Result<HttpResponse, a

let namepath = Path::new(&repoconfig.name);
let relpath = tailpath
.strip_prefix(&namepath)
.strip_prefix(namepath)
.map_err(|e| ApiError::InternalServerError(e.to_string()))?;
let path = Path::new(&repoconfig.path).join(&relpath);
let path = Path::new(&repoconfig.path).join(relpath);
if path.is_dir() {
return Err(ErrorNotFound("Ignoring directory"));
}
Expand All @@ -536,7 +537,7 @@ fn handle_repo(config: Data<Config>, req: HttpRequest) -> Result<HttpResponse, a
.or_else(|e| {
// Was this a delta, if so check the deltas queued for deletion
if relpath.starts_with("deltas") {
let tmp_path = Path::new(&repoconfig.path).join("tmp").join(&relpath);
let tmp_path = Path::new(&repoconfig.path).join("tmp").join(relpath);
if tmp_path.is_dir() {
Err(ErrorNotFound("Ignoring directory"))
} else {
Expand Down
14 changes: 7 additions & 7 deletions src/jobs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,12 +124,12 @@ Url={}
* repo not a build repo.
*/
if let Some(collection_id) = &repoconfig.collection_id {
if repoconfig.deploy_collection_id && maybe_build_id == None {
if repoconfig.deploy_collection_id && maybe_build_id.is_none() {
writeln!(contents, "DeployCollectionID={}", collection_id).unwrap();
}
};

if maybe_build_id == None {
if maybe_build_id.is_none() {
if let Some(suggested_name) = &repoconfig.suggested_repo_name {
writeln!(contents, "SuggestRemoteName={}", suggested_name).unwrap();
}
Expand Down Expand Up @@ -439,7 +439,7 @@ impl CommitJobInstance {
};

if let Some(endoflife_rebase_arg) = &endoflife_rebase_arg {
cmd.arg(&endoflife_rebase_arg);
cmd.arg(endoflife_rebase_arg);
};

if let Some(token_type) = self
Expand Down Expand Up @@ -487,8 +487,8 @@ impl CommitJobInstance {
config,
repoconfig,
);
let path = build_repo_path.join(&filename);
File::create(&path)?.write_all(contents.as_bytes())?;
let path = build_repo_path.join(filename);
File::create(path)?.write_all(contents.as_bytes())?;
}
}

Expand Down Expand Up @@ -598,7 +598,7 @@ impl CommitJobInstance {

/* Make sure all the file attributes are correct */
fileinfo.set_size(s.len().try_into().expect("integer overflow on file size"));
fileinfo.set_name(&format!("{}.xml.gz", app_id));
fileinfo.set_name(format!("{}.xml.gz", app_id));
fileinfo.set_file_type(FileType::Regular);
fileinfo.set_attribute_uint32("unix::uid", 0);
fileinfo.set_attribute_uint32("unix::gid", 0);
Expand Down Expand Up @@ -1051,7 +1051,7 @@ impl UpdateRepoJobInstance {
let src = delta.delta_path(&repo_path)?;
let dst = delta.tmp_delta_path(&repo_path)?;
let dst_parent = dst.parent().unwrap();
fs::create_dir_all(&dst_parent)?;
fs::create_dir_all(dst_parent)?;

job_log_and_info(
self.job_id,
Expand Down
6 changes: 2 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
#![allow(proc_macro_derive_resolution_fallback)]

#[macro_use]
extern crate diesel;
#[macro_use]
Expand Down Expand Up @@ -40,8 +38,8 @@ pub use errors::DeltaGenerationError;
type Pool = diesel::r2d2::Pool<ConnectionManager<PgConnection>>;

pub fn load_config(path: &path::Path) -> Arc<Config> {
let config_data = app::load_config(&path)
.unwrap_or_else(|_| panic!("Failed to read config file {:?}", &path));
let config_data =
app::load_config(path).unwrap_or_else(|_| panic!("Failed to read config file {:?}", &path));
Arc::new(config_data)
}

Expand Down
10 changes: 5 additions & 5 deletions src/ostree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -765,15 +765,15 @@ pub struct Delta {
}

fn delta_part_to_hex(part: &str) -> OstreeResult<String> {
let bytes = base64::decode(&part.replace('_', "/")).map_err(|err| {
let bytes = base64::decode(part.replace('_', "/")).map_err(|err| {
OstreeError::InternalError(format!("Invalid delta part name '{}': {}", part, err))
})?;
Ok(bytes_to_object(&bytes))
}

fn hex_to_delta_part(hex: &str) -> OstreeResult<String> {
let bytes = object_to_bytes(hex)?;
let part = base64::encode_config(&bytes, base64::STANDARD_NO_PAD);
let part = base64::encode_config(bytes, base64::STANDARD_NO_PAD);
Ok(part.replace('/', "_"))
}

Expand Down Expand Up @@ -883,7 +883,7 @@ mod tests {
pub fn list_deltas(repo_path: &path::Path) -> Vec<Delta> {
let deltas_dir = get_deltas_path(repo_path);

WalkDir::new(&deltas_dir)
WalkDir::new(deltas_dir)
.min_depth(2)
.max_depth(2)
.into_iter()
Expand Down Expand Up @@ -919,7 +919,7 @@ pub fn calc_deltas_for_ref(repo_path: &path::Path, ref_name: &str, depth: u32) -
if let Ok(commitinfo) = get_commit(repo_path, from_commit.as_ref().unwrap_or(&to_commit)) {
res.push(Delta::new(from_commit.as_deref(), &to_commit));
from_commit = commitinfo.parent;
if from_commit == None {
if from_commit.is_none() {
break;
}
} else {
Expand Down Expand Up @@ -1034,7 +1034,7 @@ pub fn generate_delta_async(
cmd.arg("--generate-static-delta-from").arg(from.clone());
};

cmd.arg(&repo_path);
cmd.arg(repo_path);

log::info!("Generating delta {}", delta.to_string());
Box::new(
Expand Down
7 changes: 5 additions & 2 deletions src/tokens.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,14 @@ impl ClaimsValidator for HttpRequest {
/* A token prefix is something like org.my.App, and should allow
* you to create refs like org.my.App, org.my.App.Debug, and
* org.my.App.Some.Long.Thing. However, it should not allow
* org.my.AppSuffix.
* org.my.AppSuffix. Also checks the "apps" field for exact matches
* only.
*/
fn has_token_prefix(&self, id: &str) -> Result<(), ApiError> {
self.validate_claims(|claims| {
if !id_matches_one_prefix(id, &claims.prefixes) {
if !id_matches_one_prefix(id, &claims.prefixes)
&& !claims.apps.contains(&id.to_string())
{
return Err(ApiError::NotEnoughPermissions(format!(
"Id {} not matching prefix in token",
id
Expand Down