Skip to content

Commit a86bb3c

Browse files
Map Compass scopes -> permissions
1 parent 857f954 commit a86bb3c

File tree

11 files changed

+268
-11
lines changed

11 files changed

+268
-11
lines changed

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

compass-scopes.yaml

+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# Sourced from https://developer.atlassian.com/cloud/compass/forge-graphql-toolkit/Classes/CompassRequests/
2+
# Required oauth scopes for compass graphql requests
3+
4+
addEventSource:
5+
- write:component:compass
6+
- write:event:compass
7+
8+
addLabels:
9+
- write:component:compass
10+
11+
attachDataManager:
12+
- write:component:compass
13+
14+
attachEventSource:
15+
- write:component:compass
16+
17+
createComponent:
18+
- write:component:compass
19+
20+
createCustomFieldDefinition:
21+
- write:component:compass
22+
23+
createEvent:
24+
- write:event:compass
25+
26+
createEventSource:
27+
- write:event:compass
28+
29+
createExternalAlias:
30+
- write:component:compass
31+
32+
createLink:
33+
- write:component:compass
34+
35+
createRelationship:
36+
- write:component:compass
37+
38+
deleteComponent:
39+
- write:component:compass
40+
41+
deleteExternalAlias:
42+
- write:component:compass
43+
44+
deleteLink:
45+
- write:component:compass
46+
47+
deleteRelationship:
48+
- write:component:compass
49+
50+
detachDataManager:
51+
- write:component:compass
52+
53+
detachEventSource:
54+
- write:component:compass
55+
56+
getAllComponentTypes:
57+
- read:component:compass
58+
59+
getComponent:
60+
- read:component:compass
61+
62+
getComponentByExternalAlias:
63+
- read:component:compass
64+
65+
getComponentsByReferences:
66+
- read:component:compass
67+
68+
getEventSource:
69+
- read:event:compass
70+
71+
insertMetricValueByExternalId:
72+
- write:metric:compass
73+
74+
removeLabels:
75+
- write:component:compass
76+
77+
searchComponents:
78+
- read:component:compass
79+
80+
syncComponentByExternalAlias:
81+
- write:component:compass
82+
- write:event:compass
83+
84+
synchronizeLinkAssociations:
85+
- write:component:compass
86+
- write:event:compass
87+
- write:metric:compass
88+
89+
unlinkExternalSource:
90+
- write:component:compass
91+
92+
updateComponent:
93+
- write:component:compass
94+
- write:event:compass
95+
96+
updateDataManager:
97+
- write:component:compass
98+
99+
updateEventSources:
100+
- write:component:compass
101+
- write:event:compass

crates/forge_analyzer/src/checkers.rs

