Skip to content

Commit c0f4b7b

Browse files
Merge pull request #453 from topcoder-platform/TCA-852_dice-check
TCA-852 Check for Dice -> TCA-851_wipro-mfa
2 parents e4cb992 + cbcfac4 commit c0f4b7b

File tree

12 files changed

+106
-31
lines changed

12 files changed

+106
-31
lines changed

src-ts/lib/functions/token-functions/token.functions.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@ export async function getAsync(): Promise<TokenModel> {
1515
}
1616

1717
try {
18-
const { handle, roles }: {
18+
const { handle, roles, userId }: {
1919
handle?: string
2020
roles?: Array<string>
21+
userId?: number
2122
} = decodeToken(token)
2223

2324
// if we didn't find the handle, we have a bad token
@@ -26,8 +27,9 @@ export async function getAsync(): Promise<TokenModel> {
2627
return Promise.resolve({})
2728
}
2829

29-
return Promise.resolve({ handle, roles, token })
30+
return Promise.resolve({ handle, roles, token, userId })
3031

32+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
3133
} catch (error: any) {
3234
logError(error)
3335
return Promise.resolve({})

src-ts/lib/functions/token-functions/token.model.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ export interface TokenModel {
22
handle?: string
33
roles?: Array<string>
44
token?: string
5+
userId?: number
56
}
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
1-
export { updatePasswordAsync as userUpdatePasswordAsync } from './user.functions'
1+
export {
2+
getDiceStatusAsync as userGetDiceStatusAsync,
3+
updatePasswordAsync as userUpdatePasswordAsync,
4+
} from './user.functions'
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
1-
export { patchAsync as userPatchAsync } from './user-xhr.store'
1+
export {
2+
getDiceStatusAsync as userStoreGetDiceStatusAsync,
3+
patchAsync as userStorePatchAsync,
4+
} from './user-xhr.store'
25
export { type UserPatchRequest } from './user-xhr.store'

src-ts/lib/functions/user-functions/user-store/user-xhr.store.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { User } from '../../../../../types/tc-auth-lib'
2-
import { xhrPatchAsync } from '../../xhr-functions'
2+
import { xhrGetAsync, xhrPatchAsync } from '../../xhr-functions'
33

44
import { user as userEndpoint } from './user-endpoint.config'
55

@@ -12,6 +12,21 @@ export interface UserPatchRequest {
1212
}
1313
}
1414

15+
export async function getDiceStatusAsync(userId: number): Promise<boolean> {
16+
17+
interface DiceStatusResult {
18+
result: {
19+
content: {
20+
diceEnabled: boolean
21+
}
22+
}
23+
}
24+
const result: DiceStatusResult
25+
= await xhrGetAsync<DiceStatusResult>(`${userEndpoint(userId)}/2fa`)
26+
27+
return !!result.result.content.diceEnabled
28+
}
29+
1530
export async function patchAsync(userId: number, request: UserPatchRequest): Promise<User> {
1631
const url: string = userEndpoint(userId)
1732
return xhrPatchAsync(url, request)

src-ts/lib/functions/user-functions/user.functions.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { userPatchAsync, UserPatchRequest } from './user-store'
1+
import { UserPatchRequest, userStoreGetDiceStatusAsync, userStorePatchAsync } from './user-store'
2+
3+
export async function getDiceStatusAsync(userId: number): Promise<boolean> {
4+
return userStoreGetDiceStatusAsync(userId)
5+
}
26

37
export async function updatePasswordAsync(userId: number, currentPassword: string, password: string): Promise<void> {
48
const request: UserPatchRequest = {
@@ -9,6 +13,6 @@ export async function updatePasswordAsync(userId: number, currentPassword: strin
913
},
1014
},
1115
}
12-
return userPatchAsync(userId, request)
16+
return userStorePatchAsync(userId, request)
1317
.then(() => undefined)
1418
}

src-ts/lib/page-footer/PageFooter.tsx

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,17 @@ const PageFooter: FC<{}> = () => {
77

88
const navElementId: string = 'footer-nav-el'
99

10-
tcUniNav(
11-
'init',
12-
navElementId,
13-
{
14-
type: 'footer',
15-
},
16-
)
10+
// delay the initialization so
11+
// the nav element has time to render
12+
setTimeout(() => {
13+
tcUniNav(
14+
'init',
15+
navElementId,
16+
{
17+
type: 'footer',
18+
},
19+
)
20+
}, 10)
1721

1822
return <div id={navElementId} />
1923
}

src-ts/lib/profile-provider/profile-functions/profile-factory/profile.factory.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,24 @@ import { UserProfile } from '../../user-profile.model'
33

44
import { UserRole } from './user-role.enum'
55

6-
export function create(profile: UserProfile, token: TokenModel): UserProfile {
7-
// TODO: create the profile full name property
6+
export function create(profile: UserProfile, token: TokenModel, hasDiceEnabled: boolean): UserProfile {
7+
88
// Currently, the "Self-Service Customer" role is being set when a user is created
99
// during the self-service workflow. There are no other roles being set to distinguish
1010
// between Customers and Members.
1111
// Therefore, the only way to know if a user is a Member is if s/he is not a Customer.
1212
// This is imperfect, bc a user could be both a Customer or a Member, but for now
1313
// we are okay with this and will have a more in-depth initiave to properly assign
14-
// rolees.
14+
// roles.
1515
profile.isCustomer = !!token.roles?.some(role => role === UserRole.customer)
1616
profile.isMember = !profile.isCustomer
17+
18+
profile.isWipro = profile.email.endsWith('@wipro.com')
19+
profile.diceEnabled = hasDiceEnabled
20+
1721
// store roles for custom capability checks
18-
profile.roles = token.roles
22+
profile.roles = token.roles || []
23+
24+
// TODO: create the profile full name property
1925
return profile
2026
}

src-ts/lib/profile-provider/profile-functions/profile.functions.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { userGetDiceStatusAsync } from '../../functions/user-functions'
12
import { tokenGetAsync, TokenModel } from '../../functions/token-functions'
23
import { EditNameRequest } from '../edit-name-request.model'
34
import { UserProfile } from '../user-profile.model'
@@ -11,19 +12,22 @@ export async function getAsync(handle?: string): Promise<UserProfile | undefined
1112
const token: TokenModel = await tokenGetAsync()
1213

1314
// get the handle
14-
const safeHandle: string | undefined = handle || token?.handle
15-
if (!safeHandle) {
15+
const safeHandle: string | undefined = handle || token.handle
16+
if (!safeHandle || !token.userId) {
1617
return Promise.resolve(undefined)
1718
}
1819

1920
// get the profile
20-
const profileResult: UserProfile = await profileStoreGet(safeHandle)
21+
const profilePromise: Promise<UserProfile> = profileStoreGet(safeHandle)
22+
const dicePromise: Promise<boolean> = userGetDiceStatusAsync(token.userId)
23+
24+
const [profileResult, diceEnabled]: [UserProfile, boolean] = await Promise.all([profilePromise, dicePromise])
2125

2226
// make the changes we need based on the token
23-
const output: UserProfile = profileFactoryCreate(profileResult, token)
27+
const output: UserProfile = profileFactoryCreate(profileResult, token, diceEnabled)
2428
return output
2529
}
2630

27-
export async function editNameAsync(handle: string, profile: EditNameRequest): Promise<any> {
31+
export async function editNameAsync(handle: string, profile: EditNameRequest): Promise<UserProfile> {
2832
return profileStorePatchName(handle, profile)
2933
}

src-ts/lib/profile-provider/user-profile.model.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
export interface UserProfile {
22
competitionCountryCode: string
33
createdAt: number
4+
diceEnabled: boolean
45
email: string
56
firstName: string
67
handle: string
78
handleLower: string
89
homeCountryCode: string
910
isCustomer?: boolean
1011
isMember?: boolean
12+
isWipro: boolean
1113
lastName: string
1214
photoURL?: string
1315
roles: Array<string>

src-ts/tools/learn/course-details/course-curriculum/CourseCurriculum.tsx

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,12 @@ interface CourseCurriculumProps {
3737
const CourseCurriculum: FC<CourseCurriculumProps> = (props: CourseCurriculumProps) => {
3838

3939
const navigate: NavigateFunction = useNavigate()
40-
const [searchParams]: any = useSearchParams()
40+
const [searchParams]: [URLSearchParams, unknown] = useSearchParams()
4141

4242
const isLoggedIn: boolean = !!props.profile
4343

44-
const [isTcAcademyPolicyModal, setIsTcAcademyPolicyModal]: [boolean, Dispatch<SetStateAction<boolean>>] = useState<boolean>(false)
44+
const [isTcAcademyPolicyModal, setIsTcAcademyPolicyModal]: [boolean, Dispatch<SetStateAction<boolean>>]
45+
= useState<boolean>(false)
4546

4647
const status: string = props.progress?.status ?? UserCertificationProgressStatus.inititialized
4748
const completedPercentage: number = (props.progress?.courseProgressPercentage ?? 0) / 100
@@ -76,6 +77,7 @@ const CourseCurriculum: FC<CourseCurriculumProps> = (props: CourseCurriculumProp
7677
* Handle user click on start course/resume/login button
7778
*/
7879
const handleStartCourseClick: () => void = useCallback(() => {
80+
7981
// if user is not logged in, redirect to login page
8082
if (!isLoggedIn) {
8183
// add a flag to the return url to show the academic policy modal
@@ -84,6 +86,14 @@ const CourseCurriculum: FC<CourseCurriculumProps> = (props: CourseCurriculumProp
8486
return
8587
}
8688

89+
// if the user is wipro and s/he hasn't set up DICE,
90+
// let the user know
91+
if (props.profile?.isWipro && !props.profile.diceEnabled) {
92+
// TODO
93+
console.debug('TODO: user needs dice')
94+
return
95+
}
96+
8797
// Check if user accepted policy and resume(or start) the course
8898
if (props.progress?.academicHonestyPolicyAcceptedAt) {
8999
handleStartCourse()
@@ -92,6 +102,7 @@ const CourseCurriculum: FC<CourseCurriculumProps> = (props: CourseCurriculumProp
92102

93103
// show the academic policy modal before starting a new course
94104
setIsTcAcademyPolicyModal(true)
105+
// eslint-disable-next-line react-hooks/exhaustive-deps
95106
}, [
96107
handleStartCourse,
97108
isLoggedIn,
@@ -130,6 +141,7 @@ const CourseCurriculum: FC<CourseCurriculumProps> = (props: CourseCurriculumProp
130141
}
131142

132143
handleStartCourse()
144+
// eslint-disable-next-line react-hooks/exhaustive-deps
133145
}, [
134146
handleStartCourse,
135147
props.course.certificationId,
@@ -149,6 +161,7 @@ const CourseCurriculum: FC<CourseCurriculumProps> = (props: CourseCurriculumProp
149161
* proceed as if the user just clicked "Start course" button
150162
*/
151163
useEffect(() => {
164+
// eslint-disable-next-line no-null/no-null
152165
if (props.progressReady && isLoggedIn && searchParams.get(LEARN_PATHS.startCourseRouteFlag) !== null) {
153166
handleStartCourseClick()
154167
}

src-ts/tools/learn/free-code-camp/FreeCodeCamp.tsx

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -436,19 +436,37 @@ const FreeCodeCamp: FC<{}> = () => {
436436

437437
/**
438438
* Check if the user accepted the academic honesty policy
439+
* and either is not a wipro user or the wipro user has dice enabled.
439440
* if not, redirect user to course details page to accept the policy
440441
*/
441442
useLayoutEffect(() => {
442-
if (ready && !(isLoggedIn && certificateProgress?.academicHonestyPolicyAcceptedAt)) {
443-
const coursePath: string = getCoursePath(
444-
providerParam,
445-
certificationParam,
446-
)
447-
navigate(coursePath)
443+
444+
// if we're not ready, there's nothing to do
445+
if (!ready) {
446+
return
448447
}
448+
449+
// if the user is logged in,
450+
// and the user is a either not wipro user or is a wipro user with dice enabled,
451+
// and if the user has accepted the academic honesty policy,
452+
// the user is permitted to take the course, so there's nothing to do.
453+
if (isLoggedIn
454+
&& (!profile?.isWipro || !!profile?.diceEnabled)
455+
&& !!certificateProgress?.academicHonestyPolicyAcceptedAt) {
456+
return
457+
}
458+
459+
// redirect the user to course details page to perform the
460+
// necessary actions
461+
const coursePath: string = getCoursePath(
462+
providerParam,
463+
certificationParam,
464+
)
465+
navigate(coursePath)
449466
}, [
450467
ready,
451468
certificateProgress,
469+
profile,
452470
providerParam,
453471
certificationParam,
454472
navigate,

0 commit comments

Comments
 (0)