Skip to content

Commit

Permalink
Merge pull request #162 from Open-EO/develop
Browse files Browse the repository at this point in the history
Release v1.4.0
  • Loading branch information
flahn authored Jan 21, 2025
2 parents 732d8a0 + a398aab commit 5e29445
Show file tree
Hide file tree
Showing 61 changed files with 945 additions and 678 deletions.
6 changes: 3 additions & 3 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
Package: openeo
Type: Package
Title: Client Interface for 'openEO' Servers
Version: 1.3.1
Version: 1.4.0
Authors@R: c(
person(given="Florian", family="Lahn", email = "florian.lahn@eftas.com", role = c("aut", "cre")),
person(given=c("Peter","James"),family = "Zellner", email="peterjames.zellner@eurac.edu",role=c("ctb")),
person(given="Matthias",family = "Mohr", email="matthias.mohr@uni-muenster.de",role=c("ctb"))
person(given="Matthias",family = "Mohr", email="m.mohr@uni-muenster.de",role=c("ctb"))
)
Description: Access data and processing functionalities of 'openEO' compliant back-ends in R.
Depends:
Expand Down Expand Up @@ -36,7 +36,7 @@ Encoding: UTF-8
LazyData: false
BugReports: https://github.com/Open-EO/openeo-r-client/issues
URL: https://open-eo.github.io/openeo-r-client/, https://github.com/Open-EO/openeo-r-client
RoxygenNote: 7.3.1
RoxygenNote: 7.3.2
VignetteBuilder: knitr
License: Apache License (>= 2)
Collate:
Expand Down
20 changes: 20 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
# Version 1.4.0

## Added

* Support for the OpenID Connect Client Credentials flow
* compute_result, create_job, update_job, create_service and update_service have an additional parameter to inject custom properties into the request (e.g., to set memory or CPU limits)
* Implement the datacube subtype through the new DataCube class (which RasterCube and VectorCube inherit from now)

## Changed

* OpenID Connect scopes can be provided as lists
* changed authentication setup for OIDC to instantiate a workflow object with a preconfigured `Provider` class

## Fixed

* `describe_collection` and `collection_viewer` accept string IDs again
* fixed issues when creating an `Argument` without schema
* replaced `is.na()` with `rlang::is_na()` in several occurrences in the `$typeCheck()` functions of argument classes, because is.na on lists and objects returns more than one result
* fixed `Number` validation with Argument `ProcessNode`

# Version 1.3.1

## Added
Expand Down
642 changes: 342 additions & 300 deletions R/argument_types.R

Large diffs are not rendered by default.

