R/development/review/decrypt_set_profile.R

Defines functions decrypt_profile set_profile

#' Initialize New Environment
#'
#' Functions to help init new R environment
#'
#' @param chk_pwd Some settings are stored as encrypted objects. Password is required to decrypt and set
#' @param prof_config optional config object (return value of \code{decrypt_profile}) If not provided, \code{decrypt_profile} will be called
#' @param show_file boolean. If true, and \code{interactive() == TRUE} then will use rstudioapi to show .Rprofile once written
#'
#' @importFrom utils install.packages installed.packages
#' @importFrom rstudioapi askForPassword navigateToFile
#' @importFrom sodium data_decrypt sha256 password_verify
#'
#' @name setup_package
NULL

#' @describeIn setup_package Retrieves the internally stored encrypted config list. Requires password used to encrypt object
#' @export
decrypt_profile <- function(chk_pwd = ""){

  # read in stored hashed password
  hash_path <- system.file("temp", "hash_pwd", package = "ninjar")
  hash_pwd  <- readRDS(hash_path)

  # read in hashed config file
  # config_path <- "inst/temp/config_ciph"
  config_path <- system.file("temp", "config_ciph", package = "ninjar")
  config_ciph <- readRDS(config_path)

  #if password provided then check. Error if wrong (if provided, likely not desirable to go into interactive mode)
  if(chk_pwd != ""){
    if(sodium::password_verify(hash_pwd, chk_pwd)){
      prof_config <- unserialize(sodium::data_decrypt(config_ciph, sodium::sha256(charToRaw(chk_pwd))))
    }else{
      stop("incorrect password provided", call. = FALSE)
    }
  }

  ## Ask user for password to verify. if match then decrypt the settings
  while(!sodium::password_verify(hash_pwd, chk_pwd)){
    chk_pwd <- rstudioapi::askForPassword("Enter password to decrypt")

    if(is.null(chk_pwd))
      stop("No password given. Canceled decryption", call. = FALSE)

    prof_config <- try(
      unserialize(sodium::data_decrypt(config_ciph, sodium::sha256(charToRaw(chk_pwd)))),
      silent = TRUE
    )
  }
  if(class(prof_config) == "try-error")
    stop("Unable able to decrypt", call. = FALSE)
  return(prof_config)
}


#' @describeIn setup_package Takes in the config object (return value of \code{decrypt_config} and updates .Rprofile and settings)
#' @export
set_profile <- function(prof_config = NULL, show_file = TRUE){
  if(is.null(prof_config))
    prof_config <- decrypt_profile()

  # set system env
  envs <- prof_config$env_vars
  envs$R_PROFILE_USER <- paste0(Sys.getenv("R_USER"), "/.Rprofile")

  # install.default cran packages and set default in .Rprofile
  cran_pkg  <- prof_config$default_cran
  cran_inst <- cran_pkg[!cran_pkg %in% row.names(utils::installed.packages())]

  bool <- FALSE
  if(length(cran_inst) > 0){
    bool <- sapply(cran_inst, function(pk){
      tryCatch({
        utils::install.packages(pk) #Ncpus = parallel::detectCores())
        return(TRUE)
      }, error=function(c){
        return(FALSE)
      })
    })
  }

  packages <- unique(c(
    cran_pkg[!cran_pkg %in% names(bool)],
    cran_inst[bool],
    getOption("defaultPackages")
  ))

  # form deparsed code to write into .Rprofile. Also, need to set .Rprofile now so on restart it runs the rest
  txt <- c(
    deparse(call("options", defaultPackages = packages)), "",
    paste0("Sys.setenv(", paste0(names(envs), ' = "', envs, '"'), ")")
  )
  Sys.setenv(R_PROFILE_USER = envs$R_PROFILE_USER)
  writeLines(txt, envs$R_PROFILE_USER)

  #if interactive AND show_file then use rstudioapi to focus file
  if(interactive() & show_file)
    rstudioapi::navigateToFile(envs$R_PROFILE_USER)
  return(TRUE)
}
bfatemi/ninjar documentation built on Sept. 8, 2019, 7:37 p.m.