Merge branch 'remove_unnecessary_repositories' of https://github.com/gradido/gradido into remove_unnecessary_repositories

This commit is contained in:
elweyn 2022-02-04 07:24:07 +01:00 committed by Ulf Gebhardt
commit 14d9366ad6
Signed by: ulfgebhardt
GPG Key ID: DA6B843E748679C9
37 changed files with 967 additions and 523 deletions

View File

@ -448,7 +448,7 @@ jobs:
report_name: Coverage Admin Interface
type: lcov
result_path: ./coverage/lcov.info
min_coverage: 81
min_coverage: 91
token: ${{ github.token }}
##############################################################################

View File

@ -60,7 +60,7 @@ docker-compose -f docker-compose.yml up
- [frontend](./frontend) Wallet frontend
- [backend](./backend) GraphQL & Business logic backend
- [mariadb](./mariadb) Database backend
- [community_server](./community_server/) Business logic backend
- [community_server](./community_server/) Business logic backend (will be removed)
We are currently restructuring the service to reduce dependencies and unify business logic into one place. Furthermore the databases defined for each service will be unified into one.

View File

@ -27,15 +27,7 @@
</b-button>
</b-jumbotron>
</div>
<b-table-lite
:items="itemsUser"
:fields="fieldsTable"
:filter="criteria"
caption-top
striped
hover
stacked="md"
>
<b-table-lite :items="itemsUser" :fields="fieldsTable" caption-top striped hover stacked="md">
<template #cell(creation)="data">
<div v-html="data.value"></div>
</template>
@ -125,7 +117,7 @@
</row-details>
</template>
<template #cell(bookmark)="row">
<div v-show="type === 'UserListSearch'">
<div v-if="type === 'UserListSearch'">
<b-button
v-if="row.item.emailChecked"
variant="warning"
@ -187,15 +179,6 @@ export default {
type: Array,
required: true,
},
criteria: {
type: String,
required: false,
default: '',
},
creation: {
type: Array,
required: false,
},
},
components: {
CreationFormular,

View File

@ -1,6 +1,9 @@
export const creationMonths = {
props: {
creation: [1000, 1000, 1000],
creation: {
type: Array,
default: () => [1000, 1000, 1000],
},
},
computed: {
creationDates() {
@ -31,5 +34,8 @@ export const creationMonths = {
}
})
},
creationLabel() {
return this.creationDates.map((date) => this.$d(date, 'monthShort')).join(' | ')
},
},
}

View File

