Skip to content

Commit

Permalink
components$securitySchemes (#57)
Browse files Browse the repository at this point in the history
* Implement security_schemes.

Closes #45.

* Add work on oauth2 schemes.

* Use collate.

* Rename `in` param to "location".

* Clean test names.

* Document ouath classes.

* Lengths and as's.

* Refactor.

* Massive security_scheme reorg.

* Standardize to "description".

Vs "document" or "definition", per discussion on the open-api Slack.

* Ouath2 tests.

* Add as_security_scheme_collection()

And most of its infrastructure.

* Finish security_scheme_collection().

* Finish security_scheme_details().

* Finish as_security_scheme().

And standardize as_* documentation.

* Remove extra noise from tests.

* Use as_*() in constructors.

* Implement oauth2_security_scheme and friends.

* Abstract my general as_*() functions

* More work finishing auth components.

* Finish tests for flows.

* Finish security scheme tests.

* Add components.

* Note use of snakecase in as_* documentation.

* Log TODOs as issues on GH.

* Add components to rapid().
  • Loading branch information
jonthegeek authored Oct 4, 2023
1 parent 7cc6f6c commit 04659df
Show file tree
Hide file tree
Showing 97 changed files with 5,211 additions and 400 deletions.
1 change: 1 addition & 0 deletions .Rbuildignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@
^docs$
^pkgdown$
^principles\.md$
^exploration$
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
.DS_Store
.quarto
docs
exploration
35 changes: 31 additions & 4 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
Package: rapid
Title: R 'API' Definitions
Title: R 'API' Descriptions
Version: 0.0.0.9000
Authors@R: c(
person("Jon", "Harmon", , "jonthegeek@gmail.com", role = c("aut", "cre"),
comment = c(ORCID = "0000-0003-4781-4346")),
person("The Linux Foundation", role = "cph",
comment = "OpenAPI Specification")
)
Description: Convert an 'API' document ('APID'), such as one that follows
the 'OpenAPI Specification', to an R 'API' definition object (a
Description: Convert an 'API' description ('APID'), such as one that follows
the 'OpenAPI Specification', to an R 'API' description object (a
"rapid"). The rapid object follows the 'OpenAPI Specification' to
make it easy to convert to and from 'API' documents.
License: MIT + file LICENSE
Expand All @@ -20,7 +20,7 @@ Imports:
glue,
purrr,
rlang (>= 1.1.0),
S7 (>= 0.1.0.9000),
S7 (>= 0.1.1),
snakecase,
stbl,
yaml
Expand All @@ -34,3 +34,30 @@ Config/testthat/parallel: true
Encoding: UTF-8
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.2.3
Collate:
'as.R'
'components-security_scheme.R'
'properties.R'
'components-security_scheme-api_key.R'
'components-security_scheme-oauth2-scopes.R'
'components-security_scheme-oauth2-flow.R'
'components-security_scheme-oauth2-authorization_code_flow.R'
'components-security_scheme-oauth2-implicit_flow.R'
'components-security_scheme-oauth2-token_flow.R'
'components-security_scheme-oauth2.R'
'components-security_scheme_details.R'
'components-security_scheme_collection.R'
'components.R'
'info-contact.R'
'info-license.R'
'info.R'
'rapid-package.R'
'servers-server_variables.R'
'servers-string_replacements.R'
'servers.R'
'utils.R'
'validate_in.R'
'validate_lengths.R'
'validate_parallel.R'
'zz-rapid.R'
'zzz.R'
19 changes: 19 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
@@ -1,16 +1,35 @@
# Generated by roxygen2: do not edit by hand

export(api_key_security_scheme)
export(as_api_key_security_scheme)
export(as_component_collection)
export(as_contact)
export(as_info)
export(as_license)
export(as_oauth2_authorization_code_flow)
export(as_oauth2_implicit_flow)
export(as_oauth2_security_scheme)
export(as_oauth2_token_flow)
export(as_rapid)
export(as_scopes)
export(as_security_scheme)
export(as_security_scheme_collection)
export(as_security_scheme_details)
export(as_server_variables)
export(as_servers)
export(as_string_replacements)
export(component_collection)
export(contact)
export(info)
export(license)
export(oauth2_authorization_code_flow)
export(oauth2_implicit_flow)
export(oauth2_security_scheme)
export(oauth2_token_flow)
export(rapid)
export(scopes)
export(security_scheme_collection)
export(security_scheme_details)
export(server_variables)
export(servers)
export(string_replacements)
Expand Down
19 changes: 17 additions & 2 deletions R/as.R
Original file line number Diff line number Diff line change
@@ -1,18 +1,32 @@
.as_class <- function(x, target_S7_class, ..., arg = rlang::caller_arg(x), call = rlang::caller_env()) {
force(arg)
x <- .validate_for_as_class(x, target_S7_class, ..., x_arg = arg, call = call)
rlang::inject({
target_S7_class(!!!x)
})
}

.validate_for_as_class <- function(x,
target_S7_class,
extra_names = NULL,
x_arg = rlang::caller_arg(x),
call = rlang::caller_env()) {
if (!length(x)) {
return(NULL)
}

valid_names <- S7::prop_names(target_S7_class())
valid_names <- c(S7::prop_names(target_S7_class()), names(extra_names))

if (rlang::is_named2(x)) {
force(x_arg)
x <- rlang::set_names(x, snakecase::to_snake_case)
if (any(names(x) %in% valid_names)) {
return(as.list(x)[names(x) %in% valid_names])
x <- as.list(x)[names(x) %in% valid_names]
if (length(extra_names)) {
to_rename <- names(x) %in% names(extra_names)
names(x)[to_rename] <- extra_names[names(x)[to_rename]]
}
return(x)
}
}

Expand All @@ -21,6 +35,7 @@
"{.arg {x_arg}} must have names {.or {.val {valid_names}}}.",
"*" = "Any other names are ignored."
),
class = "rapid_missing_names",
call = call
)
}
98 changes: 98 additions & 0 deletions R/components-security_scheme-api_key.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#' @include properties.R
#' @include components-security_scheme.R
NULL

