diff --git a/build.gradle b/build.gradle index 2d7bad89c..bf71e3989 100644 --- a/build.gradle +++ b/build.gradle @@ -131,7 +131,7 @@ dependencies { } else { - implementation 'au.org.ala:ala-ws-spring-security:6.2.0' + implementation 'au.org.ala:ala-ws-spring-security:6.3.0-SNAPSHOT' } implementation 'org.codehaus.groovy:groovy-all:3.0.11' diff --git a/src/main/java/au/org/ala/biocache/config/SecurityConfig.java b/src/main/java/au/org/ala/biocache/config/SecurityConfig.java index 4d9e6031a..3a1a4c550 100644 --- a/src/main/java/au/org/ala/biocache/config/SecurityConfig.java +++ b/src/main/java/au/org/ala/biocache/config/SecurityConfig.java @@ -4,6 +4,7 @@ import org.pac4j.core.config.Config; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; @@ -11,6 +12,7 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.core.GrantedAuthorityDefaults; import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; import javax.inject.Inject; @@ -37,4 +39,9 @@ protected void configure(HttpSecurity http) throws Exception { ).permitAll() .and().csrf().disable(); } + + @Bean + public GrantedAuthorityDefaults grantedAuthorityDefaults() { + return new GrantedAuthorityDefaults(""); + } } diff --git a/src/main/java/au/org/ala/biocache/service/SpeciesImageService.java b/src/main/java/au/org/ala/biocache/service/SpeciesImageService.java index 714550a99..3b8e4eb98 100644 --- a/src/main/java/au/org/ala/biocache/service/SpeciesImageService.java +++ b/src/main/java/au/org/ala/biocache/service/SpeciesImageService.java @@ -22,6 +22,7 @@ import au.org.ala.biocache.util.SearchUtils; import au.org.ala.biocache.util.solr.FieldMappingUtil; import com.fasterxml.jackson.core.type.TypeReference; +import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Logger; import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.common.util.SimpleOrderedMap; @@ -31,10 +32,7 @@ import org.springframework.stereotype.Component; import javax.inject.Inject; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; /** * cache of lft with the first found image info; data_resource_uid, image_url and number found. @@ -81,27 +79,26 @@ public void run() { params.setFl(OccurrenceIndex.DATA_RESOURCE_UID + "," + OccurrenceIndex.IMAGE_URL); params.setQ(OccurrenceIndex.IMAGE_URL + ":*"); - QueryResponse qr = searchDAO.searchGroupedFacets(params); + String [] requiredFqsArray = new String[0]; + if (StringUtils.isNotEmpty(requiredFqs)) { + requiredFqsArray = requiredFqs.split(","); + params.setFq(requiredFqsArray); + } + + String [] preferredFqsArray = new String[0]; + if (StringUtils.isNotEmpty(preferredFqs)) { + preferredFqsArray = preferredFqs.split(","); + } + // fill map with reverse priority search so lower priority entries are overridden by higher priorities Map map = new HashMap(); - - for (SimpleOrderedMap item : SearchUtils.getList(qr.getResponse(), "facets", OccurrenceIndex.LFT, "buckets")) { - String dataResourceUid = (String) SearchUtils.getVal(item, OccurrenceIndex.DATA_RESOURCE_UID, "buckets", 0, 0); - String imageUrl = (String) SearchUtils.getVal(item, OccurrenceIndex.IMAGE_URL, "buckets", 0, 0); - SpeciesImageDTO image = new SpeciesImageDTO(dataResourceUid, imageUrl); - if (item.getVal(1) instanceof Integer) { - image.setCount(((Integer) item.getVal(1)).longValue()); - } else if (item.getVal(1) instanceof Long){ - image.setCount((Long) item.getVal(1)); - } - try { - if (item.getVal(0) instanceof Integer) { - map.put(((Integer) item.getVal(0)).longValue(), image); - } else if(item.getVal(0) instanceof Long) { - map.put((Long) item.getVal(0), image); - } - } catch (Exception e) { - } + fillMap(map, params); // request with no preferredFq + + String [] fqs = Arrays.copyOf(requiredFqsArray, requiredFqsArray.length + 1); + for (int i = preferredFqsArray.length - 1; i >= 0; i--) { + fqs[fqs.length - 1] = preferredFqsArray[i]; + params.setFq(fqs); + fillMap(map, params); } //sort keys @@ -135,6 +132,28 @@ public void run() { } } + private void fillMap(Map map, SpatialSearchRequestDTO params) throws Exception { + QueryResponse qr = searchDAO.searchGroupedFacets(params); + for (SimpleOrderedMap item : SearchUtils.getList(qr.getResponse(), "facets", OccurrenceIndex.LFT, "buckets")) { + String dataResourceUid = (String) SearchUtils.getVal(item, OccurrenceIndex.DATA_RESOURCE_UID, "buckets", 0, 0); + String imageUrl = (String) SearchUtils.getVal(item, OccurrenceIndex.IMAGE_URL, "buckets", 0, 0); + SpeciesImageDTO image = new SpeciesImageDTO(dataResourceUid, imageUrl); + if (item.getVal(1) instanceof Integer) { + image.setCount(((Integer) item.getVal(1)).longValue()); + } else if (item.getVal(1) instanceof Long){ + image.setCount((Long) item.getVal(1)); + } + try { + if (item.getVal(0) instanceof Integer) { + map.put(((Integer) item.getVal(0)).longValue(), image); + } else if(item.getVal(0) instanceof Long) { + map.put((Long) item.getVal(0), image); + } + } catch (Exception e) { + } + } + } + @EventListener(ApplicationReadyEvent.class) public void init() { resetCache(); @@ -146,6 +165,14 @@ public void init() { @Value("${autocomplete.species.images.enabled:true}") private Boolean enabled; + // images.preferredFilters=data_resource_uid:dr660 OR data_resource_uid:dr413,-record_type:PreservedSpecimen + @Value("${images.preferredFqs:}") + private String preferredFqs; + + // images.requiredFilters=spatiallyValid:true,-user_assertions:50001,-user_assertions:50005 + @Value("${images.requiredFqs:}") + private String requiredFqs; + /** * retrieve left + count + index version * diff --git a/src/main/java/au/org/ala/biocache/web/AssertionController.java b/src/main/java/au/org/ala/biocache/web/AssertionController.java index 84fff3bdd..fd1c1e6be 100644 --- a/src/main/java/au/org/ala/biocache/web/AssertionController.java +++ b/src/main/java/au/org/ala/biocache/web/AssertionController.java @@ -470,7 +470,7 @@ List getAssertionQueries( } @SecurityRequirement(name="JWT") - @Secured({"ROLE_ADMIN"}) + @Secured({"ROLE_ADMIN", "ala/internal"}) @Operation(summary = "Synchronise assertions into the index", tags = "Monitoring") @RequestMapping(value = {"/sync"}, method = RequestMethod.GET) public @ResponseBody Boolean indexAll(HttpServletRequest request, diff --git a/src/main/java/au/org/ala/biocache/web/DownloadController.java b/src/main/java/au/org/ala/biocache/web/DownloadController.java index 4bffb07a3..4dab434a8 100644 --- a/src/main/java/au/org/ala/biocache/web/DownloadController.java +++ b/src/main/java/au/org/ala/biocache/web/DownloadController.java @@ -102,7 +102,7 @@ public class DownloadController extends AbstractSecureController { */ @Deprecated @SecurityRequirement(name="JWT") - @Secured({"ROLE_ADMIN"}) + @Secured({"ROLE_ADMIN", "ala/internal"}) @Operation(summary = "Retrieves all the downloads that are on the queue", tags = "Monitoring") @RequestMapping(value = {"occurrences/offline/download/stats"}, method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public @ResponseBody Map> getCurrentDownloads() throws Exception { @@ -257,7 +257,7 @@ private DownloadStatusDTO download(DownloadRequestDTO requestParams, } @SecurityRequirement(name="JWT") - @Secured({"ROLE_ADMIN"}) + @Secured({"ROLE_ADMIN", "ala/internal"}) @Operation(summary = "List all occurrence downloads", tags = "Monitoring") @RequestMapping(value = {"occurrences/offline/status/all"}, method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public @ResponseBody Map> allOccurrenceDownloadStatus() { @@ -270,7 +270,7 @@ private DownloadStatusDTO download(DownloadRequestDTO requestParams, } @SecurityRequirement(name="JWT") - @Secured({"ROLE_USER"}) + @Secured({"ROLE_USER", "ala/internal"}) @Operation(summary = "List all occurrence downloads by the current user", tags = "Monitoring") @RequestMapping(value = {"occurrences/offline/status"}, method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public @ResponseBody Map> allOccurrenceDownloadStatusForUser( @@ -410,7 +410,7 @@ private DownloadStatusDTO getOtherStatus(String id) { * @throws Exception */ @SecurityRequirement(name="JWT") - @Secured({"ROLE_USER"}) + @Secured({"ROLE_USER", "ala/internal"}) @Operation(summary = "Cancel an offline download", tags = "Monitoring") @RequestMapping(value = {"occurrences/offline/cancel/{id}"}, method = RequestMethod.GET) @ApiParam(value = "id", required = true) diff --git a/src/main/java/au/org/ala/biocache/web/OccurrenceController.java b/src/main/java/au/org/ala/biocache/web/OccurrenceController.java index 7a92a31e7..5ae9a7f9f 100644 --- a/src/main/java/au/org/ala/biocache/web/OccurrenceController.java +++ b/src/main/java/au/org/ala/biocache/web/OccurrenceController.java @@ -276,7 +276,7 @@ Map emptyJson() { return map; } - @Secured({"ROLE_ADMIN"}) + @Secured({"ROLE_ADMIN", "ala/internal"}) @SecurityRequirement(name = "JWT") @Operation(summary = "Get list of current downloads", tags = "Monitoring") @Tag(name = "Monitoring", description = "Admin services for monitoring the application, download stats, and index. Protected APIs require administrative role for access.") @@ -525,7 +525,7 @@ public void getIndexedFields( * @return * @throws Exception */ - @Secured({"ROLE_ADMIN"}) + @Secured({"ROLE_ADMIN", "ala/internal"}) @SecurityRequirement(name = "JWT") @Operation(summary = "Show index version information", tags = "Monitoring") @RequestMapping(value = {"index/version"}, method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) @@ -549,7 +549,7 @@ Map getIndexedFields(@RequestParam(value = "force", required = false, defaultVal * @return * @throws Exception */ - @Secured({"ROLE_ADMIN"}) + @Secured({"ROLE_ADMIN", "ala/internal"}) @SecurityRequirement(name = "JWT") @Operation(summary = "Show configured max boolean clauses", tags = "Monitoring") @RequestMapping(value = { @@ -562,8 +562,7 @@ Map getIndexedFields(@RequestParam(value = "force", required = false, defaultVal return map; } - - @Secured({"ala/internal"}) + @Secured({"ROLE_ADMIN", "ala/internal"}) @SecurityRequirement(name = "JWT") @Operation(summary = "Export of lft,image", tags = "Monitoring") @RequestMapping(value = {"index/speciesImages"}, method = RequestMethod.GET) @@ -571,6 +570,7 @@ Map getIndexedFields(@RequestParam(value = "force", required = false, defaultVal return speciesImageService.getSpeciesImages(); } + @Secured({"ROLE_ADMIN", "ala/internal"}) @SecurityRequirement(name = "JWT") @Operation(summary = "Export of lft,count", tags = "Monitoring") @RequestMapping(value = {"index/speciesOccurrences"}, method = RequestMethod.GET) @@ -578,7 +578,7 @@ Map getIndexedFields(@RequestParam(value = "force", required = false, defaultVal return speciesCountsService.getCounts(null); } - @Secured({"ROLE_ADMIN"}) + @Secured({"ROLE_ADMIN", "ala/internal"}) @SecurityRequirement(name = "JWT", scopes = {"ROLE_ADMIN"}) @Operation(summary = "Show configuration", tags = "Monitoring", description = " Public service that reports limits and other useful config for clients." @@ -951,7 +951,7 @@ SearchResultDTO occurrenceSearch( * @return * @throws Exception */ - @Secured({"ROLE_ADMIN"}) + @Secured({"ROLE_ADMIN", "ala/internal"}) @SecurityRequirement(name = "JWT") @Operation(summary = "Refresh caches", tags = "Monitoring") @RequestMapping(value = {"/cache/refresh"}, method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) diff --git a/src/main/java/au/org/ala/biocache/web/WMSController.java b/src/main/java/au/org/ala/biocache/web/WMSController.java index cad23866c..7b58b0b65 100644 --- a/src/main/java/au/org/ala/biocache/web/WMSController.java +++ b/src/main/java/au/org/ala/biocache/web/WMSController.java @@ -671,7 +671,7 @@ public SearchResultDTO occurrences( * @throws Exception */ @SecurityRequirement(name="JWT") - @Secured({"ROLE_USER", "ROLE_ADMIN"}) + @Secured({"ROLE_USER", "ROLE_ADMIN", "ala/internal"}) @Operation(summary = "Get occurrences by query as gzipped csv.", tags = "Deprecated") @Deprecated @RequestMapping(value = { diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 594a538ea..9084be673 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -8,6 +8,8 @@ application-version = 3.0 application-title = Occurrence services application-terms-url = https://www.ala.org.au +security.oidc.enabled=true + #spring.security.jwt.enabled = true #spring.security.jwt.jwk.url = https://auth-dev.ala.org.au/cas/oidc/jwks #spring.security.legacy.apikey.serviceUrl = https://auth-test.ala.org.au/apikey/ws/check?apikey= @@ -25,4 +27,7 @@ spring.autoconfigure.exclude = org.springframework.boot.autoconfigure.web.servle # #spring.security.user.name=admin #spring.security.user.password=admin -#spring.security.user.roles=manager \ No newline at end of file +#spring.security.user.roles=manager + +# authorized role and scope for JWT authorized requests +admin.roles=ROLE_ADMIN,ala/internal