Skip to content
This repository was archived by the owner on Aug 21, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 2 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 @@ -93,13 +93,8 @@ const ProfileMenu = ({ hideLogin, allowAvatarChange, isPopover, changeActiveMenu
(authState.linkedin && !oauthConnectedState.linkedin) ||
(authState.twitter && !oauthConnectedState.twitter)

const removeSocial =
(authState?.discord && oauthConnectedState.discord) ||
(authState.facebook && oauthConnectedState.facebook) ||
(authState.github && oauthConnectedState.github) ||
(authState.google && oauthConnectedState.google) ||
(authState.linkedin && oauthConnectedState.linkedin) ||
(authState.twitter && oauthConnectedState.twitter)
/**allow removing social logins if there are at least 2 social logins connected*/
const removeSocial = Object.values(oauthConnectedState).filter((value) => value).length > 1

// const loadCredentialHandler = async () => {
// try {
Expand Down Expand Up @@ -557,51 +552,53 @@ const ProfileMenu = ({ hideLogin, allowAvatarChange, isPopover, changeActiveMenu
</div>

{!selfUser?.isGuest.value && removeSocial && (
<Text align="center" variant="body2" mb={1} mt={2}>
{t('user:usermenu.profile.removeSocial')}
</Text>
)}
<>
<Text align="center" variant="body2" mb={1} mt={2}>
{t('user:usermenu.profile.removeSocial')}
</Text>

<div className={styles.socialContainer}>
{authState?.discord && oauthConnectedState.discord && (
<IconButton
id="discord"
icon={<DiscordIcon viewBox="0 0 40 40" />}
onClick={handleRemoveOAuthServiceClick}
/>
)}
{authState?.google && oauthConnectedState.google && (
<IconButton
id="google"
icon={<GoogleIcon viewBox="0 0 40 40" />}
onClick={handleRemoveOAuthServiceClick}
/>
)}
{authState?.facebook && oauthConnectedState.facebook && (
<IconButton
id="facebook"
icon={<Icon type="Facebook" viewBox="0 0 40 40" />}
onClick={handleRemoveOAuthServiceClick}
/>
)}
{authState?.linkedin && oauthConnectedState.linkedin && (
<IconButton
id="linkedin"
icon={<LinkedInIcon viewBox="0 0 40 40" />}
onClick={handleRemoveOAuthServiceClick}
/>
)}
{authState?.twitter && oauthConnectedState.twitter && (
<IconButton
id="twitter"
icon={<Icon type="Twitter" viewBox="0 0 40 40" />}
onClick={handleRemoveOAuthServiceClick}
/>
)}
{authState?.github && oauthConnectedState.github && (
<IconButton id="github" icon={<Icon type="GitHub" />} onClick={handleRemoveOAuthServiceClick} />
)}
</div>
<div className={styles.socialContainer}>
{authState?.discord && oauthConnectedState.discord && (
<IconButton
id="discord"
icon={<DiscordIcon viewBox="0 0 40 40" />}
onClick={handleRemoveOAuthServiceClick}
/>
)}
{authState?.google && oauthConnectedState.google && (
<IconButton
id="google"
icon={<GoogleIcon viewBox="0 0 40 40" />}
onClick={handleRemoveOAuthServiceClick}
/>
)}
{authState?.facebook && oauthConnectedState.facebook && (
<IconButton
id="facebook"
icon={<Icon type="Facebook" viewBox="0 0 40 40" />}
onClick={handleRemoveOAuthServiceClick}
/>
)}
{authState?.linkedin && oauthConnectedState.linkedin && (
<IconButton
id="linkedin"
icon={<LinkedInIcon viewBox="0 0 40 40" />}
onClick={handleRemoveOAuthServiceClick}
/>
)}
{authState?.twitter && oauthConnectedState.twitter && (
<IconButton
id="twitter"
icon={<Icon type="Twitter" viewBox="0 0 40 40" />}
onClick={handleRemoveOAuthServiceClick}
/>
)}
{authState?.github && oauthConnectedState.github && (
<IconButton id="github" icon={<Icon type="GitHub" />} onClick={handleRemoveOAuthServiceClick} />
)}
</div>
</>
)}
</>
)}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { NotFound } from '@feathersjs/errors'
import { MethodNotAllowed, NotFound } from '@feathersjs/errors'
import { HookContext } from '@feathersjs/feathers'
import { iff, isProvider } from 'feathers-hooks-common'

Expand Down Expand Up @@ -44,6 +44,18 @@ const checkIdentityProvider = (): any => {
}
}

const checkOnlyIdentityProvider = () => {
return async (context: HookContext): Promise<HookContext> => {
const providers = await context.app
.service('identity-provider')
.find({ query: { userId: context.params.query.userId } })
if (providers.total <= 1) {
throw new MethodNotAllowed('Cannot remove the only provider')
}
return context
}
}

export default {
before: {
all: [],
Expand All @@ -52,7 +64,7 @@ export default {
create: [],
update: [iff(isProvider('external'), authenticate() as any, checkIdentityProvider())],
patch: [iff(isProvider('external'), authenticate() as any, checkIdentityProvider())],
remove: [iff(isProvider('external'), authenticate() as any, checkIdentityProvider())]
remove: [iff(isProvider('external'), authenticate() as any, checkIdentityProvider()), checkOnlyIdentityProvider()]
},
after: {
all: [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { destroyEngine } from '@etherealengine/engine/src/ecs/classes/Engine'
import { Application } from '../../../declarations'
import { createFeathersExpressApp } from '../../createApp'

let providers: any = []
let userId: string

describe('identity-provider service', () => {
let app: Application
Expand Down Expand Up @@ -35,7 +35,8 @@ describe('identity-provider service', () => {
},
{}
)
providers.push(item)

userId = item.userId

assert.equal(item.type, type)
assert.equal(item.token, token)
Expand All @@ -49,11 +50,11 @@ describe('identity-provider service', () => {
const item = await app.service('identity-provider').create(
{
type,
token
token,
userId
},
{}
)
providers.push(item)

assert.equal(item.type, type)
assert.equal(item.token, token)
Expand All @@ -69,37 +70,61 @@ describe('identity-provider service', () => {
{
type,
token,
password
password,
userId
},
{}
)
providers.push(item)

assert.equal(item.type, type)
assert.equal(item.token, token)
assert.ok(item.userId)
})

it('should find identity providers', async () => {
for (const provider of providers) {
const item = await app.service('identity-provider').find({
query: {
userId: provider.userId
}
})

assert.ok(item, 'Identity provider item is found')
}
const item = await app.service('identity-provider').find({
query: {
userId
}
})

assert.ok(item, 'Identity provider item is found')
})

it('should remove identity providers', async () => {
for (const provider of providers) {
const item = await app.service('identity-provider').remove(null, {
query: {
userId: provider.userId
}
})
assert.ok(item, 'Identity provider item is removed')
}
const item = await app.service('identity-provider').remove(null, {
query: {
userId
}
})

assert.ok(item, 'Identity provider item is removed')
})

it('should not be able to remove the only identity provider', async () => {
const type = 'guest'
const token = v1()

const item = await app.service('identity-provider').create(
{
type,
token
},
{}
)

userId = item.userId

assert.rejects(
() =>
app.service('identity-provider').remove(null, {
query: {
userId
}
}),
{
name: 'MethodNotAllowed'
}
)
})
})