Skip to content

Conversation

@jasonmorais
Copy link
Contributor

@jasonmorais jasonmorais commented Jan 28, 2026

Summary by Sourcery

Introduce authorization and context restructuring for admin roles and update architecture tests and paths accordingly.

New Features:

  • Add visa-based permission enforcement for modifying admin roles using user passports.
  • Introduce dedicated role domain permissions and visa interfaces for role-related authorization.

Bug Fixes:

  • Correct domain context paths from Role to User for admin role entities, adapters, repositories, and tests.
  • Fix GraphQL account plan resolvers to use the typed GraphContext parameter.
  • Align user passport interfaces and implementations to expose admin role visas and ensure consistent type imports.

Enhancements:

  • Extend guest, system, personal, and admin user passports to provide visas for admin roles.
  • Refine code quality and cohesion metrics paths to use the sthrfit namespaces instead of legacy ones.
  • Add new admin role permission flag for managing staff roles and permissions in the role model schema.
  • Remove deprecated role context exports and unused personal-user-role artifacts.

Tests:

  • Update persistence adapter tests to use the new admin role user context namespace.

@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Jan 28, 2026

Reviewer's Guide

Aligns the AdminRole domain model under the User context, adds permission/visa plumbing so only authorized users can modify admin roles, updates persistence wiring and tests to the new namespace, extends user passports/visas for admin-role access, expands admin role permissions, and adjusts GraphQL resolver typing and architecture/code-quality tests to the sthrift layout.

Sequence diagram for updating an AdminRole with permission-based visa check

sequenceDiagram
  actor AdminUser
  participant AppService as AdminUserApplicationService
  participant Repo as AdminRoleRepository
  participant Role as AdminRole
  participant Passport as AdminUserUserPassport
  participant Visa as AdminUserUserVisa

  AdminUser->>AppService: updateAdminRole(command)
  AppService->>Repo: getById(roleId)
  Repo-->>AppService: AdminRole instance

  AppService->>Role: set roleName(newName)
  activate Role
  Role->>Role: validateVisa()
  Role->>Passport: forAdminRole(Role)
  Passport-->>Role: Visa
  Role->>Visa: determineIf(permissions => permissions.canManageUserRoles)
  Visa-->>Role: boolean
  alt not authorized
    Role-->>AdminUser: throw PermissionError
  else authorized
    Role->>Role: update props.roleName
    Role-->>AppService: updated AdminRole
    AppService->>Repo: save(updated AdminRole)
    Repo-->>AppService: persisted AdminRole
    AppService-->>AdminUser: success
  end
  deactivate Role
Loading

Class diagram for updated AdminRole and user passport/visa permissions

classDiagram

class AdminRole {
  -boolean isNew
  -UserVisa visa
  +AdminRole(props, passport)
  +getNewInstance(newProps, passport, roleName, isDefault) AdminRole
  +roleName string
  +isDefault boolean
  -validateVisa() void
}

class AdminRoleProps {
  <<interface>>
  +string roleName
  +boolean isDefault
}

class AdminRolePermissionsProps {
  <<interface>>
  +UserDomainPermissions userPermissions
}

class UserDomainPermissions {
  <<interface>>
  +boolean canBlockUsers
  +boolean canUnblockUsers
  +boolean canBlockListings
  +boolean canUnblockListings
  +boolean canRemoveListings
  +boolean canViewListingReports
  +boolean canViewUserReports
  +boolean canManageUserRoles
  +boolean isEditingOwnAccount
}

class RoleDomainPermissions {
  <<interface>>
  +boolean canCreateRole
  +boolean canUpdateRole
  +boolean canDeleteRole
  +boolean canAssignRole
}

class UserVisa {
  <<interface>>
  +determineIf(func) boolean
}

class RoleVisa {
  <<interface>>
  +determineIf(func) boolean
}

class UserPassport {
  <<interface>>
  +forPersonalUser(root) UserVisa
  +forAdminUser(root) UserVisa
  +forAdminRole(root) UserVisa
}

class GuestUserPassport {
  +forPersonalUser(root) UserVisa
  +forAdminUser(root) UserVisa
  +forAdminRole(root) UserVisa
}

class SystemUserPassport {
  -UserDomainPermissions permissions
  +forPersonalUser(root) UserVisa
  +forAdminUser(root) UserVisa
  +forAdminRole(root) UserVisa
}

