Skip to content

Commit 3d0add9

Browse files
Merge pull request #17 from topcoder-platform/PM-4720
PM-4720 Handle public user on Public copilot routes
2 parents 286d60b + d11bde4 commit 3d0add9

3 files changed

Lines changed: 19 additions & 13 deletions

File tree

src/api/copilot/copilot-opportunity.controller.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ export class CopilotOpportunityController {
8484
@Req() req: Request,
8585
@Res({ passthrough: true }) res: Response,
8686
@Query() query: ListOpportunitiesQueryDto,
87-
@CurrentUser() user: JwtUser,
87+
@CurrentUser() user: JwtUser | undefined,
8888
): Promise<CopilotOpportunityResponseDto[]> {
8989
const result = await this.service.listOpportunities(query, user);
9090

@@ -131,7 +131,7 @@ export class CopilotOpportunityController {
131131
@ApiResponse({ status: 404, description: 'Not found' })
132132
async getOpportunity(
133133
@Param('id') id: string,
134-
@CurrentUser() user: JwtUser,
134+
@CurrentUser() user: JwtUser | undefined,
135135
): Promise<CopilotOpportunityResponseDto> {
136136
return this.service.getOpportunity(id, user);
137137
}

src/api/copilot/copilot-opportunity.service.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,12 @@ export class CopilotOpportunityService {
8585
* Admin/manager responses also include minimal project metadata for v5 compatibility.
8686
*
8787
* @param query Pagination, sort, and noGrouping parameters.
88-
* @param user Authenticated JWT user.
88+
* @param user Authenticated JWT user, or undefined for anonymous `@Public()` callers.
8989
* @returns Paginated opportunity response payload.
9090
*/
9191
async listOpportunities(
9292
query: ListOpportunitiesQueryDto,
93-
user: JwtUser,
93+
user: JwtUser | undefined,
9494
): Promise<PaginatedOpportunityResponse> {
9595
// TODO [SECURITY]: No permission check is applied here; this is intentional for authenticated browsing and should remain explicitly documented.
9696
const [sortField, sortDirection] = parseSortExpression(
@@ -160,14 +160,14 @@ export class CopilotOpportunityService {
160160
* Admin/manager responses also include minimal project metadata for v5 compatibility.
161161
*
162162
* @param opportunityId Opportunity id path value.
163-
* @param user Authenticated JWT user.
163+
* @param user Authenticated JWT user, or undefined for anonymous `@Public()` callers.
164164
* @returns One formatted opportunity response.
165165
* @throws BadRequestException If id is non-numeric.
166166
* @throws NotFoundException If opportunity does not exist.
167167
*/
168168
async getOpportunity(
169169
opportunityId: string,
170-
user: JwtUser,
170+
user: JwtUser | undefined,
171171
): Promise<CopilotOpportunityResponseDto> {
172172
// TODO [SECURITY]: No permission check is applied; any authenticated user can access any opportunity by id.
173173
const parsedOpportunityId = parseNumericId(opportunityId, 'Opportunity');
@@ -208,7 +208,7 @@ export class CopilotOpportunityService {
208208
);
209209

210210
const canApplyAsCopilot =
211-
user.userId && user.userId.trim().length > 0
211+
user?.userId && user.userId.trim().length > 0
212212
? !members.includes(user.userId)
213213
: true;
214214

@@ -622,9 +622,9 @@ export class CopilotOpportunityService {
622622
*/
623623
private async getMembershipProjectIds(
624624
opportunities: CopilotOpportunity[],
625-
user: JwtUser,
625+
user: JwtUser | undefined,
626626
): Promise<Set<string>> {
627-
if (!user.userId || !/^\d+$/.test(user.userId)) {
627+
if (!user?.userId || !/^\d+$/.test(user.userId)) {
628628
return new Set<string>();
629629
}
630630

src/api/copilot/copilot.utils.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,13 @@ export function getCopilotTypeLabel(type: CopilotOpportunityType): string {
8181
/**
8282
* Returns true if user is admin, project manager, or manager.
8383
*
84-
* @param user Authenticated JWT user.
84+
* @param user Authenticated JWT user (undefined on anonymous `@Public()` routes).
8585
* @returns Whether the user is admin-or-manager scoped.
8686
*/
87-
export function isAdminOrManager(user: JwtUser): boolean {
87+
export function isAdminOrManager(user: JwtUser | undefined): boolean {
88+
if (!user) {
89+
return false;
90+
}
8891
const userRoles = user.roles || [];
8992

9093
return [
@@ -98,10 +101,13 @@ export function isAdminOrManager(user: JwtUser): boolean {
98101
/**
99102
* Returns true if user is admin or project manager.
100103
*
101-
* @param user Authenticated JWT user.
104+
* @param user Authenticated JWT user (undefined on anonymous `@Public()` routes).
102105
* @returns Whether the user is admin-or-pm scoped.
103106
*/
104-
export function isAdminOrPm(user: JwtUser): boolean {
107+
export function isAdminOrPm(user: JwtUser | undefined): boolean {
108+
if (!user) {
109+
return false;
110+
}
105111
const userRoles = user.roles || [];
106112

107113
return [

0 commit comments

Comments
 (0)