Merge master in this branch.

This commit is contained in:
elweyn 2021-11-10 10:38:15 +01:00
commit fabcbca5a5
9 changed files with 74 additions and 7318 deletions

View File

@ -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 }}
##############################################################################

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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",

View File

@ -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

View File

@ -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'

View File

@ -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)
})
}

View 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
}

View File

@ -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"