Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CORL-3212]: throw no username included error for sso with url for setting username #4723

Merged
merged 4 commits into from
Jan 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ interface DupErrorObj {
};
}

interface UsernameNotProvidedErrorObj {
error?: {
traceID: string;
code: string;
message: string;
};
}

const parseDuplicateEmailError = (
error: RRNLRequestError
): DupErrorObj | null => {
Expand All @@ -30,6 +38,25 @@ const parseDuplicateEmailError = (
}
};

const parseUsernameNotProvidedError = (
error: RRNLRequestError
): UsernameNotProvidedErrorObj | null => {
if (!error.res || error.res.status !== 403 || !error.res.text) {
return null;
}

try {
const json = JSON.parse(error.res.text) as UsernameNotProvidedErrorObj;
if (!json || !json.error || json.error.code !== "USERNAME_NOT_PROVIDED") {
return null;
}

return json;
} catch {
return null;
}
};

Comment on lines +41 to +59
Copy link
Contributor

Choose a reason for hiding this comment

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

heh, looks like we're going to have to be generalizing this at some point... so many error object types popping up to be parsed through the response text 😅

const computeCodeMessage = (
error: RRNLRequestError,
localeBundles: FluentBundle[]
Expand Down Expand Up @@ -70,6 +97,11 @@ const computeMessage = (
return dupeEmail.error.message;
}

const usernameNotProvided = parseUsernameNotProvidedError(error);
if (usernameNotProvided && usernameNotProvided.error) {
return usernameNotProvided.error.message;
}

if (error.res) {
return `${defaultMessage} ${computeCodeMessage(error, localeBundles)}`;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Localized } from "@fluent/react/compat";
import React, { FunctionComponent } from "react";

import TraceableError from "coral-framework/lib/errors/traceableError";
import HTMLContent from "coral-stream/common/HTMLContent";

import CallOut from "../CallOut";

Expand Down Expand Up @@ -38,7 +39,7 @@ const QueryError: FunctionComponent<Props> = ({ error }) => {
<div className={styles.heading}>Message</div>
</Localized>
<div className={styles.section} aria-live={"polite"}>
{error.message}
<HTMLContent>{error.message}</HTMLContent>
</div>
</CallOut>
);
Expand Down
1 change: 1 addition & 0 deletions common/lib/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export enum ERROR_TYPES {
}

export enum ERROR_CODES {
USERNAME_NOT_PROVIDED = "USERNAME_NOT_PROVIDED",
/**
* STORY_CLOSED is used when submitting a comment on a closed story.
*/
Expand Down
11 changes: 11 additions & 0 deletions server/src/core/server/app/middleware/passport/strategies/jwt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
JWTRevokedError,
TenantNotFoundError,
TokenInvalidError,
UsernameNotProvidedError,
} from "coral-server/errors";
import { Tenant } from "coral-server/models/tenant";
import { User } from "coral-server/models/user";
Expand Down Expand Up @@ -126,6 +127,16 @@ export async function verifyAndRetrieveUser(
throw err;
}

// If SSO token does not include a username but does include a url for user account
// management, then throw a username not found error with information on where the username
// can be set to comment.
if (validationErrors.includes('SSO: "user.username" is required')) {
const ssoToken = token as SSOToken;
if (ssoToken && ssoToken.user?.url) {
throw new UsernameNotProvidedError(ssoToken.user.url);
}
}

// If no verifier could be found, throw an error. Include validation errors for all enabled
// verifiers to help trace the issue.
throw new TokenInvalidError(
Expand Down
10 changes: 10 additions & 0 deletions server/src/core/server/errors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,16 @@ export class DuplicateEmailError extends CoralError {
}
}

export class UsernameNotProvidedError extends CoralError {
constructor(url: string) {
super({
code: ERROR_CODES.USERNAME_NOT_PROVIDED,
context: { pub: { url } },
status: 403,
});
}
}

export class DuplicateDSAReportError extends CoralError {
constructor(reportID: string) {
super({
Expand Down
1 change: 1 addition & 0 deletions server/src/core/server/errors/translations.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ERROR_CODES } from "coral-common/common/lib/errors";

export const ERROR_TRANSLATIONS: Record<ERROR_CODES, string> = {
USERNAME_NOT_PROVIDED: "error-usernameNotProvided",
COMMENT_BODY_EXCEEDS_MAX_LENGTH: "error-commentBodyExceedsMaxLength",
COMMENT_BODY_TOO_SHORT: "error-commentBodyTooShort",
COMMENT_NOT_FOUND: "error-commentNotFound",
Expand Down
1 change: 1 addition & 0 deletions server/src/core/server/locales/en-US/errors.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ error-parentCommentRejected = A comment earlier in this conversation thread was
error-ancestorRejected = A comment earlier in this conversation thread was removed. No additional replies can be submitted.
error-commentBodyExceedsMaxLength =
Comment body exceeds maximum length of {$max} characters.
error-usernameNotProvided = Username not provided. Go to <a href="{$url}">{$url}</a> to set your username to comment.
error-storyURLNotPermitted =
The specified story URL does not exist in the permitted domains list.
error-duplicateStoryURL = The specified story URL already exists.
Expand Down