Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[dicom_archive] Dicom archive siteproject main #9549

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 4 additions & 2 deletions SQL/0000-00-02-Permission.sql
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,10 @@ INSERT INTO `permissions` VALUES
(67,'document_repository_hidden','Restricted files',(SELECT ID FROM modules WHERE Name='document_repository'), 'View', '2'),
(68,'issue_tracker_site_issue','See/Edit/Comment on Own Site Issues',(SELECT ID FROM modules WHERE Name = 'issue_tracker'),NULL,2),
(69,'issue_tracker_close_site_issue','Close Own Site Issues',(SELECT ID FROM modules WHERE Name = 'issue_tracker'),NULL,2),
(70,'issue_tracker_close_all_issue','Close all Issues',(SELECT ID FROM modules WHERE Name = 'issue_tracker'),NULL,2);

(70,'issue_tracker_close_all_issue','Close all Issues',(SELECT ID FROM modules WHERE Name = 'issue_tracker'),NULL,2),
(71,'dicom_archive_nosessionid', 'DICOMs with no session ID', (SELECT ID FROM modules WHERE Name='dicom_archive'), 'View', '2'),
(72,'dicom_archive_view_ownsites', 'DICOMs - Own Sites', (SELECT ID FROM modules WHERE Name='dicom_archive'), 'View', '2')
;