#' API key security schemes
#'
#' Defines an API key security scheme that can be used by the operations.
#'
#' @param parameter_name Character vector (required). The names of the header,
#' query or cookie parameters to be used.
#' @param location Character vector (required). The location of the API key.
#' Valid values are "query", "header" or "cookie".
#'
#' @return An `api_key_security_scheme` S7 object, with fields `parameter_name`
#' and `location`.
#' @export
#'
#' @examples
#' api_key_security_scheme(
#' parameter_name = "Authorization",
#' location = "header"
#' )
api_key_security_scheme <- S7::new_class(
name = "api_key_security_scheme",
package = "rapid",
parent = security_scheme,
properties = list(
parameter_name = character_scalar_property("parameter_name"),
location = character_scalar_property("location")
),
constructor = function(parameter_name = character(),
location = character()) {
S7::new_object(
S7::S7_object(),
parameter_name = parameter_name,
location = location
)
},
validator = function(self) {
validate_parallel(
self,
"parameter_name",
required = "location"
) %|0|% validate_in_fixed(
self,
"location",
c("query", "header", "cookie")
)
}
)

S7::method(length, api_key_security_scheme) <- function(x) {
length(x@parameter_name)
}

#' Coerce lists and character vectors to API key security schemes
#'
#' `as_api_key_security_scheme()` turns an existing object into an
#' `api_key_security_scheme`. This is in contrast with
#' [api_key_security_scheme()], which builds an `api_key_security_scheme` from
#' individual properties.
#'
#' @inheritParams rlang::args_dots_empty
#' @inheritParams rlang::args_error_context
#' @param x The object to coerce. Must be empty or be a list or character vector
#' with names "name" and either "in" or "location", or names that can be
#' coerced to those names via [snakecase::to_snake_case()]. Additional names
#' are ignored.
#'
#' @return An `api_key_security_scheme` as returned by
#' [api_key_security_scheme()].
#' @export
as_api_key_security_scheme <- S7::new_generic(
"as_api_key_security_scheme",
dispatch_args = "x"
)

S7::method(as_api_key_security_scheme, api_key_security_scheme) <- function(x) {
x
}

S7::method(as_api_key_security_scheme, class_list | class_character) <- function(x) {
.as_class(
x,
api_key_security_scheme,
extra_names = c("in" = "location", "name" = "parameter_name")
)
}

S7::method(as_api_key_security_scheme, class_missing | NULL | S7::new_S3_class("S7_missing")) <- function(x) {
api_key_security_scheme()
}

S7::method(as_api_key_security_scheme, class_any) <- function(x, ..., arg = rlang::caller_arg(x)) {
cli::cli_abort(
"Can't coerce {.arg {arg}} {.cls {class(x)}} to {.cls api_key_security_scheme}."
)
}
101 changes: 101 additions & 0 deletions R/components-security_scheme-oauth2-authorization_code_flow.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#' @include components-security_scheme-oauth2-flow.R
NULL

