From 75ed00c62f655ba61a8cd5ec68f0da73519f0f57 Mon Sep 17 00:00:00 2001 From: Chris Date: Tue, 26 Sep 2023 11:07:30 +1000 Subject: [PATCH] Add JWT support for spatial calls #2992 --- .../au/org/ala/merit/SpatialService.groovy | 11 ++-- .../au/org/ala/merit/WebService.groovy | 59 +++++++++++++++---- 2 files changed, 52 insertions(+), 18 deletions(-) diff --git a/grails-app/services/au/org/ala/merit/SpatialService.groovy b/grails-app/services/au/org/ala/merit/SpatialService.groovy index 24d50b749..caf99b11f 100644 --- a/grails-app/services/au/org/ala/merit/SpatialService.groovy +++ b/grails-app/services/au/org/ala/merit/SpatialService.groovy @@ -26,7 +26,7 @@ class SpatialService { Integer deleteFromSpatialPortal(String pid) { String url = "${grailsApplication.config.getProperty('spatial.layersUrl')}${DELETE_SHAPE_PATH}/$pid" - webService.doDelete(url) + webService.doDelete(url, true) } /** @@ -41,10 +41,9 @@ class SpatialService { * attributes of that feature. eg. [shp_id: , "0":[attribute1:, attribute2:, etc], "1":[attribute1:, attribute2:, etc]]] */ Map uploadShapefile(MultipartFile shapefile) { - String userId = userService.getCurrentUserId() - String url = "${grailsApplication.config.getProperty('spatial.layersUrl')}${UPLOAD_SHAPE_PATH}?user_id=${userId}&api_key=${grailsApplication.config.getProperty('api_key')}" + String url = "${grailsApplication.config.getProperty('spatial.layersUrl')}${UPLOAD_SHAPE_PATH}" - webService.postMultipart(url, [:], shapefile) + webService.postMultipart(url, [:], shapefile, 'files', true) } /** @@ -62,11 +61,11 @@ class SpatialService { String baseUrl = "${grailsApplication.config.getProperty('spatial.layersUrl')}${UPLOAD_SHAPE_PATH}" String userId = userService.getCurrentUserId() - Map site = [name:objectName, description: objectDescription, user_id:userId, api_key:grailsApplication.config.getProperty('api_key')] + Map site = [name:objectName, description: objectDescription, user_id:userId] String url = "${baseUrl}/${shapeFileId}/${featureId}" - webService.doPost(url, site) + webService.doPost(url, site, true) } /** diff --git a/grails-app/services/au/org/ala/merit/WebService.groovy b/grails-app/services/au/org/ala/merit/WebService.groovy index 61791a671..9a69a1a29 100644 --- a/grails-app/services/au/org/ala/merit/WebService.groovy +++ b/grails-app/services/au/org/ala/merit/WebService.groovy @@ -15,6 +15,7 @@ package au.org.ala.merit +import au.org.ala.ws.tokens.TokenService import grails.converters.JSON import grails.core.GrailsApplication import grails.web.http.HttpHeaders @@ -40,6 +41,8 @@ import javax.servlet.http.HttpServletResponse @Slf4j class WebService { + TokenService tokenService + // Used to avoid a circular dependency during initialisation def getUserService() { return grailsApplication.mainContext.userService @@ -99,9 +102,16 @@ class WebService { grailsApplication.config.getProperty('webservice.connectTimeout', Integer, 2000) } - private URLConnection configureConnection(String url, boolean includeUserId, Integer timeout = null) { + private URLConnection configureConnection(String url, boolean includeUserId, Integer timeout = null, boolean useToken = false) { URLConnection conn = createAndConfigureConnection(url, timeout) - conn.setRequestProperty("Authorization", grailsApplication.config.getProperty('api_key')); + if (!useToken) { + conn.setRequestProperty("Authorization", grailsApplication.config.getProperty('api_key')) + } + else { + conn.setRequestProperty("Authorization", getToken()) + } + + def user = getUserService().getUser() if (user) { conn.setRequestProperty(grailsApplication.config.getProperty('app.http.header.userId'), user.userId) @@ -109,6 +119,10 @@ class WebService { conn } + private String getToken() { + tokenService.getAuthToken(false)?.toAuthorizationHeader() + } + private URLConnection createAndConfigureConnection(String url, Integer timeout = null) { URLConnection conn = new URL(url).openConnection() @@ -352,20 +366,26 @@ class WebService { } } - def doPost(String url, Map postBody) { + def doPost(String url, Map postBody, boolean useToken = false) { def conn = null def charEncoding = 'utf-8' try { conn = new URL(url).openConnection() conn.setDoOutput(true) conn.setRequestProperty("Content-Type", "application/json;charset=${charEncoding}"); - conn.setRequestProperty("Authorization", grailsApplication.config.getProperty('api_key')); - + if (useToken) { + conn.setRequestProperty("Authorization", getToken()) + } + else { + conn.setRequestProperty("Authorization", grailsApplication.config.getProperty('api_key')); + } def user = getUserService().getUser() if (user) { conn.setRequestProperty(grailsApplication.config.getProperty('app.http.header.userId'), user.userId) // used by ecodata conn.setRequestProperty("Cookie", "ALA-Auth="+java.net.URLEncoder.encode(user.userName, charEncoding)) // used by specieslist } + + OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream(), charEncoding) wr.write((postBody as JSON).toString()) wr.flush() @@ -389,17 +409,26 @@ class WebService { } } - def doDelete(String url) { - url += (url.indexOf('?') == -1 ? '?' : '&') + "api_key=${grailsApplication.config.getProperty('api_key')}" + def doDelete(String url, boolean useToken = false) { + if (!useToken) { + url += (url.indexOf('?') == -1 ? '?' : '&') + "api_key=${grailsApplication.config.getProperty('api_key')}" + } + def conn = null try { conn = new URL(url).openConnection() conn.setRequestMethod("DELETE") - conn.setRequestProperty("Authorization", grailsApplication.config.getProperty('api_key')); + if (useToken) { + conn.setRequestProperty("Authorization", getToken()) + } + else { + conn.setRequestProperty("Authorization", grailsApplication.config.getProperty('api_key')) + } def user = getUserService().getUser() if (user) { conn.setRequestProperty(grailsApplication.config.getProperty('app.http.header.userId'), user.userId) } + return conn.getResponseCode() } catch(Exception e){ println e.message @@ -418,9 +447,9 @@ class WebService { * @param file the Multipart file object to forward. * @return [status:, content: */ - def postMultipart(url, Map params, MultipartFile file, fileParam = 'files') { + def postMultipart(url, Map params, MultipartFile file, fileParam = 'files', boolean useToken = false) { - postMultipart(url, params, file.inputStream, file.contentType, file.originalFilename, fileParam) + postMultipart(url, params, file.inputStream, file.contentType, file.originalFilename, fileParam, null, useToken) } /** @@ -434,7 +463,7 @@ class WebService { * @param successHandler optional callback for a successful service invocation. If not supplied, a Map will be returned. * @return [status:, content: */ - def postMultipart(url, Map params, InputStream contentIn, contentType, originalFilename, fileParamName = 'files', Closure successHandler = null) { + def postMultipart(url, Map params, InputStream contentIn, contentType, originalFilename, fileParamName = 'files', Closure successHandler = null, boolean useToken = false) { def result = [:] def user = userService.getUser() @@ -449,13 +478,19 @@ class WebService { content.addPart(key, new StringBody(value.toString())) } } - headers.'Authorization' = grailsApplication.config.getProperty('api_key') + if (useToken) { + headers.'Authorization' = getToken() + } + else { + headers.'Authorization' = grailsApplication.config.getProperty('api_key') + } if (user) { headers[grailsApplication.config.getProperty('app.http.header.userId')] = user.userId } else { log.warn("No user associated with request: ${url}") } + request.setEntity(content) if (successHandler) {