mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge master in this branch.
This commit is contained in:
commit
fabcbca5a5
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@ -394,7 +394,7 @@ jobs:
|
||||
report_name: Coverage Backend
|
||||
type: lcov
|
||||
result_path: ./backend/coverage/lcov.info
|
||||
min_coverage: 41
|
||||
min_coverage: 39
|
||||
token: ${{ github.token }}
|
||||
|
||||
##############################################################################
|
||||
|
||||
@ -18,6 +18,8 @@ DB_DATABASE=gradido_community
|
||||
#EMAIL_SMTP_URL=
|
||||
#EMAIL_SMTP_PORT=587
|
||||
|
||||
#EMAIL_LINK_VERIFICATION=http://localhost/vue/checkEmail/$1
|
||||
|
||||
#KLICKTIPP_USER=
|
||||
#KLICKTIPP_PASSWORD=
|
||||
#KLICKTIPP_APIKEY_DE=
|
||||
|
||||
7218
backend/package-lock.json
generated
7218
backend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -27,7 +27,6 @@
|
||||
"graphql": "^15.5.1",
|
||||
"jest": "^27.2.4",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"libsodium-wrappers": "^0.7.9",
|
||||
"module-alias": "^2.2.2",
|
||||
"mysql2": "^2.3.0",
|
||||
"nodemailer": "^6.6.5",
|
||||
@ -41,7 +40,6 @@
|
||||
"devDependencies": {
|
||||
"@types/express": "^4.17.12",
|
||||
"@types/jsonwebtoken": "^8.5.2",
|
||||
"@types/libsodium-wrappers": "^0.7.9",
|
||||
"@types/node": "^16.10.3",
|
||||
"@types/nodemailer": "^6.4.4",
|
||||
"@typescript-eslint/eslint-plugin": "^4.28.0",
|
||||
|
||||
@ -39,6 +39,11 @@ const community = {
|
||||
process.env.COMMUNITY_DESCRIPTION || 'Die lokale Entwicklungsumgebung von Gradido.',
|
||||
}
|
||||
|
||||
const loginServer = {
|
||||
LOGIN_APP_SECRET: process.env.LOGIN_APP_SECRET || '21ffbbc616fe',
|
||||
LOGIN_SERVER_KEY: process.env.LOGIN_SERVER_KEY || 'a51ef8ac7ef1abf162fb7a65261acd7a',
|
||||
}
|
||||
|
||||
const email = {
|
||||
EMAIL: process.env.EMAIL === 'true' || false,
|
||||
EMAIL_USERNAME: process.env.EMAIL_USERNAME || 'gradido_email',
|
||||
@ -46,11 +51,8 @@ const email = {
|
||||
EMAIL_PASSWORD: process.env.EMAIL_PASSWORD || 'xxx',
|
||||
EMAIL_SMTP_URL: process.env.EMAIL_SMTP_URL || 'gmail.com',
|
||||
EMAIL_SMTP_PORT: process.env.EMAIL_SMTP_PORT || '587',
|
||||
}
|
||||
|
||||
const loginServer = {
|
||||
LOGIN_APP_SECRET: process.env.LOGIN_APP_SECRET || '21ffbbc616fe',
|
||||
LOGIN_SERVER_KEY: process.env.LOGIN_SERVER_KEY || 'a51ef8ac7ef1abf162fb7a65261acd7a',
|
||||
EMAIL_LINK_VERIFICATION:
|
||||
process.env.EMAIL_LINK_VERIFICATION || 'http://localhost/vue/checkEmail/$1',
|
||||
}
|
||||
|
||||
// This is needed by graphql-directive-auth
|
||||
|
||||
@ -4,9 +4,9 @@
|
||||
|
||||
import { Resolver, Query, Args, Authorized, Ctx, Mutation } from 'type-graphql'
|
||||
import { getCustomRepository, getConnection, QueryRunner } from 'typeorm'
|
||||
import { createTransport } from 'nodemailer'
|
||||
|
||||
import CONFIG from '../../config'
|
||||
import { sendEMail } from '../../util/sendEMail'
|
||||
|
||||
import { Transaction } from '../model/Transaction'
|
||||
import { TransactionList } from '../model/TransactionList'
|
||||
@ -33,7 +33,6 @@ import { calculateDecay, calculateDecayWithInterval } from '../../util/decay'
|
||||
import { TransactionTypeId } from '../enum/TransactionTypeId'
|
||||
import { TransactionType } from '../enum/TransactionType'
|
||||
import { hasUserAmount, isHexPublicKey } from '../../util/validate'
|
||||
import { from_hex as fromHex } from 'libsodium-wrappers'
|
||||
|
||||
/*
|
||||
# Test
|
||||
@ -201,29 +200,6 @@ INSERT INTO `transaction_signatures` (`id`, `transaction_id`, `signature`, `pubk
|
||||
(1, 1, 0x60d632479707e5d01cdc32c3326b5a5bae11173a0c06b719ee7b552f9fd644de1a0cd4afc207253329081d39dac1a63421f51571d836995c649fc39afac7480a, 0x48c45cb4fea925e83850f68f2fa8f27a1a4ed1bcba68cdb59fcd86adef3f52ee);
|
||||
*/
|
||||
|
||||
const sendEMail = async (emailDef: any): Promise<boolean> => {
|
||||
if (!CONFIG.EMAIL) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Emails are disabled via config')
|
||||
return false
|
||||
}
|
||||
const transporter = createTransport({
|
||||
host: CONFIG.EMAIL_SMTP_URL,
|
||||
port: Number(CONFIG.EMAIL_SMTP_PORT),
|
||||
secure: false, // true for 465, false for other ports
|
||||
requireTLS: true,
|
||||
auth: {
|
||||
user: CONFIG.EMAIL_USERNAME,
|
||||
pass: CONFIG.EMAIL_PASSWORD,
|
||||
},
|
||||
})
|
||||
const info = await transporter.sendMail(emailDef)
|
||||
if (!info.messageId) {
|
||||
throw new Error('error sending notification email, but transaction succeed')
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Helper function
|
||||
async function calculateAndAddDecayTransactions(
|
||||
userTransactions: dbUserTransaction[],
|
||||
@ -624,7 +600,7 @@ export class TransactionResolver {
|
||||
transactionSendCoin.userId = senderUser.id
|
||||
transactionSendCoin.senderPublic = senderUser.pubkey
|
||||
transactionSendCoin.recipiantUserId = recipiantUser.id
|
||||
transactionSendCoin.recipiantPublic = Buffer.from(fromHex(recipiantPublicKey))
|
||||
transactionSendCoin.recipiantPublic = Buffer.from(recipiantPublicKey, 'hex')
|
||||
transactionSendCoin.amount = centAmount
|
||||
transactionSendCoin.senderFinalBalance = senderStateBalance.amount
|
||||
await queryRunner.manager.save(transactionSendCoin).catch((error) => {
|
||||
@ -656,8 +632,8 @@ export class TransactionResolver {
|
||||
// send notification email
|
||||
// TODO: translate
|
||||
await sendEMail({
|
||||
from: 'Gradido (nicht antworten) <' + CONFIG.EMAIL_SENDER + '>',
|
||||
to: recipiantUser.firstName + ' ' + recipiantUser.lastName + ' <' + recipiantUser.email + '>',
|
||||
from: `Gradido (nicht antworten) <${CONFIG.EMAIL_SENDER}>`,
|
||||
to: `${recipiantUser.firstName} ${recipiantUser.lastName} <${recipiantUser.email}>`,
|
||||
subject: 'Gradido Überweisung',
|
||||
text: `Hallo ${recipiantUser.firstName} ${recipiantUser.lastName}
|
||||
|
||||
@ -668,7 +644,8 @@ export class TransactionResolver {
|
||||
|
||||
Bitte antworte nicht auf diese E-Mail!
|
||||
|
||||
Mit freundlichen Grüßen Gradido Community Server`,
|
||||
Mit freundlichen Grüßen,
|
||||
dein Gradido-Team`,
|
||||
})
|
||||
|
||||
return 'success'
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
|
||||
import fs from 'fs'
|
||||
import { Resolver, Query, Args, Arg, Authorized, Ctx, UseMiddleware, Mutation } from 'type-graphql'
|
||||
import { BaseEntity, getConnection, getCustomRepository, QueryRunner } from 'typeorm'
|
||||
import { getConnection, getCustomRepository } from 'typeorm'
|
||||
import CONFIG from '../../config'
|
||||
import { LoginViaVerificationCode } from '../model/LoginViaVerificationCode'
|
||||
import { SendPasswordResetEmailResponse } from '../model/SendPasswordResetEmailResponse'
|
||||
@ -25,23 +25,11 @@ import { CheckEmailResponse } from '../model/CheckEmailResponse'
|
||||
import { UserSettingRepository } from '../../typeorm/repository/UserSettingRepository'
|
||||
import { Setting } from '../enum/Setting'
|
||||
import { UserRepository } from '../../typeorm/repository/User'
|
||||
import { LoginUserRepository } from '../../typeorm/repository/LoginUser'
|
||||
import { LoginUserBackupRepository } from '../../typeorm/repository/LoginUserBackup'
|
||||
import { LoginUser } from '@entity/LoginUser'
|
||||
import { LoginUserBackup } from '@entity/LoginUserBackup'
|
||||
import { LoginEmailOptIn } from '@entity/LoginEmailOptIn'
|
||||
import { sendEMail } from '../../util/sendEMail'
|
||||
|
||||
// TODO apparently the types are cannot be loaded correctly? IDK whats wrong and we have to use require
|
||||
// import {
|
||||
// /* eslint-disable camelcase */
|
||||
// randombytes_random,
|
||||
// crypto_hash_sha512_instance,
|
||||
// crypto_hash_sha512_BYTES,
|
||||
// crypto_sign_seed_keypair,
|
||||
// crypto_sign_PUBLICKEYBYTES,
|
||||
// crypto_sign_SECRETKEYBYTES,
|
||||
// /* eslint-enable camelcase */
|
||||
// } from 'sodium-native'
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const sodium = require('sodium-native')
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
@ -144,7 +132,6 @@ const KeyPairEd25519Create = (passphrase: string[]): Buffer[] => {
|
||||
}
|
||||
|
||||
const SecretKeyCryptographyCreateKey = (salt: string, password: string): Buffer[] => {
|
||||
// TODO: put that in the actual config
|
||||
const configLoginAppSecret = Buffer.from(CONFIG.LOGIN_APP_SECRET, 'hex')
|
||||
const configLoginServerKey = Buffer.from(CONFIG.LOGIN_SERVER_KEY, 'hex')
|
||||
if (configLoginServerKey.length !== sodium.crypto_shorthash_KEYBYTES) {
|
||||
@ -316,8 +303,6 @@ export class UserResolver {
|
||||
async createUser(
|
||||
@Args() { email, firstName, lastName, password, language, publisherId }: CreateUserArgs,
|
||||
): Promise<string> {
|
||||
const username = ''
|
||||
|
||||
// TODO: wrong default value (should be null), how does graphql work here? Is it an required field?
|
||||
// default int publisher_id = 0;
|
||||
|
||||
@ -335,6 +320,7 @@ export class UserResolver {
|
||||
|
||||
// Validate username
|
||||
// TODO: never true
|
||||
const username = ''
|
||||
if (username.length > 3 && !this.checkUsername({ username })) {
|
||||
throw new Error('Username already in use')
|
||||
}
|
||||
@ -408,33 +394,49 @@ export class UserResolver {
|
||||
// Store EmailOptIn in DB
|
||||
const emailOptIn = new LoginEmailOptIn()
|
||||
emailOptIn.userId = loginUserId
|
||||
emailOptIn.verificationCode = random(64) // TODO generate verificationCode
|
||||
emailOptIn.verificationCode = random(64)
|
||||
emailOptIn.emailOptInTypeId = 2
|
||||
|
||||
await queryRunner.manager.save(emailOptIn).catch((error) => {
|
||||
// TODO: Send error email instead of throw error
|
||||
// if (!emailOptInModel->insertIntoDB(false)) {
|
||||
// emailOptInModel->sendErrorsAsEmail();
|
||||
// return stateError("insert emailOptIn failed");
|
||||
// }
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Error while saving emailOptIn', error)
|
||||
throw new Error('error saving email opt in')
|
||||
})
|
||||
// TODO: Send EmailOptIn to user.email
|
||||
// emailOptIn->setBaseUrl(user->getGroupBaseUrl() + ServerConfig::g_frontend_checkEmailPath);
|
||||
// em->addEmail(new model::Email(emailOptIn, user, model::Email::convertTypeFromInt(emailType)));
|
||||
|
||||
// Send EMail to user
|
||||
const activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace(
|
||||
/\$1/g,
|
||||
emailOptIn.verificationCode.toString(),
|
||||
)
|
||||
const emailSent = await sendEMail({
|
||||
from: `Gradido (nicht antworten) <${CONFIG.EMAIL_SENDER}>`,
|
||||
to: `${firstName} ${lastName} <${email}>`,
|
||||
subject: 'Gradido: E-Mail Überprüfung',
|
||||
text: `Hallo ${firstName} ${lastName},
|
||||
|
||||
Deine EMail wurde soeben bei Gradido registriert.
|
||||
|
||||
Klicke bitte auf diesen Link, um die Registrierung abzuschließen und dein Gradido-Konto zu aktivieren:
|
||||
${activationLink}
|
||||
oder kopiere den obigen Link in dein Browserfenster.
|
||||
|
||||
Mit freundlichen Grüßen,
|
||||
dein Gradido-Team`,
|
||||
})
|
||||
|
||||
// In case EMails are disabled log the activation link for the user
|
||||
if (!emailSent) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`Account confirmation link: ${activationLink}`)
|
||||
}
|
||||
await queryRunner.commitTransaction()
|
||||
return 'success'
|
||||
} catch (e) {
|
||||
await queryRunner.rollbackTransaction()
|
||||
await rollbackAutoIncrement(queryRunner, LoginUser, `login_users`)
|
||||
await rollbackAutoIncrement(queryRunner, LoginUserBackup, `login_user_backups`)
|
||||
await rollbackAutoIncrement(queryRunner, DbUser, `state_users`)
|
||||
throw e
|
||||
} finally {
|
||||
await queryRunner.release()
|
||||
}
|
||||
return 'success'
|
||||
}
|
||||
|
||||
@Query(() => SendPasswordResetEmailResponse)
|
||||
@ -606,19 +608,3 @@ export class UserResolver {
|
||||
return result.data.hasElopage
|
||||
}
|
||||
}
|
||||
|
||||
const rollbackAutoIncrement = async (
|
||||
queryRunner: QueryRunner,
|
||||
entity: typeof BaseEntity,
|
||||
entityName: string,
|
||||
) => {
|
||||
const count = await queryRunner.manager.count(entity)
|
||||
const queryString = 'ALTER TABLE `' + entityName + '` auto_increment = ' + count
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Database AlterTable Query: ', queryString)
|
||||
await queryRunner.query(queryString).catch((error) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('problems with reset auto increment: %o', error)
|
||||
throw new Error('Problems with reset auto increment: ' + error)
|
||||
})
|
||||
}
|
||||
|
||||
26
backend/src/util/sendEMail.ts
Normal file
26
backend/src/util/sendEMail.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { createTransport } from 'nodemailer'
|
||||
|
||||
import CONFIG from '../config'
|
||||
|
||||
export const sendEMail = async (emailDef: any): Promise<boolean> => {
|
||||
if (!CONFIG.EMAIL) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Emails are disabled via config')
|
||||
return false
|
||||
}
|
||||
const transporter = createTransport({
|
||||
host: CONFIG.EMAIL_SMTP_URL,
|
||||
port: Number(CONFIG.EMAIL_SMTP_PORT),
|
||||
secure: false, // true for 465, false for other ports
|
||||
requireTLS: true,
|
||||
auth: {
|
||||
user: CONFIG.EMAIL_USERNAME,
|
||||
pass: CONFIG.EMAIL_PASSWORD,
|
||||
},
|
||||
})
|
||||
const info = await transporter.sendMail(emailDef)
|
||||
if (!info.messageId) {
|
||||
throw new Error('error sending notification email, but transaction succeed')
|
||||
}
|
||||
return true
|
||||
}
|
||||
@ -918,11 +918,6 @@
|
||||
"@types/koa-compose" "*"
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/libsodium-wrappers@^0.7.9":
|
||||
version "0.7.9"
|
||||
resolved "https://registry.yarnpkg.com/@types/libsodium-wrappers/-/libsodium-wrappers-0.7.9.tgz#89c3ad2156d5143e64bce86cfeb0045a983aeccc"
|
||||
integrity sha512-LisgKLlYQk19baQwjkBZZXdJL0KbeTpdEnrAfz5hQACbklCY0gVFnsKUyjfNWF1UQsCSjw93Sj5jSbiO8RPfdw==
|
||||
|
||||
"@types/long@^4.0.0":
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9"
|
||||
@ -3918,18 +3913,6 @@ libphonenumber-js@^1.9.7:
|
||||
resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.9.37.tgz#944f59a3618a8f85d9b619767a0b6fb87523f285"
|
||||
integrity sha512-RnUR4XwiVhMLnT7uFSdnmLeprspquuDtaShAgKTA+g/ms9/S4hQU3/QpFdh3iXPHtxD52QscXLm2W2+QBmvYAg==
|
||||
|
||||
libsodium-wrappers@^0.7.9:
|
||||
version "0.7.9"
|
||||
resolved "https://registry.yarnpkg.com/libsodium-wrappers/-/libsodium-wrappers-0.7.9.tgz#4ffc2b69b8f7c7c7c5594a93a4803f80f6d0f346"
|
||||
integrity sha512-9HaAeBGk1nKTRFRHkt7nzxqCvnkWTjn1pdjKgcUnZxj0FyOP4CnhgFhMdrFfgNsukijBGyBLpP2m2uKT1vuWhQ==
|
||||
dependencies:
|
||||
libsodium "^0.7.0"
|
||||
|
||||
libsodium@^0.7.0:
|
||||
version "0.7.9"
|
||||
resolved "https://registry.yarnpkg.com/libsodium/-/libsodium-0.7.9.tgz#4bb7bcbf662ddd920d8795c227ae25bbbfa3821b"
|
||||
integrity sha512-gfeADtR4D/CM0oRUviKBViMGXZDgnFdMKMzHsvBdqLBHd9ySi6EtYnmuhHVDDYgYpAO8eU8hEY+F8vIUAPh08A==
|
||||
|
||||
load-json-file@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user