#' OAuth2 authorization code flow object
#'
#' An `oauth2_authorization_code_flow` object describes the configuration for
#' the OAuth Authorization Code flow. Previously called `accessCode` in OpenAPI
#' 2.0.
#'
#' @inheritParams oauth2_flow
#' @inheritParams rlang::args_dots_empty
#' @inheritParams oauth2_implicit_flow
#' @inheritParams oauth2_token_flow
#'
#' @export
#' @examples
#' oauth2_authorization_code_flow(
#' authorization_url = "https://example.com/authorize",
#' token_url = "https://example.com/token",
#' refresh_url = "https://example.com/refresh",
#' scopes = scopes(
#' name = c("server:read", "server:write"),
#' description = c("Read server settings", "Write server settings")
#' )
#' )
oauth2_authorization_code_flow <- S7::new_class(
name = "oauth2_authorization_code_flow",
package = "rapid",
parent = oauth2_flow,
properties = list(
authorization_url = character_scalar_property("authorization_url"),
token_url = character_scalar_property("token_url")
),
constructor = function(authorization_url = character(),
token_url = character(),
...,
refresh_url = character(),
scopes = character()) {
check_dots_empty()
S7::new_object(
S7::S7_object(),
authorization_url = authorization_url,
token_url = token_url,
refresh_url = refresh_url,
scopes = as_scopes(scopes)
)
},
validator = function(self) {
validate_lengths(
self,
"authorization_url",
required_same = "token_url",
optional_same = "refresh_url",
optional_any = "scopes"
)
}
)

S7::method(length, oauth2_authorization_code_flow) <- function(x) {
length(x@authorization_url)
}

#' Coerce lists and character vectors to OAuth2 authorization code flows
#'
#' `as_oauth2_authorization_code_flow()` turns an existing object into an
#' `oauth2_authorization_code_flow`. This is in contrast with
#' [oauth2_authorization_code_flow()], which builds an
#' `oauth2_authorization_code_flow` from individual properties.
#'
#' @inheritParams rlang::args_dots_empty
#' @inheritParams rlang::args_error_context
#' @param x The object to coerce. Must be empty or be a list of named lists,
#' each with names "refresh_url", "scopes", "authorization_url", and/or
#' "token_url", or names that can be coerced to those names via
#' [snakecase::to_snake_case()]. Additional names are ignored.
#'
#' @return An `oauth2_authorization_code_flow` as returned by
#' [oauth2_authorization_code_flow()].
#' @export
as_oauth2_authorization_code_flow <- S7::new_generic(
"as_oauth2_authorization_code_flow",
dispatch_args = "x"
)

S7::method(as_oauth2_authorization_code_flow, oauth2_authorization_code_flow) <- function(x) {
x
}

S7::method(as_oauth2_authorization_code_flow, class_list | class_character) <- function(x) {
.as_class(x, oauth2_authorization_code_flow)
}

S7::method(as_oauth2_authorization_code_flow, class_missing | NULL | S7::new_S3_class("S7_missing")) <- function(x) {
oauth2_authorization_code_flow()
}

S7::method(as_oauth2_authorization_code_flow, class_any) <- function(x, ..., arg = rlang::caller_arg(x)) {
cli::cli_abort(
"Can't coerce {.arg {arg}} {.cls {class(x)}} to {.cls oauth2_authorization_code_flow}."
)
}
27 changes: 27 additions & 0 deletions R/components-security_scheme-oauth2-flow.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#' @include components-security_scheme-oauth2-scopes.R
#' @include properties.R
NULL

#' OAuth2 flow object
#'
#' This is an abstract class that is used to define specific types of OAuth2
#' flow objects.
#'
#' @param refresh_url Character scalar (optional). The URL to be used for
#' obtaining refresh tokens. This must be in the form of a URL. The OAuth2
#' standard requires the use of TLS.
#' @param scopes An optional [scopes()] object with the available scopes for the
#' OAuth2 security scheme.
#'
#' @keywords internal
#' @seealso [oauth2_token_flow()], [oauth2_implicit_flow()], and
#' [oauth2_authorization_code_flow()]
oauth2_flow <- S7::new_class(
name = "oauth2_flow",
package = "rapid",
properties = list(
refresh_url = character_scalar_property("refresh_url"),
scopes = scopes
),
abstract = TRUE
)
Loading

0 comments on commit 04659df

Please sign in to comment.