Skip to content

Commit

Permalink
Fix check of token scopes and give more informative feedback when the…
Browse files Browse the repository at this point in the history
…y are not sufficient.
  • Loading branch information
maciekbanas committed Oct 14, 2024
1 parent 18a2574 commit e2767b6
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 24 deletions.
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: GitStats
Title: Get Statistics from GitHub and GitLab
Version: 2.1.0.9001
Version: 2.1.0.9002
Authors@R: c(
person(given = "Maciej", family = "Banas", email = "banasmaciek@gmail.com", role = c("aut", "cre")),
person(given = "Kamil", family = "Koziej", email = "koziej.k@gmail.com", role = "aut"),
Expand Down
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

- Added possibility to get repositories for individual users with `get_repos()` ([#492](https://github.com/r-world-devs/GitStats/issues/492)). Earlier this was only possible for GitHub organizations and GitLab groups.
- Fixed getting large search responses for GitHub ([#491](https://github.com/r-world-devs/GitStats/issues/491)).
- Fixed checking token scopes ([#501](https://github.com/r-world-devs/GitStats/issues/501)). If token scopes are insufficient error is returned and `GitHost` is not passed to `GitStats`. This also applies to situation when `GitStats` looks for default tokens (not defined by user). Earlier, if tests for token failed, an empty token was passed and `GitStats` was created, which was misleading for the user.

# GitStats 2.1.0

Expand Down
23 changes: 18 additions & 5 deletions R/GitHost.R
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,9 @@ GitHost <- R6::R6Class(
# A token.
token = NULL,

# Actual token scopes
token_scopes = NULL,

# public A boolean.
is_public = NULL,

Expand Down Expand Up @@ -343,8 +346,10 @@ GitHost <- R6::R6Class(
if (!private$test_token(token)) {
cli::cli_abort(c(
"x" = "Token exists but does not grant access.",
"i" = "Check if you use correct token. Check scopes your token is using."
))
"i" = "Check if you use correct token.",
"!" = "Scope that is needed: [{paste0(private$min_access_scopes, collapse = ', ')}]."
),
call = NULL)
} else {
return(token)
}
Expand Down Expand Up @@ -526,6 +531,14 @@ GitHost <- R6::R6Class(
}
}
}
if (token == "") {
cli::cli_abort(c(
"x" = "No sufficient token found among: [{paste0(pat_names, collapse = ', ')}].",
"i" = "Check if you have correct token.",
"!" = "Scope that is needed: [{paste0(private$min_access_scopes, collapse = ', ')}]."
),
call = NULL)
}
return(token)
},

Expand All @@ -537,11 +550,11 @@ GitHost <- R6::R6Class(
httr2::req_headers("Authorization" = paste0("Bearer ", token)) |>
httr2::req_perform(), silent = TRUE)
if (!is.null(response)) {
private$check_token_scopes(response, token)
TRUE
check <- private$check_token_scopes(response, token)
} else {
FALSE
check <- FALSE
}
return(check)
},

# Helper to extract organizations and repositories from vector of full names
Expand Down
10 changes: 7 additions & 3 deletions R/GitHostGitHub.R
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,12 @@ GitHostGitHub <- R6::R6Class(
# Default token name
token_name = "GITHUB_PAT",

# Minimum access scopes for token
min_access_scopes = c("public_repo", "read:org", "read:user"),

# Access scopes for token
access_scopes = c("public_repo", "read:org", "read:user"),
access_scopes = c("public_repo", "read:org", "read:user",
"repo", "admin:org", "user"),

# Methods for engines
engine_methods = list(
Expand Down Expand Up @@ -124,10 +128,10 @@ GitHostGitHub <- R6::R6Class(
# Check token scopes
# token parameter only for need of super method
check_token_scopes = function(response, token = NULL) {
token_scopes <- response$headers$`x-oauth-scopes` %>%
private$token_scopes <- response$headers$`x-oauth-scopes` %>%
stringr::str_split(", ") %>%
unlist()
all(private$access_scopes %in% token_scopes)
all(private$access_scopes %in% private$token_scopes)
},

# Retrieve only important info from repositories response
Expand Down
36 changes: 21 additions & 15 deletions R/GitHostGitLab.R
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ GitHostGitLab <- R6::R6Class("GitHostGitLab",
# Default token name
token_name = "GITLAB_PAT",

# Minimum access scopes for token
min_access_scopes = c("read_api"),

# Access scopes for token
access_scopes = c("api", "read_api"),

Expand Down Expand Up @@ -184,21 +187,24 @@ GitHostGitLab <- R6::R6Class("GitHostGitLab",
# check token scopes
# response parameter only for need of super method
check_token_scopes = function(response = NULL, token) {
token_scopes <- httr2::request(private$endpoints$tokens) %>%
httr2::req_headers("Authorization" = paste0("Bearer ", token)) %>%
httr2::req_perform() %>%
httr2::resp_body_json() %>%
purrr::keep(~ .$active) %>%
purrr::map(function(pat) {
data.frame(scopes = unlist(pat$scopes), date = pat$last_used_at)
}) %>%
purrr::list_rbind() %>%
dplyr::filter(
date == max(date)
) %>%
dplyr::select(scopes) %>%
unlist()
any(private$access_scopes %in% token_scopes)
private$token_scopes <- try({
httr2::request(private$endpoints$tokens) %>%
httr2::req_headers("Authorization" = paste0("Bearer ", token)) %>%
httr2::req_perform() %>%
httr2::resp_body_json() %>%
purrr::keep(~ .$active) %>%
purrr::map(function(pat) {
data.frame(scopes = unlist(pat$scopes), date = pat$last_used_at)
}) %>%
purrr::list_rbind() %>%
dplyr::filter(
date == max(date)
) %>%
dplyr::select(scopes) %>%
unlist()
},
silent = TRUE)
any(private$access_scopes %in% private$token_scopes)
},

# Retrieve only important info from repositories response
Expand Down
26 changes: 26 additions & 0 deletions inst/set_tokens.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# to TEST create tokens with insufficient scopes

create_gitstats() |>
set_github_host(
orgs = "r-world-devs",
token = Sys.getenv("GITHUB_PAT_INSUFFICIENT")
) |>
get_repos()

create_gitstats() |>
set_gitlab_host(
orgs = "mbtests"
) |>
get_repos()

create_gitstats() |>
set_gitlab_host(
orgs = "mbtests",
token = Sys.getenv("GITLAB_PAT_PUBLIC_INSUFFICIENT")
) |>
get_repos()

create_gitstats() |>
set_github_host(
orgs = "r-world-devs"
)

0 comments on commit e2767b6

Please sign in to comment.