Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
0fdb0ca
Increment version number to 0.1.0.9000
villegar Jun 1, 2026
07fa889
Update parsing of Armadillo credentials
villegar Jun 26, 2026
8a5ec3b
Minor user message update
villegar Jun 26, 2026
f83de7f
Add placeholders for S3 generic method for *.ArmadilloCredentials class
villegar Jul 7, 2026
ca1e9cc
New build
villegar Jul 7, 2026
0c57f6f
Add documentation to internal functions, for future developments
villegar Jul 7, 2026
15a38c4
Restructure internal methods
villegar Jul 7, 2026
39002b8
Add new arg verbose to notify users about system readiness for auditing
villegar Jul 8, 2026
ebc18e0
Update internal documentation to include S3 generic methods in the do…
villegar Jul 8, 2026
330a7af
New build
villegar Jul 8, 2026
5385d7a
Add new module to capture all the calls to functions from opalr in a …
villegar Jul 9, 2026
92e61f0
Update calls to opalr functions with backend_* S3 generics
villegar Jul 9, 2026
4815cd3
New build
villegar Jul 9, 2026
faf70cc
Add internal helpers
villegar Jul 9, 2026
88d3ca7
Update internal helper to include an argument for setting the value t…
villegar Jul 10, 2026
155fdb2
Update code to use internal .try_load function
villegar Jul 10, 2026
01c324d
Remove internal functions from documentation
villegar Jul 10, 2026
961cbf3
Update code to filter out admin/audit users from report based on the …
villegar Jul 10, 2026
3fa60fe
Add new internal generic function, filter_safe_people to extract and …
villegar Jul 10, 2026
4855c13
Add default S3 generics
villegar Jul 10, 2026
46123d5
Remove unused generic definition
villegar Jul 10, 2026
ee807ec
Bump minimum version of testthat to use local_mocked_bindings
villegar Jul 10, 2026
64acd9a
Add new unit tests
villegar Jul 10, 2026
cb5caac
New build
villegar Jul 10, 2026
c701813
Add function documentation for filter_safe_people and update S3 gener…
villegar Jul 13, 2026
9ec4ee2
Update documentation and relocate error message
villegar Jul 13, 2026
a490d7e
Update test to use local files instead of installed packages
villegar Jul 13, 2026
5c8fd9c
Update test to account for when no safe people details are found
villegar Jul 13, 2026
b8941dc
Update test to mock verification of admin and auditor roles
villegar Jul 13, 2026
484ddef
Add new generic methods for is_admin_con and is_audit_con, plus imple…
villegar Jul 13, 2026
4123a79
Relocate generics for validate_backend_version and validate_con
villegar Jul 13, 2026
7ef7c5f
Replace T by TRUE
villegar Jul 14, 2026
5900b2e
Add tmp directory for test, plust command to delete on.exit
villegar Jul 14, 2026
2e57412
Add new modules to create safe_symbol and symbol_registry objects, pl…
villegar Jul 15, 2026
c0a158a
Add steps to create symbol registry from ASSIGN operations
villegar Jul 15, 2026
9b04eff
Add S3 generic print method for safe_symbol
villegar Jul 15, 2026
b041011
New build
villegar Jul 15, 2026
8e32955
Add field for unique ID for each symbol, automatically generated by u…
villegar Jul 15, 2026
7b16c45
Update approach to register and update symbols in the registry
villegar Jul 15, 2026
4fbec12
Add new dependency, uuid
villegar Jul 15, 2026
56ddae5
Add new module with utilitarian functions for safe_call
villegar Jul 15, 2026
a9ff898
Update components for safe_symbol
villegar Jul 15, 2026
ea0afc6
Update reshaping of log entries with symbols
villegar Jul 15, 2026
dd7fdef
Update symbol registry
villegar Jul 15, 2026
549f2d1
Add new module for safe_call objects, these are used to parse the log…
villegar Jul 16, 2026
e7aadd0
Add new print generic for safe_call
villegar Jul 16, 2026
fcc5b4b
Add step to generate a list of safe_call objects
villegar Jul 16, 2026
ebefabd
Fox print.safe_call to handle when args are objects of the class safe…
villegar Jul 16, 2026
0dbc52b
Update documentation and parsing of calls
villegar Jul 16, 2026
7bf54ac
Introduce new helper, enrich_argument
villegar Jul 17, 2026
6718247
Add new S3 generic for as.data.frame.safe_call objects and update the…
villegar Jul 17, 2026
2de0aeb
Add new S3 generic for as.data.frame.safe_symbol objects and redefine…
villegar Jul 17, 2026
cc91a95
Update call to safe_call to dispatch the additional values from the l…
villegar Jul 17, 2026
3d3366e
Update the resolve_symbol function (previously resolve_symbol.symbol_…
villegar Jul 17, 2026
9df8c86
Add new helper to create a safe_reference object
villegar Jul 17, 2026
596e8dc
Update safe_ouput.opal generic to skip process of updating registry s…
villegar Jul 20, 2026
c824992
Update output object containing tibble with parsed log function calls
villegar Jul 21, 2026
edec42f
Remove unused code
villegar Jul 21, 2026
e438eda
Redefine as.data.frame.safe_symbol generic to as_tibble.safe_symbol
villegar Jul 21, 2026
0cb3219
Add new utilitarian function, resolve_dependencies to trace back root…
villegar Jul 21, 2026
2adc257
Update symbol registry with extra dependencies
villegar Jul 21, 2026
04931c0
New build
villegar Jul 21, 2026
bff352c
Remove snippets of unused code and add call to the new internal helpe…
villegar Jul 22, 2026
f328c2f
Remove resolve_argument, an unused internal function
villegar Jul 22, 2026
99831ce
Remove find_symbol_asset and add new helper resolve_symbol_asset
villegar Jul 22, 2026
e6718e2
Update lookup_symbol and register_symbol
villegar Jul 22, 2026
64f16ee
Add new arg for safe_symbol, version
villegar Jul 22, 2026
11e89d2
Add local bindings and remove commented code
villegar Jul 22, 2026
b175f7e
Convert as_tibble.safe_symbol to internal
villegar Jul 22, 2026
f0f1734
Remove unused internal methods
villegar Jul 22, 2026
197de1f
Add missing local binding
villegar Jul 22, 2026
1524832
Update examples
villegar Jul 22, 2026
4c7f9bf
Remove unused internal functions
villegar Jul 22, 2026
eb663a0
Remove generic documentation
villegar Jul 22, 2026
3cef69f
New build
villegar Jul 22, 2026
af152e9
Update report to use new 'asset' column, instead of 'table'
villegar Jul 22, 2026
18c7b93
Add additional check when loading cr8tor bundles
villegar Jul 23, 2026
404d52b
Update get_function and parse_arguments to handle cases in which the …
villegar Jul 23, 2026
f3ebc52
Bump version to next minor release
villegar Jul 24, 2026
809412c
Update list of word exceptions
villegar Jul 24, 2026
9640469
Update NEWS section for new release
villegar Jul 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: dsROCrate
Title: 'DataSHIELD' RO-Crate Governance Functions
Version: 0.1.0
Version: 0.2.0
Authors@R: c(
person(given = "Roberto",
family = "Villegas-Diaz",
Expand Down Expand Up @@ -33,7 +33,7 @@ Suggests:
fs,
knitr,
MolgenisArmadillo,
testthat (>= 3.0.0),
testthat (>= 3.1.4),
withr
Config/testthat/edition: 3
Encoding: UTF-8
Expand All @@ -50,6 +50,7 @@ Imports:
rmarkdown,
rocrateR (>= 0.1.0),
tibble,
uuid,
vtree,
xptr,
yaml
Expand Down
34 changes: 34 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
@@ -1,13 +1,33 @@
# Generated by roxygen2: do not edit by hand

S3method(as.data.frame,safe_call)
S3method(as.data.frame,symbol_registry)
S3method(audit,ArmadilloCredentials)
S3method(audit,character)
S3method(audit,cr8tor)
S3method(audit,default)
S3method(audit,list)
S3method(audit,opal)
S3method(audit,rocrate)
S3method(audit_engine,cr8tor)
S3method(audit_engine,default)
S3method(audit_engine,opal)
S3method(backend_logs,opal)
S3method(backend_options,opal)
S3method(backend_packages,opal)
S3method(backend_profile_exists,opal)
S3method(backend_project,opal)
S3method(backend_project_exists,opal)
S3method(backend_project_perms,opal)
S3method(backend_projects,opal)
S3method(backend_resource_perms,opal)
S3method(backend_resources,opal)
S3method(backend_sys_perms,opal)
S3method(backend_table_perms,opal)
S3method(backend_tables,opal)
S3method(backend_user_exists,opal)
S3method(backend_users,opal)
S3method(check_permissions,ArmadilloCredentials)
S3method(check_permissions,default)
S3method(check_permissions,opal)
S3method(extract_safe_data,opal)
Expand All @@ -20,6 +40,7 @@ S3method(extract_safe_project,opal)
S3method(extract_safe_project,rocrate)
S3method(extract_safe_setting,opal)
S3method(extract_safe_setting,rocrate)
S3method(filter_safe_people,opal)
S3method(flatten_safe_data,default)
S3method(flatten_safe_data,rocrate)
S3method(flatten_safe_output,default)
Expand All @@ -30,25 +51,37 @@ S3method(flatten_safe_project,default)
S3method(flatten_safe_project,rocrate)
S3method(flatten_safe_setting,default)
S3method(flatten_safe_setting,rocrate)
S3method(has_symbol,symbol_registry)
S3method(init,ArmadilloCredentials)
S3method(init,opal)
S3method(init,rocrate)
S3method(is_admin_con,opal)
S3method(is_audit_con,opal)
S3method(parse_user_profiles,ArmadilloCredentials)
S3method(parse_user_profiles,opal)
S3method(print,cr8tor_bundle)
S3method(print,safe_call)
S3method(print,safe_symbol)
S3method(project_exists,ArmadilloCredentials)
S3method(project_exists,opal)
S3method(report,ArmadilloCredentials)
S3method(report,character)
S3method(report,default)
S3method(report,list)
S3method(report,rocrate)
S3method(safe_call,call)
S3method(safe_call,character)
S3method(safe_data,ArmadilloCredentials)
S3method(safe_data,character)
S3method(safe_data,default)
S3method(safe_data,opal)
S3method(safe_data,rocrate)
S3method(safe_output,ArmadilloCredentials)
S3method(safe_output,character)
S3method(safe_output,default)
S3method(safe_output,opal)
S3method(safe_output,rocrate)
S3method(safe_people,ArmadilloCredentials)
S3method(safe_people,character)
S3method(safe_people,default)
S3method(safe_people,opal)
Expand All @@ -58,6 +91,7 @@ S3method(safe_project,character)
S3method(safe_project,default)
S3method(safe_project,opal)
S3method(safe_project,rocrate)
S3method(safe_setting,ArmadilloCredentials)
S3method(safe_setting,character)
S3method(safe_setting,cr8tor)
S3method(safe_setting,default)
Expand Down
32 changes: 32 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,35 @@
# dsROCrate 0.2.0

## Breaking Changes

* The audit log tibble returned via `safe_output()` no longer includes a `table` column. It has been replaced by three more granular columns: `kind` (whether the tracked object is a `table`, `resource` or `expression`), `asset` (the resolved table/resource name) and `expr` (the associated R expression, when applicable). Code that reads the `table` column directly will need to be updated.

## New Features

* Added a symbol registry and call-tracking system (`safe_symbol()`, `safe_call()`, `safe_reference()`, `symbol_registry()`) that replaces regex-based parsing of Opal logs, modelling assignments and aggregate function calls as objects and resolving arguments back to the symbols/tables they reference.
* Added a generic backend abstraction layer (`backend_logs()`, `backend_options()`, `backend_users()`, `backend_projects()`, `backend_sys_perms()`, and others) so internal code no longer calls `opalr::` directly, paving the way for additional backends.
* Added early Armadillo backend scaffolding: `check_permissions()`, `init()`, `report()`, `safe_data()`, `safe_output()`, `safe_people()` and `safe_setting()` now all have `ArmadilloCredentials` methods, returning clear "not yet implemented" errors ahead of full support.
* Added `default` S3 methods for `audit()`, `audit_engine()` and `check_permissions()`, giving clearer errors when an unsupported connection object is supplied.
* Added a `verbose` argument to `check_permissions()` (opal method) to optionally print a success message when permissions are sufficient.
* Added `print.safe_call()` and `print.safe_symbol()` methods, plus `as.data.frame()` methods for `safe_call` and `symbol_registry` objects, for readable inspection of the new internal tracking objects.

## Improvements

* `safe_people()` now determines admin/auditor status from actual system permissions and group membership, rather than a hardcoded name check against `"admin"`/`"administrator"`, improving compatibility with deployments that use differently named privileged accounts.
* Permission lookup failures during `safe_people()` now fall back gracefully to an empty result instead of aborting the whole audit.
* Consolidated backend-version and connection-validation logic into the new generic backend framework, reducing duplication between Opal-specific code paths.

## Documentation

* Removed a vignette section demonstrating direct calls to internal helper functions, which have since been renamed as part of the backend abstraction work.
* Updated documentation to reflect that `init()`, `safe_data()`, `report()` and related functions now accept either `opal` or `ArmadilloCredentials` connections.

## Internal Changes

* Added test coverage for `audit()`, `audit_engine()` and `check_permissions()`, along with a mock-connection test helper, reducing reliance on a live demo Opal server.
* Added `uuid` to `Imports`, used to generate unique IDs for tracked symbols.
* Bumped the `testthat` version requirement in `Suggests` from `>= 3.0.0` to `>= 3.1.4`.

# dsROCrate 0.1.0

## New Features
Expand Down
37 changes: 27 additions & 10 deletions R/audit.R
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,22 @@ audit <- function(x, ...) {
UseMethod("audit")
}

#' @export
audit.default <- function(x, ...) {
stop(
sprintf(
"No `audit()` method exists for objects of class: %s.",
paste(class(x), collapse = ", ")
),
call. = FALSE
)
}

#' @rdname audit
#' @export
audit.ArmadilloCredentials <- function(x, ..., intent = NULL) {
stop(
"The audit for Armadillo backend is not currently implemented!",
"The `audit()` for the Armadillo backend is not currently implemented!",
call. = FALSE
)
}
Expand All @@ -65,21 +76,27 @@ audit.character <- function(x, ..., intent = NULL) {
}

# attempt loading a `cr8tor` bundle
x_obj <- tryCatch(
load_cr8tor_bundle(x, ...),
error = function(e) NULL
)
cr8tor_res <- .try_load(load_cr8tor_bundle(x, ...))
x_obj <- cr8tor_res$value

# alternatively, attempt loading an RO-Crate
rocrate_res <- list(value = NULL, error = NULL)
if (is.null(x_obj)) {
x_obj <- tryCatch(
rocrateR::load_rocrate(x, ...),
error = function(e) NULL
)
rocrate_res <- .try_load(rocrateR::load_rocrate(x, ...))
x_obj <- rocrate_res$value
}

if (is.null(x_obj)) {
stop(
"The given path does not point to a valid `cr8tor` archive nor an `rocrate",
paste0(
"The given path does not point to a valid `cr8tor` archive nor an ",
"`rocrate`.\n\n",
" cr8tor bundle error: ",
if (is.null(cr8tor_res$error)) "(not attempted)" else cr8tor_res$error,
"\n",
" rocrate error: ",
if (is.null(rocrate_res$error)) "(not attempted)" else rocrate_res$error
),
call. = FALSE
)
}
Expand Down
68 changes: 34 additions & 34 deletions R/audit_engine.R
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
#' Internal function to create audits for various back-ends.
#'
#' @param x This can be a connection to a 'DataSHIELD' server (e.g., object with
#' the `opal` class, see [opalr::opal.login()]). Alternatively, a governance
#' archive file, representing the intent of a project and associated
#' governance details.
#' the `opal` or `ArmadilloCredentials` classes). Alternatively, a
#' governance archive file, representing the intent of a project and
#' associated governance details.
#' @param ... Other optional arguments, see full documentation for details.
#' @param project String with project name(s) from which to extra Safe Project
#' details.
Expand All @@ -26,6 +26,17 @@ audit_engine <- function(x, ...) {
UseMethod("audit_engine")
}

#' @export
audit_engine.default <- function(x, ...) {
stop(
sprintf(
"No `audit_engine()` method exists for objects of class: %s.",
paste(class(x), collapse = ", ")
),
call. = FALSE
)
}

#' @export
audit_engine.cr8tor <- function(x, ...) {
# extract individual components from cr8tor bundle
Expand Down Expand Up @@ -57,21 +68,21 @@ audit_engine.opal <- function(
path = NULL
) {
# local bindings
name <- permission <- principal <- NULL
name <- NULL

# create RO-Create with the 5 safes profile
crate <- rocrateR::rocrate_5s()

# validate backend
validate_backend(x, ...)

# if `project` is missing, then ~extract all project names~ error
# if `project` is missing, then error
if (is.null(project)) {
stop("A `project` name is required!", call. = FALSE)
}

# extract list with all projects to verify `project` contains a valid value
ds <- opalr::opal.projects(x)
ds <- backend_projects(x)
server_prjs <- ds[, "name"]
idx <- project %in% server_prjs
if (!all(idx)) {
Expand All @@ -86,41 +97,30 @@ audit_engine.opal <- function(

# Safe People ----
# get users' details
safe_people_tbl <- opalr::oadmin.user_profiles(x, df = FALSE) |>
dplyr::bind_rows() |>
dplyr::rename(name = principal) |>
# exclude system administrators from the report
dplyr::filter(!(tolower(name) %in% c("admin", "administrator")))

# if any users were found, then verify if they are admin/auditors and exclude
if (nrow(safe_people_tbl)) {
# extract system permissions
sys_perms_tbl <- opalr::oadmin.system_perm(x)
safe_people_tbl <- tryCatch(
{
safe_people_tbl |>
dplyr::left_join(sys_perms_tbl, by = c("name" = "subject")) |>
dplyr::filter(!(permission %in% c("administrate", "audit")))
},
error = function(e) {
tibble::tibble()
}
)
}

safe_people_tbl <- filter_safe_people(x)
if (!is.null(user)) {
safe_people_tbl <- safe_people_tbl |>
dplyr::filter(tolower(name) %in% user)
}

if (nrow(safe_people_tbl) == 0) {
stop(
# an audit report is not meaningful without Safe People details, whether
# that's because a `user` filter matched nobody, or because the
# permission lookup itself could not be completed
if (nrow(safe_people_tbl) == 0) {
stop(
if (is.null(user)) {
paste(
"No Safe People details could be found for this project - the",
"audit cannot proceed."
)
} else {
sprintf(
"No Safe People details were found for the user: %s!",
paste0("'", user, "'", collapse = ", ")
),
call. = FALSE
)
}
)
},
call. = FALSE
)
}

crate <- safe_people_tbl$name |>
Expand Down
Loading
Loading