Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
190 commits
Select commit Hold shift + click to select a range
216b93a
Create @apps/ui-staff bootstrap app and compose initial staff route p…
Apr 22, 2026
ccd8d38
fix build issue
Apr 22, 2026
1787b54
feat: add ui-staff route packages with basic structure and vitest con…
ttrang-nguyen Apr 22, 2026
f7e70f9
feat(ui-staff): update routing and authentication structure
ttrang-nguyen Apr 23, 2026
2c50239
add pnpm-lock.yaml
ttrang-nguyen Apr 23, 2026
cfa2563
feat: add uuid dependency to package.json and update pnpm-lock.yaml
ttrang-nguyen Apr 23, 2026
d92e935
feat: add AuthLanding component for navigation and Apollo client setup
ttrang-nguyen Apr 23, 2026
9256b16
fix: update ApolloConnectionProps interface
ttrang-nguyen Apr 23, 2026
cb0f452
Update Snyk ignore rules for UUID and adjust package versions for com…
ttrang-nguyen Apr 24, 2026
e7d8ae1
update snyke ignore
ttrang-nguyen Apr 24, 2026
19531b8
update layout and styling in ThemeProvider component
ttrang-nguyen Apr 27, 2026
c8d30de
fix: update dev script to use correct localhost domain
ttrang-nguyen Apr 27, 2026
2355bed
merge from main and resolve merge issue
ttrang-nguyen Apr 27, 2026
3385ed1
feat: integrate StaffAuthProvider for user identity management
ttrang-nguyen Apr 28, 2026
0a7817f
update OIDC configuration and add mock data for staff authentication
ttrang-nguyen Apr 29, 2026
0095df3
feat: implement login page and update routing for authentication
Apr 29, 2026
99e0148
fix merge conflicts
ttrang-nguyen Apr 29, 2026
7124050
fix build issue
ttrang-nguyen Apr 29, 2026
daac62c
chore: update TypeScript to 6.0.3 and add native-preview package; mod…
Apr 29, 2026
cdcdab1
chore: update TypeScript version to 6.0.3 in pnpm-lock.yaml
Apr 29, 2026
be5783e
refine login page and add logout functionality
ttrang-nguyen Apr 29, 2026
d01e0fc
Merge branch 'rohit-r-kumar/issue212' of github.com:CellixJs/cellixjs…
ttrang-nguyen Apr 29, 2026
49f66f8
remove dist files
ttrang-nguyen Apr 29, 2026
37ad0c1
refactor: restructure authentication flow and remove login page compo…
Apr 29, 2026
e6c5e31
remove .env from .gitignore
ttrang-nguyen Apr 29, 2026
78d2ac3
refactor: update build scripts to use tsgo and improve code formatting
ttrang-nguyen Apr 30, 2026
280a381
refactor: update package.json scripts and remove unused dependencies
Apr 30, 2026
7a40958
refactor: update entry points and project patterns in knip.json and p…
Apr 30, 2026
47c125e
refactor: remove unused dependencies from pnpm-lock.yaml
Apr 30, 2026
c204df3
fix: correct token property name in ThemeProvider and adjust children…
ttrang-nguyen Apr 30, 2026
fedc557
fix: correct typo in OIDCConfig type and instance from 'noonce' to 'n…
ttrang-nguyen Apr 30, 2026
29cdc6f
refactor: update environment variable references and improve type def…
ttrang-nguyen Apr 30, 2026
ba59183
format
ttrang-nguyen Apr 30, 2026
a67eb06
feat: integrate react-oidc-context for authentication in header compo…
ttrang-nguyen Apr 30, 2026
b4a3b77
Changes before error encountered
Copilot Apr 30, 2026
13b4897
test: add unit tests for resolver-builder module and mergeResolvers f…
May 1, 2026
93df96e
Renaming: ui-community-route-shared -> ui-community-shared
May 1, 2026
7767f71
fix: rename ui-community-route-shared to ui-community-shared in knip.…
May 1, 2026
8912120
fix: correct Biome formatting in resolver-builder.test.ts
Copilot May 1, 2026
27cb61e
feat(ui-staff): add Entra app-role route authorization (#214)
May 1, 2026
54d2f83
Merge branch 'main' of https://github.com/CellixJs/cellixjs into rohi…
May 4, 2026
ac69b4e
test(ui-staff): add route-authorization scenario tests for all 4 Entr…
May 4, 2026
440c022
chore(ui-staff-routes): remove unused deps flagged by knip
May 4, 2026
3bfe580
Revert package.json
May 4, 2026
f1e381a
test(ui-staff): add Playwright e2e tests for role-based route authori…
May 4, 2026
6396cd4
feat: update pnpm workspace and add new packages for Cucumber and Ser…
May 4, 2026
8bbf225
chore: remove react-dom version from pnpm-lock.yaml
May 4, 2026
08f44c3
Refactor code structure for improved readability and maintainability
May 5, 2026
a87bbfa
feat: implement theme storage functions and update theme context to u…
May 5, 2026
bf378d6
fix: refine theme context type and improve theme storage handling
May 5, 2026
085ed0f
fix: enhance theme context type and streamline theme storage handling
May 5, 2026
f51991e
fix: correct import order for theme storage functions in theme context
May 5, 2026
8f64ac2
feat: implement staff RBAC with backend user/role creation and permis…
May 5, 2026
d1b3d25
feat: add staff user query and permissions handling in community and …
May 6, 2026
a6f530f
feat: add staff user role csv file
ttrang-nguyen May 6, 2026
56755b5
Merge branch 'rohit-r-kumar/issue214' of github.com:CellixJs/cellixjs…
ttrang-nguyen May 6, 2026
9ce1e95
update staff role permissions and community list story
ttrang-nguyen May 6, 2026
063dda5
refactor: update community permissions structure and related queries
May 7, 2026
ff79094
refactor: improve staff role permissions test structure and enhance t…
May 7, 2026
b2f97f7
refactor: remove default roleType from StaffRoleSchema
ttrang-nguyen May 7, 2026
28ec3b6
Merge branch 'rohit-r-kumar/issue214' of github.com:CellixJs/cellixjs…
ttrang-nguyen May 7, 2026
17f83e1
add mongoose vulnerability exception with detailed reasoning
May 7, 2026
6a053ba
feat: Add staff user resolvers and tests for querying and creating st…
May 8, 2026
fceac8e
Merge Branch with Main
May 8, 2026
56324cb
refactor: Simplify function definitions and improve readability in tests
May 8, 2026
7a81701
Refactor code structure for improved readability and maintainability
May 8, 2026
9bcf52b
refactor: Remove unused imports from theme context
May 8, 2026
be4b476
feat: Update staff route management and authorization roles for commu…
ttrang-nguyen May 8, 2026
825c79a
feat: Implement role-based access control for staff routes and permis…
May 8, 2026
b9d03cc
feat: Implement role-based access control for staff routes and permis…
May 8, 2026
c0091c0
refactor: Rename VITE_FUNCTION_ENDPOINT to VITE_COMMON_API_ENDPOINT a…
May 11, 2026
fd13793
refactor: Rename VITE_FUNCTION_ENDPOINT to VITE_COMMON_API_ENDPOINT a…
May 11, 2026
a18025d
feat: Update dependencies for improved security and functionality
ttrang-nguyen May 11, 2026
53cdd3e
feat: Enhance role-based access control in staff routes and permissions
ttrang-nguyen May 11, 2026
2c6ff5e
Removed staff-user-role csv file, refactored createDefaultRoles, upda…
ttrang-nguyen May 12, 2026
7af12a2
fix snyk issue
ttrang-nguyen May 12, 2026
227fe04
e2e and acceptance-ui tests for staff
ttrang-nguyen May 12, 2026
4ad2821
Add staff users and staff roles pages to ui-staff user management sec…
May 13, 2026
564144a
update tests
ttrang-nguyen May 13, 2026
c37714e
chore: update protobufjs to resolved snyk issue
May 13, 2026
9128a02
Merge branch 'rohit-r-kumar/issue214' of https://github.com/CellixJs/…
May 13, 2026
2134ac4
refactor: replace withScopedTransaction with withTransaction in staff…
May 13, 2026
5a492a6
feat: implement StaffUserUserPassport and update StaffUserPassport to…
May 13, 2026
6202fd1
Added TechAdmin permission to finance, community-management, and user…
ttrang-nguyen May 13, 2026
c04be87
fix: downgrade packageManager to pnpm@10.30.1
ttrang-nguyen May 13, 2026
ef09d6d
downgrade packageManager to pnpm@10.30.1, add canManage permissions s…
ttrang-nguyen May 14, 2026
5b4b7e3
chore: update protobufjs to version 7.5.8
ttrang-nguyen May 14, 2026
3741499
feat: enhance staff role management by adding default role instances …
May 14, 2026
850330a
add query for fetching displayName
ttrang-nguyen May 14, 2026
7c8cac6
remove staff queries from ui-community route
ttrang-nguyen May 14, 2026
01559d7
Merge branch with main
May 14, 2026
daf83d3
Resolve conflicts
May 14, 2026
f0307ed
conflicts
ttrang-nguyen May 14, 2026
0aca918
merge conflicts
ttrang-nguyen May 14, 2026
3994cc7
undo and resolve conflicts
ttrang-nguyen May 14, 2026
c6457e5
update role names nad remove unused staff section permissions
ttrang-nguyen May 15, 2026
cb05fc9
Merge branches 'rohit-r-kumar/issue214' and 'rohit-r-kumar/issue214' …
May 15, 2026
5d0a3d1
feat(staff-role): add support for retrieving default roles by enterpr…
ttrang-nguyen May 15, 2026
1583df2
Merge branch 'rohit-r-kumar/issue214' of github.com:CellixJs/cellixjs…
ttrang-nguyen May 15, 2026
6973a3b
Revert "Merge branch 'rohit-r-kumar/issue214' of github.com:CellixJs/…
ttrang-nguyen May 15, 2026
de9ecf9
Fix knip issues
May 15, 2026
8ec3195
merge #214 and resolve merge conflicts
ttrang-nguyen May 15, 2026
9cc7d56
Merge branch 'main' of https://github.com/CellixJs/cellixjs into rohi…
May 15, 2026
a287666
refactor: update staff role handling and remove unused queries in com…
May 15, 2026
ce38a55
remove uuid and add enterpriseAppRole for finance and techAdmin
ttrang-nguyen May 15, 2026
e2161c4
feat: add enterpriseAppRole handling in staff role tests and repository
May 18, 2026
7c69013
Resolve test coverage failure and add getDefaultRoleByEnterpriseAppRole
ttrang-nguyen May 18, 2026
0960b11
chore: update dependencies for snyk issue
ttrang-nguyen May 18, 2026
7ddca09
remove staffPermission from index.tsx
ttrang-nguyen May 18, 2026
58a06ef
Pull from main
May 18, 2026
25a16ea
Pull from branch/214
May 18, 2026
fef6d15
add staff user creation ad role management components
ttrang-nguyen May 18, 2026
67fe8f8
fix knip issues
ttrang-nguyen May 18, 2026
065fbb8
remove mock store and update components
ttrang-nguyen May 19, 2026
f7481c4
Fix Snyk issue
ttrang-nguyen May 19, 2026
5938260
feat: add staff role management features
May 19, 2026
ebfe6c5
Resolve conflicts
May 19, 2026
eb52cd2
feat: enhance staff role management with enterprise app roles and per…
May 19, 2026
a2968d8
fix Snyk issue
ttrang-nguyen May 19, 2026
96913bd
feat: implement staff role update functionality with edit capabilities
May 19, 2026
1a46208
Merge branch 'rohit-r-kumar/issue250' of https://github.com/CellixJs/…
May 19, 2026
21a74d1
Added GraphQL queries and mutations for staff user creation and role …
ttrang-nguyen May 19, 2026
0462af0
fix knip and test coverage issues; added users management page access…
ttrang-nguyen May 20, 2026
9b45e4e
feat: add staff role listing and updating features
May 20, 2026
397b659
Disable self-assigning role functionality and make read-only; replace…
ttrang-nguyen May 20, 2026
45331f6
Merge branch 'rohit-r-kumar/issue250' of github.com:CellixJs/cellixjs…
ttrang-nguyen May 20, 2026
ea14967
feat: enhance staff role creation tests with permissions type validation
May 20, 2026
78fe27f
Merge branch 'rohit-r-kumar/issue250' of github.com:CellixJs/cellixjs…
ttrang-nguyen May 20, 2026
3d405d9
feat: add staff user listing feature and tests
May 20, 2026
23da639
merge from main
ttrang-nguyen May 20, 2026
3689105
add staff user role csv file
ttrang-nguyen May 21, 2026
1e5ae34
Refactor permission sets
ttrang-nguyen May 21, 2026
c42d0d4
fix build and knip issue
ttrang-nguyen May 21, 2026
454044a
remove staff user creation functionality
ttrang-nguyen May 21, 2026
44d60f6
add permission hierachy and change label to staff role
ttrang-nguyen May 22, 2026
473c344
Add save functionality and update selection handling
ttrang-nguyen May 22, 2026
116ab44
Implement activity logging for staff user actions and enhance role as…
May 22, 2026
05b5cb6
Merge branch 'rohit-r-kumar/issue250' of https://github.com/CellixJs/…
May 22, 2026
fc44a4e
replaced activity detail with activity log, refactor activityDescription
ttrang-nguyen May 22, 2026
2c4caf4
added more test cases
ttrang-nguyen May 22, 2026
5f9313a
fix lint issue and refactor staff role create
ttrang-nguyen May 22, 2026
b0dee5f
fix snyk issue
ttrang-nguyen May 22, 2026
c59fd50
add test cases
ttrang-nguyen May 22, 2026
d193099
fix build issue
ttrang-nguyen May 22, 2026
8600271
Update staff user role CSV file
May 25, 2026
8ab1447
feat: add tech admin database explorer functionality
May 26, 2026
b145119
resolve ui not showing up and test coverage issue
ttrang-nguyen May 26, 2026
0204c43
update canViewDatabaseDocuments in permissions
May 27, 2026
5f8ff9f
Update tech admin
May 27, 2026
003302c
refactor tech admin resolvers and resolve query issue with id and role
ttrang-nguyen May 27, 2026
6f3c2d2
refactor tech admin resolvers and resolve query issue with id and role
ttrang-nguyen May 27, 2026
8606f11
add lock file
ttrang-nguyen May 27, 2026
6177e42
fix duplicate exports and removed unused exports
ttrang-nguyen May 27, 2026
5062ae9
add current role to available roles in StaffUserDetailContainer
ttrang-nguyen May 28, 2026
0138aac
add snyk exception
ttrang-nguyen May 28, 2026
37c2fd8
merge from main
ttrang-nguyen May 28, 2026
68d07b1
add biome ignore
ttrang-nguyen May 28, 2026
924db46
xadd staff context step definitions and update related configurations
ttrang-nguyen May 29, 2026
8ccfde2
resolve merge conflicts
ttrang-nguyen Jun 25, 2026
95d0a0b
Resolve conflicts merge main
Jun 25, 2026
e167754
Update pnpm-lock.yaml
Jun 25, 2026
a7f31e1
resolve merge conflicts
ttrang-nguyen Jun 25, 2026
0fb1132
Merge branch 'rohit-r-kumar/issue258' of https://github.com/CellixJs/…
ttrang-nguyen Jun 25, 2026
944a23a
add lock file
ttrang-nguyen Jun 25, 2026
d351bb8
Resolve snyk issue
Jun 26, 2026
fbe2391
refactor database explorer page and Tech Admin dropdown
ttrang-nguyen Jun 26, 2026
4100144
Merge branch 'rohit-r-kumar/issue258' of https://github.com/CellixJs/…
ttrang-nguyen Jun 26, 2026
ab6689a
Created test cases of tech-admin resolver
Jun 26, 2026
facaa04
Merge branch 'rohit-r-kumar/issue258' of https://github.com/CellixJs/…
Jun 26, 2026
9e84ce2
refactor DatabaseExplorerContainer to separate draft and applied filters
ttrang-nguyen Jun 26, 2026
eac343d
Merge branch 'rohit-r-kumar/issue258' of https://github.com/CellixJs/…
ttrang-nguyen Jun 26, 2026
6cd05cf
added Database Explorer title back
ttrang-nguyen Jun 26, 2026
69429a2
Create test cases for apply-permissions.ts
Jun 26, 2026
6aa3491
Update sonar properties
Jun 29, 2026
7125ad5
implement full pop out modal and copy functionality
ttrang-nguyen Jun 29, 2026
4bfd353
merge conflicts
ttrang-nguyen Jun 29, 2026
f2d9836
upgrade fast-uri version to 3.1.3
ttrang-nguyen Jun 29, 2026
fd0b3d1
upgrade fast-uri to version 4.0.1
ttrang-nguyen Jun 29, 2026
eb47c47
Merge branch 'main' of https://github.com/CellixJs/cellixjs into rohi…
Jun 29, 2026
952a37e
format
ttrang-nguyen Jun 29, 2026
43f9c87
add json rendering functionality to DatabaseExplorer component
ttrang-nguyen Jun 30, 2026
ef7f044
upgrade braceexpansion version
ttrang-nguyen Jun 30, 2026
dd83f40
add authorization scenarios for tech admin resolvers test
ttrang-nguyen Jun 30, 2026
1479ef8
refactor techadmin resolvers; add list-collections and query-documents
ttrang-nguyen Jul 1, 2026
ad02664
add test
ttrang-nguyen Jul 1, 2026
0886bfa
fix knip issue
ttrang-nguyen Jul 1, 2026
c279c85
add lock file
ttrang-nguyen Jul 1, 2026
2935e3a
refactor: move buildDatabaseDocumentsQueryCommand to application-serv…
Jul 1, 2026
b7206bc
remove vitest set up
ttrang-nguyen Jul 1, 2026
b0db1a9
Merge branch 'main' into rohit-r-kumar/issue258
henry-casper Jul 7, 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
6 changes: 3 additions & 3 deletions apps/ui-community/mock-oidc.users.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"username": "test@example.com",
"sub": "00000000-0000-4000-8000-000000000001",
"password": "password",
"oidcConfigName": "end-user",
"oidcConfigName": "end-user",
"claims": {
"email": "test@example.com",
"given_name": "Test",
Expand All @@ -15,7 +15,7 @@
"username": "john.doe@example.com",
"sub": "00000000-0000-4000-8000-000000000002",
"password": "password",
"oidcConfigName": "end-user",
"oidcConfigName": "end-user",
"claims": {
"email": "john.doe@example.com",
"given_name": "John",
Expand All @@ -27,7 +27,7 @@
"username": "owner@test.example",
"sub": "aaaaaaaa-bbbb-1ccc-9ddd-eeeeeeeeee01",
"password": "password",
"oidcConfigName": "end-user",
"oidcConfigName": "end-user",
"claims": {
"email": "owner@test.example",
"given_name": "Test",
Expand Down
2 changes: 1 addition & 1 deletion apps/ui-staff/mock-oidc.users.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"username": "staff@ownercommunity.onmicrosoft.com",
"sub": "10000000-0000-4000-8000-000000000001",
"password": "password",
"oidcConfigName": "staff-user",
"oidcConfigName": "staff-user",
"claims": {
"email": "staff@ownercommunity.onmicrosoft.com",
"given_name": "Staff",
Expand Down
6 changes: 4 additions & 2 deletions apps/ui-staff/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,16 @@ function StaffRoutes() {
const canViewStaffUsers = perms?.canViewStaffUsers === true;
const canManageFinance = perms?.canManageFinance === true;
const canManageTechAdmin = perms?.canManageTechAdmin === true;
const canViewDatabaseDocuments = perms?.canViewDatabaseDocuments === true;
const canAccessTechAdmin = canManageTechAdmin || canViewDatabaseDocuments;
const canViewRoles = perms?.canViewRoles === true;
const canAddRole = perms?.canAddRole === true;
const canEditRole = perms?.canEditRole === true;
const canRemoveRole = perms?.canRemoveRole === true;
const canAccessUserManagement = canManageUsers || canAssignStaffRoles || canViewStaffUsers || canManageStaffRolesAndPermissions || canViewRoles || canAddRole || canEditRole || canRemoveRole || canManageTechAdmin;

let defaultStaffRoute = '/unauthorized';
if (canManageTechAdmin) {
if (canAccessTechAdmin) {
defaultStaffRoute = '/staff/tech';
} else if (canManageFinance) {
defaultStaffRoute = '/staff/finance';
Expand Down Expand Up @@ -73,7 +75,7 @@ function StaffRoutes() {
element={<Finance />}
/>
)}
{canManageTechAdmin && (
{canAccessTechAdmin && (
<Route
path="tech/*"
element={<TechAdmin />}
Expand Down
5 changes: 4 additions & 1 deletion apps/ui-staff/src/hooks/use-staff-permissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const CURRENT_STAFF_USER_QUERY = gql`
}
techAdminPermissions {
canManageTechAdmin
canViewDatabaseDocuments
}
}
}
Expand All @@ -49,6 +50,7 @@ interface StaffPermissions {
canViewStaffUsers: boolean;
canManageFinance: boolean;
canManageTechAdmin: boolean;
canViewDatabaseDocuments: boolean;
canViewRoles: boolean;
canAddRole: boolean;
canEditRole: boolean;
Expand All @@ -72,7 +74,7 @@ interface StaffUserQueryResult {
userPermissions: { canManageUsers: boolean; canAssignStaffRoles: boolean; canViewStaffUsers: boolean };
staffRolePermissions: { canViewRoles: boolean; canAddRole: boolean; canEditRole: boolean; canRemoveRole: boolean };
financePermissions: { canManageFinance: boolean };
techAdminPermissions: { canManageTechAdmin: boolean };
techAdminPermissions: { canManageTechAdmin: boolean; canViewDatabaseDocuments: boolean };
};
};
};
Expand Down Expand Up @@ -104,6 +106,7 @@ export const useStaffPermissions = (): {
canViewStaffUsers: rolePermissions.userPermissions.canViewStaffUsers || rolePermissions.userPermissions.canManageUsers || isTechAdmin,
canManageFinance: rolePermissions.financePermissions.canManageFinance || isTechAdmin,
canManageTechAdmin: isTechAdmin,
canViewDatabaseDocuments: rolePermissions.techAdminPermissions.canViewDatabaseDocuments || isTechAdmin,
canViewRoles: rolePermissions.staffRolePermissions.canViewRoles || rolePermissions.communityPermissions.canManageStaffRolesAndPermissions || isTechAdmin,
canAddRole: rolePermissions.staffRolePermissions.canAddRole || rolePermissions.communityPermissions.canManageStaffRolesAndPermissions || isTechAdmin,
canEditRole: rolePermissions.staffRolePermissions.canEditRole || rolePermissions.communityPermissions.canManageStaffRolesAndPermissions || isTechAdmin,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,7 @@ describe('file user store oidcConfigName filtering', () => {

it('users without oidcConfigName are visible to all scoped stores', async () => {
if (!tmp) throw new Error('tmp not created');
writeUsers(tmp, 'mock-oidc.users.json', [
{ username: 'shared@example.com', sub: 'sub-shared' },
]);
writeUsers(tmp, 'mock-oidc.users.json', [{ username: 'shared@example.com', sub: 'sub-shared' }]);
const endStore = createFileUserStore(tmp, 'end-user');
const staffStore = createFileUserStore(tmp, 'staff-user');

Expand Down Expand Up @@ -191,15 +189,7 @@ describe('file user store oidcConfigName filtering', () => {
expect(users).toHaveLength(1);
expect(users[0]?.username).toBe('good@example.com');
expect(warnSpy.length).toBeGreaterThan(0);
expect(
warnSpy.some((args) =>
(args as unknown[]).some(
(arg) =>
typeof arg === 'string' &&
arg.includes('"oidcConfigName" must be a string'),
),
),
).toBe(true);
expect(warnSpy.some((args) => (args as unknown[]).some((arg) => typeof arg === 'string' && arg.includes('"oidcConfigName" must be a string')))).toBe(true);
} finally {
console.warn = origWarn;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -569,11 +569,7 @@ describe('discoverPortalConfigs', () => {
envVars: { clientId: 'VITE_APP_PORTAL_STAFF_CLIENTID', redirectUri: 'VITE_APP_PORTAL_STAFF_REDIRECT' },
},
]);
writeEnv(
tmp,
'ui-portal/.env',
'VITE_APP_PORTAL_END_CLIENTID=end-cid\nVITE_APP_PORTAL_END_REDIRECT=https://portal/end/cb\nVITE_APP_PORTAL_STAFF_CLIENTID=staff-cid\nVITE_APP_PORTAL_STAFF_REDIRECT=https://portal/staff/cb\n',
);
writeEnv(tmp, 'ui-portal/.env', 'VITE_APP_PORTAL_END_CLIENTID=end-cid\nVITE_APP_PORTAL_END_REDIRECT=https://portal/end/cb\nVITE_APP_PORTAL_STAFF_CLIENTID=staff-cid\nVITE_APP_PORTAL_STAFF_REDIRECT=https://portal/staff/cb\n');

const portals = discoverPortalConfigs(tmp);
expect(portals).toHaveLength(2);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@

import '../contexts/community/step-definitions/index.ts';
import '../contexts/authentication/step-definitions/index.ts';
import '../contexts/staff/step-definitions/index.ts';
import '../contexts/staff/step-definitions/index.ts';
3 changes: 2 additions & 1 deletion packages/ocom/application-services/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
"@ocom/domain": "workspace:*",
"@ocom/persistence": "workspace:*",
"@ocom/service-blob-storage": "workspace:*",
"@ocom/service-queue-storage": "workspace:*"
"@ocom/service-queue-storage": "workspace:*",
"mongoose": "catalog:"
},
"devDependencies": {
"@cellix/archunit-tests": "workspace:*",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
type DatabaseDocumentsQueryArgs = {
collection: string;
filter?: string | null | undefined;
page?: number | null | undefined;
pageSize?: number | null | undefined;
};

const ALLOWED_OPERATORS = new Set(['$eq', '$in', '$gt', '$gte', '$lt', '$lte', '$exists', '$regex', '$and', '$or', '$not']);

function validateOperatorKey(key: string): void {
if (!key.startsWith('$')) return;
if (key === '$where' || key === '$function' || key === '$expr') {
throw new Error(`Operator ${key} is not allowed in filter`);
}
if (!ALLOWED_OPERATORS.has(key)) {
throw new Error(`Unknown operator: ${key}`);
}
}

function validateFilterOperators(obj: unknown): void {
if (obj === null || obj === undefined) return;
if (Array.isArray(obj)) {
for (const item of obj) validateFilterOperators(item);
return;
}
if (typeof obj !== 'object') return;
for (const [k, v] of Object.entries(obj as Record<string, unknown>)) {
validateOperatorKey(k);
validateFilterOperators(v);
}
}

export type DatabaseDocumentsQueryCommand = {
collection: string;
filter: Record<string, unknown>;
page: number;
pageSize: number;
};

export function buildDatabaseDocumentsQueryCommand(args: DatabaseDocumentsQueryArgs): DatabaseDocumentsQueryCommand {
if (!/^[a-zA-Z0-9_-]+$/.test(args.collection)) {
throw new Error('Invalid collection name');
}

let parsedFilter: Record<string, unknown> = {};
if (args.filter) {
try {
parsedFilter = JSON.parse(args.filter) as Record<string, unknown>;
} catch {
throw new Error('Invalid filter JSON');
}
validateFilterOperators(parsedFilter);
}

return {
collection: args.collection,
filter: parsedFilter,
pageSize: Math.min(Math.max(args.pageSize ?? 20, 1), 100),
page: Math.max(args.page ?? 1, 1),
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { ListCollections } from './list-collections.ts';
import { DatabaseDocuments } from './query-documents.ts';
export { buildDatabaseDocumentsQueryCommand } from './database-documents.command-mapper.ts';

export interface TechAdminApplicationService {
ListCollections: ReturnType<typeof ListCollections>;
DatabaseDocuments: ReturnType<typeof DatabaseDocuments>;
}

export const TechAdmin = (): TechAdminApplicationService => {
return {
ListCollections: ListCollections(),
DatabaseDocuments: DatabaseDocuments(),
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import mongoose from 'mongoose';

export const ListCollections = () => {
return async (): Promise<string[]> => {
const { db } = mongoose.connection;
if (!db) throw new Error('Database connection is not available');
const cols = await db.listCollections().toArray();
return cols
.map((c) => c.name)
.filter((n) => !n.startsWith('system.'))
.sort((a, b) => a.localeCompare(b, 'en', { sensitivity: 'base' }));
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import mongoose from 'mongoose';

function normalizeBsonValue(value: unknown): unknown {
if (value === null || value === undefined) return value;
if (Array.isArray(value)) return value.map(normalizeBsonValue);
if (value instanceof Date) return value.toISOString();
if (value && typeof (value as { toHexString?: unknown }).toHexString === 'function') return (value as { toHexString: () => string }).toHexString();
if (value && (value as { _bsontype?: unknown })._bsontype === 'Decimal128') return (value as { toString: () => string }).toString();
if (Buffer.isBuffer(value)) return value.toString('base64');
if (value instanceof Uint8Array) return Buffer.from(value).toString('base64');
if (typeof value === 'object') {
const out: Record<string, unknown> = {};
for (const [k, v] of Object.entries(value as Record<string, unknown>)) {
out[k] = normalizeBsonValue(v);
}
return out;
}
return value;
}

const OBJECT_ID_KEYS = new Set(['_id', 'role']);
const objectIdPattern = /^[a-fA-F0-9]{24}$/;

function normalizeObjectIdValue(value: unknown): unknown {
if (typeof value === 'string' && objectIdPattern.test(value)) {
return new mongoose.Types.ObjectId(value);
}
if (Array.isArray(value)) {
return value.map((item) => normalizeObjectIdValue(item));
}
if (value && typeof value === 'object') {
const out: Record<string, unknown> = {};
for (const [k, v] of Object.entries(value as Record<string, unknown>)) {
out[k] = normalizeObjectIdValue(v);
}
return out;
}
return value;
}

function normalizeFilterObjectIds(obj: unknown): unknown {
if (obj === null || obj === undefined) return obj;
if (Array.isArray(obj)) {
return obj.map((item) => normalizeFilterObjectIds(item));
}
if (typeof obj !== 'object') return obj;
const out: Record<string, unknown> = {};
for (const [k, v] of Object.entries(obj as Record<string, unknown>)) {
if (k.startsWith('$')) {
out[k] = normalizeFilterObjectIds(v);
continue;
}
if (OBJECT_ID_KEYS.has(k)) {
out[k] = normalizeObjectIdValue(v);
continue;
}
out[k] = normalizeFilterObjectIds(v);
}
return out;
}

async function enrichStaffUserRoles(collection: string, docs: Record<string, unknown>[], db: mongoose.mongo.Db): Promise<Record<string, unknown>[]> {
if (collection !== 'users') return docs;
const roleIds = new Set<string>();
for (const doc of docs) {
//biome-ignore lint:useLiteralKeys
const role = doc['role'];
if (typeof role === 'string' && role.length === 24) roleIds.add(role);
if (role && typeof role === 'object' && typeof (role as { id?: unknown }).id === 'string') roleIds.add((role as { id: string }).id);
}
if (roleIds.size === 0) return docs;
const roleObjectIds = [...roleIds].map((id) => new mongoose.Types.ObjectId(id));
const roles = await db.collection('roles').find({ _id: { $in: roleObjectIds } }, { projection: { roleName: 1, enterpriseAppRole: 1 } }).toArray();
const roleMap = new Map(
roles.map((role) => [
String(role._id),
{
//biome-ignore lint:useLiteralKeys
roleName: role['roleName'] ?? null,
//biome-ignore lint:useLiteralKeys
enterpriseAppRole: role['enterpriseAppRole'] ?? null,
},
]),
);
return docs.map((doc) => {
//biome-ignore lint:useLiteralKeys
const roleId = typeof doc['role'] === 'string' ? doc['role'] : (doc['role'] as { id?: string } | undefined)?.id ?? null;
if (!roleId) return doc;
const roleInfo = roleMap.get(roleId);
if (!roleInfo) return doc;
return { ...doc, role: { id: roleId, ...roleInfo } };
});
}

import type { DatabaseDocumentsQueryCommand } from './database-documents.command-mapper.ts';

type DatabaseDocument = {
id: string;
json: string;
};

type DatabaseDocumentPage = {
documents: DatabaseDocument[];
totalCount: number;
};

export const DatabaseDocuments = () => {
return async (command: DatabaseDocumentsQueryCommand): Promise<DatabaseDocumentPage> => {
const { db } = mongoose.connection;
if (!db) throw new Error('Database connection is not available');
const skip = (command.page - 1) * command.pageSize;
const coll = db.collection(command.collection);
const filter = normalizeFilterObjectIds(command.filter) as Record<string, unknown>;
const totalCount = await coll.countDocuments(filter);
const docs = await coll.find(filter).skip(skip).limit(command.pageSize).toArray();
const enrichedDocs = await enrichStaffUserRoles(command.collection, docs as Record<string, unknown>[], db);
return {
totalCount,
documents: enrichedDocs.map((doc) => {
const sanitized = normalizeBsonValue(doc as Record<string, unknown>);
//biome-ignore lint:useLiteralKeys
return { id: String(doc['_id']), json: JSON.stringify(sanitized) };
}),
};
};
};
3 changes: 3 additions & 0 deletions packages/ocom/application-services/src/contexts/user/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,20 @@ import type { DataSources } from '@ocom/persistence';
import { EndUser as EndUserApi, type EndUserApplicationService } from './end-user/index.ts';
import { StaffRole as StaffRoleApi, type StaffRoleApplicationService } from './staff-role/index.ts';
import { StaffUser as StaffUserApi, type StaffUserApplicationService } from './staff-user/index.ts';
import { TechAdmin as TechAdminApi, type TechAdminApplicationService } from '../tech-admin/index.ts';

export interface UserContextApplicationService {
EndUser: EndUserApplicationService;
StaffRole: StaffRoleApplicationService;
StaffUser: StaffUserApplicationService;
TechAdmin: TechAdminApplicationService;
}

export const User = (dataSources: DataSources): UserContextApplicationService => {
return {
EndUser: EndUserApi(dataSources),
StaffRole: StaffRoleApi(dataSources),
StaffUser: StaffUserApi(dataSources),
TechAdmin: TechAdminApi(),
};
};
Loading
Loading