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

Release #235

Closed
wants to merge 57 commits into from
Closed
Changes from 1 commit
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
9d27fb2
[ALS-5508] Dependabot dep bumps
Jan 18, 2024
96ee6c3
[ALS-5612] Create stored procedure to create a user (#154)
Gcolon021 Feb 13, 2024
764c185
[ALS-5612] Updated stored procedure (#155)
Gcolon021 Feb 14, 2024
dcb743d
[ALS-0000] Fix wiring issues in auth app.
Apr 19, 2024
f42ab2d
Java 21, Spring, code refactor and deploy as standalone container. (#…
Gcolon021 Jun 7, 2024
207a367
Merge Fence specific branch into release
Gcolon021 Jun 14, 2024
400776f
Remove unnecessary configurations from Jenkinsfile
Gcolon021 Jun 18, 2024
52c96f0
Update logging level in Token and Authorization services
Gcolon021 Jun 20, 2024
8813807
Remove unnecessary logging
Gcolon021 Jun 20, 2024
93c07bd
[ALS-6567] Add classes for fence_mapping.json
Gcolon021 Jun 18, 2024
1cf5de7
Update FenceMappingUtility with new model classes
Gcolon021 Jun 18, 2024
ed3fda6
Improve FENCEAuthenticationService with additional checks and code re…
Gcolon021 Jun 18, 2024
8db8910
Update persistAll method to return list of roles
Gcolon021 Jun 21, 2024
6477da2
[ALS-6398] Enable secure and http-only flags for session cookies
Gcolon021 Jun 26, 2024
13c4304
[ALS-6103] Architectural changes to support multiple auth providers (…
Gcolon021 Jul 10, 2024
e2369ff
[ALS-6103] Update StudyAccessController with new annotations
Gcolon021 Jul 10, 2024
01e97dc
[ALS-6103] Update StudyAccessController with new annotations (#184)
Gcolon021 Jul 10, 2024
96c692b
Resolve leading whitespace error programmatically (#185)
Gcolon021 Jul 11, 2024
f5e237e
[Hotfix][ALS-6861] BDC Auth showing as down on the BDC Graphana dashb…
Gcolon021 Jul 12, 2024
f19aa8e
Update AuthorizationService (#187)
Gcolon021 Jul 15, 2024
fa1115b
Merge branch 'refs/heads/ALS-6103-Study-Access' into release
Gcolon021 Jul 16, 2024
5ae8dbf
[ALS-6882] Fix migrator by dropping constraint
Jul 24, 2024
1d2fcc7
Remove AR drop
Jul 25, 2024
eab9a0f
[CHORE] - Switch fetch type to EAGER
Jul 26, 2024
36ad515
Update Dockerfile
nixj14 Aug 20, 2024
67be2c6
Update Dockerfile
nixj14 Aug 20, 2024
e726eb3
Implement RAS Authentication Provider (#195)
Gcolon021 Aug 22, 2024
35118d4
Revert "Update Dockerfile"
Luke-Sikina Sep 5, 2024
9838b55
Revert "Update Dockerfile"
Luke-Sikina Sep 5, 2024
a63a0bd
[ALS-7197] BDC Auth access: User able to access data (#203)
Gcolon021 Sep 9, 2024
535798a
Enhance logging and configuration (#207)
Gcolon021 Sep 10, 2024
706f057
ALS-7306: Add expected result type auth for PFB (#208)
ramari16 Sep 12, 2024
a66011a
Fix token validation logic for long-term tokens (#209)
Gcolon021 Sep 16, 2024
222a096
Add long-term token check in isAuthorized method (#210)
Gcolon021 Sep 17, 2024
21e6b28
Update Jenkinsfile to use uppercase for LATEST_TAG (#212)
Gcolon021 Sep 17, 2024
24acd51
[ALS-6921] Implement Open Access in PSAMA (#211)
Gcolon021 Sep 18, 2024
ab50759
Add deployment toggle to Jenkinsfile (#213)
Gcolon021 Sep 18, 2024
fd783e2
Fix type for DEPLOY parameter in Jenkinsfile (#214)
Gcolon021 Sep 18, 2024
461925a
Change Jenkins Parameter (#215)
Gcolon021 Sep 18, 2024
345122e
Add logout endpoint and improve token validation (#217)
Gcolon021 Oct 1, 2024
a433efb
Check for open access enabled (#218)
Gcolon021 Oct 1, 2024
0061638
Add AR_DICTIONARY_REQUESTS (#219)
Gcolon021 Oct 3, 2024
977a713
[ALS-6880] HttpClient with proxy
Aug 20, 2024
bcba4ac
[ALS-6880] SLF4J dep issues
Aug 21, 2024
cd5296f
restTemplate connection refactoring
nixj14 Aug 21, 2024
b8bd17e
refactor to explicitly set proxy
nixj14 Aug 23, 2024
9d2a041
Fix for error:
nixj14 Sep 6, 2024
459a744
[CHORE] int friendly default for RestClientConf
Oct 7, 2024
60cdb6c
Update Dockerfile
nixj14 Aug 20, 2024
0ea5539
Add Javadoc comments to model classes (#223)
Gcolon021 Oct 11, 2024
1e1eab2
[CHORE] This is not an error
Oct 14, 2024
39e310a
[ALS-7575] Add cache inspection and eviction services (#222)
Gcolon021 Oct 21, 2024
650ae05
[ALS-7554] User should not be able to search without a valid token (#…
Gcolon021 Oct 23, 2024
0c1a417
Test github actions (#228)
Gcolon021 Oct 23, 2024
62d030f
[ALS-7687] Create PSAMA cache unit test (#232)
Gcolon021 Oct 30, 2024
d7934dd
Update dependencies in pom.xml to latest versions (#233)
Gcolon021 Dec 13, 2024
00fa4ab
Update branch references from 'master' to 'main'
Gcolon021 Jan 9, 2025
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
Prev Previous commit
Next Next commit
Improve FENCEAuthenticationService with additional checks and code re…
…factoring

The code has been modified to include an additional check in FENCEAuthenticationService to ensure that FENCEMapping is not empty before creating access rules. This will prevent unnecessary errors and exception handling.
Gcolon021 committed Jun 21, 2024

Verified

This commit was created on github.com and signed with GitHub’s verified signature. The key has expired.
commit ed3fda62d67711044873bdfc368c2a600d0ba1f3
Original file line number Diff line number Diff line change
@@ -98,7 +98,7 @@ public FENCEAuthenticationService(UserService userService,
@Value("${fence.parent.consent.group.concept.path}") String fenceParentConceptPath,
@Value("${fence.topmed.consent.group.concept.path}") String fenceTopmedConceptPath,
@Value("${fence.consent.group.concept.path}") String fenceHarmonizedConceptPath,
AccessRuleService accessRuleService, FenceMappingUtility fenceMappingUtility){
AccessRuleService accessRuleService, FenceMappingUtility fenceMappingUtility) {
this.userService = userService;
this.roleService = roleService;
this.connectionService = connectionService;
@@ -128,7 +128,7 @@ public void initializeFenceService() {

// We need to set the underscoreFields here so that we can use them in the access rules during PostConstruct
// If we don't set them here, we will get a NullPointerException when we try to use them in the access rules
underscoreFields = new String[] {
underscoreFields = new String[]{
parentAccessionField,
topmedAccessionField,
fence_harmonized_consent_group_concept_path,
@@ -154,36 +154,40 @@ public void initializeFenceService() {
logger.info("fence_harmonized_concept_path: {}", fence_harmonized_concept_path);
logger.info("underscoreFields: {}", Arrays.toString(underscoreFields));

// Create all potential access rules using the fence mapping
Set<Role> roles = fenceMappingUtility.getFenceMappingByAuthZ().values().parallelStream().map(projectMetadata -> {
if (projectMetadata == null) {
logger.error("initializeFenceService() -> createAndUpsertRole could not find study in FENCE mapping SKIPPING: {}", projectMetadata);
return null;
}
if (!fenceMappingUtility.getFENCEMapping().isEmpty() && !fenceMappingUtility.getFenceMappingByAuthZ().isEmpty()) {
// Create all potential access rules using the fence mapping
Set<Role> roles = fenceMappingUtility.getFenceMappingByAuthZ().values().parallelStream().map(projectMetadata -> {
if (projectMetadata == null) {
logger.error("initializeFenceService() -> createAndUpsertRole could not find study in FENCE mapping SKIPPING: {}", projectMetadata);
return null;
}

if (projectMetadata.getStudyIdentifier() == null || projectMetadata.getStudyIdentifier().isEmpty()) {
logger.error("initializeFenceService() -> createAndUpsertRole could not find study identifier in FENCE mapping SKIPPING: {}", projectMetadata);
return null;
}
if (projectMetadata.getStudyIdentifier() == null || projectMetadata.getStudyIdentifier().isEmpty()) {
logger.error("initializeFenceService() -> createAndUpsertRole could not find study identifier in FENCE mapping SKIPPING: {}", projectMetadata);
return null;
}

if (projectMetadata.getAuthZ() == null || projectMetadata.getAuthZ().isEmpty()) {
logger.error("initializeFenceService() -> createAndUpsertRole could not find authZ in FENCE mapping SKIPPING: {}", projectMetadata);
return null;
}
if (projectMetadata.getAuthZ() == null || projectMetadata.getAuthZ().isEmpty()) {
logger.error("initializeFenceService() -> createAndUpsertRole could not find authZ in FENCE mapping SKIPPING: {}", projectMetadata);
return null;
}

String projectId = projectMetadata.getStudyIdentifier();
String consentCode = projectMetadata.getConsentGroupCode();
String newRoleName = StringUtils.isNotBlank(consentCode) ? "FENCE_"+projectId+"_"+consentCode : "FENCE_"+projectId;
String projectId = projectMetadata.getStudyIdentifier();
String consentCode = projectMetadata.getConsentGroupCode();
String newRoleName = StringUtils.isNotBlank(consentCode) ? "FENCE_" + projectId + "_" + consentCode : "FENCE_" + projectId;

return createRole(newRoleName, "FENCE role " + newRoleName);
}).filter(Objects::nonNull).collect(Collectors.toSet());
return createRole(newRoleName, "FENCE role " + newRoleName);
}).filter(Objects::nonNull).collect(Collectors.toSet());

roleService.persistAll(roles);
roleService.persistAll(roles);
} else {
logger.error("initializeFenceService() -> createAndUpsertRole could not find any studies in FENCE mapping");
}
}

public HashMap<String, String> getFENCEProfile(String callback_url, Map<String, String> authRequest){
public HashMap<String, String> getFENCEProfile(String callback_url, Map<String, String> authRequest) {
logger.debug("getFENCEProfile() starting...");
String fence_code = authRequest.get("code");
String fence_code = authRequest.get("code");

// Validate that the fence code is alphanumeric
if (!fence_code.matches("[a-zA-Z0-9]+")) {
@@ -197,7 +201,7 @@ public HashMap<String, String> getFENCEProfile(String callback_url, Map<String,
logger.debug("getFENCEProfile() query FENCE for user profile with code");
fence_user_profile = getFENCEUserProfile(getFENCEAccessToken(callback_url, fence_code).get("access_token").asText());

if(logger.isTraceEnabled()){
if (logger.isTraceEnabled()) {
// create object mapper instance
ObjectMapper mapper = new ObjectMapper();
// `JsonNode` to JSON string
@@ -210,8 +214,8 @@ public HashMap<String, String> getFENCEProfile(String callback_url, Map<String,
logger.debug("getFENCEProfile() .email:{}", fence_user_profile.get("email"));
} catch (Exception ex) {
logger.error("getFENCEProfile() could not retrieve the user profile from the auth provider, because {}", ex.getMessage(), ex);
throw new NotAuthorizedException("Could not get the user profile "+
"from the Gen3 authentication provider."+ex.getMessage());
throw new NotAuthorizedException("Could not get the user profile " +
"from the Gen3 authentication provider." + ex.getMessage());
}

User current_user;
@@ -243,7 +247,7 @@ public HashMap<String, String> getFENCEProfile(String callback_url, Map<String,

String projectId = projectMetadata.getStudyIdentifier();
String consentCode = projectMetadata.getConsentGroupCode();
String newRoleName = StringUtils.isNotBlank(consentCode) ? "FENCE_"+projectId+"_"+consentCode : "FENCE_"+projectId;
String newRoleName = StringUtils.isNotBlank(consentCode) ? "FENCE_" + projectId + "_" + consentCode : "FENCE_" + projectId;

roleNames.add(newRoleName);
});
@@ -287,7 +291,7 @@ public HashMap<String, String> getFENCEProfile(String callback_url, Map<String,
} catch (Exception ex) {
logger.error("upsertRole() Could not add roles to user, because {}", ex.getMessage());
}
HashMap<String, Object> claims = new HashMap<String,Object>();
HashMap<String, Object> claims = new HashMap<String, Object>();
claims.put("name", fence_user_profile.get("name"));
claims.put("email", current_user.getEmail());
claims.put("sub", current_user.getSubject());
@@ -334,7 +338,7 @@ private JsonNode getFENCEUserProfile(String access_token) {

logger.debug("getFENCEUserProfile() getting user profile from uri:{}/user/user", this.idp_provider_uri);
ResponseEntity<String> fence_user_profile_response = this.restClientUtil.retrieveGetResponse(
this.idp_provider_uri+"/user/user",
this.idp_provider_uri + "/user/user",
headers
);

@@ -363,7 +367,7 @@ private JsonNode getFENCEAccessToken(String callback_url, String fence_code) {
queryMap.add("code", fence_code);
queryMap.add("redirect_uri", callback_url);

String fence_token_url = this.idp_provider_uri+"/user/oauth2/token";
String fence_token_url = this.idp_provider_uri + "/user/oauth2/token";

HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(queryMap, headers);
JsonNode respJson = null;
@@ -403,7 +407,7 @@ private User createUserFromFENCEProfile(JsonNode node) {
logger.debug("createUserFromFENCEProfile() starting...");

User new_user = new User();
new_user.setSubject("fence|"+node.get("user_id").asText());
new_user.setSubject("fence|" + node.get("user_id").asText());
// This is not always an email address, but it is the only attribute other than the sub claim
// that is guaranteed to be populated by Fence and which makes sense as a display name for a
// user.
@@ -427,12 +431,12 @@ private User createUserFromFENCEProfile(JsonNode node) {
/**
* Insert or Update the User object's list of Roles in the database.
*
* @param u The User object the generated Role will be added to
* @param roleName Name of the Role
* @param u The User object the generated Role will be added to
* @param roleName Name of the Role
* @param roleDescription Description of the Role
* @return boolean Whether the Role was successfully added to the User or not
*/
public boolean upsertRole(User u, String roleName, String roleDescription) {
public boolean upsertRole(User u, String roleName, String roleDescription) {
boolean status = false;

// Get the User's list of Roles. The first time, this will be an empty Set.
@@ -478,7 +482,9 @@ private Set<Privilege> addFENCEPrivileges(Role r) {
//harmonized has 2 ARs for parent + harminized and harmonized only
//Topmed has up to three ARs for topmed / topmed + parent / topmed + harmonized
Set<Privilege> privs = r.getPrivileges();
if (privs == null) { privs = new HashSet<Privilege>();}
if (privs == null) {
privs = new HashSet<Privilege>();
}

//e.g. FENCE_phs0000xx_c2 or FENCE_tutorial-biolinc_camp
String project_name = extractProject(roleName);
@@ -494,7 +500,7 @@ private Set<Privilege> addFENCEPrivileges(Role r) {
// Look up the metadata by consent group.
StudyMetaData projectMetadata = getFENCEMappingforProjectAndConsent(project_name, consent_group);

if(projectMetadata == null){
if (projectMetadata == null) {
//no privileges means no access to this project. just return existing set of privs.
logger.warn("No metadata available for project {}.{}", project_name, consent_group);
return privs;
@@ -509,28 +515,28 @@ private Set<Privilege> addFENCEPrivileges(Role r) {

// we need to add escape sequence back in to the path for parsing later (also need to double escape the regex)
// we need to do this for the query Template and scopes, but should NOT do this for the rules.
if(concept_path != null) {
if (concept_path != null) {
concept_path = concept_path.replaceAll("\\\\", "\\\\\\\\");
}

if(dataType != null && dataType.contains("G")) {
if (dataType != null && dataType.contains("G")) {
//insert genomic/topmed privs - this will also add rules for including harmonized & parent data if applicable
privs.add(upsertTopmedPrivilege(project_name, projectAlias, consent_group, concept_path, isHarmonized));
}

if(dataType != null && dataType.contains("P")) {
if (dataType != null && dataType.contains("P")) {
//insert clinical privs
logger.info("addPrivileges() project:{} consent_group:{} concept_path:{}", project_name, consent_group, concept_path);
privs.add(upsertClinicalPrivilege(project_name, projectAlias, consent_group, concept_path, false));

//if harmonized study, also create harmonized privileges
if(Boolean.TRUE.equals(isHarmonized)) {
if (Boolean.TRUE.equals(isHarmonized)) {
privs.add(upsertClinicalPrivilege(project_name, projectAlias, consent_group, concept_path, true));
}
}

//projects without G or P in data_type are skipped
if(dataType == null || (!dataType.contains("P") && !dataType.contains("G"))){
if (dataType == null || (!dataType.contains("P") && !dataType.contains("G"))) {
logger.warn("Missing study type for {} {}. Skipping.", project_name, consent_group);
}

@@ -544,9 +550,9 @@ private Set<Privilege> addFENCEPrivileges(Role r) {
* Privileges created with this method will deny access if any genomic filters (topmed data) are included.
*
* @param studyIdentifier The study identifier
* @param consent_group The consent group
* @param conceptPath The concept path
* @param isHarmonized Whether the study is harmonized
* @param consent_group The consent group
* @param conceptPath The concept path
* @param isHarmonized Whether the study is harmonized
* @return The created privilege
*/
private Privilege upsertClinicalPrivilege(String studyIdentifier, String projectAlias, String consent_group, String conceptPath, boolean isHarmonized) {
@@ -574,7 +580,7 @@ private Privilege upsertClinicalPrivilege(String studyIdentifier, String project
logger.debug("Escaped consent concept path: {}", consent_concept_path);
}

if(fence_harmonized_concept_path != null && !fence_harmonized_concept_path.contains("\\\\")){
if (fence_harmonized_concept_path != null && !fence_harmonized_concept_path.contains("\\\\")) {
//these have to be escaped again so that jaxson can convert it correctly
fence_harmonized_concept_path = fence_harmonized_concept_path.replaceAll("\\\\", "\\\\\\\\");
logger.debug("upsertTopmedPrivilege(): escaped harmonized consent path" + fence_harmonized_concept_path);
@@ -627,14 +633,14 @@ private Privilege upsertClinicalPrivilege(String studyIdentifier, String project
/**
* Configures the AccessRule with gates and sub-rules.
*
* @param ar The AccessRule to configure.
* @param studyIdentifier The study identifier.
* @param consent_group The consent group.
* @param conceptPath The concept path.
* @param projectAlias The project alias.
* @param parent Whether to include parent gates.
* @param harmonized Whether to include harmonized gates.
* @param topmed Whether to include Topmed gates.
* @param ar The AccessRule to configure.
* @param studyIdentifier The study identifier.
* @param consent_group The consent group.
* @param conceptPath The concept path.
* @param projectAlias The project alias.
* @param parent Whether to include parent gates.
* @param harmonized Whether to include harmonized gates.
* @param topmed Whether to include Topmed gates.
*/
private void configureAccessRule(AccessRule ar, String studyIdentifier, String consent_group, String conceptPath, String projectAlias, boolean parent, boolean harmonized, boolean topmed) {
if (ar.getGates() == null) {
@@ -654,11 +660,11 @@ private void configureAccessRule(AccessRule ar, String studyIdentifier, String c
/**
* Configures the harmonized AccessRule with gates and sub-rules.
*
* @param ar The AccessRule to configure.
* @param studyIdentifier The study identifier.
* @param consent_group The consent group.
* @param conceptPath The concept path.
* @param projectAlias The project alias.
* @param ar The AccessRule to configure.
* @param studyIdentifier The study identifier.
* @param consent_group The consent group.
* @param conceptPath The concept path.
* @param projectAlias The project alias.
*/
private void configureHarmonizedAccessRule(AccessRule ar, String studyIdentifier, String consent_group, String conceptPath, String projectAlias) {
if (ar.getGates() == null) {
@@ -730,9 +736,10 @@ private Collection<? extends AccessRule> getTopmedRestrictedSubRules() {
/**
* Creates and returns a restricted sub-rule AccessRule for Topmed.
* topmed restriction rules don't need much configuration. Just deny all access.
* @param type The type of the Topmed restriction.
* @param rule The rule expression.
* @return The created AccessRule.
*
* @param type The type of the Topmed restriction.
* @param rule The rule expression.
* @return The created AccessRule.
*/
private AccessRule upsertTopmedRestrictedSubRule(String type, String rule) {
// Construct the AccessRule name
@@ -766,16 +773,16 @@ private Collection<? extends AccessRule> getPhenotypeSubRules(String studyIdenti
//categorical filters will always contain at least one entry (for the consent groups); it will never be empty
rules.add(createPhenotypeSubRule(fence_parent_consent_group_concept_path, "ALLOW_PARENT_CONSENT", "$.query.query.categoryFilters", AccessRule.TypeNaming.ALL_CONTAINS, "", true));

for(String underscorePath : underscoreFields) {
for (String underscorePath : underscoreFields) {
rules.add(createPhenotypeSubRule(underscorePath, "ALLOW " + underscorePath, "$.query.query.fields.[*]", AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY, "FIELDS", false));
rules.add(createPhenotypeSubRule(underscorePath, "ALLOW " + underscorePath, "$.query.query.categoryFilters", AccessRule.TypeNaming.ALL_CONTAINS, "CATEGORICAL", true));
rules.add(createPhenotypeSubRule(underscorePath, "ALLOW " + underscorePath, "$.query.query.requiredFields.[*]", AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY, "REQ_FIELDS", false));
}

rules.add(createPhenotypeSubRule(conceptPath, alias+ "_" + studyIdentifier, "$.query.query.categoryFilters", AccessRule.TypeNaming.ALL_CONTAINS, "CATEGORICAL", true));
rules.add(createPhenotypeSubRule(conceptPath, alias+ "_" + studyIdentifier, "$.query.query.numericFilters", AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY, "NUMERIC", true));
rules.add(createPhenotypeSubRule(conceptPath, alias+ "_" + studyIdentifier, "$.query.query.fields.[*]", AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY, "FIELDS", false));
rules.add(createPhenotypeSubRule(conceptPath, alias+ "_" + studyIdentifier, "$.query.query.requiredFields.[*]", AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY, "REQUIRED_FIELDS", false));
rules.add(createPhenotypeSubRule(conceptPath, alias + "_" + studyIdentifier, "$.query.query.categoryFilters", AccessRule.TypeNaming.ALL_CONTAINS, "CATEGORICAL", true));
rules.add(createPhenotypeSubRule(conceptPath, alias + "_" + studyIdentifier, "$.query.query.numericFilters", AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY, "NUMERIC", true));
rules.add(createPhenotypeSubRule(conceptPath, alias + "_" + studyIdentifier, "$.query.query.fields.[*]", AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY, "FIELDS", false));
rules.add(createPhenotypeSubRule(conceptPath, alias + "_" + studyIdentifier, "$.query.query.requiredFields.[*]", AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY, "REQUIRED_FIELDS", false));

return rules;
}
@@ -794,7 +801,7 @@ private Collection<? extends AccessRule> getHarmonizedSubRules() {
rules.add(createPhenotypeSubRule(fence_harmonized_consent_group_concept_path, "ALLOW_HARMONIZED_CONSENT", "$.query.query.categoryFilters", AccessRule.TypeNaming.ALL_CONTAINS, "", true));
rules.add(createPhenotypeSubRule(fence_topmed_consent_group_concept_path, "ALLOW_TOPMED_CONSENT", "$.query.query.categoryFilters", AccessRule.TypeNaming.ALL_CONTAINS, "", true));

for(String underscorePath : underscoreFields) {
for (String underscorePath : underscoreFields) {
rules.add(createPhenotypeSubRule(underscorePath, "ALLOW " + underscorePath, "$.query.query.fields.[*]", AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY, "FIELDS", false));
rules.add(createPhenotypeSubRule(underscorePath, "ALLOW " + underscorePath, "$.query.query.categoryFilters", AccessRule.TypeNaming.ALL_CONTAINS, "CATEGORICAL", true));
rules.add(createPhenotypeSubRule(underscorePath, "ALLOW " + underscorePath, "$.query.query.requiredFields.[*]", AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY, "REQ_FIELDS", false));
@@ -811,28 +818,30 @@ private Collection<? extends AccessRule> getHarmonizedSubRules() {

/**
* generate and return a set of rules that disallow access to phenotype data (only genomic filters allowed)
*
* @return
*/
private Collection<? extends AccessRule> getPhenotypeRestrictedSubRules(String studyIdentifier, String consentCode, String alias) {
Set<AccessRule> rules = new HashSet<AccessRule>();
//categorical filters will always contain at least one entry (for the consent groups); it will never be empty
rules.add(createPhenotypeSubRule(fence_topmed_consent_group_concept_path, "ALLOW_TOPMED_CONSENT", "$.query.query.categoryFilters", AccessRule.TypeNaming.ALL_CONTAINS, "", true));

for(String underscorePath : underscoreFields ) {
for (String underscorePath : underscoreFields) {
rules.add(createPhenotypeSubRule(underscorePath, "ALLOW " + underscorePath, "$.query.query.fields.[*]", AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY, "FIELDS", false));
rules.add(createPhenotypeSubRule(underscorePath, "ALLOW " + underscorePath, "$.query.query.categoryFilters", AccessRule.TypeNaming.ALL_CONTAINS, "CATEGORICAL", true));
rules.add(createPhenotypeSubRule(underscorePath, "ALLOW " + underscorePath, "$.query.query.requiredFields.[*]", AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY, "REQ_FIELDS", false));
}

rules.add(createPhenotypeSubRule(null, alias + "_" + studyIdentifier+ "_" + consentCode, "$.query.query.numericFilters.[*]", AccessRule.TypeNaming.IS_EMPTY, "DISALLOW_NUMERIC", false));
rules.add(createPhenotypeSubRule(null, alias + "_" + studyIdentifier+ "_" + consentCode, "$.query.query.requiredFields.[*]", AccessRule.TypeNaming.IS_EMPTY, "DISALLOW_REQUIRED_FIELDS", false));
rules.add(createPhenotypeSubRule(null, alias + "_" + studyIdentifier + "_" + consentCode, "$.query.query.numericFilters.[*]", AccessRule.TypeNaming.IS_EMPTY, "DISALLOW_NUMERIC", false));
rules.add(createPhenotypeSubRule(null, alias + "_" + studyIdentifier + "_" + consentCode, "$.query.query.requiredFields.[*]", AccessRule.TypeNaming.IS_EMPTY, "DISALLOW_REQUIRED_FIELDS", false));

return rules;
}

/**
* Return a set of gates that identify which consent values have been provided. the boolean parameters indicate
* if a value in the specified consent location should allow this gate to pass.
*
* @param parent
* @param harmonized
* @param topmed
@@ -850,6 +859,7 @@ private Collection<? extends AccessRule> getGates(boolean parent, boolean harmon
/**
* Creates a privilege for Topmed access. This has (up to) three access rules:
* 1) topmed only 2) topmed + parent 3) topmed + harmonized.
*
* @param studyIdentifier
* @param projectAlias
* @param consentGroup
@@ -1001,11 +1011,11 @@ private void addStandardAccessRules(Set<AccessRule> accessRules) {
* Generates Main rule only; gates & sub-rules attached after calling this
* prentRule should be null if this is the main rule, or the appropriate value if this is a sub-rule
*
* @param studyIdentifier The study identifier.
* @param consent_group The consent group.
* @param label The label for the rule.
* @param consent_path The consent path.
* @return The created AccessRule.
* @param studyIdentifier The study identifier.
* @param consent_group The consent group.
* @param label The label for the rule.
* @param consent_path The consent path.
* @return The created AccessRule.
*/
private AccessRule createConsentAccessRule(String studyIdentifier, String consent_group, String label, String consent_path) {
String ar_name = (consent_group != null && !consent_group.isEmpty()) ? "AR_CONSENT_" + studyIdentifier + "_" + consent_group + "_" + label : "AR_CONSENT_" + studyIdentifier;
@@ -1029,10 +1039,11 @@ private AccessRule createConsentAccessRule(String studyIdentifier, String consen
/**
* Creates and returns a Topmed access rule AccessRule.
* Generates Main Rule only; gates & sub-rules attached by calling method
*
* @param project_name The name of the project.
* @param consent_group The consent group.
* @param label The label for the rule.
* @return The created AccessRule.
* @return The created AccessRule.
*/
private AccessRule upsertTopmedAccessRule(String project_name, String consent_group, String label) {
String ar_name = (consent_group != null && !consent_group.isEmpty()) ? "AR_TOPMED_" + project_name + "_" + consent_group + "_" + label : "AR_TOPMED_" + project_name + "_" + label;
@@ -1060,7 +1071,7 @@ private AccessRule upsertTopmedAccessRule(String project_name, String consent_gr
* @param project_name The name of the project.
* @param consent_group The consent group.
* @param label The label for the rule.
* @return The created AccessRule.
* @return The created AccessRule.
*/
private AccessRule upsertHarmonizedAccessRule(String project_name, String consent_group, String label) {
String ar_name = "AR_TOPMED_" + project_name + "_" + consent_group + "_" + label;
@@ -1087,11 +1098,11 @@ private AccessRule upsertHarmonizedAccessRule(String project_name, String consen
* Insert a new gate (if it doesn't exist yet) to identify if consent values are present in the query.
* return an existing gate named GATE_{gateName}_(PRESENT|MISSING) if it exists.
*
* @param gateName The name of the gate.
* @param rule The rule expression.
* @param is_present Whether the gate is for present or missing consent.
* @param description The description of the gate.
* @return The created AccessRule.
* @param gateName The name of the gate.
* @param rule The rule expression.
* @param is_present Whether the gate is for present or missing consent.
* @param description The description of the gate.
* @return The created AccessRule.
*/
private AccessRule upsertConsentGate(String gateName, String rule, boolean is_present, String description) {
gateName = "GATE_" + gateName + "_" + (is_present ? "PRESENT" : "MISSING");