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

#236 remove API keys #264

Merged
merged 1 commit into from
Feb 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 0 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ The default develop url is http://devt.ala.org.au:8080/ws

#### Minimum configurations in external config file:

api_key: xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx
google:
apikey: xxxxxxxxxxxxxx

Expand All @@ -50,7 +49,6 @@ The default develop url is http://devt.ala.org.au:8080/ws
google:
apikey: "xxxxxxxxxxxx"

api_key: xxxxxxxxxxxxx
spatialHubUrl: https://spatial-test.ala.org.au/

geoserver:
Expand All @@ -70,9 +68,6 @@ The default develop url is http://devt.ala.org.au:8080/ws
shp2pgsql.path: "/usr/bin/shp2pgsql"
gdal.dir: "/usr/bin/"

slaveKey: "xxxxxxxxxxxxxx"
serviceKey: "xxxxxxxxxxxxxx"

layers_store.GEONETWORK_URL: 'https://spatial-test.ala.org.au/geonetwork'

# Installation
Expand Down
23 changes: 2 additions & 21 deletions grails-app/conf/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -139,13 +139,9 @@ biocacheUrl: 'https://biocache.ala.org.au'
openstreetmap:
url: 'https://spatial.ala.org.au/osm'

slave.enable: true
service.enable: true

spatialService:
url: "https://spatial.ala.org.au/ws"

serviceKey: ""
batch_sampling_passwords: ''
batch_sampling_points_limit: 1000000
batch_sampling_fields_limit: 1000
Expand All @@ -154,9 +150,6 @@ grid_buffer_size: 40960
occurrence_species_records_filename: /data/ala/data/layers/process/density/current/records

---
#
# au.org.ala.spatial.slave config
#
spatialService.url: "http://localhost:8080/ws"
data.dir: "/data/spatial-data"
shp2pgsql.path: "/usr/bin/shp2pgsql"
Expand All @@ -181,9 +174,6 @@ maxent.threads: 4

sampling.threads: 4

slaveKey: ""
serviceKey: ""

# time between pushing status updates to the master for a task
statusTime: 5000
retryCount: 10
Expand Down Expand Up @@ -237,20 +227,12 @@ security:
core:
roleAttribute: ala:role
apikey:
enabled: true
enabled: false
auth:
serviceUrl: https://auth-test.ala.org.au/apikey/
check:
serviceUrl: https://auth-test.ala.org.au/apikey/ws/check?apikey=
userdetails:
serviceUrl: https://auth-test.ala.org.au/userdetails/

#webservice:
# jwt: true
# jwt-scopes: openid users/read
# client-id: second-client-id-for-machine-to-machine-comms
# client-secret: second-client-secret-for-machine-to-machine-comms

auth.admin_role: "ROLE_ADMIN"
app.http.header.userId: "X-ALA-userId"

Expand All @@ -264,7 +246,6 @@ namematching.url: 'https://namematching-ws.ala.org.au'

records.url: 'https://archives.ala.org.au/archives/exports/lat_lon_taxon.zip'

api_key: ''
lists.url: 'https://lists.ala.org.au'
collections.url: 'https://collections.ala.org.au'
sandboxHubUrl: 'https://sandbox.ala.org.au/ala-hub'
Expand Down Expand Up @@ -402,7 +383,7 @@ environments:
dir: "/Library/Frameworks/GDAL.framework/Programs"

spatialHubUrl: "http://devt.ala.org.au:8079/"
# Slave config

spatialService:
url: "http://devt.ala.org.au:8081/ws"
remote: "https://spatial-test.ala.org.au/ws"
Expand Down
13 changes: 9 additions & 4 deletions grails-app/controllers/au/org/ala/spatial/LogController.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

package au.org.ala.spatial


import au.ala.org.ws.security.RequireApiKey
import au.org.ala.plugins.openapi.Path
import au.org.ala.web.AuthService
import com.opencsv.CSVWriter
Expand Down Expand Up @@ -64,18 +64,22 @@ class LogController {
)
@Path("/log")
@Transactional
@RequireApiKey
def index() {
Map emptyMap = [:]
try {
def lg = new Log(request.JSON as Map)
if (!lg.save()) {
lg.errors.each {
log.error(it)
}
}
render status: 200
response.status = 200
render emptyMap as JSON
} catch (Exception e) {
log.warn("log info is broken, ignored! " + e.getMessage())
render status: 400
response.status = 400
render emptyMap as JSON
}
}

