diff --git a/backend/src/apis/humhub/HumHubClient.ts b/backend/src/apis/humhub/HumHubClient.ts index dbde41feb..1b369b16e 100644 --- a/backend/src/apis/humhub/HumHubClient.ts +++ b/backend/src/apis/humhub/HumHubClient.ts @@ -67,28 +67,25 @@ export class HumHubClient { public async createAutoLoginUrl(username: string, project?: string | null) { const secret = new TextEncoder().encode(CONFIG.HUMHUB_JWT_KEY) logger.info(`user ${username} as username for humhub auto-login`) - let projectBrandingPromise: Promise | undefined + let redirectLink: string | undefined if (project) { - projectBrandingPromise = ProjectBranding.findOne({ + const projectBranding = await ProjectBranding.findOne({ where: { alias: project }, select: { spaceUrl: true }, }) + if (projectBranding?.spaceUrl) { + redirectLink = projectBranding.spaceUrl + } } - const token = await new SignJWT({ username }) + const token = await new SignJWT({ username, redirectLink }) .setProtectedHeader({ alg: 'HS256' }) .setIssuedAt() .setExpirationTime(CONFIG.JWT_EXPIRES_IN) .sign(secret) - let loginUrl = `${CONFIG.HUMHUB_API_URL}user/auth/external?authclient=jwt&jwt=${token}` - if (projectBrandingPromise) { - const projectBranding = await projectBrandingPromise - if (projectBranding?.spaceUrl) { - loginUrl += `&redirect=${projectBranding.spaceUrl as string}` - } - } - - return loginUrl + return `${CONFIG.HUMHUB_API_URL}${ + CONFIG.HUMHUB_API_URL.endsWith('/') ? '' : '/' + }user/auth/external?authclient=jwt&jwt=${token}` } /** diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index a49e882aa..3c5f45d04 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -81,8 +81,8 @@ const loginServer = { } const email = { - EMAIL: process.env.EMAIL === 'true' ?? false, - EMAIL_TEST_MODUS: process.env.EMAIL_TEST_MODUS === 'true' ?? false, + EMAIL: process.env.EMAIL === 'true' || false, + EMAIL_TEST_MODUS: process.env.EMAIL_TEST_MODUS === 'true' || false, EMAIL_TEST_RECEIVER: process.env.EMAIL_TEST_RECEIVER ?? 'stage1@gradido.net', EMAIL_USERNAME: process.env.EMAIL_USERNAME ?? '', EMAIL_SENDER: process.env.EMAIL_SENDER ?? 'info@gradido.net', @@ -123,7 +123,7 @@ const federation = { process.env.FEDERATION_VALIDATE_COMMUNITY_TIMER ?? 60000, ), FEDERATION_XCOM_SENDCOINS_ENABLED: - process.env.FEDERATION_XCOM_SENDCOINS_ENABLED === 'true' ?? false, + process.env.FEDERATION_XCOM_SENDCOINS_ENABLED === 'true' || false, // default value for community-uuid is equal uuid of stage-3 FEDERATION_XCOM_RECEIVER_COMMUNITY_UUID: process.env.FEDERATION_XCOM_RECEIVER_COMMUNITY_UUID ?? '56a55482-909e-46a4-bfa2-cd025e894ebc', diff --git a/backend/src/emails/sendEmailVariants.ts b/backend/src/emails/sendEmailVariants.ts index 5276a473a..3373f17ad 100644 --- a/backend/src/emails/sendEmailVariants.ts +++ b/backend/src/emails/sendEmailVariants.ts @@ -40,6 +40,7 @@ export const sendAccountActivationEmail = (data: { language: string activationLink: string timeDurationObject: Record + logoUrl?: string | null }): Promise | boolean | null> => { return sendEmailTranslated({ receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` }, @@ -50,6 +51,7 @@ export const sendAccountActivationEmail = (data: { locale: data.language, activationLink: data.activationLink, timeDurationObject: data.timeDurationObject, + logoUrl: data.logoUrl, resendLink: CONFIG.EMAIL_LINK_FORGOTPASSWORD, supportEmail: CONFIG.COMMUNITY_SUPPORT_MAIL, communityURL: CONFIG.COMMUNITY_URL, diff --git a/backend/src/emails/templates/accountActivation/html.pug b/backend/src/emails/templates/accountActivation/html.pug index 027dcbc63..fd976a643 100644 --- a/backend/src/emails/templates/accountActivation/html.pug +++ b/backend/src/emails/templates/accountActivation/html.pug @@ -1,6 +1,8 @@ extend ../layout.pug block content + if logoUrl + img(src=logoUrl, alt="Banner", style="max-width: 680px; max-height: 250px;") h2= t('emails.accountActivation.title') .text-block include ../includes/salutation.pug diff --git a/backend/src/graphql/arg/UnsecureLoginArgs.ts b/backend/src/graphql/arg/UnsecureLoginArgs.ts index 5e82f12f3..72459df27 100644 --- a/backend/src/graphql/arg/UnsecureLoginArgs.ts +++ b/backend/src/graphql/arg/UnsecureLoginArgs.ts @@ -14,4 +14,8 @@ export class UnsecureLoginArgs { @Field(() => Int, { nullable: true }) @IsInt() publisherId?: number | null + + @Field(() => String, { nullable: true }) + @IsString() + project?: string | null } diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index a69a970c9..bb313bfad 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -146,10 +146,10 @@ export class UserResolver { @Authorized([RIGHTS.LOGIN]) @Mutation(() => User) async login( - @Args() { email, password, publisherId }: UnsecureLoginArgs, + @Args() { email, password, publisherId, project }: UnsecureLoginArgs, @Ctx() context: Context, ): Promise { - logger.info(`login with ${email}, ***, ${publisherId} ...`) + logger.info(`login with ${email}, ***, ${publisherId}, project=${project} ...`) email = email.trim().toLowerCase() let dbUser: DbUser @@ -178,6 +178,7 @@ export class UserResolver { // request to humhub and klicktipp run in parallel let humhubUserPromise: Promise> | undefined + let projectBrandingPromise: Promise | undefined const klicktippStatePromise = getKlicktippState(dbUser.emailContact.email) if (CONFIG.HUMHUB_ACTIVE && dbUser.humhubAllowed) { const getHumhubUser = new PostUser(dbUser) @@ -185,6 +186,12 @@ export class UserResolver { getHumhubUser.account.username, ) } + if (project) { + projectBrandingPromise = ProjectBranding.findOne({ + where: { alias: project }, + select: { spaceId: true }, + }) + } if (dbUser.passwordEncryptionType !== PasswordEncryptionType.GRADIDO_ID) { dbUser.passwordEncryptionType = PasswordEncryptionType.GRADIDO_ID @@ -214,11 +221,20 @@ export class UserResolver { }) await EVENT_USER_LOGIN(dbUser) + const projectBranding = await projectBrandingPromise + logger.debug('project branding: ', projectBranding) // load humhub state if (humhubUserPromise) { try { const result = await humhubUserPromise user.humhubAllowed = result?.result?.account.status === 1 + if (user.humhubAllowed) { + let spaceId = null + if (projectBranding) { + spaceId = projectBranding.spaceId + } + void syncHumhub(null, dbUser, spaceId) + } } catch (e) { logger.error("couldn't reach out to humhub, disable for now", e) user.humhubAllowed = false @@ -255,7 +271,7 @@ export class UserResolver { ): Promise { logger.addContext('user', 'unknown') logger.info( - `createUser(email=${email}, firstName=${firstName}, lastName=${lastName}, language=${language}, publisherId=${publisherId}, redeemCode =${redeemCode})`, + `createUser(email=${email}, firstName=${firstName}, lastName=${lastName}, language=${language}, publisherId=${publisherId}, redeemCode=${redeemCode}, project=${project})`, ) // TODO: wrong default value (should be null), how does graphql work here? Is it an required field? // default int publisher_id = 0; @@ -310,7 +326,13 @@ export class UserResolver { return user } } - + let projectBrandingPromise: Promise | undefined + if (project) { + projectBrandingPromise = ProjectBranding.findOne({ + where: { alias: project }, + select: { logoUrl: true, spaceId: true }, + }) + } const gradidoID = await newGradidoID() const eventRegisterRedeem = Event( @@ -358,6 +380,7 @@ export class UserResolver { const queryRunner = getConnection().createQueryRunner() await queryRunner.connect() await queryRunner.startTransaction('REPEATABLE READ') + let projectBranding: ProjectBranding | null | undefined try { dbUser = await queryRunner.manager.save(dbUser).catch((error) => { throw new LogError('Error while saving dbUser', error) @@ -375,8 +398,11 @@ export class UserResolver { const activationLink = `${ CONFIG.EMAIL_LINK_VERIFICATION - }${emailContact.emailVerificationCode.toString()}${redeemCode ? `/${redeemCode}` : ''}` + }${emailContact.emailVerificationCode.toString()}${redeemCode ? `/${redeemCode}` : ''}${ + project ? `?project=` + project : '' + }` + projectBranding = projectBrandingPromise ? await projectBrandingPromise : undefined void sendAccountActivationEmail({ firstName, lastName, @@ -384,6 +410,7 @@ export class UserResolver { language, activationLink, timeDurationObject: getTimeDurationObject(CONFIG.EMAIL_CODE_VALID_TIME), + logoUrl: projectBranding?.logoUrl, }) logger.info(`sendAccountActivationEmail of ${firstName}.${lastName} to ${email}`) @@ -400,14 +427,8 @@ export class UserResolver { logger.info('createUser() successful...') if (CONFIG.HUMHUB_ACTIVE) { let spaceId: number | null = null - if (project) { - const projectBranding = await ProjectBranding.findOne({ - where: { alias: project, newUserToSpace: true }, - select: { spaceId: true }, - }) - if (projectBranding) { - spaceId = projectBranding.spaceId - } + if (projectBranding) { + spaceId = projectBranding.spaceId } void syncHumhub(null, dbUser, spaceId) } diff --git a/backend/src/graphql/resolver/util/syncHumhub.ts b/backend/src/graphql/resolver/util/syncHumhub.ts index d08e6114e..b8becd8a2 100644 --- a/backend/src/graphql/resolver/util/syncHumhub.ts +++ b/backend/src/graphql/resolver/util/syncHumhub.ts @@ -55,8 +55,8 @@ export async function syncHumhub( result: ExecutedHumhubAction[result as ExecutedHumhubAction], }) if (spaceId && humhubUser) { - logger.debug(`add user to space ${spaceId}`) await humhubClient.addUserToSpace(humhubUser.id, spaceId) + logger.debug(`user added to space ${spaceId}`) } return user.id } diff --git a/frontend/src/graphql/mutations.js b/frontend/src/graphql/mutations.js index 6f7889bf7..041c156a7 100644 --- a/frontend/src/graphql/mutations.js +++ b/frontend/src/graphql/mutations.js @@ -68,6 +68,7 @@ export const createUser = gql` $language: String! $publisherId: Int $redeemCode: String + $project: String ) { createUser( email: $email @@ -76,6 +77,7 @@ export const createUser = gql` language: $language publisherId: $publisherId redeemCode: $redeemCode + project: $project ) { id } @@ -166,8 +168,8 @@ export const createContributionMessage = gql` ` export const login = gql` - mutation ($email: String!, $password: String!, $publisherId: Int) { - login(email: $email, password: $password, publisherId: $publisherId) { + mutation ($email: String!, $password: String!, $publisherId: Int, $project: String) { + login(email: $email, password: $password, publisherId: $publisherId, project: $project) { gradidoID alias firstName diff --git a/frontend/src/layouts/AuthLayout.vue b/frontend/src/layouts/AuthLayout.vue index 01edd3522..d2198a881 100644 --- a/frontend/src/layouts/AuthLayout.vue +++ b/frontend/src/layouts/AuthLayout.vue @@ -97,11 +97,10 @@