mirror of
https://github.com/IT4Change/gradido.git
synced 2026-02-06 01:46:07 +00:00
make authenticateHumhubAutoLogin a mutation, auto register user on humhub if project was set and user don't exist on humhub
This commit is contained in:
parent
6fdcb0a86d
commit
9ff6470b47
@ -47,13 +47,13 @@ import { UserContact } from '@model/UserContact'
|
||||
import { UserLocationResult } from '@model/UserLocationResult'
|
||||
|
||||
import { HumHubClient } from '@/apis/humhub/HumHubClient'
|
||||
import { Account as HumhubAccount } from '@/apis/humhub/model/Account'
|
||||
import { GetUser } from '@/apis/humhub/model/GetUser'
|
||||
import { PostUser } from '@/apis/humhub/model/PostUser'
|
||||
import { subscribe } from '@/apis/KlicktippController'
|
||||
import { encode } from '@/auth/JWT'
|
||||
import { RIGHTS } from '@/auth/RIGHTS'
|
||||
import { CONFIG } from '@/config'
|
||||
import { PublishNameLogic } from '@/data/PublishName.logic'
|
||||
import {
|
||||
sendAccountActivationEmail,
|
||||
sendAccountMultiRegistrationEmail,
|
||||
@ -75,7 +75,6 @@ import {
|
||||
EVENT_ADMIN_USER_DELETE,
|
||||
EVENT_ADMIN_USER_UNDELETE,
|
||||
} from '@/event/Events'
|
||||
import { PublishNameType } from '@/graphql/enum/PublishNameType'
|
||||
import { isValidPassword } from '@/password/EncryptorUtils'
|
||||
import { encryptPassword, verifyPassword } from '@/password/PasswordEncryptor'
|
||||
import { Context, getUser, getClientTimezoneOffset } from '@/server/context'
|
||||
@ -821,7 +820,7 @@ export class UserResolver {
|
||||
}
|
||||
|
||||
@Authorized([RIGHTS.HUMHUB_AUTO_LOGIN])
|
||||
@Query(() => String)
|
||||
@Mutation(() => String)
|
||||
async authenticateHumhubAutoLogin(
|
||||
@Ctx() context: Context,
|
||||
@Arg('project', () => String, { nullable: true }) project?: string | null,
|
||||
@ -832,19 +831,23 @@ export class UserResolver {
|
||||
if (!humhubClient) {
|
||||
throw new LogError('cannot create humhub client')
|
||||
}
|
||||
const userNameLogic = new PublishNameLogic(dbUser)
|
||||
const username = userNameLogic.getUserIdentifier(dbUser.humhubPublishName as PublishNameType)
|
||||
let humhubUser = await humhubClient.userByUsername(username)
|
||||
if (!humhubUser) {
|
||||
humhubUser = await humhubClient.userByEmail(dbUser.emailContact.email)
|
||||
// should rarely happen, so we don't optimize for parallel processing
|
||||
if (!dbUser.humhubAllowed && project) {
|
||||
await ProjectBranding.findOneOrFail({ where: { alias: project } })
|
||||
dbUser.humhubAllowed = true
|
||||
await dbUser.save()
|
||||
}
|
||||
const humhubUserAccount = new HumhubAccount(dbUser)
|
||||
const autoLoginUrlPromise = humhubClient.createAutoLoginUrl(humhubUserAccount.username, project)
|
||||
const humhubUser = await syncHumhub(null, dbUser)
|
||||
if (!humhubUser) {
|
||||
throw new LogError("user don't exist (any longer) on humhub")
|
||||
throw new LogError("user don't exist (any longer) on humhub and couldn't be created")
|
||||
}
|
||||
if (humhubUser.account.status !== 1) {
|
||||
throw new LogError('user status is not 1', humhubUser.account.status)
|
||||
}
|
||||
return await humhubClient.createAutoLoginUrl(humhubUser.account.username, project)
|
||||
const autoLoginUrl = await autoLoginUrlPromise
|
||||
return autoLoginUrl
|
||||
}
|
||||
|
||||
@Authorized([RIGHTS.SEARCH_ADMIN_USERS])
|
||||
|
||||
@ -3,7 +3,9 @@ import { User } from '@entity/User'
|
||||
import { HumHubClient } from '@/apis/humhub/HumHubClient'
|
||||
import { GetUser } from '@/apis/humhub/model/GetUser'
|
||||
import { ExecutedHumhubAction, syncUser } from '@/apis/humhub/syncUser'
|
||||
import { PublishNameLogic } from '@/data/PublishName.logic'
|
||||
import { UpdateUserInfosArgs } from '@/graphql/arg/UpdateUserInfosArgs'
|
||||
import { PublishNameType } from '@/graphql/enum/PublishNameType'
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
|
||||
/**
|
||||
@ -16,7 +18,7 @@ export async function syncHumhub(
|
||||
updateUserInfosArg: UpdateUserInfosArgs | null,
|
||||
user: User,
|
||||
spaceId?: number | null,
|
||||
): Promise<number | undefined> {
|
||||
): Promise<GetUser | null | undefined> {
|
||||
// check for humhub relevant changes
|
||||
if (
|
||||
updateUserInfosArg &&
|
||||
@ -36,7 +38,9 @@ export async function syncHumhub(
|
||||
return
|
||||
}
|
||||
logger.debug('retrieve user from humhub')
|
||||
let humhubUser = await humhubClient.userByUsername(user.alias ?? user.gradidoID)
|
||||
const userNameLogic = new PublishNameLogic(user)
|
||||
const username = userNameLogic.getUserIdentifier(user.humhubPublishName as PublishNameType)
|
||||
let humhubUser = await humhubClient.userByUsername(username)
|
||||
if (!humhubUser) {
|
||||
humhubUser = await humhubClient.userByEmail(user.emailContact.email)
|
||||
}
|
||||
@ -58,5 +62,8 @@ export async function syncHumhub(
|
||||
await humhubClient.addUserToSpace(humhubUser.id, spaceId)
|
||||
logger.debug(`user added to space ${spaceId}`)
|
||||
}
|
||||
return user.id
|
||||
if (result !== ExecutedHumhubAction.SKIP) {
|
||||
return await humhubClient.userByUsername(username)
|
||||
}
|
||||
return humhubUser
|
||||
}
|
||||
|
||||
@ -32,10 +32,10 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import { useQuery } from '@vue/apollo-composable'
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useMutation } from '@vue/apollo-composable'
|
||||
import { useStore } from 'vuex'
|
||||
import { authenticateHumhubAutoLogin } from '@/graphql/queries'
|
||||
import { authenticateHumhubAutoLogin } from '@/graphql/mutations'
|
||||
|
||||
const store = useStore()
|
||||
|
||||
@ -44,12 +44,21 @@ const humhubUri = ref('')
|
||||
|
||||
const humhubAllowed = computed(() => store.state.humhubAllowed)
|
||||
|
||||
const { onResult, onError } = useQuery(authenticateHumhubAutoLogin, null, {
|
||||
fetchPolicy: 'network-only',
|
||||
enabled: true,
|
||||
})
|
||||
const {
|
||||
onDone,
|
||||
mutate: mutateHumhubAutoLogin,
|
||||
onError,
|
||||
called,
|
||||
} = useMutation(
|
||||
authenticateHumhubAutoLogin,
|
||||
{},
|
||||
{
|
||||
fetchPolicy: 'network-only',
|
||||
enabled: true,
|
||||
},
|
||||
)
|
||||
|
||||
onResult(({ data }) => {
|
||||
onDone(({ data }) => {
|
||||
if (data) {
|
||||
humhubUri.value = data.authenticateHumhubAutoLogin
|
||||
enableButton.value = true
|
||||
@ -61,6 +70,12 @@ onError(() => {
|
||||
humhubUri.value = ''
|
||||
store.commit('humhubAllowed', false)
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
if (!called.value) {
|
||||
mutateHumhubAutoLogin()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style scoped>
|
||||
.card {
|
||||
|
||||
@ -60,6 +60,17 @@ export const updateUserInfos = gql`
|
||||
}
|
||||
`
|
||||
|
||||
export const authenticateHumhubAutoLogin = gql`
|
||||
mutation {
|
||||
authenticateHumhubAutoLogin
|
||||
}
|
||||
`
|
||||
export const authenticateHumhubAutoLoginProject = gql`
|
||||
mutation ($project: String!) {
|
||||
authenticateHumhubAutoLogin(project: $project)
|
||||
}
|
||||
`
|
||||
|
||||
export const createUser = gql`
|
||||
mutation (
|
||||
$firstName: String!
|
||||
|
||||
@ -37,17 +37,6 @@ export const userLocationQuery = gql`
|
||||
}
|
||||
`
|
||||
|
||||
export const authenticateHumhubAutoLogin = gql`
|
||||
query {
|
||||
authenticateHumhubAutoLogin
|
||||
}
|
||||
`
|
||||
export const authenticateHumhubAutoLoginProject = gql`
|
||||
query ($project: String!) {
|
||||
authenticateHumhubAutoLogin(project: $project)
|
||||
}
|
||||
`
|
||||
|
||||
export const transactionsQuery = gql`
|
||||
query ($currentPage: Int = 1, $pageSize: Int = 25, $order: Order = DESC) {
|
||||
transactionList(currentPage: $currentPage, pageSize: $pageSize, order: $order) {
|
||||
|
||||
@ -1,103 +0,0 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest'
|
||||
import { nextTick } from 'vue'
|
||||
import Circles from './Circles'
|
||||
import { authenticateHumhubAutoLogin } from '@/graphql/queries'
|
||||
import { createStore } from 'vuex'
|
||||
import { useQuery } from '@vue/apollo-composable'
|
||||
|
||||
vi.mock('@vue/apollo-composable', () => ({
|
||||
useQuery: vi.fn(),
|
||||
}))
|
||||
|
||||
const TEST_URL_WITH_JWT_TOKEN =
|
||||
'https://community.gradido.net/user/auth/external?authclient=jwt&jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTY4NTI0NjQxMn0.V2h4Rf3LfdOYDsx2clVCx-jbhKoY5F4Ks5-YJGVtHRk'
|
||||
|
||||
describe('Circles', () => {
|
||||
let wrapper
|
||||
let store
|
||||
let mockOnResult
|
||||
let mockRefetch
|
||||
let mockOnError
|
||||
|
||||
beforeEach(() => {
|
||||
store = createStore({
|
||||
state: {
|
||||
humhubAllowed: true,
|
||||
},
|
||||
mutations: {
|
||||
humhubAllowed(state, value) {
|
||||
state.humhubAllowed = value
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
mockOnResult = vi.fn()
|
||||
mockRefetch = vi.fn()
|
||||
mockOnError = vi.fn()
|
||||
|
||||
vi.mocked(useQuery).mockReturnValue({
|
||||
refetch: mockRefetch,
|
||||
onResult: mockOnResult,
|
||||
onError: mockOnError,
|
||||
})
|
||||
|
||||
wrapper = mount(Circles, {
|
||||
global: {
|
||||
plugins: [store],
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
$i18n: {
|
||||
locale: 'en',
|
||||
},
|
||||
},
|
||||
stubs: {
|
||||
BContainer: true,
|
||||
BRow: true,
|
||||
BCol: true,
|
||||
BButton: true,
|
||||
RouterLink: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
it('renders the circles page', () => {
|
||||
expect(wrapper.find('div.circles').exists()).toBe(true)
|
||||
})
|
||||
|
||||
it('calls authenticateHumhubAutoLogin on mount', () => {
|
||||
expect(useQuery).toHaveBeenCalledWith(
|
||||
authenticateHumhubAutoLogin,
|
||||
null,
|
||||
expect.objectContaining({
|
||||
fetchPolicy: 'network-only',
|
||||
enabled: true,
|
||||
}),
|
||||
)
|
||||
expect(mockRefetch).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('sets humhubUri and enables button on success', async () => {
|
||||
const successCallback = mockOnResult.mock.calls[0][0]
|
||||
successCallback({ data: { authenticateHumhubAutoLogin: TEST_URL_WITH_JWT_TOKEN } })
|
||||
|
||||
await nextTick()
|
||||
expect(wrapper.vm.humhubUri).toBe(TEST_URL_WITH_JWT_TOKEN)
|
||||
expect(wrapper.vm.enableButton).toBe(true)
|
||||
})
|
||||
|
||||
it('handles error in authenticateHumhubAutoLogin', async () => {
|
||||
const errorCallback = mockOnError.mock.calls[0][0]
|
||||
errorCallback(new Error('Test error'))
|
||||
|
||||
await nextTick()
|
||||
expect(wrapper.vm.enableButton).toBe(true)
|
||||
expect(wrapper.vm.humhubUri).toBe('')
|
||||
expect(store.state.humhubAllowed).toBe(false)
|
||||
})
|
||||
})
|
||||
@ -1,79 +0,0 @@
|
||||
<template>
|
||||
<div class="circles">
|
||||
<BContainer class="bg-white app-box-shadow gradido-border-radius p-4 mt--3">
|
||||
<div class="h3">{{ $t('circles.headline') }}</div>
|
||||
<div class="my-4 text-small">
|
||||
<span v-for="(line, lineNumber) of $t('circles.text').split('\n')" :key="lineNumber">
|
||||
{{ line }}
|
||||
<br />
|
||||
</span>
|
||||
</div>
|
||||
<BRow class="my-5">
|
||||
<BCol cols="12">
|
||||
<div class="text-lg-end">
|
||||
<BButton
|
||||
v-if="humhubAllowed"
|
||||
:href="humhubUri"
|
||||
variant="gradido"
|
||||
:disabled="enableButton === false"
|
||||
target="_blank"
|
||||
>
|
||||
{{ $t('circles.button') }}
|
||||
</BButton>
|
||||
<RouterLink v-else to="/settings/extern">
|
||||
<BButton variant="gradido">
|
||||
{{ $t('circles.button') }}
|
||||
</BButton>
|
||||
</RouterLink>
|
||||
</div>
|
||||
</BCol>
|
||||
</BRow>
|
||||
</BContainer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useQuery } from '@vue/apollo-composable'
|
||||
import { useStore } from 'vuex'
|
||||
import { authenticateHumhubAutoLogin } from '@/graphql/queries'
|
||||
|
||||
const store = useStore()
|
||||
|
||||
const enableButton = ref(false)
|
||||
const humhubUri = ref('')
|
||||
|
||||
const humhubAllowed = computed(() => store.state.humhubAllowed)
|
||||
|
||||
const {
|
||||
refetch: refetchAuthenticateHumhub,
|
||||
onResult,
|
||||
onError,
|
||||
} = useQuery(authenticateHumhubAutoLogin, null, {
|
||||
fetchPolicy: 'network-only',
|
||||
enabled: true,
|
||||
})
|
||||
|
||||
onResult(({ data }) => {
|
||||
if (data) {
|
||||
humhubUri.value = data.authenticateHumhubAutoLogin
|
||||
enableButton.value = true
|
||||
}
|
||||
})
|
||||
|
||||
onError(() => {
|
||||
enableButton.value = true
|
||||
humhubUri.value = ''
|
||||
store.commit('humhubAllowed', false)
|
||||
})
|
||||
|
||||
const handleAuthenticateHumhubAutoLogin = async () => {
|
||||
enableButton.value = false
|
||||
humhubUri.value = null
|
||||
await refetchAuthenticateHumhub()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
handleAuthenticateHumhubAutoLogin()
|
||||
})
|
||||
</script>
|
||||
@ -61,14 +61,13 @@
|
||||
import InputPassword from '@/components/Inputs/InputPassword'
|
||||
import InputEmail from '@/components/Inputs/InputEmail'
|
||||
import Message from '@/components/Message/Message'
|
||||
import { login } from '@/graphql/mutations'
|
||||
import { authenticateHumhubAutoLoginProject } from '@/graphql/queries'
|
||||
import { login, authenticateHumhubAutoLoginProject } from '@/graphql/mutations '
|
||||
import { ref, computed } from 'vue'
|
||||
import { useStore } from 'vuex'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { useForm } from 'vee-validate'
|
||||
import { useMutation, useLazyQuery } from '@vue/apollo-composable'
|
||||
import { useMutation } from '@vue/apollo-composable'
|
||||
import { useAppToast } from '@/composables/useToast'
|
||||
import { useAuthLinks } from '@/composables/useAuthLinks'
|
||||
import CONFIG from '@/config'
|
||||
@ -80,7 +79,12 @@ const route = useRoute()
|
||||
const store = useStore()
|
||||
const { t } = useI18n()
|
||||
const { mutate } = useMutation(login)
|
||||
const { load } = useLazyQuery(authenticateHumhubAutoLoginProject)
|
||||
const { mutate: mutateHumhubAutoLogin } = useMutation(authenticateHumhubAutoLoginProject, {
|
||||
variables: {
|
||||
project: store.state.project,
|
||||
},
|
||||
enabled: store.state.project,
|
||||
})
|
||||
// const $loading = useLoading() // TODO needs to be updated but there is some sort of an issue that breaks the app.
|
||||
const { toastError } = useAppToast()
|
||||
const { register } = useAuthLinks()
|
||||
@ -117,10 +121,8 @@ const onSubmit = handleSubmit(async (values) => {
|
||||
// await loader.hide()
|
||||
|
||||
if (store.state.project) {
|
||||
const result = await load(authenticateHumhubAutoLoginProject, {
|
||||
project: store.state.project,
|
||||
})
|
||||
window.location.href = result.authenticateHumhubAutoLogin
|
||||
const result = await mutateHumhubAutoLogin()
|
||||
window.location.href = result.data.authenticateHumhubAutoLogin
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user