Expand Down Expand Up @@ -129,7 +133,7 @@ class LogController {
@Parameter(
name = "admin",
in = QUERY,
description = "When true, return results for all users if request has a valid api_key or admin role.",
description = "When true, return results for all users if request has an admin role.",
required = false,
example = "true"
),
Expand Down Expand Up @@ -188,6 +192,7 @@ class LogController {
)
@Path("/log/search")
@Produces("application/json")
@RequireApiKey
def search() {
def searchResult = logService.search(params, authService.getUserId(), spatialAuthService.userInRole(spatialConfig.auth.admin_role))
def totalCount = logService.searchCount(params, authService.getUserId(), spatialAuthService.userInRole(spatialConfig.auth.admin_role))
Expand Down
78 changes: 3 additions & 75 deletions grails-app/controllers/au/org/ala/spatial/LoginInterceptor.groovy
Original file line number Diff line number Diff line change
@@ -1,30 +1,12 @@
package au.org.ala.spatial

import au.org.ala.spatial.RequireAdmin
import au.org.ala.spatial.RequireLogin
import au.org.ala.spatial.RequirePermission
import au.org.ala.spatial.SkipSecurityCheck
import au.org.ala.spatial.SpatialConfig
import au.org.ala.web.AuthService
import com.google.common.base.Strings
import grails.converters.JSON

/**
* Copy and simplify from plugin "ala-ws-security-plugin:2.0"
* Remove IP/method/controller whitelist support
* collecting api_key from POST body (backward compatiblity support)
*
* TODO use ALA standard apiKey method: store apiKey in hearder
*/
import org.grails.web.util.WebUtils

import java.text.MessageFormat

class LoginInterceptor {
static final int STATUS_UNAUTHORISED = 401
static final int STATUS_FORBIDDEN = 403
static final String[] USERID_HEADER_NAME = ["X-ALA-userId", "userId", "user_id"]
static final String[] API_KEY_HEADER_NAME = ["apiKey", "api_key", "api-key"]

int order = LOWEST_PRECEDENCE

Expand All @@ -42,8 +24,6 @@ class LoginInterceptor {
return true
}

def isAdmin = spatialAuthService.userInRole(spatialConfig.auth.admin_role)

def controller = grailsApplication.getArtefactByLogicalPropertyName("Controller", controllerName)
Class controllerClass = controller?.clazz
def method = controllerClass?.getMethod(actionName ?: "index", [] as Class[])
Expand All @@ -55,18 +35,14 @@ class LoginInterceptor {
//Calculating the required permission.
def permissionLevel = null
//Permission on method has the top priority
if (method?.isAnnotationPresent(RequirePermission.class)) {
permissionLevel = RequirePermission
} else if (method?.isAnnotationPresent(RequireLogin.class)) {
if (method?.isAnnotationPresent(RequireLogin.class)) {
permissionLevel = RequireLogin
} else if (method?.isAnnotationPresent(RequireAdmin.class)) {
permissionLevel = RequireAdmin
}

if (Objects.isNull(permissionLevel)) {
if (controllerClass?.isAnnotationPresent(RequirePermission.class)) {
permissionLevel = RequirePermission
} else if (controllerClass?.isAnnotationPresent(RequireLogin.class)) {
if (controllerClass?.isAnnotationPresent(RequireLogin.class)) {
permissionLevel = RequireLogin
} else if (controllerClass?.isAnnotationPresent(RequireAdmin.class)) {
permissionLevel = RequireAdmin
Expand All @@ -75,15 +51,7 @@ class LoginInterceptor {

//Permission check
def role // if require a certain level of ROLE
if (hasValidApiKey()) {
return true
} else if (permissionLevel == RequirePermission) {
if (authService.getUserId()) {
return true
} else {
return accessDenied(STATUS_UNAUTHORISED, 'Forbidden, ApiKey or user login required!')
}
} else if (permissionLevel == RequireAdmin) {
if (permissionLevel == RequireAdmin) {
role = spatialConfig.auth.admin_role
} else if (permissionLevel == RequireLogin) {
RequireLogin requireAuthentication = method.getAnnotation(RequireLogin.class)
Expand Down Expand Up @@ -126,44 +94,4 @@ class LoginInterceptor {
return false
}
}

def hasValidApiKey() {
isValid(getApiKey())
}

def isValid(key) {
if (key == null) {
return false
}

Boolean result = testedKeys.get(key)

if (result == null) {
String url = MessageFormat.format(grailsApplication.config.apiKeyCheckUrlTemplate.toString(), key)

result = key == grailsApplication.config.serviceKey || Util.getUrl(url).contains('"valid":true')
testedKeys.put(key, result)
}

return result
}

private getApiKey() {
String apikey
def request = WebUtils.retrieveGrailsWebRequest().getCurrentRequest()

for (name in API_KEY_HEADER_NAME) {
if (request.getHeader(name)) {
return request.getHeader(name).split(",|;")[0]
}
if (request.getParameter(name)) {
return request.getParameter(name).split(",|;")[0]
}
}

//Last try
apikey = request.JSON?.api_key

apikey
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import javax.servlet.http.HttpServletResponse
import javax.transaction.Transactional
import java.text.SimpleDateFormat

@RequirePermission
@Transactional
class ManageLayersController {

Expand All @@ -41,10 +40,11 @@ class ManageLayersController {
AuthService authService

/**
* admin only or api_key
* admin only
*
* @return
*/
@RequireAdmin
def index() {
}

Expand All @@ -53,6 +53,7 @@ class ManageLayersController {
*
* @return
*/
@RequireAdmin
def layers() {
log.debug("List avaliable layers")
Map map = [:]
Expand Down Expand Up @@ -121,7 +122,6 @@ class ManageLayersController {
*
* @return
*/

@RequireAdmin
def uploads() {
Map map = [:]
Expand Down Expand Up @@ -174,7 +174,6 @@ class ManageLayersController {
* admin only
*
* @param req
* @param apiKey
* @return
* @throws Exception
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -647,12 +647,9 @@ class ShapesController {
JSONRequestBodyParser reqBodyParser = new JSONRequestBodyParser()
reqBodyParser.addParameter("user_id", String.class, false)
reqBodyParser.addParameter("shp_file_url", String.class, false)
reqBodyParser.addParameter("api_key", String.class, false)

if (reqBodyParser.parseJSON(jsonRequestBody)) {

String shpFileUrl = (String) reqBodyParser.getParsedValue("shp_file_url")
String apiKey = (String) reqBodyParser.getParsedValue("api_key")

// Use shape file url from json body
FileUtils.copyURLToFile(new URL(shpFileUrl), tmpZipFile)
Expand Down
Loading