diff --git a/R/privileges.R b/R/privileges.R index c337d0e..b55870a 100644 --- a/R/privileges.R +++ b/R/privileges.R @@ -4,7 +4,7 @@ #' @param .data an s3 object of class `privilege` #' @param ... one of all, select, update, insert, delete #' @param cols (character) vector of column names -#' @examplesIf interactive() && has_postgres() +#' @examplesIf has_postgres() #' library(DBI) #' library(RPostgres) #' con <- dbConnect(Postgres()) @@ -23,6 +23,9 @@ #' rls_tbl(con, "passwd") %>% #' grant(update, select, cols = c("real_name", "home_phone")) %>% #' to(jane) +#' +#' # cleanup +#' dbDisconnect(con) grant <- function(.data, ..., cols = NULL) { pipe_autoexec(toggle = rls_env$auto_pipe) .data <- as_priv(.data) @@ -40,7 +43,7 @@ grant <- function(.data, ..., cols = NULL) { #' #' @export #' @inheritParams grant -#' @examplesIf interactive() && has_postgres() +#' @examplesIf has_postgres() #' library(DBI) #' library(RPostgres) #' con <- dbConnect(Postgres()) @@ -55,6 +58,9 @@ grant <- function(.data, ..., cols = NULL) { #' rls_tbl(con, "passwd") %>% #' revoke(update, cols = c("real_name", "home_phone")) %>% #' from(jane) +#' +#' # cleanup +#' dbDisconnect(con) revoke <- function(.data, ..., cols = NULL) { pipe_autoexec(toggle = rls_env$auto_pipe) .data <- as_priv(.data) @@ -81,13 +87,23 @@ revoke <- function(.data, ..., cols = NULL) { #' setup_example_table(con, "passwd") #' } #' -#' rls_tbl(con, "passwd") %>% to(jane) -#' rls_tbl(con, "passwd") %>% to(jane, bob, alice) +#' rls_tbl(con, "passwd") %>% grant(select) %>% to(jane) +#' rls_tbl(con, "passwd") %>% grant(select) %>% from(jane) +#' rls_tbl(con, "passwd") %>% grant(select) %>% to(jane, bob, alice) +#' +#' # Errors: doesn't make sense to pass rls_tbl output directly to to/from +#' # rls_tbl(con, "passwd") %>% from(jane) +#' # #> ! must pass privilege or row_policy to to/from +#' +#' # cleanup +#' dbDisconnect(con) to <- function(.data, ...) { pipe_autoexec(toggle = rls_env$auto_pipe) - .data <- switch(class(.data), + assert_is(.data, c("privilege", "row_policy", "tbl_sql")) + .data <- switch_multiclass(class(.data), privilege = as_priv(.data), - row_policy = as_row_policy(.data) + row_policy = as_row_policy(.data), + tbl_sql = rls_abort("must pass privilege or row_policy to to/from") ) .data$user <- dot_names(...) .data @@ -103,7 +119,7 @@ from <- to #' @keywords internal #' @param priv an S3 object of class `privilege`, required #' @param con DBI connection object, required -#' @examplesIf interactive() +#' @examplesIf interactive() && has_postgres() #' library(tibble) #' library(RPostgres) #' library(DBI) @@ -121,7 +137,7 @@ from <- to #' # ON fruits #' # TO jane #' priv <- -#' rls_tbl(con, "flights") %>% +#' rls_tbl(con, "fruits") %>% #' grant(select) %>% #' to(jane) #' priv @@ -147,7 +163,13 @@ from <- to #' to(jane) #' priv #' sql <- translate_privilege(priv, con) +#' sql #' dbExecute(con, sql) +#' +#' # cleanup +#' dbRemoveTable(con, "fruits") +#' dbExecute(con, "DROP ROLE jane") +#' dbDisconnect(con) translate_privilege <- function(priv, con) { assert_is(priv, "privilege") is_conn(con) diff --git a/R/row_policy.R b/R/row_policy.R index 9a3a37a..d2dc3ca 100644 --- a/R/row_policy.R +++ b/R/row_policy.R @@ -3,7 +3,7 @@ #' @export #' @inheritParams grant #' @param name (character) scalar name for the policy. required -#' @examplesIf interactive() && has_postgres() +#' @examplesIf has_postgres() #' library(DBI) #' library(RPostgres) #' con <- dbConnect(Postgres()) @@ -13,6 +13,11 @@ #' rls_tbl(con, "passwd") %>% #' row_policy("my_policy") %>% #' rls_run() +#' rls_policies(con) +#' +#' # cleanup +#' rls_drop_policies(con) +#' dbDisconnect(con) row_policy <- function(.data, name) { pipe_autoexec(toggle = rls_env$auto_pipe) assert_is(name, "character") @@ -26,7 +31,7 @@ row_policy <- function(.data, name) { #' #' @export #' @inheritParams grant -#' @examplesIf interactive() && has_postgres() +#' @examplesIf has_postgres() #' library(DBI) #' library(RPostgres) #' con <- dbConnect(Postgres()) @@ -36,6 +41,9 @@ row_policy <- function(.data, name) { #' rls_tbl(con, "passwd") %>% #' row_policy("their_policy") %>% #' commands(update) +#' +#' # cleanup +#' dbDisconnect(con) commands <- function(.data, ...) { pipe_autoexec(toggle = rls_env$auto_pipe) .data <- as_row_policy(.data) @@ -50,7 +58,7 @@ commands <- function(.data, ...) { #' @param using an expression to use to check against existing rows #' @param sql (character) sql syntax to use for existing rows #' @details Use either `using` or `sql`, not both -#' @examplesIf interactive() && has_postgres() +#' @examplesIf has_postgres() #' library(DBI) #' library(RPostgres) #' con <- dbConnect(Postgres()) @@ -61,6 +69,9 @@ commands <- function(.data, ...) { #' row_policy("a_good_policy") %>% #' commands(update) %>% #' rows_existing(sql = 'current_user = "user_name"') +#' +#' # cleanup +#' dbDisconnect(con) rows_existing <- function(.data, using = NULL, sql = NULL) { pipe_autoexec(toggle = rls_env$auto_pipe) using_quo <- enquo(using) @@ -84,7 +95,7 @@ rows_existing <- function(.data, using = NULL, sql = NULL) { #' new rows or editing of existing rows #' @param sql (character) sql syntax to use for new rows #' @details Use either `check` or `sql`, not both -#' @examplesIf interactive() && has_postgres() +#' @examplesIf has_postgres() #' library(DBI) #' library(RPostgres) #' con <- dbConnect(Postgres()) @@ -105,6 +116,9 @@ rows_existing <- function(.data, using = NULL, sql = NULL) { #' rows_existing(sql = 'current_user = "user_name"') %>% #' rows_new(home_phone == "098-765-4321") %>% #' to(jane) +#' +#' # cleanup +#' dbDisconnect(con) rows_new <- function(.data, check = NULL, sql = NULL) { pipe_autoexec(toggle = rls_env$auto_pipe) check_quo <- enquo(check) @@ -137,11 +151,14 @@ express <- function(x) { #' @param policy an S3 object of class `row_policy`, required #' @param con DBI connection object, required #' @references -#' @examplesIf interactive() -#' library(RPostgres) +#' @examplesIf has_postgres() #' library(DBI) +#' library(RPostgres) #' con <- dbConnect(Postgres()) #' setup_example_table(con) +#' +#' # create role +#' dbExecute(con, "CREATE ROLE jane") #' #' if (rls_policy_exists(con, "blue_policy")) { #' rls_drop_policy(con, name = "blue_policy", table = "passwd") @@ -157,6 +174,11 @@ express <- function(x) { #' sql <- translate_row_policy(policy, con) #' sql #' dbExecute(con, sql) +#' +#' # cleanup +#' rls_drop_policies(con) +#' dbExecute(con, "DROP ROLE jane") +#' dbDisconnect(con) translate_row_policy <- function(policy, con) { is_conn(con) create_statement <- switch(class(con), diff --git a/R/utils.R b/R/utils.R index 6872066..5b037e4 100644 --- a/R/utils.R +++ b/R/utils.R @@ -76,3 +76,14 @@ dot_names <- function(...) { collapse <- function(x) { paste(x, collapse = ", ") } + +#' switch that handles more than 1 class in the EXPR variable +#' @noRd +switch_multiclass <- function(EXPR, ...) { + classes <- c("tbl_sql", "privilege", "row_policy") + check_in <- classes %in% EXPR + if (!any(check_in)) { + rls_abort(format_error(".data must be one of {clz_col(classes)}")) + } + switch(classes[check_in], ...) +} diff --git a/man/commands.Rd b/man/commands.Rd index 36faace..03a753c 100644 --- a/man/commands.Rd +++ b/man/commands.Rd @@ -15,7 +15,7 @@ commands(.data, ...) Commands } \examples{ -\dontshow{if (interactive() && has_postgres()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} +\dontshow{if (has_postgres()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} library(DBI) library(RPostgres) con <- dbConnect(Postgres()) @@ -25,5 +25,8 @@ if (!dbExistsTable(con, "passwd")) { rls_tbl(con, "passwd") \%>\% row_policy("their_policy") \%>\% commands(update) + +# cleanup +dbDisconnect(con) \dontshow{\}) # examplesIf} } diff --git a/man/grant.Rd b/man/grant.Rd index 6824b38..0220d30 100644 --- a/man/grant.Rd +++ b/man/grant.Rd @@ -17,7 +17,7 @@ grant(.data, ..., cols = NULL) Grant } \examples{ -\dontshow{if (interactive() && has_postgres()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} +\dontshow{if (has_postgres()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} library(DBI) library(RPostgres) con <- dbConnect(Postgres()) @@ -36,5 +36,8 @@ rls_tbl(con, "passwd") \%>\% rls_tbl(con, "passwd") \%>\% grant(update, select, cols = c("real_name", "home_phone")) \%>\% to(jane) + +# cleanup +dbDisconnect(con) \dontshow{\}) # examplesIf} } diff --git a/man/revoke.Rd b/man/revoke.Rd index 551adc6..c233473 100644 --- a/man/revoke.Rd +++ b/man/revoke.Rd @@ -17,7 +17,7 @@ revoke(.data, ..., cols = NULL) Revoke } \examples{ -\dontshow{if (interactive() && has_postgres()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} +\dontshow{if (has_postgres()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} library(DBI) library(RPostgres) con <- dbConnect(Postgres()) @@ -32,5 +32,8 @@ rls_tbl(con, "passwd") \%>\% rls_tbl(con, "passwd") \%>\% revoke(update, cols = c("real_name", "home_phone")) \%>\% from(jane) + +# cleanup +dbDisconnect(con) \dontshow{\}) # examplesIf} } diff --git a/man/row_policy.Rd b/man/row_policy.Rd index eb6dd4b..cccec42 100644 --- a/man/row_policy.Rd +++ b/man/row_policy.Rd @@ -15,7 +15,7 @@ row_policy(.data, name) Row policy } \examples{ -\dontshow{if (interactive() && has_postgres()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} +\dontshow{if (has_postgres()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} library(DBI) library(RPostgres) con <- dbConnect(Postgres()) @@ -25,5 +25,10 @@ if (!dbExistsTable(con, "passwd")) { rls_tbl(con, "passwd") \%>\% row_policy("my_policy") \%>\% rls_run() +rls_policies(con) + +# cleanup +rls_drop_policies(con) +dbDisconnect(con) \dontshow{\}) # examplesIf} } diff --git a/man/rows_existing.Rd b/man/rows_existing.Rd index 286b98c..e9d3f0b 100644 --- a/man/rows_existing.Rd +++ b/man/rows_existing.Rd @@ -20,7 +20,7 @@ Create rule for existing rows Use either \code{using} or \code{sql}, not both } \examples{ -\dontshow{if (interactive() && has_postgres()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} +\dontshow{if (has_postgres()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} library(DBI) library(RPostgres) con <- dbConnect(Postgres()) @@ -31,5 +31,8 @@ rls_tbl(con, "passwd") \%>\% row_policy("a_good_policy") \%>\% commands(update) \%>\% rows_existing(sql = 'current_user = "user_name"') + +# cleanup +dbDisconnect(con) \dontshow{\}) # examplesIf} } diff --git a/man/rows_new.Rd b/man/rows_new.Rd index eaba3ff..65044e0 100644 --- a/man/rows_new.Rd +++ b/man/rows_new.Rd @@ -21,7 +21,7 @@ Create rule for new rows Use either \code{check} or \code{sql}, not both } \examples{ -\dontshow{if (interactive() && has_postgres()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} +\dontshow{if (has_postgres()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} library(DBI) library(RPostgres) con <- dbConnect(Postgres()) @@ -42,5 +42,8 @@ rls_tbl(con, "passwd") \%>\% rows_existing(sql = 'current_user = "user_name"') \%>\% rows_new(home_phone == "098-765-4321") \%>\% to(jane) + +# cleanup +dbDisconnect(con) \dontshow{\}) # examplesIf} } diff --git a/man/to.Rd b/man/to.Rd index 14a3714..a8b4ceb 100644 --- a/man/to.Rd +++ b/man/to.Rd @@ -26,7 +26,15 @@ if (!dbExistsTable(con, "passwd")) { setup_example_table(con, "passwd") } -rls_tbl(con, "passwd") \%>\% to(jane) -rls_tbl(con, "passwd") \%>\% to(jane, bob, alice) +rls_tbl(con, "passwd") \%>\% grant(select) \%>\% to(jane) +rls_tbl(con, "passwd") \%>\% grant(select) \%>\% from(jane) +rls_tbl(con, "passwd") \%>\% grant(select) \%>\% to(jane, bob, alice) + +# Errors: doesn't make sense to pass rls_tbl output directly to to/from +# rls_tbl(con, "passwd") \%>\% from(jane) +# #> ! must pass privilege or row_policy to to/from + +# cleanup +dbDisconnect(con) \dontshow{\}) # examplesIf} } diff --git a/man/translate_privilege.Rd b/man/translate_privilege.Rd index 41765dc..f4b793d 100644 --- a/man/translate_privilege.Rd +++ b/man/translate_privilege.Rd @@ -15,7 +15,7 @@ translate_privilege(priv, con) Translate privilege } \examples{ -\dontshow{if (interactive()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} +\dontshow{if (interactive() && has_postgres()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} library(tibble) library(RPostgres) library(DBI) @@ -33,7 +33,7 @@ auto_pipe(FALSE) # ON fruits # TO jane priv <- - rls_tbl(con, "flights") \%>\% + rls_tbl(con, "fruits") \%>\% grant(select) \%>\% to(jane) priv @@ -59,7 +59,13 @@ priv <- to(jane) priv sql <- translate_privilege(priv, con) +sql dbExecute(con, sql) + +# cleanup +dbRemoveTable(con, "fruits") +dbExecute(con, "DROP ROLE jane") +dbDisconnect(con) \dontshow{\}) # examplesIf} } \keyword{internal} diff --git a/man/translate_row_policy.Rd b/man/translate_row_policy.Rd index 135c901..b010d9b 100644 --- a/man/translate_row_policy.Rd +++ b/man/translate_row_policy.Rd @@ -15,12 +15,15 @@ translate_row_policy(policy, con) Translate row policy } \examples{ -\dontshow{if (interactive()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} -library(RPostgres) +\dontshow{if (has_postgres()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} library(DBI) +library(RPostgres) con <- dbConnect(Postgres()) setup_example_table(con) +# create role +dbExecute(con, "CREATE ROLE jane") + if (rls_policy_exists(con, "blue_policy")) { rls_drop_policy(con, name = "blue_policy", table = "passwd") } @@ -35,6 +38,11 @@ policy sql <- translate_row_policy(policy, con) sql dbExecute(con, sql) + +# cleanup +rls_drop_policies(con) +dbExecute(con, "DROP ROLE jane") +dbDisconnect(con) \dontshow{\}) # examplesIf} } \references{