diff --git a/src/api.rs b/src/api.rs index f97d00b..aaa85e7 100644 --- a/src/api.rs +++ b/src/api.rs @@ -105,6 +105,7 @@ pub struct TokenSubsetArgs { scope: Vec, duration: i64, prefixes: Option>, + apps: Option>, repos: Option>, name: String, } @@ -135,6 +136,13 @@ pub fn prefix_is_subset( } } +pub fn apps_is_subset(maybe_subset_apps: &Option>, 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, config: Data, @@ -148,6 +156,7 @@ 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, &claims.apps) && repos_is_subset(&args.repos, &claims.repos) { let new_claims = Claims { @@ -161,6 +170,13 @@ pub fn token_subset( claims.prefixes.clone() } }, + apps: { + if let Some(ref apps) = args.apps { + apps.clone() + } else { + claims.apps.clone() + } + }, repos: { if let Some(ref repos) = args.repos { repos.clone() diff --git a/src/app.rs b/src/app.rs index 746eef7..44cc43f 100644 --- a/src/app.rs +++ b/src/app.rs @@ -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, #[serde(default)] pub prefixes: Vec, // [''] => all, ['org.foo'] => org.foo + org.foo.bar (but not org.foobar) #[serde(default)] + pub apps: Vec, // like prefixes, but only exact matches + #[serde(default)] pub repos: Vec, // list of repo names or a '' for match all pub name: Option, // for debug/logs only } diff --git a/src/tokens.rs b/src/tokens.rs index 222cc95..339f4f5 100644 --- a/src/tokens.rs +++ b/src/tokens.rs @@ -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