From 84a5d12cd461706c6d1446c35f9bcbedaea0fc05 Mon Sep 17 00:00:00 2001 From: Romain Lesur Date: Mon, 21 Oct 2019 03:06:47 +0200 Subject: [PATCH] init --- .Rbuildignore | 5 ++ .gitignore | 5 ++ DESCRIPTION | 14 +++++ LICENSE | 2 + LICENSE.md | 21 +++++++ NAMESPACE | 3 + R/insee_auth.R | 152 ++++++++++++++++++++++++++++++++++++++++++++++ R/zzz.R | 18 ++++++ README.Rmd | 31 ++++++++++ README.md | 22 +++++++ apinsee.Rproj | 21 +++++++ man/insee_auth.Rd | 35 +++++++++++ 12 files changed, 329 insertions(+) create mode 100644 .Rbuildignore create mode 100644 .gitignore create mode 100644 DESCRIPTION create mode 100644 LICENSE create mode 100644 LICENSE.md create mode 100644 NAMESPACE create mode 100644 R/insee_auth.R create mode 100644 R/zzz.R create mode 100644 README.Rmd create mode 100644 README.md create mode 100644 apinsee.Rproj create mode 100644 man/insee_auth.Rd diff --git a/.Rbuildignore b/.Rbuildignore new file mode 100644 index 0000000..d1b80dd --- /dev/null +++ b/.Rbuildignore @@ -0,0 +1,5 @@ +^apinsee\.Rproj$ +^\.Rproj\.user$ +^LICENSE\.md$ +^README\.Rmd$ +^\.httr-oauth$ diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d5a36a0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.Rproj.user +.Rhistory +.RData +.Ruserdata +.httr-oauth diff --git a/DESCRIPTION b/DESCRIPTION new file mode 100644 index 0000000..a73fd9b --- /dev/null +++ b/DESCRIPTION @@ -0,0 +1,14 @@ +Package: apinsee +Title: Manage Access to Insee APIs +Version: 0.0.0.9000 +Authors@R: c( + person("Romain", "Lesur", role = c("aut", "cre"), email = "romain.lesur@gmail.com", comment = c(ORCID = "0000-0002-0721-5595")), + person() + ) +Description: Ease access to Insee APIs. +License: MIT + file LICENSE +Encoding: UTF-8 +LazyData: true +Imports: + httr +RoxygenNote: 6.1.1 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..55c3c8e --- /dev/null +++ b/LICENSE @@ -0,0 +1,2 @@ +YEAR: 2019 +COPYRIGHT HOLDER: Romain Lesur diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..99ec366 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +# MIT License + +Copyright (c) 2019 Romain Lesur + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/NAMESPACE b/NAMESPACE new file mode 100644 index 0000000..60d3bdc --- /dev/null +++ b/NAMESPACE @@ -0,0 +1,3 @@ +# Generated by roxygen2: do not edit by hand + +export(insee_auth) diff --git a/R/insee_auth.R b/R/insee_auth.R new file mode 100644 index 0000000..71cde73 --- /dev/null +++ b/R/insee_auth.R @@ -0,0 +1,152 @@ +# environment to store credentials +.state <- new.env(parent = emptyenv()) + +#' Authenticate to an Insee application +#' +#' @param token optional; an actual token object or the path to a valid token +#' stored as an \code{.rds} file. +#' @param new_app logical, defaults to \code{FALSE}. Set to \code{TRUE} if you +#' want to wipe the slate clean and re-authenticate with the same or different +#' application. This disables the \code{.httr-oauth} file in current +#' working directory. +#' @param appname application name. +#' @param key,secret consumer key and secret of the application. +#' @param cache logical indicating if \code{apinsee} should cache +#' credentials in the default cache file \code{.httr-oauth}. +#' @param verbose print message. +#' +#' @return A token. +#' @export +insee_auth <- function( + token = NULL, + new_app = FALSE, + appname = "DefaultApplication", + key = Sys.getenv("INSEE_API_KEY"), + secret = Sys.getenv("INSEE_API_SECRET"), + cache = getOption("httr_oauth_cache"), + verbose = TRUE +) { + + if (new_app) { + insee_deauth(clear_cache = TRUE, verbose = verbose) + } + + if (is.null(token)) { + + scope_list <- c(.state$nomenclatures_url, + .state$sirene_url, + "https://api.insee.fr/entreprises/sirene/") + + insee_endpoint <- httr::oauth_endpoint( + base_url = "https://api.insee.fr", + request = NULL, + authorize = NULL, + access = "token", + revoke = "revoke" + ) + + user_app <- httr::oauth_app(appname = appname, key = key, secret = secret) + + insee_token <- httr::oauth2.0_token( + insee_endpoint, + user_app, + scope = scope_list, + use_basic_auth = TRUE, + cache = cache, + client_credentials = TRUE + ) + stopifnot(is_legit_token(insee_token, verbose = TRUE)) + .state$token <- insee_token + + } else if (inherits(token, "Token2.0")) { + + stopifnot(is_legit_token(token, verbose = TRUE)) + .state$token <- token + + } else if (inherits(token, "character")) { + + insee_token <- try(suppressWarnings(readRDS(token)), silent = TRUE) + if (inherits(insee_token, "try-error")) { + stop(sprintf("Cannot read token from alleged .rds file:\n%s", token), call. = FALSE) + } else if (!is_legit_token(insee_token, verbose = TRUE)) { + stop(sprintf("File does not contain a proper token:\n%s", token), call. = FALSE) + } + .state$token <- insee_token + } else { + stop("Input provided via 'token' is neither a", + "token,\nnor a path to an .rds file containing a token.", call. = FALSE) + } + + invisible(.state$token) + +} + +insee_deauth <- function( + clear_cache = TRUE, verbose = TRUE +) { + + if (clear_cache && file.exists(".httr-oauth")) { + if (verbose) { + message("Disabling .httr-oauth by renaming to .httr-oauth-SUSPENDED") + } + file.rename(".httr-oauth", ".httr-oauth-SUSPENDED") + } + + if (token_available(verbose = FALSE)) { + if (verbose) { + message("Removing token stashed internally in 'apinsee'.") + } + rm("token", envir = .state) + } else { + message("No token currently in force.") + } + + invisible(NULL) + +} + +token_available <- function(verbose = TRUE) { + + if (is.null(.state$token)) { + if (verbose) { + if (file.exists(".httr-oauth")) { + message("A .httr-oauth file exists in current working ", + "directory.\nWhen/if needed, the credentials cached in ", + ".httr-oauth will be used for this session.\nOr run insee_auth() ", + "for explicit authentication and authorization.") + } else { + message("No .httr-oauth file exists in current working directory.\n", + "When/if needed, 'apinsee' will initiate authentication ", + "and authorization.\nOr run insee_auth() to trigger this ", + "explicitly.") + } + } + return(FALSE) + } + + TRUE + +} + +is_legit_token <- function(x, verbose = FALSE) { + + if (!inherits(x, "Token2.0")) { + if (verbose) message("Not a Token2.0 object.") + return(FALSE) + } + + if ("invalid_client" %in% unlist(x$credentials)) { + if (verbose) { + message("Authorization error. Please check application key and secret.") + } + return(FALSE) + } + + if ("invalid_request" %in% unlist(x$credentials)) { + if (verbose) message("Authorization error. No access token obtained.") + return(FALSE) + } + + TRUE + +} diff --git a/R/zzz.R b/R/zzz.R new file mode 100644 index 0000000..24cf7db --- /dev/null +++ b/R/zzz.R @@ -0,0 +1,18 @@ +# nocov start +.onLoad <- function(libname, pkgname) { + + op <- options() + op.apinsee <- list( + httr_oauth_cache = NA + ) + toset <- !(names(op.apinsee) %in% names(op)) + if(any(toset)) options(op.apinsee[toset]) + + invisible() + +} + +# store base urls in the '.state' internal environment (created in insee_auth.R) +.state$sirene_url <- "https://api.insee.fr/entreprises/sirene/V3" +.state$nomenclatures_url <- "https://api.insee.fr/metadonnees/nomenclatures/v1" +# nocov end diff --git a/README.Rmd b/README.Rmd new file mode 100644 index 0000000..3bb12a3 --- /dev/null +++ b/README.Rmd @@ -0,0 +1,31 @@ +--- +output: github_document +--- + + + +```{r, include = FALSE} +knitr::opts_chunk$set( + collapse = TRUE, + comment = "#>", + fig.path = "man/figures/README-", + out.width = "100%" +) +``` +# apinsee + + + + +The goal of apinsee is to ease access to Insee APIs available at + +## Installation + +You can install the development version from [GitHub](https://github.com/) with: + +``` r +remotes::install_github("rlesur/apinsee") +``` +## Example + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..ddb0394 --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ + + + +# apinsee + + + + + +The goal of apinsee is to ease access to Insee APIs available at + + +## Installation + +You can install the development version from +[GitHub](https://github.com/) with: + +``` r +remotes::install_github("rlesur/apinsee") +``` + +## Example diff --git a/apinsee.Rproj b/apinsee.Rproj new file mode 100644 index 0000000..cba1b6b --- /dev/null +++ b/apinsee.Rproj @@ -0,0 +1,21 @@ +Version: 1.0 + +RestoreWorkspace: No +SaveWorkspace: No +AlwaysSaveHistory: Default + +EnableCodeIndexing: Yes +UseSpacesForTab: Yes +NumSpacesForTab: 2 +Encoding: UTF-8 + +RnwWeave: Sweave +LaTeX: pdfLaTeX + +AutoAppendNewline: Yes +StripTrailingWhitespace: Yes + +BuildType: Package +PackageUseDevtools: Yes +PackageInstallArgs: --no-multiarch --with-keep.source +PackageRoxygenize: rd,collate,namespace diff --git a/man/insee_auth.Rd b/man/insee_auth.Rd new file mode 100644 index 0000000..282de2a --- /dev/null +++ b/man/insee_auth.Rd @@ -0,0 +1,35 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/insee_auth.R +\name{insee_auth} +\alias{insee_auth} +\title{Authenticate to an Insee application} +\usage{ +insee_auth(token = NULL, new_app = FALSE, + appname = "DefaultApplication", key = Sys.getenv("INSEE_API_KEY"), + secret = Sys.getenv("INSEE_API_SECRET"), + cache = getOption("httr_oauth_cache"), verbose = TRUE) +} +\arguments{ +\item{token}{optional; an actual token object or the path to a valid token +stored as an \code{.rds} file.} + +\item{new_app}{logical, defaults to \code{FALSE}. Set to \code{TRUE} if you +want to wipe the slate clean and re-authenticate with the same or different +application. This disables the \code{.httr-oauth} file in current +working directory.} + +\item{appname}{application name.} + +\item{key, secret}{consumer key and secret of the application.} + +\item{cache}{logical indicating if \code{apinsee} should cache +credentials in the default cache file \code{.httr-oauth}.} + +\item{verbose}{print message.} +} +\value{ +A token. +} +\description{ +Authenticate to an Insee application +}