Skip to content

GroupImageRepository

âš  High-severity security finding (SC-SDN-01): GetFilteredImageGroups' deleted-image filter is inverted relative to the deletion convention used elsewhere in the codebase. See Behavioral notes and Security and authorization before relying on this method for access control.

GroupImageRepository is the RepositoryBase<ImageGroup> implementation backing group image search and soft-delete, and it is the subject of this library's highest-severity security finding: its in-memory deleted-image filter (ImageDeleteCode != null || CanUserSeeDeletedImages) is inverted, so users without the GroupImagingDeleted permission are shown soft-deleted images while never-deleted images with a null delete code are hidden (SC-SDN-01, Security and authorization). Both visibility filters (deleted and confidential) run in memory after ToList(), so every image in scope is loaded into the process before filtering (SC-SDN-02).

Purpose

  • Retrieve a single ImageGroup by id, with existence-check helpers (ImageFound, GetImageGroupOrDefault).
  • Soft-delete an image by setting its ImageDeleteCode to "Y" and stamping the update user/date, only if it is not already deleted, per SDN-BR-040.
  • Search group images by group/parent group, category, document type, create user, and date range, applying sentinel-value "no filter" semantics (SDN-BR-041) and deleted/confidential visibility rules (SDN-BR-042, SDN-BR-043) — subject to the SC-SDN-01 inversion noted above.

Context

  • Namespace: SAIF.Data.NHibernate.Repositories.Groups
  • Assembly/package: SAIF.Data.NHibernate (ships inside the SAIF.SAIFCode.Core NuGet package)
  • Base class: RepositoryBase<ImageGroup> from SAIF.Framework.Data.NHibernate (external, resolved via Legends CLI — see Inherited public members below).
  • Implements: IGroupImageRepository, defined in SAIF.Data.Interfaces.Repositories.Groups.

Public members

Member Signature Purpose
GroupImageRepository(NHibernateUnitOfWork) public GroupImageRepository(NHibernateUnitOfWork uow) Constructor; passes uow to the RepositoryBase<ImageGroup> base.
ImageFound public bool ImageFound(long groupImageId) Returns true if an ImageGroup with the given id exists, by calling GetImageGroupOrDefault and checking for a non-null result (SQL-022).
Delete public void Delete(ImageDelete imageDelete) Loads the ImageGroup for imageDelete.ImageGroupId; if its Image.ImageDeleteCode is blank/null/" ", sets it to "Y" and stamps UpdateUser/UpdateDate from imageDelete. No-op (does not re-stamp) if already marked deleted (SDN-BR-040, SQL-054). Does not check CanUserEditImages; enforcement is expected in the caller (SC-SDN-06, Security and authorization).
GetImageGroup public ImageGroup GetImageGroup(long groupImageId) Returns the single ImageGroup matching groupImageId via Session.Query<ImageGroup>().Single(), throwing if none exists. Applies neither the confidential nor the deleted-image visibility filter (SQL-023; see Security and authorization).
GetImageGroupOrDefault public ImageGroup GetImageGroupOrDefault(long groupImageId) Same as GetImageGroup but returns null instead of throwing when no match exists (SQL-024; applies neither visibility filter — see Security and authorization).
GetFilteredImageGroups public IEnumerable<ImageGroup> GetFilteredImageGroups(string groupId, ImageFilterCriteria filter) Returns ImageGroups for the given group/parent-group id, filtered by category, document type, create user, and date range (sentinel values mean "no filter", SDN-BR-041), then applies the deleted-image filter (ImageDeleteCode != null \|\| filter.CanUserSeeDeletedImages, inverted — see SC-SDN-01) and the confidential-image filter (SecurityLevel == NonConfidentialSecurityLevel \|\| filter.CanUserSeeConfidentialImages, SDN-BR-043) in memory after materializing the query with ToList() (SQL-025).

Inherited public members

Members in this section are inherited from RepositoryBase<ImageGroup> in SAIF.Framework.Data.NHibernate. They are available on this type through inheritance.

Member Signature Purpose Origin
Add public void Add(ImageGroup entity) Registers a new ImageGroup entity for insertion on the next unit-of-work commit.† SAIF.Framework.Data.NHibernate — RepositoryBase<ImageGroup>
Update public void Update(ImageGroup entity) Marks an existing ImageGroup entity as modified for the next unit-of-work commit.† SAIF.Framework.Data.NHibernate — RepositoryBase<ImageGroup>
Delete(ImageGroup) public void Delete(ImageGroup entity) Registers an ImageGroup entity for deletion on the next unit-of-work commit; distinct from this type's own Delete(ImageDelete) overload above, which performs a soft delete instead.† SAIF.Framework.Data.NHibernate — RepositoryBase<ImageGroup>
Query public IQueryable<ImageGroup> Query() Returns a LINQ-queryable session projection over ImageGroup for building ad hoc filters.† SAIF.Framework.Data.NHibernate — RepositoryBase<ImageGroup>
Get public ImageGroup Get(object id) Loads an ImageGroup by primary key, returning null if not found.† SAIF.Framework.Data.NHibernate — RepositoryBase<ImageGroup>
Load public ImageGroup Load(object id) Loads an ImageGroup by primary key as a (possibly proxied) reference, throwing if not found.† SAIF.Framework.Data.NHibernate — RepositoryBase<ImageGroup>
SystemDate public DateTime SystemDate { get; } Current system date/time as tracked by the underlying session/unit of work.† SAIF.Framework.Data.NHibernate — RepositoryBase<ImageGroup>

Behavioral notes

  • SC-SDN-01 (High): GetFilteredImageGroups keeps an image when ImageDeleteCode != null || CanUserSeeDeletedImages. Elsewhere (GroupImageRepository.Delete, ImagingDataModelExtensions.IsDeleted), null/empty/" " means not deleted and "Y" means deleted — so this predicate is backwards: it returns soft-deleted images ("Y", non-null) to users without CanUserSeeDeletedImages, while hiding never-deleted images (null code). Services/SAIF.Services/Groups/Imaging/ImageService.GetImages relies on this filter and applies no compensating filter of its own; by contrast, Services/SAIF.Services/Agencies/Imaging/ImageService.cs uses the correct !Image.IsDeleted() || CanUserSeeDeletedImages pattern. This is an authorization bypass for deleted group images (saif-data-nhibernate-security-analysis.md SC-SDN-01).
  • SC-SDN-02 (Medium): Both the deleted and confidential filters in GetFilteredImageGroups run in memory after ToList(), so every image in the group is loaded before filtering — a performance and exposure-surface concern.
  • GetImageGroup, GetImageGroupOrDefault, and ImageFound apply neither the confidential nor the deleted filter; any caller exposing an image by id via these members must enforce visibility itself (DLA-SDN-01/02, Security and authorization).
  • Delete performs a soft delete only and does not check CanUserEditImages; enforcement of edit permission is expected in the caller (SC-SDN-06).

Constraints and usage notes

  • Do not treat GetFilteredImageGroups' current deleted-image behavior as correct access control until SC-SDN-01 is fixed or confirmed intentional; verify with the owning team before relying on it in a new caller.
  • GetImageGroup throws if no matching ImageGroup exists; use GetImageGroupOrDefault or ImageFound first when existence is not guaranteed.

Note: Statements marked with † are derived from dependency library documentation via the Legends CLI. Statements marked with * were inferred from code structure or contextual evidence. Statements marked with ** could not be confirmed; treat as unknown until verified.

Sources

  • Data/SAIF.Data.NHibernate/Repositories/Groups/GroupImageRepository.cs