+15-3
Original file line numberDiff line numberDiff line change
@@ -1050,7 +1050,7 @@ impl<'cx> Dataflow<'cx> for PermissionDataflow {
10501050
if let Intrinsic::ApiCall(name) | Intrinsic::SafeCall(name) | Intrinsic::Authorize(name) =
10511051
intrinsic
10521052
{
1053-
intrinsic_argument.name = Some(*name);
1053+
intrinsic_argument.name = Some(name.clone());
10541054
let (first, second) = (operands.first(), operands.get(1));
10551055
if let Some(operand) = first {
10561056
match operand {
@@ -1092,9 +1092,21 @@ impl<'cx> Dataflow<'cx> for PermissionDataflow {
10921092
}
10931093
}
10941094

1095-
let mut permissions_within_call: Vec<String> = vec![];
10961095
let intrinsic_func_type = intrinsic_argument.name.unwrap();
10971096

1097+
// Handle RequestCompass case first
1098+
if let IntrinsicName::RequestCompass(api_name) = intrinsic_func_type {
1099+
if let Some(required_permissions) =
1100+
interp.compass_permission_resolver.get(&api_name)
1101+
{
1102+
interp
1103+
.permissions
1104+
.retain(|permissions| !required_permissions.contains(permissions));
1105+
}
1106+
return initial_state;
1107+
}
1108+
1109+
let mut permissions_within_call: Vec<String> = vec![];
10981110
let (resolver, regex_map) = match intrinsic_func_type {
10991111
IntrinsicName::RequestJiraAny => (
11001112
interp.jira_any_permission_resolver,
@@ -1119,7 +1131,7 @@ impl<'cx> Dataflow<'cx> for PermissionDataflow {
11191131
interp.bitbucket_permission_resolver,
11201132
interp.bitbucket_regex_map,
11211133
),
1122-
IntrinsicName::RequestCompass | IntrinsicName::Other => {
1134+
IntrinsicName::RequestCompass(_) | IntrinsicName::Other => {
11231135
(&PermissionHashMap::new(), &HashMap::<String, Regex>::new())
11241136
}
11251137
};

crates/forge_analyzer/src/definitions.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -621,15 +621,15 @@ enum LowerStage {
621621
Create,
622622
}
623623

624-
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
624+
#[derive(Debug, Clone, PartialEq, Eq)]
625625
pub enum IntrinsicName {
626626
RequestJiraAny,
627627
RequestJiraSoftware,
628628
RequestJiraServiceManagement,
629629
RequestConfluence,
630630
RequestJira,
631631
RequestBitbucket,
632-
RequestCompass,
632+
RequestCompass(String),
633633
Other,
634634
}
635635

@@ -1204,10 +1204,10 @@ impl FunctionAnalyzer<'_> {
12041204
{
12051205
match authn.first() {
12061206
Some(PropPath::MemberCall(name)) if name == "asApp" => {
1207-
Some(Intrinsic::ApiCall(IntrinsicName::RequestCompass))
1207+
Some(Intrinsic::ApiCall(IntrinsicName::RequestCompass(function_name.to_string())))
12081208
}
12091209
Some(PropPath::MemberCall(name)) if name == "asUser" => {
1210-
Some(Intrinsic::SafeCall(IntrinsicName::RequestCompass))
1210+
Some(Intrinsic::SafeCall(IntrinsicName::RequestCompass(function_name.to_string())))
12111211
}
12121212
_ => None,
12131213
}

crates/forge_analyzer/src/interp.rs

+4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use std::{
1111
};
1212

1313
use forge_permission_resolver::permissions_resolver::PermissionHashMap;
14+
use forge_permission_resolver::permissions_resolver_compass::CompassPermissionResolver;
1415
use forge_utils::{FxHashMap, FxHashSet};
1516
use itertools::Itertools;
1617
use regex::Regex;
@@ -396,6 +397,7 @@ pub struct Interp<'cx, C: Runner<'cx>> {
396397
pub jira_permission_resolver: &'cx PermissionHashMap,
397398
pub confluence_permission_resolver: &'cx PermissionHashMap,
398399
pub bitbucket_permission_resolver: &'cx PermissionHashMap,
400+
pub compass_permission_resolver: &'cx CompassPermissionResolver,
399401
pub jira_any_regex_map: &'cx HashMap<String, Regex>,
400402
pub jira_software_regex_map: &'cx HashMap<String, Regex>,
401403
pub jira_service_management_regex_map: &'cx HashMap<String, Regex>,
@@ -526,6 +528,7 @@ impl<'cx, C: Runner<'cx>> Interp<'cx, C> {
526528
confluence_regex_map: &'cx HashMap<String, Regex>,
527529
bitbucket_permission_resolver: &'cx PermissionHashMap,
528530
bitbucket_regex_map: &'cx HashMap<String, Regex>,
531+
compass_permission_resolver: &'cx CompassPermissionResolver,
529532
) -> Self {
530533
let call_graph = CallGraph::new(env);
531534

@@ -558,6 +561,7 @@ impl<'cx, C: Runner<'cx>> Interp<'cx, C> {
558561
jira_permission_resolver,
559562
confluence_permission_resolver,
560563
bitbucket_permission_resolver,
564+
compass_permission_resolver,
561565
jira_any_regex_map,
562566
jira_software_regex_map,
563567
jira_service_management_regex_map,

crates/forge_permission_resolver/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ workspace = true
1313
[dependencies]
1414
serde.workspace = true
1515
serde_json.workspace = true
16+
serde_yaml.workspace = true
1617
tracing.workspace = true
1718
regex.workspace = true
1819
ureq = { version = "2.9.6", features = ["json", "charset"] }
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
pub mod permissions_resolver;
2+
pub mod permissions_resolver_compass;

crates/forge_permission_resolver/src/permissions_resolver.rs

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::permissions_resolver_compass::CompassPermissionResolver;
12
use regex::Regex;
23
use serde::Deserialize;
34
use std::{cmp::Reverse, collections::HashMap, hash::Hash};
@@ -142,6 +143,10 @@ pub fn get_permission_resolver_bitbucket() -> (PermissionHashMap, HashMap<String
142143
get_permission_resolver(bitbucket_url)
143144
}
144145

146+
pub fn get_permission_resolver_compass() -> CompassPermissionResolver {
147+
CompassPermissionResolver::new()
148+
}
149+
145150
pub fn get_permission_resolver(url: &str) -> (PermissionHashMap, HashMap<String, Regex>) {
146151
let mut endpoint_map: PermissionHashMap = HashMap::default();
147152
let mut endpoint_regex: HashMap<String, Regex> = HashMap::default();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
use serde_yaml;
2+
use std::collections::HashMap;
3+
use tracing::warn;
4+
5+
#[derive(Debug)]
6+
pub struct CompassPermissionResolver {
7+
api_scopes_map: HashMap<String, Vec<String>>,
8+
}
9+
10+
impl CompassPermissionResolver {
11+
/// Factory method to create a new `CompassPermissionResolver`
12+
pub fn new() -> Self {
13+
let permissions = load_compass_api_scopes();
14+
CompassPermissionResolver {
15+
api_scopes_map: permissions,
16+
}
17+
}
18+
19+
/// Retrieves permissions for a given key. Prints a warning if the key is missing.
20+
pub fn get(&self, key: &str) -> Option<&Vec<String>> {
21+
if let Some(permissions) = self.api_scopes_map.get(key) {
22+
Some(permissions)
23+
} else {
24+
warn!(
25+
"Warning: compass API '{}' not found in the api->scopes mapping.",
26+
key
27+
);
28+
None
29+
}
30+
}
31+
}
32+
33+
impl Default for CompassPermissionResolver {
34+
fn default() -> Self {
35+
Self::new()
36+
}
37+
}
38+
39+
// This is for handling https://developer.atlassian.com/cloud/compass/forge-graphql-toolkit/
40+
fn load_compass_api_scopes() -> HashMap<String, Vec<String>> {
41+
let file = include_str!("../../../compass-scopes.yaml");
42+
serde_yaml::from_str(file)
43+
.expect("Error: Failed to parse compass-scopes YAML file. Ensure it is properly formatted.")
44+
}
45+
46+
#[cfg(test)]
47+
mod test {
48+
use super::*;
49+
50+
#[test]
51+
fn test_load_compass_api_scopes() {
52+
let scopes = load_compass_api_scopes();
53+
54+
// Verify that the scopes are loaded correctly
55+
assert!(scopes.contains_key("addEventSource"));
56+
assert_eq!(
57+
scopes.get("addEventSource").unwrap(),
58+
&vec![
59+
"write:component:compass".to_string(),
60+
"write:event:compass".to_string()
61+
]
62+
);
63+
64+
assert!(scopes.contains_key("addLabels"));
65+
assert_eq!(
66+
scopes.get("addLabels").unwrap(),
67+
&vec!["write:component:compass".to_string()]
68+
);
69+
}
70+
}

crates/fsrt/src/main.rs

+10-3
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ mod test;
66

77
use clap::{Parser, ValueHint};
88
use forge_permission_resolver::permissions_resolver::{
9-
get_permission_resolver_bitbucket, get_permission_resolver_confluence,
10-
get_permission_resolver_jira, get_permission_resolver_jira_any,
11-
get_permission_resolver_jira_service_management, get_permission_resolver_jira_software,
9+
get_permission_resolver_bitbucket, get_permission_resolver_compass,
10+
get_permission_resolver_confluence, get_permission_resolver_jira,
11+
get_permission_resolver_jira_any, get_permission_resolver_jira_service_management,
12+
get_permission_resolver_jira_software,
1213
};
1314
use glob::glob;
1415
use std::{
@@ -418,6 +419,7 @@ pub(crate) fn scan_directory<'a>(
418419
let (confluence_permission_resolver, confluence_regex_map) =
419420
get_permission_resolver_confluence();
420421
let (bitbucket_permission_resolver, bitbucket_regex_map) = get_permission_resolver_bitbucket();
422+
let compass_permission_resolver = get_permission_resolver_compass();
421423

422424
let mut definition_analysis_interp = Interp::<DefinitionAnalysisRunner>::new(
423425
&proj.env,
@@ -436,6 +438,7 @@ pub(crate) fn scan_directory<'a>(
436438
&confluence_regex_map,
437439
&bitbucket_permission_resolver,
438440
&bitbucket_regex_map,
441+
&compass_permission_resolver,
439442
);
440443

441444
let mut interp = Interp::new(
@@ -455,6 +458,7 @@ pub(crate) fn scan_directory<'a>(
455458
&confluence_regex_map,
456459
&bitbucket_permission_resolver,
457460
&bitbucket_regex_map,
461+
&compass_permission_resolver,
458462
);
459463
let mut authn_interp = Interp::new(
460464
&proj.env,
@@ -473,6 +477,7 @@ pub(crate) fn scan_directory<'a>(
473477
&confluence_regex_map,
474478
&bitbucket_permission_resolver,
475479
&bitbucket_regex_map,
480+
&compass_permission_resolver,
476481
);
477482

478483
let mut reporter = Reporter::new();
@@ -493,6 +498,7 @@ pub(crate) fn scan_directory<'a>(
493498
&confluence_regex_map,
494499
&bitbucket_permission_resolver,
495500
&bitbucket_regex_map,
501+
&compass_permission_resolver,
496502
);
497503
reporter.add_app(opts.appkey.clone().unwrap_or_default(), name.to_owned());
498504

@@ -513,6 +519,7 @@ pub(crate) fn scan_directory<'a>(
513519
&confluence_regex_map,
514520
&bitbucket_permission_resolver,
515521
&bitbucket_regex_map,
522+
&compass_permission_resolver,
516523
);
517524
for func in &proj.funcs {
518525
let mut def_checker = DefinitionAnalysisRunner::new();

0 commit comments

Comments
 (0)