@ -1,4 +1,4 @@
import { shallowMount } from '@vue/test-utils'
import { mount } from '@vue/test-utils'
import Creation from './Creation.vue'
const localVue = global.localVue
@ -14,6 +14,7 @@ const apolloQueryMock = jest.fn().mockResolvedValue({
lastName: 'Bloxberg',
email: 'bibi@bloxberg.de',
creation: [200, 400, 600],
emailChecked: true,
},
{
userId: 2,
@ -21,6 +22,7 @@ const apolloQueryMock = jest.fn().mockResolvedValue({
lastName: 'Blümchen',
email: 'benjamin@bluemchen.de',
creation: [800, 600, 400],
emailChecked: true,
},
],
},
@ -51,10 +53,10 @@ describe('Creation', () => {
let wrapper
const Wrapper = () => {
return shallowMount(Creation, { localVue, mocks })
return mount(Creation, { localVue, mocks })
}
describe('shallowMount', () => {
describe('mount', () => {
beforeEach(() => {
jest.clearAllMocks()
wrapper = Wrapper()
@ -77,64 +79,66 @@ describe('Creation', () => {
)
})
it('sets the data of itemsList', () => {
expect(wrapper.vm.itemsList).toEqual([
{
userId: 1,
firstName: 'Bibi',
lastName: 'Bloxberg',
email: 'bibi@bloxberg.de',
creation: [200, 400, 600],
showDetails: false,
},
{
userId: 2,
firstName: 'Benjamin',
lastName: 'Blümchen',
email: 'benjamin@bluemchen.de',
creation: [800, 600, 400],
showDetails: false,
},
])
it('has two rows in the left table', () => {
expect(wrapper.findAll('table').at(0).findAll('tbody > tr')).toHaveLength(2)
})
it('has nwo rows in the right table', () => {
expect(wrapper.findAll('table').at(1).findAll('tbody > tr')).toHaveLength(0)
})
it('has correct data in first row ', () => {
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(0).text()).toContain('Bibi')
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(0).text()).toContain(
'Bloxberg',
)
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(0).text()).toContain(
'200 | 400 | 600',
)
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(0).text()).toContain(
'bibi@bloxberg.de',
)
})
it('has correct data in second row ', () => {
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(1).text()).toContain(
'Benjamin',
)
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(1).text()).toContain(
'Blümchen',
)
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(1).text()).toContain(
'800 | 600 | 400',
)
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(1).text()).toContain(
'benjamin@bluemchen.de',
)
})
})
describe('push item', () => {
beforeEach(() => {
wrapper.findComponent({ name: 'UserTable' }).vm.$emit('push-item', {
userId: 2,
firstName: 'Benjamin',
lastName: 'Blümchen',
email: 'benjamin@bluemchen.de',
creation: [800, 600, 400],
showDetails: false,
})
wrapper.findAll('table').at(0).findAll('tbody > tr').at(1).find('button').trigger('click')
})
it('removes the pushed item from itemsList', () => {
expect(wrapper.vm.itemsList).toEqual([
{
userId: 1,
firstName: 'Bibi',
lastName: 'Bloxberg',
email: 'bibi@bloxberg.de',
creation: [200, 400, 600],
showDetails: false,
},
])
it('has one item in left table', () => {
expect(wrapper.findAll('table').at(0).findAll('tbody > tr')).toHaveLength(1)
})
it('adds the pushed item to itemsMassCreation', () => {
expect(wrapper.vm.itemsMassCreation).toEqual([
{
userId: 2,
firstName: 'Benjamin',
lastName: 'Blümchen',
email: 'benjamin@bluemchen.de',
creation: [800, 600, 400],
showDetails: false,
},
])
it('has one item in right table', () => {
expect(wrapper.findAll('table').at(1).findAll('tbody > tr')).toHaveLength(1)
})
it('has the correct user in left table', () => {
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(0).text()).toContain(
'bibi@bloxberg.de',
)
})
it('has the correct user in right table', () => {
expect(wrapper.findAll('table').at(1).findAll('tbody > tr').at(0).text()).toContain(
'benjamin@bluemchen.de',
)
})
it('updates userSelectedInMassCreation in store', () => {
@ -146,6 +150,7 @@ describe('Creation', () => {
email: 'benjamin@bluemchen.de',
creation: [800, 600, 400],
showDetails: false,
emailChecked: true,
},
])
})
@ -153,73 +158,89 @@ describe('Creation', () => {
describe('remove item', () => {
beforeEach(async () => {
await wrapper.findComponent({ name: 'UserTable' }).vm.$emit('push-item', {
userId: 2,
firstName: 'Benjamin',
lastName: 'Blümchen',
email: 'benjamin@bluemchen.de',
creation: [800, 600, 400],
showDetails: false,
})
await wrapper
.findAllComponents({ name: 'UserTable' })
.findAll('table')
.at(0)
.findAll('tbody > tr')
.at(1)
.vm.$emit('remove-item', {
userId: 2,
firstName: 'Benjamin',
lastName: 'Blümchen',
email: 'benjamin@bluemchen.de',
creation: [800, 600, 400],
showDetails: false,
})
.find('button')
.trigger('click')
await wrapper
.findAll('table')
.at(1)
.findAll('tbody > tr')
.at(0)
.find('button')
.trigger('click')
})
it('adds the removed item to itemsList', () => {
expect(wrapper.vm.itemsList).toEqual([
{
userId: 2,
firstName: 'Benjamin',
lastName: 'Blümchen',
email: 'benjamin@bluemchen.de',
creation: [800, 600, 400],
showDetails: false,
},
{
userId: 1,
firstName: 'Bibi',
lastName: 'Bloxberg',
email: 'bibi@bloxberg.de',
creation: [200, 400, 600],
showDetails: false,
},
])
it('opens a dialog', () => {
expect(wrapper.findAll('#overlay').at(1).isVisible()).toBeTruthy()
})
it('removes the item from itemsMassCreation', () => {
expect(wrapper.vm.itemsMassCreation).toEqual([])
describe('cancel remove item', () => {
beforeEach(async () => {
await wrapper.findAll('#overlay').at(1).findAll('button').at(0).trigger('click')
})
it('closes the dialog', () => {
expect(wrapper.findAll('#overlay').at(1).isVisible()).toBeFalsy()
})
it('has one item in left table', () => {
expect(wrapper.findAll('table').at(0).findAll('tbody > tr')).toHaveLength(1)
})
it('has one item in right table', () => {
expect(wrapper.findAll('table').at(1).findAll('tbody > tr')).toHaveLength(1)
})
})
it('commits empty array as userSelectedInMassCreation', () => {
expect(storeCommitMock).toBeCalledWith('setUserSelectedInMassCreation', [])
describe('confirm remove item', () => {
beforeEach(async () => {
await wrapper.findAll('#overlay').at(1).findAll('button').at(1).trigger('click')
})
it('closes the dialog', () => {
expect(wrapper.findAll('#overlay').at(1).isVisible()).toBeFalsy()
})
it('has two items in left table', () => {
expect(wrapper.findAll('table').at(0).findAll('tbody > tr')).toHaveLength(2)
})
it('has the removed user in first row', () => {
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(0).text()).toContain(
'benjamin@bluemchen.de',
)
})
it('has no items in right table', () => {
expect(wrapper.findAll('table').at(1).findAll('tbody > tr')).toHaveLength(0)
})
it('commits empty array as userSelectedInMassCreation', () => {
expect(storeCommitMock).toBeCalledWith('setUserSelectedInMassCreation', [])
})
})
})
// this can only happen after API call in CreationForm
describe('remove all bookmarks', () => {
beforeEach(async () => {
await wrapper.findComponent({ name: 'UserTable' }).vm.$emit('push-item', {
userId: 2,
firstName: 'Benjamin',
lastName: 'Blümchen',
email: 'benjamin@bluemchen.de',
creation: [800, 600, 400],
showDetails: false,
})
await wrapper
.findAll('table')
.at(0)
.findAll('tbody > tr')
.at(1)
.find('button')
.trigger('click')
jest.clearAllMocks()
wrapper.findComponent({ name: 'CreationFormular' }).vm.$emit('remove-all-bookmark')
})
it('removes all items from itemsMassCreation', () => {
expect(wrapper.vm.itemsMassCreation).toEqual([])
it('has no items in right table', () => {
expect(wrapper.findAll('table').at(1).findAll('tbody > tr')).toHaveLength(0)
})
it('commits empty array to userSelectedInMassCreation', () => {
@ -241,22 +262,24 @@ describe('Creation', () => {
email: 'benjamin@bluemchen.de',
creation: [800, 600, 400],
showDetails: false,
emailChecked: true,
},
]
wrapper = Wrapper()
})
it('has only one item itemsList', () => {
expect(wrapper.vm.itemsList).toEqual([
{
userId: 1,
firstName: 'Bibi',
lastName: 'Bloxberg',
email: 'bibi@bloxberg.de',
creation: [200, 400, 600],
showDetails: false,
},
])
it('has one item in left table', () => {
expect(wrapper.findAll('table').at(0).findAll('tbody > tr')).toHaveLength(1)
})
it('has one item in right table', () => {
expect(wrapper.findAll('table').at(1).findAll('tbody > tr')).toHaveLength(1)
})
it('has the stored user in second row', () => {
expect(wrapper.findAll('table').at(1).findAll('tbody > tr').at(0).text()).toContain(
'benjamin@bluemchen.de',
)
})
})

View File

@ -14,8 +14,6 @@
type="UserListSearch"
:itemsUser="itemsList"
:fieldsTable="Searchfields"
:criteria="criteria"
:creation="creation"
@push-item="pushItem"
/>
<b-pagination
@ -33,8 +31,6 @@
type="UserListMassCreation"
:itemsUser="itemsMassCreation"
:fieldsTable="fields"
:criteria="null"
:creation="creation"
@remove-item="removeItem"
/>
<div v-if="itemsMassCreation.length === 0">
@ -55,9 +51,11 @@
import CreationFormular from '../components/CreationFormular.vue'
import UserTable from '../components/UserTable.vue'
import { searchUsers } from '../graphql/searchUsers'
import { creationMonths } from '../mixins/creationMonths'
export default {
name: 'Creation',
mixins: [creationMonths],
components: {
CreationFormular,
UserTable,
@ -69,7 +67,6 @@ export default {
itemsMassCreation: this.$store.state.userSelectedInMassCreation,
radioSelectedMass: '',
criteria: '',
creation: [null, null, null],
rows: 0,
currentPage: 1,
perPage: 25,
@ -163,16 +160,6 @@ export default {
{ key: 'bookmark', label: this.$t('remove') },
]
},
creationLabel() {
const now = new Date(this.now)
const lastMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1)
const beforeLastMonth = new Date(now.getFullYear(), now.getMonth() - 2, 1)
return [
this.$d(beforeLastMonth, 'monthShort'),
this.$d(lastMonth, 'monthShort'),
this.$d(now, 'monthShort'),
].join(' | ')
},
},
watch: {
currentPage() {

View File

@ -9,10 +9,35 @@ const apolloQueryMock = jest.fn().mockResolvedValue({
userCount: 1,
userList: [
{
userId: 1,
firstName: 'Bibi',
lastName: 'Bloxberg',
email: 'bibi@bloxberg.de',
creation: [200, 400, 600],
emailChecked: true,
},
{
userId: 2,
firstName: 'Benjamin',
lastName: 'Blümchen',
email: 'benjamin@bluemchen.de',
creation: [1000, 1000, 1000],
emailChecked: true,
},
{
userId: 3,
firstName: 'Peter',
lastName: 'Lustig',
email: 'peter@lustig.de',
creation: [0, 0, 0],
emailChecked: true,
},
{
userId: 4,
firstName: 'New',
lastName: 'User',
email: 'new@user.ch',
creation: [1000, 1000, 1000],
emailChecked: false,
},
],
@ -24,7 +49,7 @@ const toastErrorMock = jest.fn()
const mocks = {
$t: jest.fn((t) => t),
$d: jest.fn((d) => d),
$d: jest.fn((d) => String(d)),
$apollo: {
query: apolloQueryMock,
},
@ -42,6 +67,7 @@ describe('UserSearch', () => {
describe('mount', () => {
beforeEach(() => {
jest.clearAllMocks()
wrapper = Wrapper()
})
@ -49,13 +75,167 @@ describe('UserSearch', () => {
expect(wrapper.find('div.user-search').exists()).toBeTruthy()
})
it('calls the API', () => {
expect(apolloQueryMock).toBeCalledWith(
expect.objectContaining({
variables: {
searchText: '',
currentPage: 1,
pageSize: 25,
notActivated: false,
},
}),
)
})
describe('row toggling', () => {
it('has 4 users in the table', () => {
expect(wrapper.findAll('tbody > tr')).toHaveLength(4)
})
describe('user with email not activated', () => {
it('has no details button', () => {
expect(
wrapper.findAll('tbody > tr').at(3).findAll('td').at(4).find('button').exists(),
).toBeFalsy()
})
it('has a red confirmed button with envelope item', () => {
const row = wrapper.findAll('tbody > tr').at(3)
expect(row.findAll('td').at(5).find('button').exists()).toBeTruthy()
expect(row.findAll('td').at(5).find('button').classes('btn-danger')).toBeTruthy()
expect(row.findAll('td').at(5).find('svg').classes('bi-envelope')).toBeTruthy()
})
describe('click on envelope', () => {
it('opens the details', async () => {
await wrapper
.findAll('tbody > tr')
.at(3)
.findAll('td')
.at(5)
.find('button')
.trigger('click')
expect(wrapper.findAll('tbody > tr')).toHaveLength(6)
expect(wrapper.findAll('tbody > tr').at(5).find('input').element.value).toBe(
'new@user.ch',
)
expect(wrapper.findAll('tbody > tr').at(5).text()).toContain(
'unregister_mail.text_false',
)
})
describe('click on envelope again', () => {
it('closes the details', async () => {
await wrapper
.findAll('tbody > tr')
.at(3)
.findAll('td')
.at(5)
.find('button')
.trigger('click')
expect(wrapper.findAll('tbody > tr')).toHaveLength(4)
})
})
describe('click on close details', () => {
it('closes the details', async () => {
await wrapper
.findAll('tbody > tr')
.at(3)
.findAll('td')
.at(5)
.find('button')
.trigger('click')
await wrapper.findAll('tbody > tr').at(5).findAll('button').at(1).trigger('click')
expect(wrapper.findAll('tbody > tr')).toHaveLength(4)
})
})
})
})
describe('different details', () => {
it.skip('shows the creation formular for second user', async () => {
await wrapper
.findAll('tbody > tr')
.at(1)
.findAll('td')
.at(4)
.find('button')
.trigger('click')
expect(wrapper.findAll('tbody > tr')).toHaveLength(6)
expect(
wrapper.findAll('tbody > tr').at(3).find('div.component-creation-formular').exists(),
).toBeTruthy()
})
it.skip('shows the transactions for third user', async () => {
await wrapper
.findAll('tbody > tr')
.at(4)
.findAll('td')
.at(6)
.find('button')
.trigger('click')
expect(wrapper.findAll('tbody > tr')).toHaveLength(6)
})
})
})
describe('unconfirmed emails', () => {
beforeEach(async () => {
await wrapper.find('button.btn-block').trigger('click')
})
it('filters the users by unconfirmed emails', () => {
expect(wrapper.vm.searchResult).toHaveLength(1)
it('calls API with filter', () => {
expect(apolloQueryMock).toBeCalledWith(
expect.objectContaining({
variables: {
searchText: '',
currentPage: 1,
pageSize: 25,
notActivated: true,
},
}),
)
})
})
describe('pagination', () => {
beforeEach(async () => {
wrapper.setData({ currentPage: 2 })
})
it('calls the API with new page', () => {
expect(apolloQueryMock).toBeCalledWith(
expect.objectContaining({
variables: {
searchText: '',
currentPage: 2,
pageSize: 25,
notActivated: false,
},
}),
)
})
})
describe('user search', () => {
beforeEach(async () => {
wrapper.setData({ criteria: 'search string' })
})
it('calls the API with search string', () => {
expect(apolloQueryMock).toBeCalledWith(
expect.objectContaining({
variables: {
searchText: 'search string',
currentPage: 1,
pageSize: 25,
notActivated: false,
},
}),
)
})
})

View File

@ -12,15 +12,8 @@
v-model="criteria"
class="shadow p-3 mb-3 bg-white rounded"
:placeholder="$t('user_search')"
@input="getUsers"
></b-input>
<user-table
type="PageUserSearch"
:itemsUser="searchResult"
:fieldsTable="fields"
:criteria="criteria"
/>
<user-table type="PageUserSearch" :itemsUser="searchResult" :fieldsTable="fields" />
<b-pagination
pills
size="lg"
@ -35,9 +28,11 @@
<script>
import UserTable from '../components/UserTable.vue'
import { searchUsers } from '../graphql/searchUsers'
import { creationMonths } from '../mixins/creationMonths'
export default {
name: 'UserSearch',
mixins: [creationMonths],
components: {
UserTable,
},
@ -83,16 +78,11 @@ export default {
currentPage() {
this.getUsers()
},
criteria() {
this.getUsers()
},
},
computed: {
lastMonthDate() {
const now = new Date(this.now)
return new Date(now.getFullYear(), now.getMonth() - 1, 1)
},
beforeLastMonthDate() {
const now = new Date(this.now)
return new Date(now.getFullYear(), now.getMonth() - 2, 1)
},
fields() {
return [
{ key: 'email', label: this.$t('e_mail') },
@ -100,11 +90,7 @@ export default {
{ key: 'lastName', label: this.$t('lastname') },
{
key: 'creation',
label: [
this.$d(this.beforeLastMonthDate, 'monthShort'),
this.$d(this.lastMonthDate, 'monthShort'),
this.$d(this.now, 'monthShort'),
].join(' | '),
label: this.creationLabel,
formatter: (value, key, item) => {
return value.join(' | ')
},

View File

@ -4,7 +4,7 @@ import dotenv from 'dotenv'
dotenv.config()
const constants = {
DB_VERSION: '0016-transaction_signatures',
DB_VERSION: '0019-replace_login_user_id_with_state_user_id',
}
const server = {

View File

@ -20,8 +20,8 @@ import { UserTransaction } from '@entity/UserTransaction'
import { UserTransactionRepository } from '../../typeorm/repository/UserTransaction'
import { BalanceRepository } from '../../typeorm/repository/Balance'
import { calculateDecay } from '../../util/decay'
import { LoginUserRepository } from '../../typeorm/repository/LoginUser'
import { AdminPendingCreation } from '@entity/AdminPendingCreation'
import { User as dbUser } from '@entity/User'
@Resolver()
export class AdminResolver {
@ -373,7 +373,6 @@ function isCreationValid(creations: number[], amount: number, creationDate: Date
}
async function hasActivatedEmail(email: string): Promise<boolean> {
const repository = getCustomRepository(LoginUserRepository)
const user = await repository.findByEmail(email)
const user = await dbUser.findOne({ email })
return user ? user.emailChecked : false
}

View File

@ -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 { LoginUserRepository } from '../../typeorm/repository/LoginUser'
import { RIGHTS } from '../../auth/RIGHTS'
// Helper function
@ -290,14 +289,13 @@ async function addUserTransaction(
}
async function getPublicKey(email: string): Promise<string | null> {
const loginUserRepository = getCustomRepository(LoginUserRepository)
const loginUser = await loginUserRepository.findOne({ email: email })
const user = await dbUser.findOne({ email: email })
// User not found
if (!loginUser) {
if (!user) {
return null
}
return loginUser.pubKey.toString('hex')
return user.pubKey.toString('hex')
}
@Resolver()
@ -364,7 +362,7 @@ export class TransactionResolver {
// validate sender user (logged in)
const userRepository = getCustomRepository(UserRepository)
const senderUser = await userRepository.findByPubkeyHex(context.pubKey)
if (senderUser.pubkey.length !== 32) {
if (senderUser.pubKey.length !== 32) {
throw new Error('invalid sender public key')
}
if (!hasUserAmount(senderUser, amount)) {
@ -454,7 +452,7 @@ export class TransactionResolver {
const transactionSendCoin = new dbTransactionSendCoin()
transactionSendCoin.transactionId = transaction.id
transactionSendCoin.userId = senderUser.id
transactionSendCoin.senderPublic = senderUser.pubkey
transactionSendCoin.senderPublic = senderUser.pubKey
transactionSendCoin.recipiantUserId = recipiantUser.id
transactionSendCoin.recipiantPublic = Buffer.from(recipiantPublicKey, 'hex')
transactionSendCoin.amount = centAmount

View File

@ -14,18 +14,14 @@ import UnsecureLoginArgs from '../arg/UnsecureLoginArgs'
import UpdateUserInfosArgs from '../arg/UpdateUserInfosArgs'
import { klicktippNewsletterStateMiddleware } from '../../middleware/klicktippMiddleware'
import { UserSettingRepository } from '../../typeorm/repository/UserSettingRepository'
import { LoginUserRepository } from '../../typeorm/repository/LoginUser'
import { Setting } from '../enum/Setting'
import { UserRepository } from '../../typeorm/repository/User'
import { LoginUser } from '@entity/LoginUser'
import { LoginUserBackup } from '@entity/LoginUserBackup'
import { LoginEmailOptIn } from '@entity/LoginEmailOptIn'
import { sendResetPasswordEmail } from '../../mailer/sendResetPasswordEmail'
import { sendAccountActivationEmail } from '../../mailer/sendAccountActivationEmail'
import { klicktippSignIn } from '../../apis/KlicktippController'
import { RIGHTS } from '../../auth/RIGHTS'
import { ROLE_ADMIN } from '../../auth/ROLES'
import { randomBytes } from 'crypto'
import { LoginElopageBuys } from '@entity/LoginElopageBuys'
import { ServerUser } from '@entity/ServerUser'
@ -186,10 +182,10 @@ const createEmailOptIn = async (
return emailOptIn
}
const getOptInCode = async (loginUser: LoginUser): Promise<LoginEmailOptIn> => {
const getOptInCode = async (loginUserId: number): Promise<LoginEmailOptIn> => {
const loginEmailOptInRepository = await getRepository(LoginEmailOptIn)
let optInCode = await loginEmailOptInRepository.findOne({
userId: loginUser.id,
userId: loginUserId,
emailOptInTypeId: EMAIL_OPT_IN_RESET_PASSWORD,
})
@ -207,7 +203,7 @@ const getOptInCode = async (loginUser: LoginUser): Promise<LoginEmailOptIn> => {
} else {
optInCode = new LoginEmailOptIn()
optInCode.verificationCode = random(64)
optInCode.userId = loginUser.id
optInCode.userId = loginUserId
optInCode.emailOptInTypeId = EMAIL_OPT_IN_RESET_PASSWORD
}
await loginEmailOptInRepository.save(optInCode)
@ -223,17 +219,15 @@ export class UserResolver {
// TODO refactor and do not have duplicate code with login(see below)
const userRepository = getCustomRepository(UserRepository)
const userEntity = await userRepository.findByPubkeyHex(context.pubKey)
const loginUserRepository = getCustomRepository(LoginUserRepository)
const loginUser = await loginUserRepository.findByEmail(userEntity.email)
const user = new User()
user.id = userEntity.id
user.email = userEntity.email
user.firstName = userEntity.firstName
user.lastName = userEntity.lastName
user.username = userEntity.username
user.description = loginUser.description
user.pubkey = userEntity.pubkey.toString('hex')
user.language = loginUser.language
user.description = userEntity.description
user.pubkey = userEntity.pubKey.toString('hex')
user.language = userEntity.language
// Elopage Status & Stored PublisherId
user.hasElopage = await this.hasElopage(context)
@ -259,76 +253,50 @@ export class UserResolver {
@Ctx() context: any,
): Promise<User> {
email = email.trim().toLowerCase()
const loginUserRepository = getCustomRepository(LoginUserRepository)
const loginUser = await loginUserRepository.findByEmail(email).catch(() => {
const dbUser = await DbUser.findOneOrFail({ email }).catch(() => {
throw new Error('No user with this credentials')
})
if (!loginUser.emailChecked) {
if (!dbUser.emailChecked) {
throw new Error('User email not validated')
}
if (loginUser.password === BigInt(0)) {
if (dbUser.password === BigInt(0)) {
// TODO we want to catch this on the frontend and ask the user to check his emails or resend code
throw new Error('User has no password set yet')
}
if (!loginUser.pubKey || !loginUser.privKey) {
if (!dbUser.pubKey || !dbUser.privKey) {
// TODO we want to catch this on the frontend and ask the user to check his emails or resend code
throw new Error('User has no private or publicKey')
}
const passwordHash = SecretKeyCryptographyCreateKey(email, password) // return short and long hash
const loginUserPassword = BigInt(loginUser.password.toString())
const loginUserPassword = BigInt(dbUser.password.toString())
if (loginUserPassword !== passwordHash[0].readBigUInt64LE()) {
throw new Error('No user with this credentials')
}
// TODO: If user has no pubKey Create it again and update user.
const userRepository = getCustomRepository(UserRepository)
let userEntity: void | DbUser
const loginUserPubKey = loginUser.pubKey
const loginUserPubKeyString = loginUserPubKey.toString('hex')
userEntity = await userRepository.findByPubkeyHex(loginUserPubKeyString).catch(() => {
// User not stored in state_users
// TODO: Check with production data - email is unique which can cause problems
userEntity = new DbUser()
userEntity.firstName = loginUser.firstName
userEntity.lastName = loginUser.lastName
userEntity.username = loginUser.username
userEntity.email = loginUser.email
userEntity.pubkey = loginUser.pubKey
userRepository.save(userEntity).catch(() => {
throw new Error('error by save userEntity')
})
})
if (!userEntity) {
throw new Error('error with cannot happen')
}
const user = new User()
user.id = userEntity.id
user.id = dbUser.id
user.email = email
user.firstName = loginUser.firstName
user.lastName = loginUser.lastName
user.username = loginUser.username
user.description = loginUser.description
user.pubkey = loginUserPubKeyString
user.language = loginUser.language
user.firstName = dbUser.firstName
user.lastName = dbUser.lastName
user.username = dbUser.username
user.description = dbUser.description
user.pubkey = dbUser.pubKey.toString('hex')
user.language = dbUser.language
// Elopage Status & Stored PublisherId
user.hasElopage = await this.hasElopage({ pubKey: loginUserPubKeyString })
user.hasElopage = await this.hasElopage({ pubKey: dbUser.pubKey.toString('hex') })
if (!user.hasElopage && publisherId) {
user.publisherId = publisherId
// TODO: Check if we can use updateUserInfos
// await this.updateUserInfos({ publisherId }, { pubKey: loginUser.pubKey })
const loginUserRepository = getCustomRepository(LoginUserRepository)
const loginUser = await loginUserRepository.findOneOrFail({ email: userEntity.email })
loginUser.publisherId = publisherId
loginUserRepository.save(loginUser)
dbUser.publisherId = publisherId
DbUser.save(dbUser)
}
// coinAnimation
const userSettingRepository = getCustomRepository(UserSettingRepository)
const coinanimation = await userSettingRepository
.readBoolean(userEntity.id, Setting.COIN_ANIMATION)
.readBoolean(dbUser.id, Setting.COIN_ANIMATION)
.catch((error) => {
throw new Error(error)
})
@ -340,7 +308,7 @@ export class UserResolver {
context.setHeaders.push({
key: 'token',
value: encode(loginUser.pubKey),
value: encode(dbUser.pubKey),
})
return user
@ -392,18 +360,21 @@ export class UserResolver {
// const encryptedPrivkey = SecretKeyCryptographyEncrypt(keyPair[1], passwordHash[1])
const emailHash = getEmailHash(email)
// Table: login_users
const loginUser = new LoginUser()
loginUser.email = email
loginUser.firstName = firstName
loginUser.lastName = lastName
loginUser.username = username
loginUser.description = ''
// Table: state_users
const dbUser = new DbUser()
dbUser.email = email
dbUser.firstName = firstName
dbUser.lastName = lastName
dbUser.username = username
dbUser.description = ''
dbUser.emailHash = emailHash
dbUser.language = language
dbUser.publisherId = publisherId
dbUser.passphrase = passphrase.join(' ')
// TODO this field has no null allowed unlike the loginServer table
// dbUser.pubKey = Buffer.from(randomBytes(32)) // Buffer.alloc(32, 0) default to 0000...
// dbUser.pubkey = keyPair[0]
// loginUser.password = passwordHash[0].readBigUInt64LE() // using the shorthash
loginUser.emailHash = emailHash
loginUser.language = language
loginUser.groupId = 1
loginUser.publisherId = publisherId
// loginUser.pubKey = keyPair[0]
// loginUser.privKey = encryptedPrivkey
@ -411,43 +382,15 @@ export class UserResolver {
await queryRunner.connect()
await queryRunner.startTransaction('READ UNCOMMITTED')
try {
const { id: loginUserId } = await queryRunner.manager.save(loginUser).catch((error) => {
await queryRunner.manager.save(dbUser).catch((error) => {
// eslint-disable-next-line no-console
console.log('insert LoginUser failed', error)
throw new Error('insert user failed')
})
// Table: login_user_backups
const loginUserBackup = new LoginUserBackup()
loginUserBackup.userId = loginUserId
loginUserBackup.passphrase = passphrase.join(' ') // login server saves trailing space
loginUserBackup.mnemonicType = 2 // ServerConfig::MNEMONIC_BIP0039_SORTED_ORDER;
await queryRunner.manager.save(loginUserBackup).catch((error) => {
// eslint-disable-next-line no-console
console.log('insert LoginUserBackup failed', error)
throw new Error('insert user backup failed')
})
// Table: state_users
const dbUser = new DbUser()
dbUser.email = email
dbUser.firstName = firstName
dbUser.lastName = lastName
dbUser.username = username
// TODO this field has no null allowed unlike the loginServer table
dbUser.pubkey = Buffer.from(randomBytes(32)) // Buffer.alloc(32, 0) default to 0000...
// dbUser.pubkey = keyPair[0]
await queryRunner.manager.save(dbUser).catch((er) => {
// eslint-disable-next-line no-console
console.log('Error while saving dbUser', er)
console.log('Error while saving dbUser', error)
throw new Error('error saving user')
})
// Store EmailOptIn in DB
// TODO: this has duplicate code with sendResetPasswordEmail
const emailOptIn = await createEmailOptIn(loginUserId, queryRunner)
const emailOptIn = await createEmailOptIn(dbUser.id, queryRunner)
const activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace(
/{code}/g,
@ -479,15 +422,14 @@ export class UserResolver {
@Authorized([RIGHTS.SEND_ACTIVATION_EMAIL])
@Mutation(() => Boolean)
async sendActivationEmail(@Arg('email') email: string): Promise<boolean> {
const loginUserRepository = getCustomRepository(LoginUserRepository)
const loginUser = await loginUserRepository.findOneOrFail({ email: email })
const user = await DbUser.findOneOrFail({ email: email })
const queryRunner = getConnection().createQueryRunner()
await queryRunner.connect()
await queryRunner.startTransaction('READ UNCOMMITTED')
try {
const emailOptIn = await createEmailOptIn(loginUser.id, queryRunner)
const emailOptIn = await createEmailOptIn(user.id, queryRunner)
const activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace(
/{code}/g,
@ -496,8 +438,8 @@ export class UserResolver {
const emailSent = await sendAccountActivationEmail({
link: activationLink,
firstName: loginUser.firstName,
lastName: loginUser.lastName,
firstName: user.firstName,
lastName: user.lastName,
email,
})
@ -521,10 +463,9 @@ export class UserResolver {
async sendResetPasswordEmail(@Arg('email') email: string): Promise<boolean> {
// TODO: this has duplicate code with createUser
const loginUserRepository = await getCustomRepository(LoginUserRepository)
const loginUser = await loginUserRepository.findOneOrFail({ email })
const user = await DbUser.findOneOrFail({ email })
const optInCode = await getOptInCode(loginUser)
const optInCode = await getOptInCode(user.id)
const link = CONFIG.EMAIL_LINK_SETPASSWORD.replace(
/{code}/g,
@ -533,8 +474,8 @@ export class UserResolver {
const emailSent = await sendResetPasswordEmail({
link,
firstName: loginUser.firstName,
lastName: loginUser.lastName,
firstName: user.firstName,
lastName: user.lastName,
email,
})
@ -574,34 +515,18 @@ export class UserResolver {
throw new Error('Code is older than 10 minutes')
}
// load loginUser
const loginUserRepository = await getCustomRepository(LoginUserRepository)
const loginUser = await loginUserRepository
.findOneOrFail({ id: optInCode.userId })
.catch(() => {
throw new Error('Could not find corresponding Login User')
})
// load user
const dbUserRepository = await getCustomRepository(UserRepository)
const dbUser = await dbUserRepository.findOneOrFail({ email: loginUser.email }).catch(() => {
throw new Error('Could not find corresponding User')
const user = await DbUser.findOneOrFail({ id: optInCode.userId }).catch(() => {
throw new Error('Could not find corresponding Login User')
})
const loginUserBackupRepository = await getRepository(LoginUserBackup)
let loginUserBackup = await loginUserBackupRepository.findOne({ userId: loginUser.id })
// Generate Passphrase if needed
if (!loginUserBackup) {
if (!user.passphrase) {
const passphrase = PassphraseGenerate()
loginUserBackup = new LoginUserBackup()
loginUserBackup.userId = loginUser.id
loginUserBackup.passphrase = passphrase.join(' ') // login server saves trailing space
loginUserBackup.mnemonicType = 2 // ServerConfig::MNEMONIC_BIP0039_SORTED_ORDER;
loginUserBackupRepository.save(loginUserBackup)
user.passphrase = passphrase.join(' ')
}
const passphrase = loginUserBackup.passphrase.split(' ')
const passphrase = user.passphrase.split(' ')
if (passphrase.length < PHRASE_WORD_COUNT) {
// TODO if this can happen we cannot recover from that
// this seem to be good on production data, if we dont
@ -610,29 +535,23 @@ export class UserResolver {
}
// Activate EMail
loginUser.emailChecked = true
user.emailChecked = true
// Update Password
const passwordHash = SecretKeyCryptographyCreateKey(loginUser.email, password) // return short and long hash
const passwordHash = SecretKeyCryptographyCreateKey(user.email, password) // return short and long hash
const keyPair = KeyPairEd25519Create(passphrase) // return pub, priv Key
const encryptedPrivkey = SecretKeyCryptographyEncrypt(keyPair[1], passwordHash[1])
loginUser.password = passwordHash[0].readBigUInt64LE() // using the shorthash
loginUser.pubKey = keyPair[0]
loginUser.privKey = encryptedPrivkey
dbUser.pubkey = keyPair[0]
user.password = passwordHash[0].readBigUInt64LE() // using the shorthash
user.pubKey = keyPair[0]
user.privKey = encryptedPrivkey
const queryRunner = getConnection().createQueryRunner()
await queryRunner.connect()
await queryRunner.startTransaction('READ UNCOMMITTED')
try {
// Save loginUser
await queryRunner.manager.save(loginUser).catch((error) => {
throw new Error('error saving loginUser: ' + error)
})
// Save user
await queryRunner.manager.save(dbUser).catch((error) => {
await queryRunner.manager.save(user).catch((error) => {
throw new Error('error saving user: ' + error)
})
@ -653,12 +572,7 @@ export class UserResolver {
// TODO do we always signUp the user? How to handle things with old users?
if (optInCode.emailOptInTypeId === EMAIL_OPT_IN_REGISTER) {
try {
await klicktippSignIn(
loginUser.email,
loginUser.language,
loginUser.firstName,
loginUser.lastName,
)
await klicktippSignIn(user.email, user.language, user.firstName, user.lastName)
} catch {
// TODO is this a problem?
// eslint-disable-next-line no-console
@ -688,8 +602,6 @@ export class UserResolver {
): Promise<boolean> {
const userRepository = getCustomRepository(UserRepository)
const userEntity = await userRepository.findByPubkeyHex(context.pubKey)
const loginUserRepository = getCustomRepository(LoginUserRepository)
const loginUser = await loginUserRepository.findOneOrFail({ email: userEntity.email })
if (username) {
throw new Error('change username currently not supported!')
@ -703,46 +615,44 @@ export class UserResolver {
}
if (firstName) {
loginUser.firstName = firstName
userEntity.firstName = firstName
}
if (lastName) {
loginUser.lastName = lastName
userEntity.lastName = lastName
}
if (description) {
loginUser.description = description
userEntity.description = description
}
if (language) {
if (!isLanguage(language)) {
throw new Error(`"${language}" isn't a valid language`)
}
loginUser.language = language
userEntity.language = language
}
if (password && passwordNew) {
// TODO: This had some error cases defined - like missing private key. This is no longer checked.
const oldPasswordHash = SecretKeyCryptographyCreateKey(loginUser.email, password)
if (BigInt(loginUser.password.toString()) !== oldPasswordHash[0].readBigUInt64LE()) {
const oldPasswordHash = SecretKeyCryptographyCreateKey(userEntity.email, password)
if (BigInt(userEntity.password.toString()) !== oldPasswordHash[0].readBigUInt64LE()) {
throw new Error(`Old password is invalid`)
}
const privKey = SecretKeyCryptographyDecrypt(loginUser.privKey, oldPasswordHash[1])
const privKey = SecretKeyCryptographyDecrypt(userEntity.privKey, oldPasswordHash[1])
const newPasswordHash = SecretKeyCryptographyCreateKey(loginUser.email, passwordNew) // return short and long hash
const newPasswordHash = SecretKeyCryptographyCreateKey(userEntity.email, passwordNew) // return short and long hash
const encryptedPrivkey = SecretKeyCryptographyEncrypt(privKey, newPasswordHash[1])
// Save new password hash and newly encrypted private key
loginUser.password = newPasswordHash[0].readBigUInt64LE()
loginUser.privKey = encryptedPrivkey
userEntity.password = newPasswordHash[0].readBigUInt64LE()
userEntity.privKey = encryptedPrivkey
}
// Save publisherId only if Elopage is not yet registered
if (publisherId && !(await this.hasElopage(context))) {
loginUser.publisherId = publisherId
userEntity.publisherId = publisherId
}
const queryRunner = getConnection().createQueryRunner()
@ -759,10 +669,6 @@ export class UserResolver {
})
}
await queryRunner.manager.save(loginUser).catch((error) => {
throw new Error('error saving loginUser: ' + error)
})
await queryRunner.manager.save(userEntity).catch((error) => {
throw new Error('error saving user: ' + error)
})
@ -792,7 +698,7 @@ export class UserResolver {
throw new Error(`Username must be at minimum ${MIN_CHARACTERS_USERNAME} characters long.`)
}
const usersFound = await LoginUser.count({ username })
const usersFound = await DbUser.count({ username })
// Username already present?
if (usersFound !== 0) {

View File

@ -1,24 +0,0 @@
import { EntityRepository, Repository } from '@dbTools/typeorm'
import { LoginUser } from '@entity/LoginUser'
@EntityRepository(LoginUser)
export class LoginUserRepository extends Repository<LoginUser> {
async findByEmail(email: string): Promise<LoginUser> {
return this.createQueryBuilder('loginUser')
.where('loginUser.email = :email', { email })
.getOneOrFail()
}
async findBySearchCriteria(searchCriteria: string): Promise<LoginUser[]> {
return await this.createQueryBuilder('user')
.where(
'user.firstName like :name or user.lastName like :lastName or user.email like :email',
{
name: `%${searchCriteria}%`,
lastName: `%${searchCriteria}%`,
email: `%${searchCriteria}%`,
},
)
.getMany()
}
}

View File

@ -5,7 +5,7 @@ import { User } from '@entity/User'
export class UserRepository extends Repository<User> {
async findByPubkeyHex(pubkeyHex: string): Promise<User> {
return this.createQueryBuilder('user')
.where('hex(user.pubkey) = :pubkeyHex', { pubkeyHex })
.where('hex(user.pubKey) = :pubkeyHex', { pubkeyHex })
.getOneOrFail()
}

View File

@ -30,7 +30,7 @@
import { LoginElopageBuys } from '@entity/LoginElopageBuys'
import { getCustomRepository } from '@dbTools/typeorm'
import { UserResolver } from '../graphql/resolver/UserResolver'
import { LoginUserRepository } from '../typeorm/repository/LoginUser'
import { User as dbUser } from '@entity/User'
export const elopageWebhook = async (req: any, res: any): Promise<void> => {
// eslint-disable-next-line no-console
@ -112,8 +112,7 @@ export const elopageWebhook = async (req: any, res: any): Promise<void> => {
}
// Do we already have such a user?
const loginUserRepository = await getCustomRepository(LoginUserRepository)
if ((await loginUserRepository.count({ email })) !== 0) {
if ((await dbUser.count({ email })) !== 0) {
// eslint-disable-next-line no-console
console.log(`Did not create User - already exists with email: ${email}`)
return

View File

@ -7,13 +7,13 @@ export class User extends BaseEntity {
@PrimaryGeneratedColumn('increment', { unsigned: true })
id: number
@Column({ name: 'index_id', default: 0 })
@Column({ name: 'index_id', type: 'smallint', default: 0, nullable: false })
indexId: number
@Column({ name: 'group_id', default: 0, unsigned: true })
groupId: number
@Column({ type: 'binary', length: 32, name: 'public_key' })
@Column({ name: 'public_key', type: 'binary', length: 32, default: null, nullable: true })
pubkey: Buffer
@Column({ length: 255, nullable: true, default: null, collation: 'utf8mb4_unicode_ci' })
@ -40,7 +40,7 @@ export class User extends BaseEntity {
@Column({ length: 255, nullable: true, default: null, collation: 'utf8mb4_unicode_ci' })
username: string
@Column()
@Column({ type: 'bool', default: false })
disabled: boolean
@OneToOne(() => Balance, (balance) => balance.user)

View File

@ -7,13 +7,13 @@ export class User extends BaseEntity {
@PrimaryGeneratedColumn('increment', { unsigned: true })
id: number
@Column({ name: 'index_id', default: 0 })
@Column({ name: 'index_id', type: 'smallint', default: 0, nullable: false })
indexId: number
@Column({ name: 'group_id', default: 0, unsigned: true })
groupId: number
@Column({ type: 'binary', length: 32, name: 'public_key' })
@Column({ name: 'public_key', type: 'binary', length: 32, default: null, nullable: true })
pubkey: Buffer
@Column({ length: 255, nullable: true, default: null, collation: 'utf8mb4_unicode_ci' })
@ -40,7 +40,7 @@ export class User extends BaseEntity {
@Column({ length: 255, nullable: true, default: null, collation: 'utf8mb4_unicode_ci' })
username: string
@Column()
@Column({ type: 'bool', default: false })
disabled: boolean
@OneToMany(() => UserSetting, (userSetting) => userSetting.user)

View File

@ -1,5 +1,5 @@
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToOne } from 'typeorm'
import { LoginUserBackup } from '../LoginUserBackup'
import { LoginUserBackup } from './LoginUserBackup'
// Moriz: I do not like the idea of having two user tables
@Entity('login_users')
@ -19,7 +19,7 @@ export class LoginUser extends BaseEntity {
@Column({ length: 255, default: '' })
username: string
@Column({ default: '', nullable: true })
@Column({ type: 'mediumtext', default: '', nullable: true })
description: string
@Column({ type: 'bigint', default: 0, unsigned: true })
@ -34,19 +34,19 @@ export class LoginUser extends BaseEntity {
@Column({ name: 'email_hash', type: 'binary', length: 32, default: null, nullable: true })
emailHash: Buffer
@Column({ name: 'created', default: () => 'CURRENT_TIMESTAMP' })
@Column({ name: 'created', default: () => 'CURRENT_TIMESTAMP', nullable: false })
createdAt: Date
@Column({ name: 'email_checked', default: 0 })
@Column({ name: 'email_checked', type: 'bool', nullable: false, default: false })
emailChecked: boolean
@Column({ name: 'passphrase_shown', default: 0 })
@Column({ name: 'passphrase_shown', type: 'bool', nullable: false, default: false })
passphraseShown: boolean
@Column({ length: 4, default: 'de' })
@Column({ length: 4, default: 'de', nullable: false })
language: string
@Column({ default: 0 })
@Column({ type: 'bool', default: false })
disabled: boolean
@Column({ name: 'group_id', default: 0, unsigned: true })

View File

@ -1,5 +1,5 @@
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, JoinColumn, OneToOne } from 'typeorm'
import { LoginUser } from '../LoginUser'
import { LoginUser } from './LoginUser'
@Entity('login_user_backups')
export class LoginUserBackup extends BaseEntity {

View File

@ -1,5 +1,5 @@
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToOne } from 'typeorm'
import { LoginUserBackup } from '../LoginUserBackup'
import { LoginUserBackup } from '../0003-login_server_tables/LoginUserBackup'
// Moriz: I do not like the idea of having two user tables
@Entity('login_users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' })
@ -19,7 +19,7 @@ export class LoginUser extends BaseEntity {
@Column({ length: 255, default: '', collation: 'utf8mb4_unicode_ci' })
username: string
@Column({ default: '', collation: 'utf8mb4_unicode_ci', nullable: true })
@Column({ type: 'mediumtext', default: '', collation: 'utf8mb4_unicode_ci', nullable: true })
description: string
@Column({ type: 'bigint', default: 0, unsigned: true })
@ -34,19 +34,19 @@ export class LoginUser extends BaseEntity {
@Column({ name: 'email_hash', type: 'binary', length: 32, default: null, nullable: true })
emailHash: Buffer
@Column({ name: 'created', default: () => 'CURRENT_TIMESTAMP' })
@Column({ name: 'created', default: () => 'CURRENT_TIMESTAMP', nullable: false })
createdAt: Date
@Column({ name: 'email_checked', default: 0 })
@Column({ name: 'email_checked', type: 'bool', nullable: false, default: false })
emailChecked: boolean
@Column({ name: 'passphrase_shown', default: 0 })
@Column({ name: 'passphrase_shown', type: 'bool', nullable: false, default: false })
passphraseShown: boolean
@Column({ length: 4, default: 'de', collation: 'utf8mb4_unicode_ci' })
@Column({ length: 4, default: 'de', collation: 'utf8mb4_unicode_ci', nullable: false })
language: string
@Column({ default: 0 })
@Column({ type: 'bool', default: false })
disabled: boolean
@Column({ name: 'group_id', default: 0, unsigned: true })

View File

@ -0,0 +1,16 @@
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm'
@Entity('login_user_backups')
export class LoginUserBackup extends BaseEntity {
@PrimaryGeneratedColumn('increment', { unsigned: true })
id: number
@Column({ type: 'text', name: 'passphrase', nullable: false })
passphrase: string
@Column({ name: 'user_id', nullable: false })
userId: number
@Column({ name: 'mnemonic_type', default: -1 })
mnemonicType: number
}

View File

@ -0,0 +1,74 @@
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm'
import { UserSetting } from '../UserSetting'
@Entity('state_users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' })
export class User extends BaseEntity {
@PrimaryGeneratedColumn('increment', { unsigned: true })
id: number
@Column({ name: 'login_user_id', default: null, unsigned: true })
loginUserId: number
@Column({ name: 'index_id', type: 'smallint', default: 0, nullable: false })
indexId: number
@Column({ name: 'public_key', type: 'binary', length: 32, default: null, nullable: true })
pubKey: Buffer
@Column({ name: 'privkey', type: 'binary', length: 80, default: null, nullable: true })
privKey: Buffer
@Column({ length: 255, unique: true, nullable: false, collation: 'utf8mb4_unicode_ci' })
email: string
@Column({
name: 'first_name',
length: 255,
nullable: true,
default: null,
collation: 'utf8mb4_unicode_ci',
})
firstName: string
@Column({
name: 'last_name',
length: 255,
nullable: true,
default: null,
collation: 'utf8mb4_unicode_ci',
})
lastName: string
@Column({ length: 255, nullable: true, default: null, collation: 'utf8mb4_unicode_ci' })
username: string
@Column({ type: 'bool', default: false })
disabled: boolean
@Column({ type: 'mediumtext', default: '', collation: 'utf8mb4_unicode_ci', nullable: true })
description: string
@Column({ type: 'bigint', default: 0, unsigned: true })
password: BigInt
@Column({ name: 'email_hash', type: 'binary', length: 32, default: null, nullable: true })
emailHash: Buffer
@Column({ name: 'created', default: () => 'CURRENT_TIMESTAMP', nullable: false })
createdAt: Date
@Column({ name: 'email_checked', type: 'bool', nullable: false, default: false })
emailChecked: boolean
@Column({ name: 'passphrase_shown', type: 'bool', nullable: false, default: false })
passphraseShown: boolean
@Column({ length: 4, default: 'de', collation: 'utf8mb4_unicode_ci', nullable: false })
language: string
@Column({ name: 'publisher_id', default: 0 })
publisherId: number
@OneToMany(() => UserSetting, (userSetting) => userSetting.user)
settings: UserSetting[]
}

View File

@ -0,0 +1,83 @@
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm'
import { UserSetting } from '../UserSetting'
@Entity('state_users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' })
export class User extends BaseEntity {
@PrimaryGeneratedColumn('increment', { unsigned: true })
id: number
@Column({ name: 'login_user_id', default: null, unsigned: true })
loginUserId: number
@Column({ name: 'index_id', type: 'smallint', default: 0, nullable: false })
indexId: number
@Column({ name: 'public_key', type: 'binary', length: 32, default: null, nullable: true })
pubKey: Buffer
@Column({ name: 'privkey', type: 'binary', length: 80, default: null, nullable: true })
privKey: Buffer
@Column({ length: 255, unique: true, nullable: false, collation: 'utf8mb4_unicode_ci' })
email: string
@Column({
name: 'first_name',
length: 255,
nullable: true,
default: null,
collation: 'utf8mb4_unicode_ci',
})
firstName: string
@Column({
name: 'last_name',
length: 255,
nullable: true,
default: null,
collation: 'utf8mb4_unicode_ci',
})
lastName: string
@Column({ length: 255, nullable: true, default: null, collation: 'utf8mb4_unicode_ci' })
username: string
@Column({ type: 'bool', default: false })
disabled: boolean
@Column({ type: 'mediumtext', default: '', collation: 'utf8mb4_unicode_ci', nullable: true })
description: string
@Column({ type: 'bigint', default: 0, unsigned: true })
password: BigInt
@Column({ name: 'email_hash', type: 'binary', length: 32, default: null, nullable: true })
emailHash: Buffer
@Column({ name: 'created', default: () => 'CURRENT_TIMESTAMP', nullable: false })
createdAt: Date
@Column({ name: 'email_checked', type: 'bool', nullable: false, default: false })
emailChecked: boolean
@Column({ name: 'passphrase_shown', type: 'bool', nullable: false, default: false })
passphraseShown: boolean
@Column({ length: 4, default: 'de', collation: 'utf8mb4_unicode_ci', nullable: false })
language: string
@Column({ name: 'publisher_id', default: 0 })
publisherId: number
@Column({
type: 'text',
name: 'passphrase',
collation: 'utf8mb4_unicode_ci',
nullable: true,
default: null,
})
passphrase: string
@OneToMany(() => UserSetting, (userSetting) => userSetting.user)
settings: UserSetting[]
}

View File

@ -0,0 +1,80 @@
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm'
import { UserSetting } from '../UserSetting'
@Entity('state_users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' })
export class User extends BaseEntity {
@PrimaryGeneratedColumn('increment', { unsigned: true })
id: number
@Column({ name: 'index_id', type: 'smallint', default: 0, nullable: false })
indexId: number
@Column({ name: 'public_key', type: 'binary', length: 32, default: null, nullable: true })
pubKey: Buffer
@Column({ name: 'privkey', type: 'binary', length: 80, default: null, nullable: true })
privKey: Buffer
@Column({ length: 255, unique: true, nullable: false, collation: 'utf8mb4_unicode_ci' })
email: string
@Column({
name: 'first_name',
length: 255,
nullable: true,
default: null,
collation: 'utf8mb4_unicode_ci',
})
firstName: string
@Column({
name: 'last_name',
length: 255,
nullable: true,
default: null,
collation: 'utf8mb4_unicode_ci',
})
lastName: string
@Column({ length: 255, nullable: true, default: null, collation: 'utf8mb4_unicode_ci' })
username: string
@Column({ type: 'bool', default: false })
disabled: boolean
@Column({ type: 'mediumtext', default: '', collation: 'utf8mb4_unicode_ci', nullable: true })
description: string
@Column({ type: 'bigint', default: 0, unsigned: true })
password: BigInt
@Column({ name: 'email_hash', type: 'binary', length: 32, default: null, nullable: true })
emailHash: Buffer
@Column({ name: 'created', default: () => 'CURRENT_TIMESTAMP', nullable: false })
createdAt: Date
@Column({ name: 'email_checked', type: 'bool', nullable: false, default: false })
emailChecked: boolean
@Column({ name: 'passphrase_shown', type: 'bool', nullable: false, default: false })
passphraseShown: boolean
@Column({ length: 4, default: 'de', collation: 'utf8mb4_unicode_ci', nullable: false })
language: string
@Column({ name: 'publisher_id', default: 0 })
publisherId: number
@Column({
type: 'text',
name: 'passphrase',
collation: 'utf8mb4_unicode_ci',
nullable: true,
default: null,
})
passphrase: string
@OneToMany(() => UserSetting, (userSetting) => userSetting.user)
settings: UserSetting[]
}

View File

@ -1 +0,0 @@
export { LoginUser } from './0006-login_users_collation/LoginUser'

View File

@ -1 +0,0 @@
export { LoginUserBackup } from './0003-login_server_tables/LoginUserBackup'

View File

@ -1 +1 @@
export { User } from './0002-add_settings/User'
export { User } from './0019-replace_login_user_id_with_state_user_id/User'

View File

@ -1,8 +1,6 @@
import { Balance } from './Balance'
import { LoginElopageBuys } from './LoginElopageBuys'
import { LoginEmailOptIn } from './LoginEmailOptIn'
import { LoginUser } from './LoginUser'
import { LoginUserBackup } from './LoginUserBackup'
import { Migration } from './Migration'
import { ServerUser } from './ServerUser'
import { Transaction } from './Transaction'
@ -18,8 +16,6 @@ export const entities = [
Balance,
LoginElopageBuys,
LoginEmailOptIn,
LoginUser,
LoginUserBackup,
Migration,
ServerUser,
Transaction,

View File

@ -0,0 +1,150 @@
/* MIGRATION TO COMBINE LOGIN_USERS WITH STATE_USERS TABLE
*
* This migration combines the table `login_users` with
* the `state_users` table, where the later is the target.
*/
export async function upgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
// Drop column `group_id` since it contains uniform data which is not the same as the uniform data
// on login_users. Since we do not need this data anyway, we sjust throw it away.
await queryFn('ALTER TABLE `state_users` DROP COLUMN `group_id`;')
// Remove the unique constraint from the pubkey
await queryFn('ALTER TABLE `state_users` DROP INDEX `public_key`;')
// Allow NULL on the `state_users` pubkey like it is allowed on `login_users`
await queryFn('ALTER TABLE `state_users` MODIFY COLUMN `public_key` binary(32) DEFAULT NULL;')
// instead use a unique constraint for the email like on `login_users`
// therefore do not allow null on `email` anymore
await queryFn(
'ALTER TABLE `state_users` MODIFY COLUMN `email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL;',
)
await queryFn('ALTER TABLE `state_users` ADD CONSTRAINT `email` UNIQUE KEY (`email`);')
// Create `login_user_id` column - to store the login_users.id field to not break references.
await queryFn(
'ALTER TABLE `state_users` ADD COLUMN `login_user_id` int(10) unsigned DEFAULT NULL AFTER `id`;',
)
// Create missing data columns for the data stored in `login_users`
await queryFn(
"ALTER TABLE `state_users` ADD COLUMN `description` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT '' AFTER `disabled`;",
)
await queryFn(
'ALTER TABLE `state_users` ADD COLUMN `password` bigint(20) unsigned DEFAULT 0 AFTER `description`;',
)
await queryFn(
'ALTER TABLE `state_users` ADD COLUMN `privkey` binary(80) DEFAULT NULL AFTER `public_key`;',
)
await queryFn(
'ALTER TABLE `state_users` ADD COLUMN `email_hash` binary(32) DEFAULT NULL AFTER `password`;',
)
await queryFn(
'ALTER TABLE `state_users` ADD COLUMN `created` datetime NOT NULL DEFAULT current_timestamp() AFTER `email_hash`;',
)
await queryFn(
'ALTER TABLE `state_users` ADD COLUMN `email_checked` tinyint(4) NOT NULL DEFAULT 0 AFTER `created`;',
)
await queryFn(
'ALTER TABLE `state_users` ADD COLUMN `passphrase_shown` tinyint(4) NOT NULL DEFAULT 0 AFTER `email_checked`;',
)
await queryFn(
"ALTER TABLE `state_users` ADD COLUMN `language` varchar(4) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'de' AFTER `passphrase_shown`;",
)
await queryFn(
'ALTER TABLE `state_users` ADD COLUMN `publisher_id` int(11) DEFAULT 0 AFTER `language`;',
)
// Move data from `login_users` to the newly modified `state_users` table.
// The following rules for overwriting data applies:
// email is the matching criteria
// public_key is overwritten by `login_users`.`pubkey` (we have validated the passphrases here) (2 keys differ)
// first_name is more accurate on `state_users` and stays unchanged (1 users with different first_* & last_name)
// last_name is more accurate on `state_users` and stays unchanged (1 users with different first_* & last_name)
// username does not contain any relevant data, either NULL or '' and therefore we do not change anything here
// disabled does not differ, we can omit it
await queryFn(`
UPDATE state_users
LEFT JOIN login_users ON state_users.email = login_users.email
SET state_users.public_key = login_users.pubkey,
state_users.login_user_id = login_users.id,
state_users.description = login_users.description,
state_users.password = login_users.password,
state_users.privkey = login_users.privkey,
state_users.email_hash = login_users.email_hash,
state_users.created = login_users.created,
state_users.email_checked = login_users.email_checked,
state_users.passphrase_shown = login_users.passphrase_shown,
state_users.language = login_users.language,
state_users.publisher_id = login_users.publisher_id
;
`)
// Drop `login_users` table
await queryFn('DROP TABLE `login_users`;')
}
export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
await queryFn(`
CREATE TABLE \`login_users\` (
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
\`email\` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
\`first_name\` varchar(150) COLLATE utf8mb4_unicode_ci NOT NULL,
\`last_name\` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT '',
\`username\` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT '',
\`description\` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT '',
\`password\` bigint(20) unsigned DEFAULT 0,
\`pubkey\` binary(32) DEFAULT NULL,
\`privkey\` binary(80) DEFAULT NULL,
\`email_hash\` binary(32) DEFAULT NULL,
\`created\` datetime NOT NULL DEFAULT current_timestamp(),
\`email_checked\` tinyint(4) NOT NULL DEFAULT 0,
\`passphrase_shown\` tinyint(4) NOT NULL DEFAULT 0,
\`language\` varchar(4) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'de',
\`disabled\` tinyint(4) DEFAULT 0,
\`group_id\` int(10) unsigned DEFAULT 0,
\`publisher_id\` int(11) DEFAULT 0,
PRIMARY KEY (\`id\`),
UNIQUE KEY \`email\` (\`email\`)
) ENGINE=InnoDB AUTO_INCREMENT=2363 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`)
await queryFn(`
INSERT INTO login_users
( id, email, first_name, last_name, username,
description, password, pubkey, privkey, email_hash,
created, email_checked, passphrase_shown, language,
disabled, group_id, publisher_id )
( SELECT login_user_id AS id, email, first_name,
last_name, username, description, password,
public_key AS pubkey, privkey, email_hash,
created, email_checked, passphrase_shown,
language, disabled, '1' AS group_id,
publisher_id
FROM state_users )
;
`)
await queryFn('ALTER TABLE `state_users` DROP COLUMN `publisher_id`;')
await queryFn('ALTER TABLE `state_users` DROP COLUMN `language`;')
await queryFn('ALTER TABLE `state_users` DROP COLUMN `passphrase_shown`;')
await queryFn('ALTER TABLE `state_users` DROP COLUMN `email_checked`;')
await queryFn('ALTER TABLE `state_users` DROP COLUMN `created`;')
await queryFn('ALTER TABLE `state_users` DROP COLUMN `email_hash`;')
await queryFn('ALTER TABLE `state_users` DROP COLUMN `privkey`;')
await queryFn('ALTER TABLE `state_users` DROP COLUMN `password`;')
await queryFn('ALTER TABLE `state_users` DROP COLUMN `description`;')
await queryFn('ALTER TABLE `state_users` DROP COLUMN `login_user_id`;')
await queryFn('ALTER TABLE `state_users` DROP INDEX `email`;')
await queryFn(
'ALTER TABLE `state_users` MODIFY COLUMN `email` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL;',
)
// Note: if the public_key is NULL, we need to set a random key in order to meet the constraint
await queryFn(
'UPDATE `state_users` SET public_key = UNHEX(SHA1(RAND())) WHERE public_key IS NULL;',
)
await queryFn('ALTER TABLE `state_users` MODIFY COLUMN `public_key` binary(32) NOT NULL;')
await queryFn('ALTER TABLE `state_users` ADD CONSTRAINT `public_key` UNIQUE KEY (`public_key`);')
await queryFn(
'ALTER TABLE `state_users` ADD COLUMN `group_id` int(10) unsigned NOT NULL DEFAULT 0 AFTER index_id;',
)
}

View File

@ -0,0 +1,48 @@
/* MIGRATION TO COMBINE LOGIN_BACKUP_USERS TABLE WITH STATE_USERS
*
* This migration combines the table `login_user_backups` into
* the `state_users` table, where the later is the target.
*/
export async function upgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
// We only keep the passphrase, the mnemonic type is a constant,
// since every passphrase was converted to mnemonic type 2
await queryFn(
'ALTER TABLE `state_users` ADD COLUMN `passphrase` text DEFAULT NULL AFTER `publisher_id`;',
)
// Move data from `login_user_backups` to the newly modified `state_users` table.
await queryFn(`
UPDATE state_users
LEFT JOIN login_user_backups ON state_users.login_user_id = login_user_backups.user_id
SET state_users.passphrase = login_user_backups.passphrase
WHERE login_user_backups.passphrase IS NOT NULL
;
`)
// Drop `login_user_backups` table
await queryFn('DROP TABLE `login_user_backups`;')
}
export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
await queryFn(`
CREATE TABLE \`login_user_backups\` (
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
\`user_id\` int(11) NOT NULL,
\`passphrase\` text NOT NULL,
\`mnemonic_type\` int(11) DEFAULT -1,
PRIMARY KEY (\`id\`)
) ENGINE=InnoDB AUTO_INCREMENT=1862 DEFAULT CHARSET=utf8mb4;
`)
await queryFn(`
INSERT INTO login_user_backups
( user_id, passphrase, mnemonic_type )
( SELECT login_user_id AS user_id,
passphrase,
'2' as mnemonic_type
FROM state_users
WHERE passphrase IS NOT NULL )
;
`)
await queryFn('ALTER TABLE `state_users` DROP COLUMN `passphrase`;')
}

View File

@ -0,0 +1,57 @@
/* MIGRATION TO REPLACE LOGIN_USER_ID WITH STATE_USER_ID
*
* This migration replaces the `login_user_id with` the
* `state_user.id` and removes corresponding columns.
* The table affected is `login_email_opt_in`
*/
export async function upgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
// Delete email opt in codes which can not be linked to an user
await queryFn(`
DELETE FROM \`login_email_opt_in\`
WHERE user_id NOT IN
( SELECT login_user_id FROM state_users )
`)
// Replace user_id in `login_email_opt_in`
await queryFn(`
UPDATE login_email_opt_in
LEFT JOIN state_users ON state_users.login_user_id = login_email_opt_in.user_id
SET login_email_opt_in.user_id = state_users.id;
`)
// Remove the column `login_user_id` from `state_users`
await queryFn('ALTER TABLE `state_users` DROP COLUMN `login_user_id`;')
}
export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
await queryFn(
'ALTER TABLE `state_users` ADD COLUMN `login_user_id` int(10) unsigned DEFAULT NULL AFTER `id`;',
)
// Instead of generating new `login_user_id`'s we just use the id of state user.
// This way we do not need to alter the `user_id`'s of `login_email_opt_in` table
// at all when migrating down.
// This is possible since there are no old `login_user.id` referenced anymore and
// we can freely choose them
await queryFn('UPDATE `state_users` SET login_user_id = id')
// Insert back broken data, since we generate new `user_id`'s the old data might be now
// linked to existing accounts. To prevent that all invalid `user_id`'s are now negative.
// This renders them invalid while still keeping the original value
await queryFn(`
INSERT INTO login_email_opt_in
(id, user_id, verification_code, email_opt_in_type_id, created, resend_count, updated)
VALUES
('38','-41','7544440030630126261','0','2019-11-09 13:58:21','0','2020-07-17 13:58:29'),
('1262','-1185','2702555860489093775','3','2020-10-17 00:57:29','0','2020-10-17 00:57:29'),
('1431','-1319','9846213635571107141','3','2020-12-29 00:07:32','0','2020-12-29 00:07:32'),
('1548','-1185','1009203004512986277','1','2021-01-26 01:07:29','0','2021-01-26 01:07:29'),
('1549','-1185','2144334450300724903','1','2021-01-26 01:07:32','0','2021-01-26 01:07:32'),
('1683','-1525','14803676216828342915','3','2021-03-10 08:39:39','0','2021-03-10 08:39:39'),
('1899','-1663','16616172057370363741','3','2021-04-12 14:49:18','0','2021-04-12 14:49:18'),
('2168','-1865','13129474130315401087','3','2021-07-08 11:58:54','0','2021-07-08 11:58:54'),
('2274','-1935','5775135935896874129','3','2021-08-24 11:40:04','0','2021-08-24 11:40:04'),
('2318','-1967','5713731625139303791','3','2021-09-06 21:38:30','0','2021-09-06 21:38:30'),
('2762','-2263','6997866521554931275','1','2021-12-25 11:44:30','0','2021-12-25 11:44:30');
`)
}

View File

@ -1,18 +0,0 @@
import Faker from 'faker'
import { define } from 'typeorm-seeding'
import { LoginUserBackup } from '../../entity/LoginUserBackup'
import { LoginUserBackupContext } from '../interface/UserContext'
define(LoginUserBackup, (faker: typeof Faker, context?: LoginUserBackupContext) => {
if (!context || !context.userId) {
throw new Error('LoginUserBackup: No userId present!')
}
const userBackup = new LoginUserBackup()
// TODO: Get the real passphrase
userBackup.passphrase = context.passphrase ? context.passphrase : faker.random.words(24)
userBackup.mnemonicType = context.mnemonicType ? context.mnemonicType : 2
userBackup.userId = context.userId
return userBackup
})

View File

@ -1,30 +0,0 @@
import Faker from 'faker'
import { define } from 'typeorm-seeding'
import { LoginUser } from '../../entity/LoginUser'
import { randomBytes } from 'crypto'
import { LoginUserContext } from '../interface/UserContext'
define(LoginUser, (faker: typeof Faker, context?: LoginUserContext) => {
if (!context) context = {}
const user = new LoginUser()
user.email = context.email ? context.email : faker.internet.email()
user.firstName = context.firstName ? context.firstName : faker.name.firstName()
user.lastName = context.lastName ? context.lastName : faker.name.lastName()
user.username = context.username ? context.username : faker.internet.userName()
user.description = context.description ? context.description : faker.random.words(4)
// TODO Create real password and keys/hash
user.password = context.password ? context.password : BigInt(0)
user.pubKey = context.pubKey ? context.pubKey : randomBytes(32)
user.privKey = context.privKey ? context.privKey : randomBytes(80)
user.emailHash = context.emailHash ? context.emailHash : randomBytes(32)
user.createdAt = context.createdAt ? context.createdAt : faker.date.recent()
user.emailChecked = context.emailChecked === undefined ? false : context.emailChecked
user.passphraseShown = context.passphraseShown ? context.passphraseShown : false
user.language = context.language ? context.language : 'en'
user.disabled = context.disabled ? context.disabled : false
user.groupId = context.groupId ? context.groupId : 1
user.publisherId = context.publisherId ? context.publisherId : 0
return user
})

View File

@ -1,21 +1,31 @@
import Faker from 'faker'
import { define } from 'typeorm-seeding'
import { User } from '../../entity/User'
import { randomBytes } from 'crypto'
import { randomBytes, randomInt } from 'crypto'
import { UserContext } from '../interface/UserContext'
define(User, (faker: typeof Faker, context?: UserContext) => {
if (!context) context = {}
const user = new User()
user.pubkey = context.pubkey ? context.pubkey : randomBytes(32)
user.pubKey = context.pubKey ? context.pubKey : randomBytes(32)
user.email = context.email ? context.email : faker.internet.email()
user.firstName = context.firstName ? context.firstName : faker.name.firstName()
user.lastName = context.lastName ? context.lastName : faker.name.lastName()
user.username = context.username ? context.username : faker.internet.userName()
user.disabled = context.disabled ? context.disabled : false
user.groupId = 0
user.indexId = 0
user.description = context.description ? context.description : faker.random.words(4)
// TODO Create real password and keys/hash
user.password = context.password ? context.password : BigInt(0)
user.privKey = context.privKey ? context.privKey : randomBytes(80)
user.emailHash = context.emailHash ? context.emailHash : randomBytes(32)
user.createdAt = context.createdAt ? context.createdAt : faker.date.recent()
user.emailChecked = context.emailChecked === undefined ? false : context.emailChecked
user.passphraseShown = context.passphraseShown ? context.passphraseShown : false
user.language = context.language ? context.language : 'en'
user.publisherId = context.publisherId ? context.publisherId : 0
user.passphrase = context.passphrase ? context.passphrase : faker.random.words(24)
return user
})

View File

@ -1,35 +1,20 @@
export interface UserContext {
pubkey?: Buffer
pubKey?: Buffer
email?: string
firstName?: string
lastName?: string
username?: string
disabled?: boolean
}
export interface LoginUserContext {
email?: string
firstName?: string
lastName?: string
username?: string
description?: string
password?: BigInt
pubKey?: Buffer
privKey?: Buffer
emailHash?: Buffer
createdAt?: Date
emailChecked?: boolean
passphraseShown?: boolean
language?: string
disabled?: boolean
groupId?: number
publisherId?: number
}
export interface LoginUserBackupContext {
userId?: number
passphrase?: string
mnemonicType?: number
}
export interface ServerUserContext {
@ -42,8 +27,3 @@ export interface ServerUserContext {
created?: Date
modified?: Date
}
export interface LoginUserRolesContext {
userId?: number
roleId?: number
}

View File

@ -1,5 +1,5 @@
export interface UserInterface {
// from login user (contains state user)
// from user
email?: string
firstName?: string
lastName?: string
@ -16,9 +16,7 @@ export interface UserInterface {
disabled?: boolean
groupId?: number
publisherId?: number
// from login user backup
passphrase?: string
mnemonicType?: number
// from server user
serverUserPassword?: string
role?: string

View File

@ -1,10 +1,4 @@
import {
UserContext,
LoginUserContext,
LoginUserBackupContext,
ServerUserContext,
LoginUserRolesContext,
} from '../../interface/UserContext'
import { UserContext, ServerUserContext } from '../../interface/UserContext'
import {
BalanceContext,
TransactionContext,
@ -13,8 +7,6 @@ import {
} from '../../interface/TransactionContext'
import { UserInterface } from '../../interface/UserInterface'
import { User } from '../../../entity/User'
import { LoginUser } from '../../../entity/LoginUser'
import { LoginUserBackup } from '../../../entity/LoginUserBackup'
import { ServerUser } from '../../../entity/ServerUser'
import { Balance } from '../../../entity/Balance'
import { Transaction } from '../../../entity/Transaction'
@ -24,9 +16,6 @@ import { Factory } from 'typeorm-seeding'
export const userSeeder = async (factory: Factory, userData: UserInterface): Promise<void> => {
const user = await factory(User)(createUserContext(userData)).create()
if (!userData.email) userData.email = user.email
const loginUser = await factory(LoginUser)(createLoginUserContext(userData)).create()
await factory(LoginUserBackup)(createLoginUserBackupContext(userData, loginUser)).create()
if (userData.isAdmin) {
await factory(ServerUser)(createServerUserContext(userData)).create()
@ -49,47 +38,24 @@ export const userSeeder = async (factory: Factory, userData: UserInterface): Pro
const createUserContext = (context: UserInterface): UserContext => {
return {
pubkey: context.pubKey,
pubKey: context.pubKey,
email: context.email,
firstName: context.firstName,
lastName: context.lastName,
username: context.username,
disabled: context.disabled,
}
}
const createLoginUserContext = (context: UserInterface): LoginUserContext => {
return {
email: context.email,
firstName: context.firstName,
lastName: context.lastName,
username: context.username,
description: context.description,
password: context.password,
pubKey: context.pubKey,
privKey: context.privKey,
emailHash: context.emailHash,
createdAt: context.createdAt,
emailChecked: context.emailChecked,
passphraseShown: context.passphraseShown,
language: context.language,
disabled: context.disabled,
groupId: context.groupId,
publisherId: context.publisherId,
}
}
const createLoginUserBackupContext = (
context: UserInterface,
loginUser: LoginUser,
): LoginUserBackupContext => {
return {
passphrase: context.passphrase,
mnemonicType: context.mnemonicType,
userId: loginUser.id,
}
}
const createServerUserContext = (context: UserInterface): ServerUserContext => {
return {
role: context.role,
@ -103,13 +69,6 @@ const createServerUserContext = (context: UserInterface): ServerUserContext => {
}
}
const createLoginUserRolesContext = (loginUser: LoginUser): LoginUserRolesContext => {
return {
userId: loginUser.id,
roleId: 1,
}
}
const createBalanceContext = (context: UserInterface, user: User): BalanceContext => {
return {
modified: context.balanceModified,