95 changes: 55 additions & 40 deletions R/authentication.R
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ IAuth <- R6Class(
#' \item{`password`}{the user password}
#' }
#'
#' @return an object of type [R6Class()] representing basic authentication
#' @return an object of type [R6::R6Class()] representing basic authentication
#' @importFrom R6 R6Class
NULL

Expand Down Expand Up @@ -168,6 +168,8 @@ BasicAuth <- R6Class(
#' \itemize{
#' \item{authorization_code}
#' \item{authorization_code+pkce}
#' \item{client_credentials}
#' \item{urn:ietf:params:oauth:grant-type:device_code}
#' \item{urn:ietf:params:oauth:grant-type:device_code+pkce}
#' }
#'
Expand Down Expand Up @@ -225,7 +227,9 @@ AbstractOIDCAuthentication <- R6Class(
args = list(...)
if ("force" %in% names(args)) private$force_use = args[["force"]]
# comfort function select provider by name if one is provided
provider = .get_oidc_provider(provider)
if (class(provider) != "Provider") {
provider = .get_oidc_provider(provider)
}

private$setIssuer(provider$issuer)

Expand All @@ -246,15 +250,19 @@ AbstractOIDCAuthentication <- R6Class(
}

# user knows best, allow custom scopes...
if (length(config$scopes) > 0 && is.character(config$scopes)) {
private$scopes = config$scopes
if (length(config$scopes) > 0) {
if (is.character(config$scopes)) {
private$scopes = list(config$scopes)
} else {
private$scopes = config$scopes
}
} else if (length(provider$scopes) == 0) {
private$scopes = list("openid")
} else {
private$scopes = provider$scopes

#TODO remove later, this is used for automatic reconnect
if (!"offline_access" %in% private$scopes) {
if (private$grant_type != "client_credentials" && !"offline_access" %in% private$scopes) {
private$scopes = c(private$scopes, "offline_access")
}
}
Expand All @@ -267,8 +275,8 @@ AbstractOIDCAuthentication <- R6Class(

private$client_id = config$client_id

if (private$grant_type == "authorization_code") {
# in this case we need a client_id and secrect, which is basically the old OIDC Auth Code implementation
if (private$grant_type == "authorization_code" || private$grant_type == "client_credentials") {
# in this case we need a client_id and secrect
if (!all(c("client_id","secret") %in% names(config))) {
stop("'client_id' and 'secret' are not present in the configuration.")
}
Expand All @@ -280,7 +288,6 @@ AbstractOIDCAuthentication <- R6Class(
secret = config$secret
)
} else {

private$oauth_client = oauth_client(
id = private$client_id,
token_url = private$endpoints$token_endpoint,
Expand Down Expand Up @@ -446,16 +453,9 @@ OIDCDeviceCodeFlow <- R6Class(
public = list(
# functions ####
login = function() {

client <- oauth_client(
id = private$client_id,
token_url = private$endpoints$token_endpoint,
name = "openeo-r-oidc-auth"
)

private$auth = rlang::with_interactive(
oauth_flow_device(
client = client,
client = private$oauth_client,
auth_url = private$endpoints$device_authorization_endpoint,
scope = paste0(private$scopes, collapse = " ")
),
Expand Down Expand Up @@ -488,16 +488,9 @@ OIDCDeviceCodeFlowPkce <- R6Class(
public = list(
# functions ####
login = function() {

client <- oauth_client(
id = private$client_id,
token_url = private$endpoints$token_endpoint,
name = "openeo-r-oidc-auth"
)

private$auth = rlang::with_interactive(
oauth_flow_device(
client = client,
client = private$oauth_client,
auth_url = private$endpoints$device_authorization_endpoint,
scope = paste0(private$scopes, collapse = " "),
pkce = TRUE
Expand Down Expand Up @@ -533,16 +526,9 @@ OIDCAuthCodeFlowPKCE <- R6Class(
# attributes ####
# functions ####
login = function() {

client <- oauth_client(
id = private$client_id,
token_url = private$endpoints$token_endpoint,
name = "openeo-r-oidc-auth"
)

private$auth = rlang::with_interactive(
oauth_flow_auth_code(
client = client,
client = private$oauth_client,
auth_url = private$endpoints$authorization_endpoint,
scope = paste0(private$scopes, collapse = " "),
pkce = TRUE,
Expand Down Expand Up @@ -580,15 +566,9 @@ OIDCAuthCodeFlow <- R6Class(

# functions ####
login = function() {
client <- oauth_client(
id = private$client_id,
token_url = private$endpoints$token_endpoint,
name = "openeo-r-oidc-auth"
)

private$auth = rlang::with_interactive(
oauth_flow_auth_code(
client = client,
client = private$oauth_client,
auth_url = private$endpoints$authorization_endpoint,
scope = paste0(private$scopes, collapse = " "),
pkce = FALSE,
Expand Down Expand Up @@ -616,8 +596,43 @@ OIDCAuthCodeFlow <- R6Class(
)
)

# [OIDCClientCredentialsFlow] ----
OIDCClientCredentialsFlow <- R6Class(
"OIDCClientCredentialsFlow",
inherit = AbstractOIDCAuthentication,
# public ====
public = list(
# functions ####
login = function() {
private$auth = oauth_flow_client_credentials(
client = private$oauth_client,
scope = paste0(private$scopes, collapse = " ")
)

invisible(self)
}
),
# private ====
private = list(
# attributes ####
grant_type = "client_credentials", # not used internally by httr2, but maybe useful in openeo

# functions ####
isGrantTypeSupported = function(grant_types) {
if (!"client_credentials" %in% grant_types) {
stop("Client Credentials flow is not supported by the authentication provider")
}
invisible(TRUE)
}
)
)

# utility functions ----
.get_oidc_provider = function(provider, oidc_providers=NULL) {
if (is.debugging()) {
if (class(provider) == "Provider") return(provider)
}

if (is.null(oidc_providers)) {
oidc_providers = list_oidc_providers()
}
Expand Down
48 changes: 30 additions & 18 deletions R/client.R
Original file line number Diff line number Diff line change
Expand Up @@ -369,15 +369,15 @@ OpenEOClient <- R6Class(
loginOIDC = function(provider = NULL, config = NULL) {
suppressWarnings({
tryCatch({


# old implementation
# probably fetch resolve the potential string into a provider here
provider = .get_oidc_provider(provider)

auth_code = "authorization_code"
auth_pkce = "authorization_code+pkce"
device_pkce = "urn:ietf:params:oauth:grant-type:device_code+pkce"
device_code = "urn:ietf:params:oauth:grant-type:device_code"
client_credentials = "client_credentials"

has_default_clients = "default_clients" %in% names(provider) && length(provider[["default_clients"]]) > 0
client_id_given = "client_id" %in% names(config)
Expand All @@ -388,14 +388,18 @@ OpenEOClient <- R6Class(
}

full_credentials = all(c("client_id","secret") %in% names(config))
is_auth_code = length(config$grant_type) > 0 && config$grant_type == 'authorization_code'
is_auth_code = length(config$grant_type) > 0 && config$grant_type == auth_code
is_client_credentials = length(config$grant_type) > 0 && config$grant_type == client_credentials

# either credentials are set and / or authorization_code as grant_type
if (full_credentials && (is_auth_code || is.null(config$grant_type))) {
# either credentials are set and / or authorization_code or client_credentials as grant_type
if (full_credentials && is_client_credentials) {
private$auth_client = OIDCClientCredentialsFlow$new(provider = provider, config = config, force=TRUE)
}
else if (full_credentials && (is_auth_code || is.null(config$grant_type))) {
private$auth_client = OIDCAuthCodeFlow$new(provider = provider, config = config, force=TRUE)
}
else if (is_auth_code) {
stop("For grant type 'authorization_code' a client_id and secret must be provided")
else if (is_auth_code || is_client_credentials) {
stop("For grant type 'authorization_code' and 'client_credentials' a client_id and secret must be provided")
}
else if (client_id_given && has_default_clients) {
default_clients = provider[["default_clients"]]
Expand All @@ -412,7 +416,7 @@ OpenEOClient <- R6Class(
}
}

if (has_default_clients && !client_id_given) {
if (is.null(private$auth_client) && has_default_clients && !client_id_given) {
default_clients = provider[["default_clients"]]

# check whether user has chosen a grant type
Expand All @@ -433,21 +437,24 @@ OpenEOClient <- R6Class(
}
}



if (is.null(config$client_id)) {
stop("Please provide a client id or a valid combination of client_id and grant_type.")
}
}

if (device_pkce == config$grant_type) {
private$auth_client = OIDCDeviceCodeFlowPkce$new(provider=provider, config = config)
} else if (device_code == config$grant_type) {
private$auth_client = OIDCDeviceCodeFlow$new(provider=provider, config = config)
} else if (is.null(config$grant_type) || auth_pkce == config$grant_type) {
private$auth_client = OIDCAuthCodeFlowPKCE$new(provider=provider, config = config)
}

if (is.null(private$auth_client)) {
has_grant = "grant_type" %in% names(config)
if (has_grant && device_pkce == config$grant_type) {
private$auth_client = OIDCDeviceCodeFlowPkce$new(provider=provider, config = config)
} else if (has_grant && device_code == config$grant_type) {
private$auth_client = OIDCDeviceCodeFlow$new(provider=provider, config = config)
} else if (has_grant && client_credentials == config$grant_type) {
private$auth_client = OIDCClientCredentialsFlow$new(provider=provider, config = config)
} else if (is.null(config$grant_type) || (has_grant && auth_pkce == config$grant_type)) {
private$auth_client = OIDCAuthCodeFlowPKCE$new(provider=provider, config = config)
}
}

if (is.null(private$auth_client)) {
stop("The grant_type selected is not supported")
}
Expand Down Expand Up @@ -475,8 +482,13 @@ OpenEOClient <- R6Class(
#endpoint,user,password
private$auth_client = BasicAuth$new(endpoint,user,password)

# user_id is probably user in BasicAuth, is this a relict from openeo 0.4?
self$user_id = private$auth_client$login()
cat("Login successful.\n")

# in case there was no user_id provided in the authentication, set this user as user_id
if (length(self$user_id)==0) self$user_id = user

return(invisible(self))

},
Expand Down
3 changes: 1 addition & 2 deletions R/collections.R
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,7 @@ describe_collection = function(collection = NA, con=NULL) {
return(NULL)
})

missing_collection = !(length(collection) > 0 && any(!is.na(collection)) && !(is.character(collection) && length(collection) == 1 && nchar(collection) > 0))

missing_collection = length(collection) == 0 || any(is.na(collection)) || (is.character(collection) && length(collection) == 1 && nchar(collection) == 0)
if (missing_collection) {
message("No or invalid collection id(s)")
invisible(NULL)
Expand Down
Loading

0 comments on commit 5e29445

Please sign in to comment.