INSERT INTO `user_perm_rel` (userID, permID)
SELECT u.ID, p.permID
Expand Down
7 changes: 4 additions & 3 deletions SQL/0000-00-03-ConfigTables.sql
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,9 @@ INSERT INTO ConfigSettings (Name, Description, Visible, AllowMultiple, DataType,
INSERT INTO ConfigSettings (Name, Description, Visible, AllowMultiple, DataType, Parent, Label, OrderNumber) SELECT 'LegoPhantomRegex', 'Regex for identifying a Lego Phantom scan header', 1, 0, 'text', ID, 'Lego phantom regex', 3 FROM ConfigSettings WHERE Name="imaging_modules";
INSERT INTO ConfigSettings (Name, Description, Visible, AllowMultiple, DataType, Parent, Label, OrderNumber) SELECT 'LivingPhantomRegex', 'Regex to be used on Living Phantom scan header', 1, 0, 'text', ID, 'Living phantom regex', 4 FROM ConfigSettings WHERE Name="imaging_modules";
INSERT INTO ConfigSettings (Name, Description, Visible, AllowMultiple, DataType, Parent, Label, OrderNumber) SELECT 'showTransferStatus', 'Show transfer status in the DICOM Archive table', 1, 0, 'boolean', ID, 'Show transfer status', 5 FROM ConfigSettings WHERE Name="imaging_modules";
INSERT INTO ConfigSettings (Name, Description, Visible, AllowMultiple, DataType, Parent, Label, OrderNumber) SELECT 'tblScanTypes', 'Scan types from the mri_scan_type table that the project wants to see displayed in Imaging Browser table', 1, 1, 'scan_type', ID, 'Imaging Browser Tabulated Scan Types', 6 FROM ConfigSettings WHERE Name="imaging_modules";
INSERT INTO ConfigSettings (Name, Description, Visible, AllowMultiple, DataType, Parent, Label, OrderNumber) SELECT 'ImagingBrowserLinkedInstruments', 'Instruments that the users want to see linked from Imaging Browser', 1, 1, 'instrument', ID, 'Imaging Browser Links to Instruments', 7 FROM ConfigSettings WHERE Name="imaging_modules";

INSERT INTO ConfigSettings (Name, Description, Visible, AllowMultiple, DataType, Parent, Label, OrderNumber) SELECT 'useImagingSiteProjectPermissions', 'Restricts access to data based on both sites and project. Allows access to data with no session affiliated using a special permission only', 1, 0, 'boolean', ID, 'Use Advanced Site Project Permissions', 6 FROM ConfigSettings WHERE Name="imaging_modules";
INSERT INTO ConfigSettings (Name, Description, Visible, AllowMultiple, DataType, Parent, Label, OrderNumber) SELECT 'tblScanTypes', 'Scan types from the mri_scan_type table that the project wants to see displayed in Imaging Browser table', 1, 1, 'scan_type', ID, 'Imaging Browser Tabulated Scan Types', 7 FROM ConfigSettings WHERE Name="imaging_modules";
INSERT INTO ConfigSettings (Name, Description, Visible, AllowMultiple, DataType, Parent, Label, OrderNumber) SELECT 'ImagingBrowserLinkedInstruments', 'Instruments that the users want to see linked from Imaging Browser', 1, 1, 'instrument', ID, 'Imaging Browser Links to Instruments', 8 FROM ConfigSettings WHERE Name="imaging_modules";

INSERT INTO ConfigSettings (Name, Description, Visible, AllowMultiple, Label, OrderNumber) VALUES ('statistics', 'Statistics module settings', 1, 0, 'Statistics', 7);
INSERT INTO ConfigSettings (Name, Description, Visible, AllowMultiple, DataType, Parent, Label, OrderNumber) SELECT 'excludedMeasures', 'Instruments to be excluded from Statistics calculations', 1, 1, 'instrument', ID, 'Excluded instruments', 1 FROM ConfigSettings WHERE Name="statistics";
Expand Down Expand Up @@ -231,6 +231,7 @@ INSERT INTO Config (ConfigID, Value) SELECT ID, "." FROM ConfigSettings WHERE Na
INSERT INTO Config (ConfigID, Value) SELECT ID, "(?i)phantom" FROM ConfigSettings WHERE Name="LegoPhantomRegex";
INSERT INTO Config (ConfigID, Value) SELECT ID, "(?i)phantom" FROM ConfigSettings WHERE Name="LivingPhantomRegex";
INSERT INTO Config (ConfigID, Value) SELECT ID, "false" FROM ConfigSettings WHERE Name="showTransferStatus";
INSERT INTO Config (ConfigID, Value) SELECT ID, "false" FROM ConfigSettings WHERE Name="useImagingSiteProjectPermissions";
INSERT INTO Config (ConfigID, Value) SELECT cs.ID, GROUP_CONCAT(mst.MriScanTypeName) FROM ConfigSettings cs JOIN mri_scan_type mst WHERE cs.Name="tblScanTypes" AND mst.MriScanTypeID=44;
INSERT INTO Config (ConfigID, Value) SELECT cs.ID, GROUP_CONCAT(mst.MriScanTypeName) FROM ConfigSettings cs JOIN mri_scan_type mst WHERE cs.Name="tblScanTypes" AND mst.MriScanTypeID=45;
INSERT INTO Config (ConfigID, Value) SELECT cs.ID, "mri_parameter_form" FROM ConfigSettings cs WHERE cs.Name="ImagingBrowserLinkedInstruments";
Expand Down
9 changes: 9 additions & 0 deletions SQL/New_patches/2025-01-31-dicom_archive_permissions.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
INSERT INTO permissions (code, description, moduleID, `action`, categoryID)
SELECT 'dicom_archive_nosessionid', 'DICOMs with no session ID', ID, 'View', 2 FROM modules WHERE Name='dicom_archive';

INSERT INTO permissions (code, description, moduleID, `action`, categoryID)
SELECT 'dicom_archive_view_ownsites', 'DICOMs - Own Sites', ID, 'View', 2 FROM modules WHERE Name='dicom_archive';

INSERT IGNORE INTO ConfigSettings (Name, Description, Visible, AllowMultiple, DataType, Parent, Label, OrderNumber)
SELECT 'useImagingSiteProjectPermissions', 'Restricts access to data based on both sites and project. Allows access to data with no session affiliated using a special permission only', 1, 0, 'boolean', ID, 'Use Advanced Site Project Permissions', 6 FROM ConfigSettings WHERE Name='imaging_modules';

36 changes: 34 additions & 2 deletions modules/dicom_archive/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,32 @@ user's system.

## Permissions

The permission `dicom_archive_view_allsites` is required to access
the DICOM Archive module.
*In the interest of backwards compatibility, permission behaviour varies slightly
based on the `useImagingSiteProjectPermissions` configuration*

Any of the following permissions grants access to the module.

`dicom_archive_view_allsites`:
- If `useImagingSiteProjectPermissions` is disabled, this permission gives access
to all DICOMs in the database (backwards compatible with projects not requiring a
session ID to be defined).
- If `useImagingSiteProjectPermissions` is enabled, this permission gives access to
all DICOMs as long as they are associated to a session and the session is affiliated
to a project that the user is affiliated with. When combined with
`dicom_archive_nosessionid`, user gets access to their projects' data as well as
DICOMs with no session ID associated.

`dicom_archive_view_ownsites`:
- If `useImagingSiteProjectPermissions` is disabled, this permission gives access
to all DICOMs as long as they are associated to a session and the session is affiliated
to a site that the user is affiliated with. When combined with
`dicom_archive_nosessionid`, user gets access to their sites' data as well as
DICOMs with no session ID associated.
- If `useImagingSiteProjectPermissions` is enabled, this permission gives access to
all DICOMs as long as they are associated to a session and the session is affiliated
to both a site and a project that the user is affiliated with. When combined with
`dicom_archive_nosessionid`, user gets access to their sites' and projects' data as well as
DICOMs with no session ID associated.

## Configurations

Expand All @@ -46,6 +70,14 @@ The `showTransferStatus` configuration option is obsolete and should
not be used, but determines if a first "Transfer Status" column
appears in the menu table.

The `useImagingSiteProjectPermissions` configuration enables more advanced Site and
Project access control (Although Site permissions are enabled without this
configuration, "all sites" gives access to DICOMS with no Session ID if this
configuration is turned off). If enabled, users accessing the module can only see
DICOMs where a session ID has been found and are thus linked to the site and project
of the session AND the site and project match the user's. Access to DICOMs with no
session is granted by the `dicom_archive_nosessionid` permission (see permissions section)

#### Install Configurations

For downloading large DICOM files, it may be necessary to increase the
Expand Down
104 changes: 102 additions & 2 deletions modules/dicom_archive/php/dicom_archive.class.inc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@
*/
namespace LORIS\dicom_archive;

use LORIS\Data\Filters\CompositeORFilter;
use LORIS\Data\Filters\CompositeANDFilter;
use LORIS\Data\Filters\UserSiteMatch;
use LORIS\Data\Filters\UserHasAnyPermission;
use LORIS\Data\Filters\UserProjectMatch;
use LORIS\Data\Filters\ResourceHasNoSession;
use LORIS\Data\Filters\UserIsCreator;

/**
* Provides the PHP code for the menu filter for the dicom archive
*
Expand All @@ -27,6 +35,7 @@ namespace LORIS\dicom_archive;
*/
class Dicom_Archive extends \DataFrameworkMenu
{
protected $dataSessionCanBeNull = true;

/**
* Determine whether the user has permission to view this page
Expand All @@ -37,7 +46,12 @@ class Dicom_Archive extends \DataFrameworkMenu
*/
function _hasAccess(\User $user) : bool
{
return $user->hasPermission('dicom_archive_view_allsites');
return $user->hasAnyPermission(
[
'dicom_archive_view_allsites',
'dicom_archive_view_ownsites',
]
);
}

/**
Expand All @@ -59,7 +73,24 @@ class Dicom_Archive extends \DataFrameworkMenu
*/
public function useProjectFilter() : bool
{
return false;
// Only enable project filtering if the site project permissions are enabled
// to be compatible with projects not requiring a session ID for all DICOMS
$config = \NDB_Factory::singleton()->config();
$siteprojectperms = $config->getSetting(
'useImagingSiteProjectPermissions'
);
return $siteprojectperms === 'true';
}

/**
* Determines which permissions (if any) allows the user access to resources
* where the project is null.
*
* @return ?array
*/
public function nullSessionPermissionNames() : ?array
{
return ['dicom_archive_nosessionid'];
}

/**
Expand Down Expand Up @@ -92,6 +123,75 @@ class Dicom_Archive extends \DataFrameworkMenu
return $provisioner;
}

/**
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

* Return a data provisioner of the same type as BaseDataProvisioner, with
* default LORIS filters applied. A subclass may override this to remove (or
* change) filters.
*
* @return \LORIS\Data\Provisioner a provisioner with default filters added
*/
public function getDataProvisionerWithFilters() : \LORIS\Data\Provisioner
{
$provisioner = $this->getBaseDataProvisioner();
$allSitePerms = $this->allSitePermissionNames();
$nullSessionPerms = $this->nullSessionPermissionNames();

// Set filter default returns based on if the data loaded by this module can
// have or not a null session
$defaultReturn = $this->dataSessionCanBeNull ? false : null;

//Default filter to User Site Match
$filter = new UserSiteMatch($defaultReturn);

//Combine filter with Null session permissions if data can have null sessions
if ($this->dataSessionCanBeNull) {
$filter = new CompositeORFilter(
$filter,
new CompositeANDFilter(
new UserHasAnyPermission($nullSessionPerms),
new ResourceHasNoSession(),
),
);
}

// If the user has any of the All Site permissions, combine as an OR filter
if (!empty($allSitePerms)) {
$filter = new CompositeORFilter(
$filter,
new UserHasAnyPermission($allSitePerms),
);
}

// Check if module uses Project filters
if ($this->useProjectFilter()) {
//Default project filter to User Project Match
$projectFilter = new UserProjectMatch($defaultReturn);

// Check if resources with null project should be included in the results
if (!empty($nullSessionPerms)) {
$projectFilter = new CompositeORFilter(
new CompositeANDFilter(
new ResourceHasNoSession(),
new UserHasAnyPermission($nullSessionPerms)
),
new UserProjectMatch($defaultReturn)
);
}

$filter = new CompositeANDFilter($filter, $projectFilter);
}

// Final filter, check if the user looking at the data is the user that
// uploaded the data
$filter = new CompositeORFilter(
$filter,
new UserIsCreator(),
);

$provisioner = $provisioner->filter($filter);
return $provisioner;
}

/**
* Overrides base getJSDependencies() to add support for dicom specific
* React column formatters.
Expand Down
16 changes: 13 additions & 3 deletions modules/dicom_archive/php/dicomarchiveanonymizer.class.inc
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,20 @@ class DICOMArchiveAnonymizer implements Mapper
}

if (!$resource instanceof \LORIS\StudyEntities\SiteHaver) {
return new DICOMArchiveRowWithoutSession($newrow);
'@phan-var object $resource';
return new DICOMArchiveRowWithoutSession(
$newrow,
$resource->CreatedBy()
);
} else {
$cid = $resource->getCenterID();
return new DICOMArchiveRowWithSession($newrow, $cid);
'@phan-var object $resource';
return new DICOMArchiveRowWithSession(
$newrow,
$resource->getCenterID(),
$resource->getProjectID(),
$resource->getSessionID(),
$resource->CreatedBy()
);
}
}
}
21 changes: 17 additions & 4 deletions modules/dicom_archive/php/dicomarchiverowprovisioner.class.inc
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ class DicomArchiveRowProvisioner extends \LORIS\Data\Provisioners\DBRowProvision
t.TarchiveID AS TarchiveID,
s.ID AS SessionID,
s.CenterID AS CenterID,
m.IsPhantom AS IsPhantom
s.ProjectID AS ProjectID,
m.IsPhantom AS IsPhantom,
t.CreatingUser
FROM tarchive t
LEFT JOIN session s ON (s.ID=t.SessionID)
LEFT JOIN candidate c ON (c.ID=s.CandidateID)
Expand All @@ -77,12 +79,23 @@ class DicomArchiveRowProvisioner extends \LORIS\Data\Provisioners\DBRowProvision
*/
public function getInstance($row) : \LORIS\Data\DataInstance
{
if ($row['CenterID'] !== null) {
$creator = \User::factory($row['CreatingUser']);

if ($row['CenterID'] !== null
&& $row['ProjectID'] !== null
&& $row['SessionID'] !== null
) {
$cid = \CenterID::singleton(intval($row['CenterID']));
$pid = \ProjectID::singleton(intval($row['ProjectID']));
$sid = new \SessionID(strval($row['SessionID']));
unset($row['CenterID']);
return new DICOMArchiveRowWithSession($row, $cid);
unset($row['ProjectID']);
unset($row['CreatingUser']);
return new DICOMArchiveRowWithSession($row, $cid, $pid, $sid, $creator);
}
unset($row['CenterID']);
return new DICOMArchiveRowWithoutSession($row);
unset($row['ProjectID']);
unset($row['CreatingUser']);
return new DICOMArchiveRowWithoutSession($row, $creator);
}
}
34 changes: 30 additions & 4 deletions modules/dicom_archive/php/dicomarchiverowwithoutsession.class.inc
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,43 @@ namespace LORIS\dicom_archive;
class DICOMArchiveRowWithoutSession implements \LORIS\Data\DataInstance
{
protected $DBRow;
protected $CreatedBy;


/**
* Create a new DICOMArchiveRow without a session.
*
* Session-less DICOMArchiveRows do not have CenterIDs to be filtered
* by.
*
* @param array $row The row (in the same format as \Database::pselectRow
* returns)
* @param array $row The row (in the same format as \Database::pselectRow
* returns)
* @param \User $creator The user who created this row
*/
public function __construct(array $row, \User $creator)
{
$this->DBRow = $row;
$this->CreatedBy = $creator;
}

/**
* Returns Null always since this class is specifically for no session IDs.
*
* @return ?\SessionID The SessionID
*/
public function __construct(array $row)
public function getSessionID(): ?\SessionID
{
$this->DBRow = $row;
return null;
}

/**
* Return the User who created this row.
*
* @return \User The user who created this row
*/
public function createdBy() : \User
{
return $this->CreatedBy;
}

/**
Expand All @@ -53,4 +77,6 @@ class DICOMArchiveRowWithoutSession implements \LORIS\Data\DataInstance
{
return $this->DBRow;
}


}
Loading
Loading