Merge pull request #15669 from overleaf/ab-fix-sso-linking-status

[web] Fix SSO status in group members table

GitOrigin-RevId: e54e7b0c9640f0b96d9692c0208357e3bac2de91
This commit is contained in:
Alexandre Bourdin 2023-11-09 17:26:46 +01:00 committed by Copybot
parent 1819f3e4e7
commit c4bea21ee2
7 changed files with 92 additions and 35 deletions

View file

@ -66,6 +66,7 @@ function buildUserViewModel(user, isInvite) {
? {
managedBy: user.enrollment.managedBy,
enrolledAt: user.enrollment.enrolledAt,
sso: user.enrollment.sso,
}
: undefined,
}

View file

@ -1,13 +1,27 @@
import { useTranslation } from 'react-i18next'
import getMeta from '@/utils/meta'
import { User } from '../../../../../../types/group-management/user'
import MaterialIcon from '@/shared/components/material-icon'
type SSOStatusProps = {
user: User
}
export default function SSOStatus({ user }: SSOStatusProps) {
const groupId = getMeta('ol-groupId')
if (user.invite) {
return <PendingInvite />
}
const linkedSSO = user.enrollment?.sso?.some(sso => sso.groupId === groupId)
return linkedSSO ? <SSOLinked /> : <SSOUnlinked />
}
function PendingInvite() {
const { t } = useTranslation()
const invitedSSO = (
return (
<span className="security-state-invite-pending">
<MaterialIcon
type="schedule"
@ -17,22 +31,24 @@ export default function SSOStatus({ user }: SSOStatusProps) {
&nbsp; {t('sso')}
</span>
)
const acceptedSSO = (
}
function SSOLinked() {
const { t } = useTranslation()
return (
<span className="security-state-managed">
<MaterialIcon type="check" accessibilityLabel={t('sso_active')} />
&nbsp; {t('sso')}
</span>
)
const notAcceptedSSO = (
}
function SSOUnlinked() {
const { t } = useTranslation()
return (
<span className="security-state-not-managed">
<MaterialIcon type="close" accessibilityLabel={t('sso_not_active')} />
&nbsp; {t('sso')}
</span>
)
if (user.invite) {
return invitedSSO
}
return user.enrollment?.sso ? acceptedSSO : notAcceptedSSO
}

View file

@ -1,5 +1,6 @@
import GroupMembers from '@/features/group-management/components/group-members'
import { GroupMembersProvider } from '@/features/group-management/context/group-members-context'
import { User } from '../../../../../types/group-management/user'
const GROUP_ID = '777fff777fff'
const PATHS = {
@ -173,7 +174,7 @@ describe('GroupMembers', function () {
})
describe('with Managed Users enabled', function () {
const JOHN_DOE = {
const JOHN_DOE: User = {
_id: 'abc123def456',
first_name: 'John',
last_name: 'Doe',
@ -181,7 +182,7 @@ describe('GroupMembers', function () {
last_active_at: new Date('2023-01-15'),
invite: true,
}
const BOBBY_LAPOINTE = {
const BOBBY_LAPOINTE: User = {
_id: 'bcd234efa567',
first_name: 'Bobby',
last_name: 'Lapointe',
@ -189,7 +190,7 @@ describe('GroupMembers', function () {
last_active_at: new Date('2023-01-02'),
invite: false,
}
const CLAIRE_JENNINGS = {
const CLAIRE_JENNINGS: User = {
_id: 'defabc231453',
first_name: 'Claire',
last_name: 'Jennings',
@ -199,10 +200,13 @@ describe('GroupMembers', function () {
enrollment: {
managedBy: GROUP_ID,
enrolledAt: new Date('2023-01-03'),
sso: {
providerId: '123',
externalId: '123',
},
sso: [
{
groupId: GROUP_ID,
linkedAt: new Date(),
primary: true,
},
],
},
}
@ -376,7 +380,7 @@ describe('GroupMembers', function () {
})
describe('with Group SSO enabled', function () {
const JOHN_DOE = {
const JOHN_DOE: User = {
_id: 'abc123def456',
first_name: 'John',
last_name: 'Doe',
@ -384,7 +388,7 @@ describe('GroupMembers', function () {
last_active_at: new Date('2023-01-15'),
invite: true,
}
const BOBBY_LAPOINTE = {
const BOBBY_LAPOINTE: User = {
_id: 'bcd234efa567',
first_name: 'Bobby',
last_name: 'Lapointe',
@ -392,7 +396,7 @@ describe('GroupMembers', function () {
last_active_at: new Date('2023-01-02'),
invite: false,
}
const CLAIRE_JENNINGS = {
const CLAIRE_JENNINGS: User = {
_id: 'defabc231453',
first_name: 'Claire',
last_name: 'Jennings',
@ -402,10 +406,13 @@ describe('GroupMembers', function () {
enrollment: {
managedBy: GROUP_ID,
enrolledAt: new Date('2023-01-03'),
sso: {
providerId: '123',
externalId: '123',
},
sso: [
{
groupId: GROUP_ID,
linkedAt: new Date(),
primary: true,
},
],
},
}

View file

@ -1,8 +1,9 @@
import GroupMembers from '@/features/group-management/components/group-members'
import { GroupMembersProvider } from '@/features/group-management/context/group-members-context'
import { User } from '../../../../../types/group-management/user'
const GROUP_ID = '777fff777fff'
const JOHN_DOE = {
const JOHN_DOE: User = {
_id: 'abc123def456',
first_name: 'John',
last_name: 'Doe',
@ -10,15 +11,24 @@ const JOHN_DOE = {
last_active_at: new Date('2023-01-15'),
invite: true,
}
const BOBBY_LAPOINTE = {
const BOBBY_LAPOINTE: User = {
_id: 'bcd234efa567',
first_name: 'Bobby',
last_name: 'Lapointe',
email: 'bobby.lapointe@test.com',
last_active_at: new Date('2023-01-02'),
invite: false,
enrollment: {
sso: [
{
groupId: 'another',
linkedAt: new Date(),
primary: true,
},
],
},
}
const CLAIRE_JENNINGS = {
const CLAIRE_JENNINGS: User = {
_id: 'defabc231453',
first_name: 'Claire',
last_name: 'Jennings',
@ -28,10 +38,13 @@ const CLAIRE_JENNINGS = {
enrollment: {
managedBy: GROUP_ID,
enrolledAt: new Date('2023-01-03'),
sso: {
providerId: '123',
externalId: '123',
},
sso: [
{
groupId: GROUP_ID,
linkedAt: new Date(),
primary: true,
},
],
},
}
const PATHS = {

View file

@ -233,6 +233,7 @@ describe('ManagedUserDropdownButton', function () {
},
isEntityAdmin: undefined,
}
beforeEach(function () {
cy.window().then(win => {
win.metaAttributesCache.set('ol-users', [user])
@ -241,11 +242,13 @@ describe('ManagedUserDropdownButton', function () {
win.metaAttributesCache.set('ol-groupSSOActive', true)
})
})
it('should show resend invite when user is admin', function () {
mountDropDownComponent({ ...user, isEntityAdmin: true }, '123abc')
cy.get('.action-btn').click()
cy.findByTestId('resend-sso-link-invite-action').should('exist')
})
it('should not show resend invite when SSO is disabled', function () {
cy.window().then(win => {
win.metaAttributesCache.set('ol-groupSSOActive', false)
@ -254,6 +257,7 @@ describe('ManagedUserDropdownButton', function () {
cy.get('.action-btn').click()
cy.findByTestId('resend-sso-link-invite-action').should('not.exist')
})
it('should not show resend invite when user has accepted SSO already', function () {
cy.window().then(win => {
win.metaAttributesCache.set('ol-groupSSOActive', false)
@ -264,10 +268,13 @@ describe('ManagedUserDropdownButton', function () {
enrollment: {
managedBy: 'some-group',
enrolledAt: new Date(),
sso: {
providerId: '123',
externalId: '123',
},
sso: [
{
groupId: 'abc123abc123',
linkedAt: new Date(),
primary: true,
},
],
},
},
'123abc'
@ -275,6 +282,7 @@ describe('ManagedUserDropdownButton', function () {
cy.get('.action-btn').click()
cy.findByTestId('resend-sso-link-invite-action').should('not.exist')
})
it('should show the resend SSO invite option when dropdown button is clicked', function () {
cy.window().then(win => {
win.metaAttributesCache.set('ol-groupSSOActive', true)
@ -286,6 +294,7 @@ describe('ManagedUserDropdownButton', function () {
Cypress.dom.isVisible($el)
})
})
it('should make the correct post request when resend SSO invite is clicked ', function () {
cy.window().then(win => {
win.metaAttributesCache.set('ol-groupSSOActive', true)

View file

@ -42,6 +42,11 @@ describe('UserMembershipViewModel', function () {
enrollment: {
managedBy: 'mock-group-id',
enrolledAt: new Date(),
sso: {
groupId: 'abc123abc123',
linkedAt: new Date(),
primary: true,
},
},
}
})

View file

@ -1,7 +1,13 @@
export type SSOEnrollment = {
groupId: string
linkedAt: Date
primary: boolean
}
export type UserEnrollment = {
managedBy?: string
enrolledAt?: Date
sso?: object
sso?: SSOEnrollment[]
}
export type User = {