class PersonalUserUserPassport {
  -PersonalUserEntityReference _user
  +forPersonalUser(root) UserVisa
  +forAdminUser(root) UserVisa
  +forAdminRole(root) UserVisa
}

class AdminUserUserPassport {
  -AdminUserEntityReference _user
  +forPersonalUser(root) UserVisa
  +forAdminUser(root) UserVisa
  +forAdminRole(root) UserVisa
}

class AdminRoleDomainAdapter {
  +roleName string
  +isDefault boolean
  +permissions AdminRolePermissionsDomainAdapter
}

class AdminRolePermissionsDomainAdapter {
  +userPermissions AdminRoleUserPermissionsDomainAdapter
}

AdminRole --> AdminRoleProps : props
AdminRole --> UserVisa : uses
AdminRole ..> UserPassport : via passport
AdminRole --> UserDomainPermissions : permission checks

RoleVisa --> RoleDomainPermissions
UserVisa --> UserDomainPermissions

GuestUserPassport ..|> UserPassport
SystemUserPassport ..|> UserPassport
PersonalUserUserPassport ..|> UserPassport
AdminUserUserPassport ..|> UserPassport

AdminRoleDomainAdapter ..|> AdminRoleProps
AdminRolePermissionsDomainAdapter ..|> AdminRolePermissionsProps
AdminRolePermissionsDomainAdapter --> AdminRoleDomainAdapter
AdminRoleDomainAdapter --> AdminRolePermissionsDomainAdapter
Loading

File-Level Changes

Change Details Files
Move AdminRole from the standalone Role context into the User context and update persistence and unit-of-work wiring accordingly.
  • Change AdminRole type parameters, interfaces, and props to use Domain.Contexts.User.Role.AdminRole.* instead of Domain.Contexts.Role.AdminRole.* in the Mongoose domain adapter and permissions adapters.
  • Update AdminRole repository and unit-of-work types to reference Domain.Contexts.User.Role.AdminRole.AdminRole and AdminRoleUnitOfWork under the User context.
  • Adjust AdminUserDomainAdapter to depend on Domain.Contexts.User.Role.AdminRole.* for role props and entity references, and update tests that construct or assert against AdminRole entities/adapters.
  • Remove the top-level Role context export and adjust AdminUser entity to import AdminRoleEntityReference from the nested user/role path.
packages/sthrift/persistence/src/datasources/domain/role/admin-role/admin-role.domain-adapter.ts
packages/sthrift/persistence/src/datasources/domain/role/admin-role/admin-role.repository.ts
packages/sthrift/persistence/src/datasources/domain/role/admin-role/admin-role.uow.ts
packages/sthrift/persistence/src/datasources/domain/user/admin-user/admin-user.domain-adapter.ts
packages/sthrift/persistence/src/datasources/domain/user/admin-user/admin-user.domain-adapter.test.ts
packages/sthrift/application-services/src/contexts/user/admin-user/create-if-not-exists.ts
packages/sthrift/application-services/src/contexts/user/admin-user/update.ts
packages/sthrift/domain/src/domain/contexts/user/admin-user/admin-user.entity.ts
packages/sthrift/domain/src/domain/contexts/index.ts
packages/sthrift/domain/src/domain/contexts/user/admin-user/admin-user.ts
packages/sthrift/domain/src/domain/contexts/user/index.ts
packages/sthrift/domain/src/domain/contexts/role/admin-role/admin-role.uow.ts
packages/sthrift/domain/src/domain/contexts/user/role/admin-role/admin-role.uow.ts
Introduce permission-based authorization for AdminRole mutations via user passports/visas and supporting role permission types.
  • Extend the AdminRole aggregate to accept a Passport, cache a UserVisa via passport.user.forAdminRole(this), and gate setters like roleName and isDefault behind a validateVisa() permission check that requires canManageUserRoles.
  • Add forAdminRole methods to the UserPassport interface and to concrete passport implementations for guest, system user, personal user, and admin user, wiring them to appropriate UserVisa implementations or no-op (always false) visas.
  • Define RoleDomainPermissions and RoleVisa interfaces as a dedicated permission model for role-related operations (create/update/delete/assign).
  • Normalize PersonalUserUserVisa and UserDomainPermissions formatting while ensuring canManageUserRoles and related flags remain part of the evaluated permission set.
  • Add a new canManageStaffRolesAndPermissions flag to the AdminRole Mongoose schema under permissions.userPermissions.
