From edcf423f8c250cbed51f5d7363db43f7e10f5043 Mon Sep 17 00:00:00 2001 From: Candace Savonen Date: Wed, 9 Aug 2023 15:21:27 -0400 Subject: [PATCH 01/12] Pair coding authorization steps with Howard and Candace --- .Rbuildignore | 1 + .gitignore | 2 + NAMESPACE | 7 + R/auth.R | 169 ++++++++++++------ R/data.R | 50 ++++++ R/notes_to_fig_alt.R | 7 + inst/extdata/tmp/encrypt.rds | Bin 0 -> 356 bytes inst/extdata/tmp/encrypt_pass.rds | Bin 0 -> 122 bytes .../tmp/encrypted_default_user_creds.rds | Bin 0 -> 564 bytes man/auth_from_secret.Rd | 32 ++++ man/authorize.Rd | 32 +--- man/encrypt_creds_path.Rd | 11 ++ man/key_encrypt_creds_path.Rd | 11 ++ man/markdown_quiz_path.Rd | 23 +++ 14 files changed, 265 insertions(+), 80 deletions(-) create mode 100644 R/data.R create mode 100644 inst/extdata/tmp/encrypt.rds create mode 100644 inst/extdata/tmp/encrypt_pass.rds create mode 100644 inst/extdata/tmp/encrypted_default_user_creds.rds create mode 100644 man/auth_from_secret.Rd create mode 100644 man/encrypt_creds_path.Rd create mode 100644 man/key_encrypt_creds_path.Rd create mode 100644 man/markdown_quiz_path.Rd diff --git a/.Rbuildignore b/.Rbuildignore index 1f5de73..93e63ee 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -29,3 +29,4 @@ vignettes/.httr-oauth$ ^vignettes/toc_close.css$ ^package_bundles/leanbuild_0.1.2.tar.gz* ^package_bundles/leanbuild_0.1.2.tgz* +inst/extdata/tmp/default* diff --git a/.gitignore b/.gitignore index 7f2dadd..08d9016 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,5 @@ tests/testthat/googlesheets_token.rds run_test.R /doc/ /Meta/ +.secrets +inst/extdata/tmp/default* \ No newline at end of file diff --git a/NAMESPACE b/NAMESPACE index 816c569..fa697d1 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,6 +1,7 @@ # Generated by roxygen2: do not edit by hand export("%>%") +export(auth_from_secret) export(authorize) export(bad_quiz_path) export(bookdown_destination) @@ -40,6 +41,7 @@ export(gs_png_url) export(include_slide) export(leanpub_check) export(make_embed_markdown) +export(markdown_quiz_path) export(parse_q_tag) export(parse_quiz) export(parse_quiz_df) @@ -60,10 +62,15 @@ importFrom(httr,GET) importFrom(httr,accept_json) importFrom(httr,config) importFrom(httr,content) +importFrom(httr,oauth2.0_token) +importFrom(httr,oauth_app) +importFrom(httr,oauth_endpoints) importFrom(jsonlite,fromJSON) importFrom(magrittr,"%>%") importFrom(readr,write_tsv) importFrom(utils,download.file) +importFrom(utils,installed.packages) +importFrom(utils,menu) importFrom(utils,unzip) importFrom(xml2,read_xml) importFrom(xml2,xml_find_all) diff --git a/R/auth.R b/R/auth.R index 7f0c1db..eb170fb 100644 --- a/R/auth.R +++ b/R/auth.R @@ -1,74 +1,131 @@ -# Make an empty environment where the token will be stored -.tokenEnv <- new.env(parent = emptyenv()) +.onAttach <- function(libname, pkgname) { + packageStartupMessage("Use authorize() function to begin give the package the proper credentials to run. ") +} -# For now the Token is gonna be NULL because we don't have it yet. -.tokenEnv$Token <- NULL +.classroomEnv <- new.env(parent = emptyenv()) +.classroomEnv$Token <- NULL -# A function to set token to environment +# Set token to environment set_token <- function(value) { - .tokenEnv$Token <- value + .classroomEnv$Token <- value return(value) } -# A function to retrieve token from environment +# Get token from environment get_token <- function() { - .tokenEnv$Token + .classroomEnv$Token } +### Declare all the scopes +scopes_list <- c( + "https://www.googleapis.com/auth/drive", + "https://www.googleapis.com/auth/drive.file", + "https://www.googleapis.com/auth/drive.readonly", + "https://www.googleapis.com/auth/presentations", + "https://www.googleapis.com/auth/presentations.readonly" +) -#' Authorize R package to access Google Slides API -#' -#' By providing a Google Cloud Client ID and Client Secret, you obtain an access -#' token from Google's OAuth 2.0 server. This access token is used to access the -#' Google Slides API. If you supply a token, this function will save it for -#' future use. For instructions on creating a Client ID and Client Secret, see -#' \url{https://www.hairizuan.com/rgoogleslides-using-your-own-account-client-id-and-secret/}. -#' -#' If this is your first time running authorize(), it will ask you if you want -#' to use a local file ('.httr-oauth') to cache the access token. If you say -#' "Yes", you will not have to run this function in future R sessions. Make sure -#' to provide ottrpal complete access to your Google Drive files and Google -#' Slides presentations. -#' -#' @param client_id Google Cloud Client ID -#' @param client_secret Google Cloud Client secret -#' @param token OAuth 2.0 Access Token -#' @param ... Additional arguments to [httr::oauth2.0_token()] -#' -#' @return A Token2.0 reference class (RC) object. + +#' Authorize R package to access Google slides API +#' @description This is a function to authorize the R package to access the Googleclassroom API interactively. +#' @param token an output from \code{\link{oauth2.0_token}} to set as the authentication token. +#' @param cache Should the token be cached as an .httr-oauth file? +#' @param ... additional arguments to send to \code{\link{oauth2.0_token}} +#' @return OAuth token saved to the environment so the package can use the users' Google data +#' @importFrom utils menu installed.packages +#' @importFrom httr oauth_app oauth_endpoints oauth2.0_token #' @export +#' @examples \dontrun{ #' -#' @examples -#' \dontrun{ -#' # Generate token from Client ID and Client Secret -#' authorize(client_id = "MY_CLIENT_ID", client_secret = "MY_CLIENT_SECRET") -#' -#' # Provides user-generated token -#' authorize(token = my_token) +#' authorize() #' } -authorize <- function(client_id = NULL, - client_secret = NULL, - token = NULL, - ...) { - # client id or secret not provided - if ((is.null(client_id) | is.null(client_secret)) & is.null(token)) { - stop("Please generate a client secret and client key following these instructions:\n", - "https://www.hairizuan.com/rgoogleslides-using-your-own-account-client-id-and-secret/") +authorize <- function(token = NULL, cache = FALSE, ...) { + if (!cache) { + cache_it <- menu(c("Yes store credentials as .httr-oauth file", "No do not store credentials, I will re-run this authorize() in my next R session")) + if (cache_it == 1) { + message("You chose to cache your credentials, if you change your mind, just delete the .httr-oauth. Be careful not to push this file to GitHub or share it anywhere.") + } + } else { + cache_it <- 1 } if (is.null(token)) { - # setup app - app <- httr::oauth_app(appname = "googleslides", - key = client_id, - secret = client_secret) - # google endpoints - endpoint <- httr::oauth_endpoints("google") - # generate token - token <- httr::oauth2.0_token(endpoint = endpoint, - app = app, - scope = c("https://www.googleapis.com/auth/presentations", - "https://www.googleapis.com/auth/drive.readonly"), - ...) + token <- httr::oauth2.0_token( + endpoint = app_set_up()$endpoint, + app = app_set_up()$app, + cache = cache_it == 1, + scope = scopes_list, + ... + ) } set_token(token) - invisible(token) + return(invisible(token)) } + +#' Use secrets to Authorize R package to access Google slides API +#' @description This is a function to authorize the R package to access the Slides API. If no +#' client.id and client.secret is provided, the package would provide predefined values. +#' @param access_token Access token can be obtained from running authorize interactively: token <-authorize(); token$credentials$access_token +#' @param refresh_token Refresh token can be obtained from running authorize interactively: token <-authorize(); token$credentials$refresh_token +#' @return OAuth token saved to the environment so the package can use the users' Google data +#' @importFrom utils menu installed.packages +#' @importFrom httr oauth_app oauth_endpoints oauth2.0_token +#' @export +#' @examples \dontrun{ +#' +#' token <- authorize() +#' +#' auth_from_secret( +#' token$credentials$access_token, +#' token$credentials$refresh_token +#' ) +#' } +#' +auth_from_secret <- function(access_token = NULL, refresh_token = NULL) { + + if (is.null(access_token) | is.null(refresh_token)) { + decrypted <- openssl::aes_cbc_decrypt( + readRDS(encrypt_creds_user_path()), + key = readRDS(key_encrypt_creds_path()) + ) + } + + credentials <- list( + access_token = unserialize(decrypted)[[1]]$access_token, + expires_in = 3599L, + refresh_token = unserialize(decrypted)[[1]]$refresh_token, + scope = scopes_list, + token_type = "Bearer" + ) + + token <- httr::oauth2.0_token( + endpoint = app_set_up()$endpoint, + app = app_set_up()$app, + scope = scopes_list, + credentials = credentials + ) + + set_token(token) + return(invisible(token)) +} + +# This sets up the app creds no matter which way authorization is called +app_set_up <- function() { + decrypted <- openssl::aes_cbc_decrypt( + readRDS(encrypt_creds_path()), + key = readRDS(key_encrypt_creds_path()) + ) + + app <- oauth_app( + appname = "ottrpal", + key = unserialize(decrypted)$client_id, + secret = unserialize(decrypted)$client_secret + ) + endpoint <- oauth_endpoints("google") + + return(list(app = app, endpoint = endpoint)) +} + + + + + diff --git a/R/data.R b/R/data.R new file mode 100644 index 0000000..b5b0b47 --- /dev/null +++ b/R/data.R @@ -0,0 +1,50 @@ + +#' Get file path to an key encryption RDS +key_encrypt_creds_path <- function() { + list.files( + pattern = "encrypt_pass.rds", + recursive = TRUE, + system.file("extdata", package = "ottrpal"), + full.names = TRUE + ) +} +#' Get file path to an encrypted credentials RDS +encrypt_creds_path <- function() { + list.files( + pattern = "encrypt.rds", + recursive = TRUE, + system.file("extdata", package = "ottrpal"), + full.names = TRUE + ) +} + +#' Get file path to an default credentials RDS +encrypt_creds_user_path <- function() { + list.files( + pattern = "encrypted_default_user_creds.rds", + recursive = TRUE, + system.file("extdata", package = "ottrpal"), + full.names = TRUE + ) +} + + +#' Get file path to an example quiz +#' +#' @return A file path to a markua markdown quiz example you can use for testing +#' @export +#' @examples \dontrun{ +#' +#' # Find quiz path +#' +#' quiz_path <- markdown_quiz_path() +#' } +#' +markdown_quiz_path <- function() { + list.files( + pattern = "quiz.md", + recursive = TRUE, + system.file("extdata", package = "rgoogleclassroom"), + full.names = TRUE + ) +} diff --git a/R/notes_to_fig_alt.R b/R/notes_to_fig_alt.R index 50d3628..bec849a 100644 --- a/R/notes_to_fig_alt.R +++ b/R/notes_to_fig_alt.R @@ -304,6 +304,13 @@ extract_object_id = function(slide_url, token = NULL) { # if token not provided, fetch token if (is.null(token)) { + + token_try <- try(get_token(), silent = TRUE) + + # We will supply credentials if none can be grabbed by get_token() + if (is.null(token_try)) { + auth_from_secret() + } token <- get_token() } # else user provides token diff --git a/inst/extdata/tmp/encrypt.rds b/inst/extdata/tmp/encrypt.rds new file mode 100644 index 0000000000000000000000000000000000000000..dc4bbdc69fa95b88961c97168925ebd51836cddd GIT binary patch literal 356 zcmV-q0h|6GiwFP!000000|89|vi?{K0000300aR700RX800005Ra8bTH~;_w7ytkP z0H`Q*K6vTswDRf_0nkz^D)6yPdeWnlFUkOtABcGOqoN_HVWk9y zz@EEVY4oouqOdvoeQbFt6r;we%OZ`y_f`Kp@iDrBBjex1Wmfg$Bzdf$(30HtWxQkI zoX6+cU^;sr2g0}M)_Y6fiTNR%jwNgm)vMus^F?ay)bKA8z{`T-XH!nER6MH=;BRO$ z#ysJ8qUVc<{90%>*t000;O000o!05=4)Hv4((3vHpc7DsRZ0091{4S`5a0RRB> C44HNS literal 0 HcmV?d00001 diff --git a/inst/extdata/tmp/encrypt_pass.rds b/inst/extdata/tmp/encrypt_pass.rds new file mode 100644 index 0000000000000000000000000000000000000000..f50ca13c8780d8ff2399be4813377ab96b3108de GIT binary patch literal 122 zcmb2|=3oE==I#ec2?+^l35h9532CfGk`d0%cS>{{l7t-koZi2m(V8kc=lK*hD_7%} zy7%mDwS0eU)LZ>2zVP@8U3)JvLxT+lj8_efHW(B%nW#Tk5ff*YWO6veBARm2L+4z> Yrkh>HLcKm`8`jh_6wO_0Sp~Eb0B67~`Tzg` literal 0 HcmV?d00001 diff --git a/inst/extdata/tmp/encrypted_default_user_creds.rds b/inst/extdata/tmp/encrypted_default_user_creds.rds new file mode 100644 index 0000000000000000000000000000000000000000..bb436e0f2b7c23275755df39be5d47c6daf0a342 GIT binary patch literal 564 zcmV-40?Yj$iwFP!000000|6ZZ;{8|(0000300aR700RX800005Ra8bTH~;_w7ytkP z(81RZ<6pHdY{hMqdb4TFYg6XDrPk|C@2(>dL^5=t0qUW+&3?Fi_!bu3Rfj&1AQ%&j zN$@_A6;p#BTb1p$9~6l#TNXluhs0SM2&h_0u`Ud+>U_YIBv$Kg^C(HRN1;Z$F~NG< zUJ`A_0ax1dS0)Hh6i{$&UjkTJ?jnYo&Igm$rM37J1b>t4FB*mqer_Qzly@TTxzCNO zy(!u(nPL>0Oe+)%fx}=TCihvXmDoU@`IK$Gm_I(U9ydJ5FRg(J%Hnba5<`}0ftWX> zg|6((U&itK-Jn^S#;O&7bQNSUcH+lWri7Z?AAfb*!)?}vN6}sA+h_7ILi?zin34+H zazZNLJ@LD`Q?Y1=KA9%2`s7k9EftkDt|se0BH2~x;NyE*KZ?wE^r13~FsWv;3BJJ) zjLf?|QEK?FY2Z$ZW^#cY;vv}sfth~Gvbe$o3D0F+;^88(GPNQsA&t*dZDwKXuJ~uU zm3s%jfN;kW&)h#WmO43dh@;WI^CIdTiCklm&>&g0Yaqn({vu?%(u=%sOOP(S-gqA`2&Pu0lB3-wMuHG2=Cg(zscccS?>LLKnVV*mgI z0ssI20RRL52><{90%>*t000;O000oqVhrI{yjVnUl3do{PuI=>00928w}grv0ssJb CZuoxy literal 0 HcmV?d00001 diff --git a/man/auth_from_secret.Rd b/man/auth_from_secret.Rd new file mode 100644 index 0000000..fad3114 --- /dev/null +++ b/man/auth_from_secret.Rd @@ -0,0 +1,32 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/auth.R +\name{auth_from_secret} +\alias{auth_from_secret} +\title{Use secrets to Authorize R package to access Google classroom API} +\usage{ +auth_from_secret(access_token, refresh_token) +} +\arguments{ +\item{access_token}{Access token can be obtained from running authorize interactively: token <-authorize(); token$credentials$access_token} + +\item{refresh_token}{Refresh token can be obtained from running authorize interactively: token <-authorize(); token$credentials$refresh_token} +} +\value{ +OAuth token saved to the environment so the package can use the users' Google data +} +\description{ +This is a function to authorize the R package to access the Googleclassroom API. If no +client.id and client.secret is provided, the package would provide predefined values. +} +\examples{ +\dontrun{ + +token <- authorize() + +auth_from_secret( + token$credentials$access_token, + token$credentials$refresh_token +) +} + +} diff --git a/man/authorize.Rd b/man/authorize.Rd index 925a339..32873c0 100644 --- a/man/authorize.Rd +++ b/man/authorize.Rd @@ -2,42 +2,26 @@ % Please edit documentation in R/auth.R \name{authorize} \alias{authorize} -\title{Authorize R package to access Google Slides API} +\title{Authorize R package to access Google classroom API} \usage{ -authorize(client_id = NULL, client_secret = NULL, token = NULL, ...) +authorize(token = NULL, cache = FALSE, ...) } \arguments{ -\item{client_id}{Google Cloud Client ID} +\item{token}{an output from \code{\link{oauth2.0_token}} to set as the authentication token.} -\item{client_secret}{Google Cloud Client secret} +\item{cache}{Should the token be cached as an .httr-oauth file?} -\item{token}{OAuth 2.0 Access Token} - -\item{...}{Additional arguments to [httr::oauth2.0_token()]} +\item{...}{additional arguments to send to \code{\link{oauth2.0_token}}} } \value{ -A Token2.0 reference class (RC) object. +OAuth token saved to the environment so the package can use the users' Google data } \description{ -By providing a Google Cloud Client ID and Client Secret, you obtain an access -token from Google's OAuth 2.0 server. This access token is used to access the -Google Slides API. If you supply a token, this function will save it for -future use. For instructions on creating a Client ID and Client Secret, see -\url{https://www.hairizuan.com/rgoogleslides-using-your-own-account-client-id-and-secret/}. -} -\details{ -If this is your first time running authorize(), it will ask you if you want -to use a local file ('.httr-oauth') to cache the access token. If you say -"Yes", you will not have to run this function in future R sessions. Make sure -to provide ottrpal complete access to your Google Drive files and Google -Slides presentations. +This is a function to authorize the R package to access the Googleclassroom API interactively. } \examples{ \dontrun{ -# Generate token from Client ID and Client Secret -authorize(client_id = "MY_CLIENT_ID", client_secret = "MY_CLIENT_SECRET") -# Provides user-generated token -authorize(token = my_token) +authorize() } } diff --git a/man/encrypt_creds_path.Rd b/man/encrypt_creds_path.Rd new file mode 100644 index 0000000..64ee34b --- /dev/null +++ b/man/encrypt_creds_path.Rd @@ -0,0 +1,11 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/data.R +\name{encrypt_creds_path} +\alias{encrypt_creds_path} +\title{Get file path to an encrypted credentials RDS} +\usage{ +encrypt_creds_path() +} +\description{ +Get file path to an encrypted credentials RDS +} diff --git a/man/key_encrypt_creds_path.Rd b/man/key_encrypt_creds_path.Rd new file mode 100644 index 0000000..6661719 --- /dev/null +++ b/man/key_encrypt_creds_path.Rd @@ -0,0 +1,11 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/data.R +\name{key_encrypt_creds_path} +\alias{key_encrypt_creds_path} +\title{Get file path to an key encryption RDS} +\usage{ +key_encrypt_creds_path() +} +\description{ +Get file path to an key encryption RDS +} diff --git a/man/markdown_quiz_path.Rd b/man/markdown_quiz_path.Rd new file mode 100644 index 0000000..be441db --- /dev/null +++ b/man/markdown_quiz_path.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/data.R +\name{markdown_quiz_path} +\alias{markdown_quiz_path} +\title{Get file path to an example quiz} +\usage{ +markdown_quiz_path() +} +\value{ +A file path to a markua markdown quiz example you can use for testing +} +\description{ +Get file path to an example quiz +} +\examples{ +\dontrun{ + +# Find quiz path + +quiz_path <- markdown_quiz_path() +} + +} From 7a2c0cacf3bf0495abf21c9b79691bc69b0a4913 Mon Sep 17 00:00:00 2001 From: howardbaek Date: Wed, 9 Aug 2023 14:08:15 -0700 Subject: [PATCH 02/12] `.classroomEnv` --> `.tokenEnv` --- R/auth.R | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/R/auth.R b/R/auth.R index eb170fb..369c99f 100644 --- a/R/auth.R +++ b/R/auth.R @@ -2,18 +2,18 @@ packageStartupMessage("Use authorize() function to begin give the package the proper credentials to run. ") } -.classroomEnv <- new.env(parent = emptyenv()) -.classroomEnv$Token <- NULL +.tokenEnv <- new.env(parent = emptyenv()) +.tokenEnv$Token <- NULL # Set token to environment set_token <- function(value) { - .classroomEnv$Token <- value + .tokenEnv$Token <- value return(value) } # Get token from environment get_token <- function() { - .classroomEnv$Token + .tokenEnv$Token } ### Declare all the scopes From e827f0e14ecff0e55cefe824e1a397b991a5af7d Mon Sep 17 00:00:00 2001 From: howardbaek Date: Wed, 9 Aug 2023 15:04:11 -0700 Subject: [PATCH 03/12] Take out leftovers from rgoogleclassroom --- R/data.R | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/R/data.R b/R/data.R index b5b0b47..40c1c60 100644 --- a/R/data.R +++ b/R/data.R @@ -27,24 +27,3 @@ encrypt_creds_user_path <- function() { full.names = TRUE ) } - - -#' Get file path to an example quiz -#' -#' @return A file path to a markua markdown quiz example you can use for testing -#' @export -#' @examples \dontrun{ -#' -#' # Find quiz path -#' -#' quiz_path <- markdown_quiz_path() -#' } -#' -markdown_quiz_path <- function() { - list.files( - pattern = "quiz.md", - recursive = TRUE, - system.file("extdata", package = "rgoogleclassroom"), - full.names = TRUE - ) -} From 0f9b7c58cc4d601cacac04c84b7cfc9069d2f6de Mon Sep 17 00:00:00 2001 From: howardbaek Date: Wed, 9 Aug 2023 15:06:06 -0700 Subject: [PATCH 04/12] `.onAttach()` already exists in zzz.R --- R/auth.R | 4 ---- 1 file changed, 4 deletions(-) diff --git a/R/auth.R b/R/auth.R index 369c99f..66b4b1d 100644 --- a/R/auth.R +++ b/R/auth.R @@ -1,7 +1,3 @@ -.onAttach <- function(libname, pkgname) { - packageStartupMessage("Use authorize() function to begin give the package the proper credentials to run. ") -} - .tokenEnv <- new.env(parent = emptyenv()) .tokenEnv$Token <- NULL From 4e7f92275cb55c460aceb0380b36bf15be4eaa2d Mon Sep 17 00:00:00 2001 From: howardbaek Date: Wed, 9 Aug 2023 15:17:35 -0700 Subject: [PATCH 05/12] Remove `markdown_quiz_path()` from the documentation --- NAMESPACE | 1 - man/auth_from_secret.Rd | 6 +++--- man/authorize.Rd | 2 +- man/encrypt_creds_user_path.Rd | 11 +++++++++++ man/markdown_quiz_path.Rd | 23 ----------------------- 5 files changed, 15 insertions(+), 28 deletions(-) create mode 100644 man/encrypt_creds_user_path.Rd delete mode 100644 man/markdown_quiz_path.Rd diff --git a/NAMESPACE b/NAMESPACE index fa697d1..1202f35 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -41,7 +41,6 @@ export(gs_png_url) export(include_slide) export(leanpub_check) export(make_embed_markdown) -export(markdown_quiz_path) export(parse_q_tag) export(parse_quiz) export(parse_quiz_df) diff --git a/man/auth_from_secret.Rd b/man/auth_from_secret.Rd index fad3114..35da89b 100644 --- a/man/auth_from_secret.Rd +++ b/man/auth_from_secret.Rd @@ -2,9 +2,9 @@ % Please edit documentation in R/auth.R \name{auth_from_secret} \alias{auth_from_secret} -\title{Use secrets to Authorize R package to access Google classroom API} +\title{Use secrets to Authorize R package to access Google slides API} \usage{ -auth_from_secret(access_token, refresh_token) +auth_from_secret(access_token = NULL, refresh_token = NULL) } \arguments{ \item{access_token}{Access token can be obtained from running authorize interactively: token <-authorize(); token$credentials$access_token} @@ -15,7 +15,7 @@ auth_from_secret(access_token, refresh_token) OAuth token saved to the environment so the package can use the users' Google data } \description{ -This is a function to authorize the R package to access the Googleclassroom API. If no +This is a function to authorize the R package to access the Slides API. If no client.id and client.secret is provided, the package would provide predefined values. } \examples{ diff --git a/man/authorize.Rd b/man/authorize.Rd index 32873c0..89d3705 100644 --- a/man/authorize.Rd +++ b/man/authorize.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/auth.R \name{authorize} \alias{authorize} -\title{Authorize R package to access Google classroom API} +\title{Authorize R package to access Google slides API} \usage{ authorize(token = NULL, cache = FALSE, ...) } diff --git a/man/encrypt_creds_user_path.Rd b/man/encrypt_creds_user_path.Rd new file mode 100644 index 0000000..116c94e --- /dev/null +++ b/man/encrypt_creds_user_path.Rd @@ -0,0 +1,11 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/data.R +\name{encrypt_creds_user_path} +\alias{encrypt_creds_user_path} +\title{Get file path to an default credentials RDS} +\usage{ +encrypt_creds_user_path() +} +\description{ +Get file path to an default credentials RDS +} diff --git a/man/markdown_quiz_path.Rd b/man/markdown_quiz_path.Rd deleted file mode 100644 index be441db..0000000 --- a/man/markdown_quiz_path.Rd +++ /dev/null @@ -1,23 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/data.R -\name{markdown_quiz_path} -\alias{markdown_quiz_path} -\title{Get file path to an example quiz} -\usage{ -markdown_quiz_path() -} -\value{ -A file path to a markua markdown quiz example you can use for testing -} -\description{ -Get file path to an example quiz -} -\examples{ -\dontrun{ - -# Find quiz path - -quiz_path <- markdown_quiz_path() -} - -} From b917f278791c3b7a894172f17bea1de13d85a160 Mon Sep 17 00:00:00 2001 From: howardbaek Date: Wed, 9 Aug 2023 16:39:48 -0700 Subject: [PATCH 06/12] Fix spacing --- R/auth.R | 5 ----- R/notes_to_fig_alt.R | 2 -- 2 files changed, 7 deletions(-) diff --git a/R/auth.R b/R/auth.R index 66b4b1d..59c4489 100644 --- a/R/auth.R +++ b/R/auth.R @@ -120,8 +120,3 @@ app_set_up <- function() { return(list(app = app, endpoint = endpoint)) } - - - - - diff --git a/R/notes_to_fig_alt.R b/R/notes_to_fig_alt.R index bec849a..2dff70d 100644 --- a/R/notes_to_fig_alt.R +++ b/R/notes_to_fig_alt.R @@ -324,8 +324,6 @@ extract_object_id = function(slide_url, token = NULL) { result_list$slides$objectId } - - #' Retrieve Speaker Notes and their corresponding Object (Slide) IDs from a Google Slides presentation #' #' Google Slides API calls a presentation slide ID as an 'object ID'. From 4810e3ffa16b11a2d7e8b6841feef52a65d86277 Mon Sep 17 00:00:00 2001 From: howardbaek Date: Thu, 10 Aug 2023 14:47:22 -0700 Subject: [PATCH 07/12] Fix function documentation --- R/auth.R | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/R/auth.R b/R/auth.R index 59c4489..0565e09 100644 --- a/R/auth.R +++ b/R/auth.R @@ -22,11 +22,11 @@ scopes_list <- c( ) -#' Authorize R package to access Google slides API -#' @description This is a function to authorize the R package to access the Googleclassroom API interactively. -#' @param token an output from \code{\link{oauth2.0_token}} to set as the authentication token. +#' Authorize R package to access the Google Slides API +#' @description This is a function to authorize the R package to access the Google Slides API interactively. +#' @param token An output from \code{\link{oauth2.0_token}} to set as the authentication token. #' @param cache Should the token be cached as an .httr-oauth file? -#' @param ... additional arguments to send to \code{\link{oauth2.0_token}} +#' @param ... Additional arguments to send to \code{\link{oauth2.0_token}} #' @return OAuth token saved to the environment so the package can use the users' Google data #' @importFrom utils menu installed.packages #' @importFrom httr oauth_app oauth_endpoints oauth2.0_token @@ -57,11 +57,11 @@ authorize <- function(token = NULL, cache = FALSE, ...) { return(invisible(token)) } -#' Use secrets to Authorize R package to access Google slides API -#' @description This is a function to authorize the R package to access the Slides API. If no +#' Use secrets to authorize R package to access Google Slides API +#' @description This is a function to authorize the R package to access the Google Slides API. If no #' client.id and client.secret is provided, the package would provide predefined values. -#' @param access_token Access token can be obtained from running authorize interactively: token <-authorize(); token$credentials$access_token -#' @param refresh_token Refresh token can be obtained from running authorize interactively: token <-authorize(); token$credentials$refresh_token +#' @param access_token Access token can be obtained from running authorize() interactively: token <-authorize(); token$credentials$access_token +#' @param refresh_token Refresh token can be obtained from running authorize() interactively: token <-authorize(); token$credentials$refresh_token #' @return OAuth token saved to the environment so the package can use the users' Google data #' @importFrom utils menu installed.packages #' @importFrom httr oauth_app oauth_endpoints oauth2.0_token @@ -77,7 +77,6 @@ authorize <- function(token = NULL, cache = FALSE, ...) { #' } #' auth_from_secret <- function(access_token = NULL, refresh_token = NULL) { - if (is.null(access_token) | is.null(refresh_token)) { decrypted <- openssl::aes_cbc_decrypt( readRDS(encrypt_creds_user_path()), From d4aab6a6791b5a5fd44fa63d9bd7e15f202bd51b Mon Sep 17 00:00:00 2001 From: howardbaek Date: Fri, 11 Aug 2023 11:25:06 -0700 Subject: [PATCH 08/12] Minor spacing fix --- R/auth.R | 1 - 1 file changed, 1 deletion(-) diff --git a/R/auth.R b/R/auth.R index 0565e09..a0ae939 100644 --- a/R/auth.R +++ b/R/auth.R @@ -109,7 +109,6 @@ app_set_up <- function() { readRDS(encrypt_creds_path()), key = readRDS(key_encrypt_creds_path()) ) - app <- oauth_app( appname = "ottrpal", key = unserialize(decrypted)$client_id, From 4406a21c7f24bd4f262bb3eae432f3b02ee61c70 Mon Sep 17 00:00:00 2001 From: howardbaek Date: Mon, 14 Aug 2023 11:31:58 -0700 Subject: [PATCH 09/12] Namespace `httr` functions --- R/auth.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/auth.R b/R/auth.R index a0ae939..098802c 100644 --- a/R/auth.R +++ b/R/auth.R @@ -109,12 +109,12 @@ app_set_up <- function() { readRDS(encrypt_creds_path()), key = readRDS(key_encrypt_creds_path()) ) - app <- oauth_app( + app <- httr::oauth_app( appname = "ottrpal", key = unserialize(decrypted)$client_id, secret = unserialize(decrypted)$client_secret ) - endpoint <- oauth_endpoints("google") + endpoint <- httr::oauth_endpoints("google") return(list(app = app, endpoint = endpoint)) } From 9c2db972f6169274fb02109f53a16666385dc978 Mon Sep 17 00:00:00 2001 From: Candace Savonen Date: Mon, 14 Aug 2023 14:53:17 -0400 Subject: [PATCH 10/12] Update handling of refresh and access tokens --- R/auth.R | 8 ++++++-- R/notes_to_fig_alt.R | 8 +++++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/R/auth.R b/R/auth.R index 098802c..608642e 100644 --- a/R/auth.R +++ b/R/auth.R @@ -77,17 +77,21 @@ authorize <- function(token = NULL, cache = FALSE, ...) { #' } #' auth_from_secret <- function(access_token = NULL, refresh_token = NULL) { + + # If no tokens are specified, we'll grab the default ones. if (is.null(access_token) | is.null(refresh_token)) { decrypted <- openssl::aes_cbc_decrypt( readRDS(encrypt_creds_user_path()), key = readRDS(key_encrypt_creds_path()) ) + access_token <- unserialize(decrypted)[[1]]$access_token + refresh_token <- unserialize(decrypted)[[1]]$refresh_token } credentials <- list( - access_token = unserialize(decrypted)[[1]]$access_token, + access_token = access_token, expires_in = 3599L, - refresh_token = unserialize(decrypted)[[1]]$refresh_token, + refresh_token = refresh_token, scope = scopes_list, token_type = "Bearer" ) diff --git a/R/notes_to_fig_alt.R b/R/notes_to_fig_alt.R index 2dff70d..9cb0714 100644 --- a/R/notes_to_fig_alt.R +++ b/R/notes_to_fig_alt.R @@ -279,7 +279,8 @@ xml_notes <- function(file, collapse_text = TRUE, xpath = "//a:r//a:t") { #' @param slide_url URL whose 'General access' is set to 'Anyone with the link' #' @param token OAuth 2.0 Access Token. If you don't have a token, use [authorize()] #' to obtain an access token from Google's OAuth 2.0 server. -#' +#' @param access_token Access token can be obtained from running authorize() interactively: token <-authorize(); token$credentials$access_token This allows it to be passed in using two secrets +#' @param refresh_token Refresh token can be obtained from running authorize() interactively: token <-authorize(); token$credentials$refresh_token This allows it to be passed in using two secrets #' @return Character vector of object ID(s) #' @importFrom httr config #' @importFrom httr GET @@ -295,7 +296,7 @@ xml_notes <- function(file, collapse_text = TRUE, xpath = "//a:r//a:t") { #' extract_object_id(slide_url = "https://docs.google.com/presentation/d/1H5aF_ROKVxE-H #' FHhoOy9vU2Y-y2M_PiV0q-JBL17Gss/edit?usp=sharing") #' } -extract_object_id = function(slide_url, token = NULL) { +extract_object_id = function(slide_url, token = NULL, access_token = NULL, refresh_token = NULL) { # Get Slide ID from URL id <- get_slide_id(slide_url) # Using Slide ID, create url that we'll send to GET @@ -309,7 +310,8 @@ extract_object_id = function(slide_url, token = NULL) { # We will supply credentials if none can be grabbed by get_token() if (is.null(token_try)) { - auth_from_secret() + auth_from_secret(access_token = access_token, + refresh_token = refresh_token) } token <- get_token() } # else user provides token From 043b79052efa0339e1376086a34f11bcf48dff1e Mon Sep 17 00:00:00 2001 From: howardbaek Date: Mon, 14 Aug 2023 11:55:18 -0700 Subject: [PATCH 11/12] Reflow comments --- R/notes_to_fig_alt.R | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/R/notes_to_fig_alt.R b/R/notes_to_fig_alt.R index 9cb0714..d2eb53e 100644 --- a/R/notes_to_fig_alt.R +++ b/R/notes_to_fig_alt.R @@ -273,14 +273,18 @@ xml_notes <- function(file, collapse_text = TRUE, xpath = "//a:r//a:t") { #' Extract Object IDs using Google Slides API #' -#' Performs a HTTP GET method to request the IDs of every slide in a Google Slides presentation. -#' The ID of the first slide is always 'p'. +#' Performs a HTTP GET method to request the IDs of every slide in a Google +#' Slides presentation. The ID of the first slide is always 'p'. #' #' @param slide_url URL whose 'General access' is set to 'Anyone with the link' -#' @param token OAuth 2.0 Access Token. If you don't have a token, use [authorize()] -#' to obtain an access token from Google's OAuth 2.0 server. -#' @param access_token Access token can be obtained from running authorize() interactively: token <-authorize(); token$credentials$access_token This allows it to be passed in using two secrets -#' @param refresh_token Refresh token can be obtained from running authorize() interactively: token <-authorize(); token$credentials$refresh_token This allows it to be passed in using two secrets +#' @param token OAuth 2.0 Access Token. If you don't have a token, use +#' [authorize()] to obtain an access token from Google's OAuth 2.0 server. +#' @param access_token Access token can be obtained from running authorize() +#' interactively: token <-authorize(); token$credentials$access_token This +#' allows it to be passed in using two secrets +#' @param refresh_token Refresh token can be obtained from running authorize() +#' interactively: token <-authorize(); token$credentials$refresh_token This +#' allows it to be passed in using two secrets #' @return Character vector of object ID(s) #' @importFrom httr config #' @importFrom httr GET From d0e7d6a10d037ca34f4e8bce0fa7d226d3871de1 Mon Sep 17 00:00:00 2001 From: howardbaek Date: Mon, 14 Aug 2023 12:06:37 -0700 Subject: [PATCH 12/12] Declare `openssl::aes_cbc_decrypt()` as a dependency --- DESCRIPTION | 1 + NAMESPACE | 1 + R/auth.R | 1 + R/notes_to_fig_alt.R | 8 ++++---- man/auth_from_secret.Rd | 8 ++++---- man/authorize.Rd | 8 ++++---- man/extract_object_id.Rd | 23 ++++++++++++++++++----- 7 files changed, 33 insertions(+), 17 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index bafd1cd..2d43b82 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -27,6 +27,7 @@ Imports: jsonlite, knitr (>= 1.33), magrittr, + openssl, purrr, R.utils, readr, diff --git a/NAMESPACE b/NAMESPACE index 1202f35..68aaac3 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -66,6 +66,7 @@ importFrom(httr,oauth_app) importFrom(httr,oauth_endpoints) importFrom(jsonlite,fromJSON) importFrom(magrittr,"%>%") +importFrom(openssl,aes_cbc_decrypt) importFrom(readr,write_tsv) importFrom(utils,download.file) importFrom(utils,installed.packages) diff --git a/R/auth.R b/R/auth.R index 608642e..2b871ef 100644 --- a/R/auth.R +++ b/R/auth.R @@ -65,6 +65,7 @@ authorize <- function(token = NULL, cache = FALSE, ...) { #' @return OAuth token saved to the environment so the package can use the users' Google data #' @importFrom utils menu installed.packages #' @importFrom httr oauth_app oauth_endpoints oauth2.0_token +#' @importFrom openssl aes_cbc_decrypt #' @export #' @examples \dontrun{ #' diff --git a/R/notes_to_fig_alt.R b/R/notes_to_fig_alt.R index d2eb53e..7640f8a 100644 --- a/R/notes_to_fig_alt.R +++ b/R/notes_to_fig_alt.R @@ -280,11 +280,11 @@ xml_notes <- function(file, collapse_text = TRUE, xpath = "//a:r//a:t") { #' @param token OAuth 2.0 Access Token. If you don't have a token, use #' [authorize()] to obtain an access token from Google's OAuth 2.0 server. #' @param access_token Access token can be obtained from running authorize() -#' interactively: token <-authorize(); token$credentials$access_token This -#' allows it to be passed in using two secrets +#' interactively (token <-authorize(); token$credentials$access_token). This +#' allows it to be passed in using two secrets. #' @param refresh_token Refresh token can be obtained from running authorize() -#' interactively: token <-authorize(); token$credentials$refresh_token This -#' allows it to be passed in using two secrets +#' interactively (token <-authorize(); token$credentials$refresh_token). This +#' allows it to be passed in using two secrets. #' @return Character vector of object ID(s) #' @importFrom httr config #' @importFrom httr GET diff --git a/man/auth_from_secret.Rd b/man/auth_from_secret.Rd index 35da89b..e99bbc6 100644 --- a/man/auth_from_secret.Rd +++ b/man/auth_from_secret.Rd @@ -2,20 +2,20 @@ % Please edit documentation in R/auth.R \name{auth_from_secret} \alias{auth_from_secret} -\title{Use secrets to Authorize R package to access Google slides API} +\title{Use secrets to authorize R package to access Google Slides API} \usage{ auth_from_secret(access_token = NULL, refresh_token = NULL) } \arguments{ -\item{access_token}{Access token can be obtained from running authorize interactively: token <-authorize(); token$credentials$access_token} +\item{access_token}{Access token can be obtained from running authorize() interactively: token <-authorize(); token$credentials$access_token} -\item{refresh_token}{Refresh token can be obtained from running authorize interactively: token <-authorize(); token$credentials$refresh_token} +\item{refresh_token}{Refresh token can be obtained from running authorize() interactively: token <-authorize(); token$credentials$refresh_token} } \value{ OAuth token saved to the environment so the package can use the users' Google data } \description{ -This is a function to authorize the R package to access the Slides API. If no +This is a function to authorize the R package to access the Google Slides API. If no client.id and client.secret is provided, the package would provide predefined values. } \examples{ diff --git a/man/authorize.Rd b/man/authorize.Rd index 89d3705..f26d32f 100644 --- a/man/authorize.Rd +++ b/man/authorize.Rd @@ -2,22 +2,22 @@ % Please edit documentation in R/auth.R \name{authorize} \alias{authorize} -\title{Authorize R package to access Google slides API} +\title{Authorize R package to access the Google Slides API} \usage{ authorize(token = NULL, cache = FALSE, ...) } \arguments{ -\item{token}{an output from \code{\link{oauth2.0_token}} to set as the authentication token.} +\item{token}{An output from \code{\link{oauth2.0_token}} to set as the authentication token.} \item{cache}{Should the token be cached as an .httr-oauth file?} -\item{...}{additional arguments to send to \code{\link{oauth2.0_token}}} +\item{...}{Additional arguments to send to \code{\link{oauth2.0_token}}} } \value{ OAuth token saved to the environment so the package can use the users' Google data } \description{ -This is a function to authorize the R package to access the Googleclassroom API interactively. +This is a function to authorize the R package to access the Google Slides API interactively. } \examples{ \dontrun{ diff --git a/man/extract_object_id.Rd b/man/extract_object_id.Rd index fc9d87a..18e8f43 100644 --- a/man/extract_object_id.Rd +++ b/man/extract_object_id.Rd @@ -4,20 +4,33 @@ \alias{extract_object_id} \title{Extract Object IDs using Google Slides API} \usage{ -extract_object_id(slide_url, token = NULL) +extract_object_id( + slide_url, + token = NULL, + access_token = NULL, + refresh_token = NULL +) } \arguments{ \item{slide_url}{URL whose 'General access' is set to 'Anyone with the link'} -\item{token}{OAuth 2.0 Access Token. If you don't have a token, use [authorize()] -to obtain an access token from Google's OAuth 2.0 server.} +\item{token}{OAuth 2.0 Access Token. If you don't have a token, use +[authorize()] to obtain an access token from Google's OAuth 2.0 server.} + +\item{access_token}{Access token can be obtained from running authorize() +interactively (token <-authorize(); token$credentials$access_token). This +allows it to be passed in using two secrets.} + +\item{refresh_token}{Refresh token can be obtained from running authorize() +interactively (token <-authorize(); token$credentials$refresh_token). This +allows it to be passed in using two secrets.} } \value{ Character vector of object ID(s) } \description{ -Performs a HTTP GET method to request the IDs of every slide in a Google Slides presentation. -The ID of the first slide is always 'p'. +Performs a HTTP GET method to request the IDs of every slide in a Google +Slides presentation. The ID of the first slide is always 'p'. } \examples{ \dontrun{