Skip to content

Commit

Permalink
[dicom_archive] Site&Project check with config setting and permission
Browse files Browse the repository at this point in the history
DA refactor

undo changes dataframework
  • Loading branch information
ridz1208 committed Feb 5, 2025
1 parent 196a12a commit b62e336
Show file tree
Hide file tree
Showing 15 changed files with 366 additions and 32 deletions.
5 changes: 4 additions & 1 deletion SQL/0000-00-02-Permission.sql
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,10 @@ INSERT INTO `permissions` VALUES
(64,'dataquery_admin','Admin dataquery queries',(SELECT ID FROM modules WHERE Name='dataquery'),NULL,'2'),
(65,'schedule_module','Schedule Module - edit and delete the appointment',(SELECT ID FROM modules WHERE Name='schedule_module'),'View/Create/Edit','2'),
(66,'document_repository_categories','Categories',(SELECT ID FROM modules WHERE Name='document_repository'), 'Edit/Upload/Delete', '2'),
(67,'document_repository_hidden','Restricted files',(SELECT ID FROM modules WHERE Name='document_repository'), 'View', '2');
(67,'document_repository_hidden','Restricted files',(SELECT ID FROM modules WHERE Name='document_repository'), 'View', '2')
(68,'dicom_archive_nosessionid', 'DICOMs with no session ID', (SELECT ID FROM modules WHERE Name='dicom_archive'), 'View', '2')
(69,'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: 5 additions & 2 deletions SQL/0000-00-03-ConfigTables.sql
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,10 @@ 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, 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', 9 FROM ConfigSettings WHERE Name="imaging_modules";


INSERT INTO ConfigSettings (Name, Description, Visible, AllowMultiple, Label, OrderNumber) VALUES ('statistics', 'Statistics module settings', 1, 0, 'Statistics', 7);
Expand Down Expand Up @@ -232,6 +234,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';

29 changes: 27 additions & 2 deletions modules/dicom_archive/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,25 @@ 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

`dicom_archive_view_ownsites`: This permission only grants access to DICOMS
associated with a session and where the session is affiliated to a site that the user
has access to

`dicom_archive_nosessionid`: This permission can be used with the all-sites or
own-sites perissions to give a user access to DICOMs not associated with a session.

## Configurations

Expand All @@ -46,6 +63,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
105 changes: 103 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,13 @@ 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',
'dicom_archive_nosessionid',
]
);
}

/**
Expand All @@ -59,7 +74,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(
'useSiteProjectPermissions'
);
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 +124,75 @@ class Dicom_Archive extends \DataFrameworkMenu
return $provisioner;
}

/**
* 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()
);
}
}
}
24 changes: 20 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,10 @@ 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,
s.ID AS SessionID,
m.IsPhantom AS IsPhantom,
t.CreatingUser
FROM tarchive t
LEFT JOIN session s ON (s.ID=t.SessionID)
LEFT JOIN candidate c ON (c.CandID=s.CandID)
Expand All @@ -77,12 +80,25 @@ 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($row['SessionID']);
unset($row['CenterID']);
return new DICOMArchiveRowWithSession($row, $cid);
unset($row['ProjectID']);
unset($row['SessionID']);
unset($row['CreatingUser']);
return new DICOMArchiveRowWithSession($row, $cid, $pid, $sid, $creator);
}
unset($row['CenterID']);
return new DICOMArchiveRowWithoutSession($row);
unset($row['ProjectID']);
unset($row['SessionID']);
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

0 comments on commit b62e336

Please sign in to comment.