Merge branch 'master' into 2473-feature-emails-further-design-of-html-emails

This commit is contained in:
Hannes Heine 2023-06-15 15:00:02 +02:00 committed by GitHub
commit 80b742f6cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 97 additions and 62 deletions

View File

@ -25,10 +25,12 @@ module.exports = {
}, },
node: true, node: true,
}, },
// the parser cannot handle the split sodium import
'import/ignore': ['sodium-native'],
}, },
rules: { rules: {
'no-console': 'error', 'no-console': 'error',
camelcase: ['error', { allow: ['FederationClient_*'] }], camelcase: ['error', { allow: ['FederationClient_*', 'crypto_*', 'randombytes_random'] }],
'no-debugger': 'error', 'no-debugger': 'error',
'prettier/prettier': [ 'prettier/prettier': [
'error', 'error',
@ -58,7 +60,10 @@ module.exports = {
'import/no-dynamic-require': 'error', 'import/no-dynamic-require': 'error',
'import/no-internal-modules': 'off', 'import/no-internal-modules': 'off',
'import/no-relative-packages': 'error', 'import/no-relative-packages': 'error',
'import/no-relative-parent-imports': ['error', { ignore: ['@/*', 'random-bigint'] }], 'import/no-relative-parent-imports': [
'error',
{ ignore: ['@/*', 'random-bigint', 'sodium-native'] },
],
'import/no-self-import': 'error', 'import/no-self-import': 'error',
'import/no-unresolved': 'error', 'import/no-unresolved': 'error',
'import/no-useless-path-segments': 'error', 'import/no-useless-path-segments': 'error',

View File

@ -0,0 +1,8 @@
// eslint-disable-next-line import/no-unresolved
export * from '@/node_modules/@types/sodium-native'
declare module 'sodium-native' {
export function crypto_hash_sha512_init(state: Buffer, key?: Buffer, outlen?: Buffer): void
export function crypto_hash_sha512_update(state: Buffer, input: Buffer): void
export function crypto_hash_sha512_final(state: Buffer, out: Buffer): void
}

View File

@ -55,6 +55,7 @@
"@types/lodash.clonedeep": "^4.5.6", "@types/lodash.clonedeep": "^4.5.6",
"@types/node": "^16.10.3", "@types/node": "^16.10.3",
"@types/nodemailer": "^6.4.4", "@types/nodemailer": "^6.4.4",
"@types/sodium-native": "^2.3.5",
"@types/uuid": "^8.3.4", "@types/uuid": "^8.3.4",
"@typescript-eslint/eslint-plugin": "^5.57.1", "@typescript-eslint/eslint-plugin": "^5.57.1",
"@typescript-eslint/parser": "^5.57.1", "@typescript-eslint/parser": "^5.57.1",

View File

@ -71,15 +71,13 @@ import { hasElopageBuys } from '@/util/hasElopageBuys'
import { getTimeDurationObject, printTimeDuration } from '@/util/time' import { getTimeDurationObject, printTimeDuration } from '@/util/time'
import random from 'random-bigint' import random from 'random-bigint'
import { randombytes_random } from 'sodium-native'
import { FULL_CREATION_AVAILABLE } from './const/const' import { FULL_CREATION_AVAILABLE } from './const/const'
import { getUserCreations } from './util/creations' import { getUserCreations } from './util/creations'
import { findUserByIdentifier } from './util/findUserByIdentifier' import { findUserByIdentifier } from './util/findUserByIdentifier'
import { validateAlias } from './util/validateAlias' import { validateAlias } from './util/validateAlias'
// eslint-disable-next-line @typescript-eslint/no-var-requires, import/no-commonjs
const sodium = require('sodium-native')
const LANGUAGES = ['de', 'en', 'es', 'fr', 'nl'] const LANGUAGES = ['de', 'en', 'es', 'fr', 'nl']
const DEFAULT_LANGUAGE = 'de' const DEFAULT_LANGUAGE = 'de'
const isLanguage = (language: string): boolean => { const isLanguage = (language: string): boolean => {
@ -237,7 +235,7 @@ export class UserResolver {
// TODO: this is unsecure, but the current implementation of the login server. This way it can be queried if the user with given EMail is existent. // TODO: this is unsecure, but the current implementation of the login server. This way it can be queried if the user with given EMail is existent.
const user = new User(communityDbUser) const user = new User(communityDbUser)
user.id = sodium.randombytes_random() % (2048 * 16) // TODO: for a better faking derive id from email so that it will be always the same id when the same email comes in? user.id = randombytes_random() % (2048 * 16) // TODO: for a better faking derive id from email so that it will be always the same id when the same email comes in?
user.gradidoID = uuidv4() user.gradidoID = uuidv4()
user.firstName = firstName user.firstName = firstName
user.lastName = lastName user.lastName = lastName

View File

@ -10,8 +10,19 @@ import { CONFIG } from '@/config'
import { LogError } from '@/server/LogError' import { LogError } from '@/server/LogError'
import { backendLogger as logger } from '@/server/logger' import { backendLogger as logger } from '@/server/logger'
// eslint-disable-next-line @typescript-eslint/no-var-requires, import/no-commonjs import {
const sodium = require('sodium-native') crypto_shorthash_KEYBYTES,
crypto_box_SEEDBYTES,
crypto_hash_sha512_init,
crypto_hash_sha512_update,
crypto_hash_sha512_final,
crypto_hash_sha512_BYTES,
crypto_hash_sha512_STATEBYTES,
crypto_shorthash_BYTES,
crypto_pwhash_SALTBYTES,
crypto_pwhash,
crypto_shorthash,
} from 'sodium-native'
// We will reuse this for changePassword // We will reuse this for changePassword
export const isValidPassword = (password: string): boolean => { export const isValidPassword = (password: string): boolean => {
@ -22,36 +33,36 @@ export const SecretKeyCryptographyCreateKey = (salt: string, password: string):
logger.trace('SecretKeyCryptographyCreateKey...') logger.trace('SecretKeyCryptographyCreateKey...')
const configLoginAppSecret = Buffer.from(CONFIG.LOGIN_APP_SECRET, 'hex') const configLoginAppSecret = Buffer.from(CONFIG.LOGIN_APP_SECRET, 'hex')
const configLoginServerKey = Buffer.from(CONFIG.LOGIN_SERVER_KEY, 'hex') const configLoginServerKey = Buffer.from(CONFIG.LOGIN_SERVER_KEY, 'hex')
if (configLoginServerKey.length !== sodium.crypto_shorthash_KEYBYTES) { if (configLoginServerKey.length !== crypto_shorthash_KEYBYTES) {
throw new LogError( throw new LogError(
'ServerKey has an invalid size', 'ServerKey has an invalid size',
configLoginServerKey.length, configLoginServerKey.length,
sodium.crypto_shorthash_KEYBYTES, crypto_shorthash_KEYBYTES,
) )
} }
const state = Buffer.alloc(sodium.crypto_hash_sha512_STATEBYTES) const state = Buffer.alloc(crypto_hash_sha512_STATEBYTES)
sodium.crypto_hash_sha512_init(state) crypto_hash_sha512_init(state)
sodium.crypto_hash_sha512_update(state, Buffer.from(salt)) crypto_hash_sha512_update(state, Buffer.from(salt))
sodium.crypto_hash_sha512_update(state, configLoginAppSecret) crypto_hash_sha512_update(state, configLoginAppSecret)
const hash = Buffer.alloc(sodium.crypto_hash_sha512_BYTES) const hash = Buffer.alloc(crypto_hash_sha512_BYTES)
sodium.crypto_hash_sha512_final(state, hash) crypto_hash_sha512_final(state, hash)
const encryptionKey = Buffer.alloc(sodium.crypto_box_SEEDBYTES) const encryptionKey = Buffer.alloc(crypto_box_SEEDBYTES)
const opsLimit = 10 const opsLimit = 10
const memLimit = 33554432 const memLimit = 33554432
const algo = 2 const algo = 2
sodium.crypto_pwhash( crypto_pwhash(
encryptionKey, encryptionKey,
Buffer.from(password), Buffer.from(password),
hash.slice(0, sodium.crypto_pwhash_SALTBYTES), hash.slice(0, crypto_pwhash_SALTBYTES),
opsLimit, opsLimit,
memLimit, memLimit,
algo, algo,
) )
const encryptionKeyHash = Buffer.alloc(sodium.crypto_shorthash_BYTES) const encryptionKeyHash = Buffer.alloc(crypto_shorthash_BYTES)
sodium.crypto_shorthash(encryptionKeyHash, encryptionKey, configLoginServerKey) crypto_shorthash(encryptionKeyHash, encryptionKey, configLoginServerKey)
return [encryptionKeyHash, encryptionKey] return [encryptionKeyHash, encryptionKey]
} }

View File

@ -1181,6 +1181,13 @@
"@types/mime" "^1" "@types/mime" "^1"
"@types/node" "*" "@types/node" "*"
"@types/sodium-native@^2.3.5":
version "2.3.5"
resolved "https://registry.yarnpkg.com/@types/sodium-native/-/sodium-native-2.3.5.tgz#5d2681e7b6b67bcbdc63cfb133e303ec9e942e43"
integrity sha512-a3DAIpW8+36XAY8aIR36JBQQsfOabxHuJwx11DL/PTvnbwEWPAXW66b8QbMi0r2vUnkOfREsketxdvjBmQxqDQ==
dependencies:
"@types/node" "*"
"@types/stack-utils@^2.0.0": "@types/stack-utils@^2.0.0":
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c"
@ -3645,7 +3652,7 @@ graceful-fs@^4.1.6, graceful-fs@^4.2.0:
integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
"gradido-database@file:../database": "gradido-database@file:../database":
version "1.20.0" version "1.21.0"
dependencies: dependencies:
"@types/uuid" "^8.3.4" "@types/uuid" "^8.3.4"
cross-env "^7.0.3" cross-env "^7.0.3"

View File

@ -101,43 +101,43 @@ module.exports = {
// ], // ],
// 'import/prefer-default-export': 'off', // 'import/prefer-default-export': 'off',
// n // n
// 'n/handle-callback-err': 'error', 'n/handle-callback-err': 'error',
// 'n/no-callback-literal': 'error', 'n/no-callback-literal': 'error',
// 'n/no-exports-assign': 'error', 'n/no-exports-assign': 'error',
// 'n/no-extraneous-import': 'error', // 'n/no-extraneous-import': 'error',
// 'n/no-extraneous-require': 'error', 'n/no-extraneous-require': 'error',
// 'n/no-hide-core-modules': 'error', 'n/no-hide-core-modules': 'error',
// 'n/no-missing-import': 'off', // not compatible with typescript 'n/no-missing-import': 'off', // not compatible with typescript
// 'n/no-missing-require': 'error', 'n/no-missing-require': 'error',
// 'n/no-new-require': 'error', 'n/no-new-require': 'error',
// 'n/no-path-concat': 'error', 'n/no-path-concat': 'error',
// 'n/no-process-exit': 'error', // 'n/no-process-exit': 'error',
// 'n/no-unpublished-bin': 'error', 'n/no-unpublished-bin': 'error',
// 'n/no-unpublished-import': 'off', // TODO need to exclude seeds 'n/no-unpublished-import': 'off', // TODO need to exclude seeds
// 'n/no-unpublished-require': 'error', 'n/no-unpublished-require': 'error',
// 'n/no-unsupported-features': ['error', { ignores: ['modules'] }], 'n/no-unsupported-features': ['error', { ignores: ['modules'] }],
// 'n/no-unsupported-features/es-builtins': 'error', 'n/no-unsupported-features/es-builtins': 'error',
// 'n/no-unsupported-features/es-syntax': 'error', 'n/no-unsupported-features/es-syntax': 'error',
// 'n/no-unsupported-features/node-builtins': 'error', 'n/no-unsupported-features/node-builtins': 'error',
// 'n/process-exit-as-throw': 'error', 'n/process-exit-as-throw': 'error',
// 'n/shebang': 'error', 'n/shebang': 'error',
// 'n/callback-return': 'error', 'n/callback-return': 'error',
// 'n/exports-style': 'error', 'n/exports-style': 'error',
// 'n/file-extension-in-import': 'off', 'n/file-extension-in-import': 'off',
// 'n/global-require': 'error', 'n/global-require': 'error',
// 'n/no-mixed-requires': 'error', 'n/no-mixed-requires': 'error',
// 'n/no-process-env': 'error', 'n/no-process-env': 'error',
// 'n/no-restricted-import': 'error', 'n/no-restricted-import': 'error',
// 'n/no-restricted-require': 'error', 'n/no-restricted-require': 'error',
// 'n/no-sync': 'error', // 'n/no-sync': 'error',
// 'n/prefer-global/buffer': 'error', 'n/prefer-global/buffer': 'error',
// 'n/prefer-global/console': 'error', 'n/prefer-global/console': 'error',
// 'n/prefer-global/process': 'error', 'n/prefer-global/process': 'error',
// 'n/prefer-global/text-decoder': 'error', 'n/prefer-global/text-decoder': 'error',
// 'n/prefer-global/text-encoder': 'error', 'n/prefer-global/text-encoder': 'error',
// 'n/prefer-global/url': 'error', 'n/prefer-global/url': 'error',
// 'n/prefer-global/url-search-params': 'error', 'n/prefer-global/url-search-params': 'error',
// 'n/prefer-promises/dns': 'error', 'n/prefer-promises/dns': 'error',
// 'n/prefer-promises/fs': 'error', // 'n/prefer-promises/fs': 'error',
// promise // promise
// 'promise/catch-or-return': 'error', // 'promise/catch-or-return': 'error',

View File

@ -48,5 +48,8 @@
"ts-mysql-migrate": "^1.0.2", "ts-mysql-migrate": "^1.0.2",
"typeorm": "^0.2.38", "typeorm": "^0.2.38",
"uuid": "^8.3.2" "uuid": "^8.3.2"
},
"engines": {
"node": ">=14"
} }
} }

View File

@ -1,4 +1,4 @@
// ATTENTION: DO NOT PUT ANY SECRETS IN HERE (or the .env) /* eslint-disable n/no-process-env */
import dotenv from 'dotenv' import dotenv from 'dotenv'
dotenv.config() dotenv.config()

View File

@ -24,6 +24,8 @@ type CommunityApi = {
url: string url: string
} }
type KeyPair = { publicKey: Buffer; secretKey: Buffer }
export const startDHT = async (topic: string): Promise<void> => { export const startDHT = async (topic: string): Promise<void> => {
try { try {
const TOPIC = DHT.hash(Buffer.from(topic)) const TOPIC = DHT.hash(Buffer.from(topic))
@ -32,11 +34,11 @@ export const startDHT = async (topic: string): Promise<void> => {
CONFIG.FEDERATION_DHT_SEED CONFIG.FEDERATION_DHT_SEED
? Buffer.alloc(KEY_SECRET_SEEDBYTES, CONFIG.FEDERATION_DHT_SEED) ? Buffer.alloc(KEY_SECRET_SEEDBYTES, CONFIG.FEDERATION_DHT_SEED)
: null, : null,
) ) as KeyPair
const pubKeyString = keyPair.publicKey.toString('hex') const pubKeyString = keyPair.publicKey.toString('hex')
logger.info(`keyPairDHT: publicKey=${pubKeyString}`) logger.info(`keyPairDHT: publicKey=${pubKeyString}`)
logger.debug(`keyPairDHT: secretKey=${keyPair.secretKey.toString('hex')}`) logger.debug(`keyPairDHT: secretKey=${keyPair.secretKey.toString('hex')}`)
await writeHomeCommunityEntry(pubKeyString) await writeHomeCommunityEntry(keyPair)
const ownApiVersions = await writeFederatedHomeCommunityEntries(pubKeyString) const ownApiVersions = await writeFederatedHomeCommunityEntries(pubKeyString)
logger.info(`ApiList: ${JSON.stringify(ownApiVersions)}`) logger.info(`ApiList: ${JSON.stringify(ownApiVersions)}`)
@ -212,13 +214,13 @@ async function writeFederatedHomeCommunityEntries(pubKey: string): Promise<Commu
return homeApiVersions return homeApiVersions
} }
async function writeHomeCommunityEntry(pubKey: string): Promise<void> { async function writeHomeCommunityEntry(keyPair: KeyPair): Promise<void> {
try { try {
// check for existing homeCommunity entry // check for existing homeCommunity entry
let homeCom = await DbCommunity.findOne({ foreign: false }) let homeCom = await DbCommunity.findOne({ foreign: false })
if (homeCom) { if (homeCom) {
// simply update the existing entry, but it MUST keep the ID and UUID because of possible relations // simply update the existing entry, but it MUST keep the ID and UUID because of possible relations
homeCom.publicKey = Buffer.from(pubKey) homeCom.publicKey = keyPair.publicKey
homeCom.url = CONFIG.FEDERATION_COMMUNITY_URL + '/api/' homeCom.url = CONFIG.FEDERATION_COMMUNITY_URL + '/api/'
homeCom.name = CONFIG.COMMUNITY_NAME homeCom.name = CONFIG.COMMUNITY_NAME
homeCom.description = CONFIG.COMMUNITY_DESCRIPTION homeCom.description = CONFIG.COMMUNITY_DESCRIPTION
@ -228,7 +230,7 @@ async function writeHomeCommunityEntry(pubKey: string): Promise<void> {
// insert a new homecommunity entry including a new ID and a new but ensured unique UUID // insert a new homecommunity entry including a new ID and a new but ensured unique UUID
homeCom = new DbCommunity() homeCom = new DbCommunity()
homeCom.foreign = false homeCom.foreign = false
homeCom.publicKey = Buffer.from(pubKey) homeCom.publicKey = keyPair.publicKey
homeCom.communityUuid = await newCommunityUuid() homeCom.communityUuid = await newCommunityUuid()
homeCom.url = CONFIG.FEDERATION_COMMUNITY_URL + '/api/' homeCom.url = CONFIG.FEDERATION_COMMUNITY_URL + '/api/'
homeCom.name = CONFIG.COMMUNITY_NAME homeCom.name = CONFIG.COMMUNITY_NAME