packages/sthrift/domain/src/domain/contexts/user/role/admin-role/admin-role.ts
packages/sthrift/domain/src/domain/contexts/user/user.passport.ts
packages/sthrift/domain/src/domain/iam/guest/contexts/guest.user.passport.ts
packages/sthrift/domain/src/domain/iam/system/contexts/system.user.passport.ts
packages/sthrift/domain/src/domain/iam/user/personal-user/personal-user.user.passport.ts
packages/sthrift/domain/src/domain/iam/user/admin-user/admin-user.user.passport.ts
packages/sthrift/domain/src/domain/iam/user/personal-user/contexts/personal-user.user.passport.ts
packages/sthrift/domain/src/domain/iam/user/personal-user/contexts/personal-user.user.visa.ts
packages/sthrift/domain/src/domain/contexts/user/user.domain-permissions.ts
packages/sthrift/domain/src/domain/contexts/user/role/role.domain-permissions.ts
packages/sthrift/domain/src/domain/contexts/user/role/role.visa.ts
packages/sthrift/data-sources-mongoose-models/src/models/role/admin-role.model.ts
Update tests and architecture/code-quality checks to the new sthrift layout and AdminRole namespace.
  • Change persistence adapter tests for AdminRole and AdminUser to reference Domain.Contexts.User.Role.AdminRole.* types instead of the old Role context.
  • Adjust code quality arch-unit tests to point to ../sthrift/* paths (domain, ui-components, service-* apps, and ui-sharethrift) instead of the legacy ocom/* and ui-community locations.
  • Introduce new placeholder architecture tests for domain conventions and GraphQL resolver conventions.
packages/sthrift/persistence/src/datasources/domain/role/admin-role/admin-role.domain-adapter.test.ts
packages/sthrift/persistence/src/datasources/domain/user/admin-user/admin-user.domain-adapter.test.ts
packages/arch-unit-tests/src/code-quality.test.ts
packages/arch-unit-tests/src/domain-conventions.test.ts
packages/arch-unit-tests/src/graphql-resolver-conventions.test.ts
Type GraphQL account plan resolvers with the shared GraphContext.
  • Annotate GraphQL account plan Query and Mutation resolvers to accept a context: GraphContext parameter, ensuring type safety and consistency with the GraphContext-based applicationServices access pattern.
  • Leave the underlying resolver behavior intact, only changing the TypeScript typing of the context parameter.
packages/sthrift/graphql/src/schema/types/account-plan/account-plan.resolvers.ts
Remove unused personal-user role artifacts under the old Role context.
  • Delete personal-user-role feature files and permission implementations now that roles have been re-scoped under the User context.
packages/sthrift/domain/src/domain/contexts/role/personal-user-role/features/personal-user-role.feature
packages/sthrift/domain/src/domain/contexts/role/personal-user-role/features/personal-user-role.value-objects.feature
packages/sthrift/domain/src/domain/contexts/role/personal-user-role/personal-user-role-account-plan-permissions.ts
packages/sthrift/domain/src/domain/contexts/role/personal-user-role/personal-user-role-user-permissions.ts

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@jasonmorais jasonmorais marked this pull request as ready for review January 28, 2026 19:48
@jasonmorais jasonmorais requested a review from a team January 28, 2026 19:48
Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've left some high level feedback:

  • Changing AdminRole.isNew from protected to private and never resetting it to false means newly created roles will continue to bypass validateVisa() even after persistence, so the permission gate will never run for them—please either expose the flag to the base class/unit of work or explicitly flip it off after the first save.
  • The new validateVisa() hook only guards the roleName and isDefault setters; all other mutable properties (notably the nested permission structures exposed through this.props.permissions) remain writable without any visa check, so you should ensure those mutation paths also call validateVisa() to actually enforce the new authorization policy.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Changing `AdminRole.isNew` from `protected` to `private` and never resetting it to `false` means newly created roles will continue to bypass `validateVisa()` even after persistence, so the permission gate will never run for them—please either expose the flag to the base class/unit of work or explicitly flip it off after the first save.
- The new `validateVisa()` hook only guards the `roleName` and `isDefault` setters; all other mutable properties (notably the nested permission structures exposed through `this.props.permissions`) remain writable without any visa check, so you should ensure those mutation paths also call `validateVisa()` to actually enforce the new authorization policy.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants