Merge pull request #1126 from gradido/backend_rights

backend_rights
This commit is contained in:
Ulf Gebhardt 2021-11-25 18:53:25 +01:00 committed by GitHub
commit d894358426
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 243 additions and 158 deletions

View File

@ -0,0 +1,5 @@
import { JwtPayload } from 'jsonwebtoken'
export interface CustomJwtPayload extends JwtPayload {
pubKey: Buffer
}

View File

@ -0,0 +1,13 @@
import { RIGHTS } from './RIGHTS'
export const INALIENABLE_RIGHTS = [
RIGHTS.LOGIN,
RIGHTS.GET_COMMUNITY_INFO,
RIGHTS.COMMUNITIES,
RIGHTS.LOGIN_VIA_EMAIL_VERIFICATION_CODE,
RIGHTS.CREATE_USER,
RIGHTS.SEND_RESET_PASSWORD_EMAIL,
RIGHTS.RESET_PASSWORD,
RIGHTS.CHECK_USERNAME,
RIGHTS.CHECK_EMAIL,
]

19
backend/src/auth/JWT.ts Normal file
View File

@ -0,0 +1,19 @@
import jwt from 'jsonwebtoken'
import CONFIG from '../config/'
import { CustomJwtPayload } from './CustomJwtPayload'
export const decode = (token: string): CustomJwtPayload | null => {
if (!token) throw new Error('401 Unauthorized')
try {
return <CustomJwtPayload>jwt.verify(token, CONFIG.JWT_SECRET)
} catch (err) {
return null
}
}
export const encode = (pubKey: Buffer): string => {
const token = jwt.sign({ pubKey }, CONFIG.JWT_SECRET, {
expiresIn: CONFIG.JWT_EXPIRES_IN,
})
return token
}

View File

@ -0,0 +1,26 @@
export enum RIGHTS {
LOGIN = 'LOGIN',
VERIFY_LOGIN = 'VERIFY_LOGIN',
BALANCE = 'BALANCE',
GET_COMMUNITY_INFO = 'GET_COMMUNITY_INFO',
COMMUNITIES = 'COMMUNITIES',
LIST_GDT_ENTRIES = 'LIST_GDT_ENTRIES',
EXIST_PID = 'EXIST_PID',
GET_KLICKTIPP_USER = 'GET_KLICKTIPP_USER',
GET_KLICKTIPP_TAG_MAP = 'GET_KLICKTIPP_TAG_MAP',
UNSUBSCRIBE_NEWSLETTER = 'UNSUBSCRIBE_NEWSLETTER',
SUBSCRIBE_NEWSLETTER = 'SUBSCRIBE_NEWSLETTER',
TRANSACTION_LIST = 'TRANSACTION_LIST',
SEND_COINS = 'SEND_COINS',
LOGIN_VIA_EMAIL_VERIFICATION_CODE = 'LOGIN_VIA_EMAIL_VERIFICATION_CODE',
LOGOUT = 'LOGOUT',
CREATE_USER = 'CREATE_USER',
SEND_RESET_PASSWORD_EMAIL = 'SEND_RESET_PASSWORD_EMAIL',
RESET_PASSWORD = 'RESET_PASSWORD',
UPDATE_USER_INFOS = 'UPDATE_USER_INFOS',
CHECK_USERNAME = 'CHECK_USERNAME',
CHECK_EMAIL = 'CHECK_EMAIL',
HAS_ELOPAGE = 'HAS_ELOPAGE',
// Admin
SEARCH_USERS = 'SEARCH_USERS',
}

25
backend/src/auth/ROLES.ts Normal file
View File

@ -0,0 +1,25 @@
import { INALIENABLE_RIGHTS } from './INALIENABLE_RIGHTS'
import { RIGHTS } from './RIGHTS'
import { Role } from './Role'
export const ROLE_UNAUTHORIZED = new Role('unauthorized', INALIENABLE_RIGHTS)
export const ROLE_USER = new Role('user', [
...INALIENABLE_RIGHTS,
RIGHTS.VERIFY_LOGIN,
RIGHTS.BALANCE,
RIGHTS.LIST_GDT_ENTRIES,
RIGHTS.EXIST_PID,
RIGHTS.GET_KLICKTIPP_USER,
RIGHTS.GET_KLICKTIPP_TAG_MAP,
RIGHTS.UNSUBSCRIBE_NEWSLETTER,
RIGHTS.SUBSCRIBE_NEWSLETTER,
RIGHTS.TRANSACTION_LIST,
RIGHTS.SEND_COINS,
RIGHTS.LOGOUT,
RIGHTS.UPDATE_USER_INFOS,
RIGHTS.HAS_ELOPAGE,
])
export const ROLE_ADMIN = new Role('admin', Object.values(RIGHTS)) // all rights
// TODO from database
export const ROLES = [ROLE_UNAUTHORIZED, ROLE_USER, ROLE_ADMIN]

15
backend/src/auth/Role.ts Normal file
View File

@ -0,0 +1,15 @@
import { RIGHTS } from './RIGHTS'
export class Role {
id: string
rights: RIGHTS[]
constructor(id: string, rights: RIGHTS[]) {
this.id = id
this.rights = rights
}
hasRight = (right: RIGHTS): boolean => {
return this.rights.includes(right)
}
}

View File

@ -11,6 +11,9 @@ export default class CreateUserArgs {
@Field(() => String)
lastName: string
@Field(() => String)
password: string
@Field(() => String)
language?: string // Will default to DEFAULT_LANGUAGE

View File

@ -2,19 +2,44 @@
import { AuthChecker } from 'type-graphql'
import decode from '../../jwt/decode'
import encode from '../../jwt/encode'
import { decode, encode } from '../../auth/JWT'
import { ROLE_UNAUTHORIZED, ROLE_USER, ROLE_ADMIN } from '../../auth/ROLES'
import { RIGHTS } from '../../auth/RIGHTS'
import { ServerUserRepository } from '../../typeorm/repository/ServerUser'
import { getCustomRepository } from 'typeorm'
import { UserRepository } from '../../typeorm/repository/User'
const isAuthorized: AuthChecker<any> = async (
{ /* root, args, */ context /*, info */ } /*, roles */,
) => {
const isAuthorized: AuthChecker<any> = async ({ context }, rights) => {
context.role = ROLE_UNAUTHORIZED // unauthorized user
// Do we have a token?
if (context.token) {
const decoded = decode(context.token)
if (!decoded) {
// we always throw on an invalid token
throw new Error('403.13 - Client certificate revoked')
}
// Set context pubKey
context.pubKey = Buffer.from(decoded.pubKey).toString('hex')
// set new header token
// TODO - load from database dynamically & admin - maybe encode this in the token to prevent many database requests
// TODO this implementation is bullshit - two database queries cause our user identifiers are not aligned and vary between email, id and pubKey
const userRepository = await getCustomRepository(UserRepository)
const user = await userRepository.findByPubkeyHex(context.pubKey)
const serverUserRepository = await getCustomRepository(ServerUserRepository)
const countServerUsers = await serverUserRepository.count({ email: user.email })
context.role = countServerUsers > 0 ? ROLE_ADMIN : ROLE_USER
context.setHeaders.push({ key: 'token', value: encode(decoded.pubKey) })
return true
}
throw new Error('401 Unauthorized')
// check for correct rights
const missingRights = (<RIGHTS[]>rights).filter((right) => !context.role.hasRight(right))
if (missingRights.length !== 0) {
throw new Error('401 Unauthorized')
}
return true
}
export default isAuthorized

View File

@ -1,10 +1,12 @@
import { Resolver, Query, Arg } from 'type-graphql'
import { Resolver, Query, Arg, Authorized } from 'type-graphql'
import { getCustomRepository } from 'typeorm'
import { UserAdmin } from '../model/UserAdmin'
import { LoginUserRepository } from '../../typeorm/repository/LoginUser'
import { RIGHTS } from '../../auth/RIGHTS'
@Resolver()
export class AdminResolver {
@Authorized([RIGHTS.SEARCH_USERS])
@Query(() => [UserAdmin])
async searchUsers(@Arg('searchText') searchText: string): Promise<UserAdmin[]> {
const loginUserRepository = getCustomRepository(LoginUserRepository)

View File

@ -8,10 +8,11 @@ import { BalanceRepository } from '../../typeorm/repository/Balance'
import { UserRepository } from '../../typeorm/repository/User'
import { calculateDecay } from '../../util/decay'
import { roundFloorFrom4 } from '../../util/round'
import { RIGHTS } from '../../auth/RIGHTS'
@Resolver()
export class BalanceResolver {
@Authorized()
@Authorized([RIGHTS.BALANCE])
@Query(() => Balance)
async balance(@Ctx() context: any): Promise<Balance> {
// load user and balance

View File

@ -1,12 +1,14 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Resolver, Query } from 'type-graphql'
import { Resolver, Query, Authorized } from 'type-graphql'
import { RIGHTS } from '../../auth/RIGHTS'
import CONFIG from '../../config'
import { Community } from '../model/Community'
@Resolver()
export class CommunityResolver {
@Authorized([RIGHTS.GET_COMMUNITY_INFO])
@Query(() => Community)
async getCommunityInfo(): Promise<Community> {
return new Community({
@ -17,6 +19,7 @@ export class CommunityResolver {
})
}
@Authorized([RIGHTS.COMMUNITIES])
@Query(() => [Community])
async communities(): Promise<Community[]> {
if (CONFIG.PRODUCTION)

View File

@ -9,10 +9,11 @@ import Paginated from '../arg/Paginated'
import { apiGet } from '../../apis/HttpRequest'
import { UserRepository } from '../../typeorm/repository/User'
import { Order } from '../enum/Order'
import { RIGHTS } from '../../auth/RIGHTS'
@Resolver()
export class GdtResolver {
@Authorized()
@Authorized([RIGHTS.LIST_GDT_ENTRIES])
@Query(() => GdtEntryList)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
async listGDTEntries(
@ -33,7 +34,7 @@ export class GdtResolver {
return new GdtEntryList(resultGDT.data)
}
@Authorized()
@Authorized([RIGHTS.EXIST_PID])
@Query(() => Number)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
async existPid(@Arg('pid') pid: number): Promise<number> {

View File

@ -8,29 +8,30 @@ import {
unsubscribe,
signIn,
} from '../../apis/KlicktippController'
import { RIGHTS } from '../../auth/RIGHTS'
import SubscribeNewsletterArgs from '../arg/SubscribeNewsletterArgs'
@Resolver()
export class KlicktippResolver {
@Authorized()
@Authorized([RIGHTS.GET_KLICKTIPP_USER])
@Query(() => String)
async getKlicktippUser(@Arg('email') email: string): Promise<string> {
return await getKlickTippUser(email)
}
@Authorized()
@Authorized([RIGHTS.GET_KLICKTIPP_TAG_MAP])
@Query(() => String)
async getKlicktippTagMap(): Promise<string> {
return await getKlicktippTagMap()
}
@Authorized()
@Authorized([RIGHTS.UNSUBSCRIBE_NEWSLETTER])
@Mutation(() => Boolean)
async unsubscribeNewsletter(@Arg('email') email: string): Promise<boolean> {
return await unsubscribe(email)
}
@Authorized()
@Authorized([RIGHTS.SUBSCRIBE_NEWSLETTER])
@Mutation(() => Boolean)
async subscribeNewsletter(
@Args() { email, language }: SubscribeNewsletterArgs,

View File

@ -34,6 +34,7 @@ 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'
/*
# Test
@ -465,7 +466,7 @@ async function getPublicKey(email: string): Promise<string | null> {
@Resolver()
export class TransactionResolver {
@Authorized()
@Authorized([RIGHTS.TRANSACTION_LIST])
@Query(() => TransactionList)
async transactionList(
@Args() { currentPage = 1, pageSize = 25, order = Order.DESC }: Paginated,
@ -499,7 +500,7 @@ export class TransactionResolver {
return transactions
}
@Authorized()
@Authorized([RIGHTS.SEND_COINS])
@Mutation(() => String)
async sendCoins(
@Args() { email, amount, memo }: TransactionSendArgs,

View File

@ -9,7 +9,7 @@ import { LoginViaVerificationCode } from '../model/LoginViaVerificationCode'
import { SendPasswordResetEmailResponse } from '../model/SendPasswordResetEmailResponse'
import { User } from '../model/User'
import { User as DbUser } from '@entity/User'
import encode from '../../jwt/encode'
import { encode } from '../../auth/JWT'
import ChangePasswordArgs from '../arg/ChangePasswordArgs'
import CheckUsernameArgs from '../arg/CheckUsernameArgs'
import CreateUserArgs from '../arg/CreateUserArgs'
@ -30,6 +30,9 @@ import { LoginUserBackup } from '@entity/LoginUserBackup'
import { LoginEmailOptIn } from '@entity/LoginEmailOptIn'
import { sendEMail } from '../../util/sendEMail'
import { LoginElopageBuysRepository } from '../../typeorm/repository/LoginElopageBuys'
import { RIGHTS } from '../../auth/RIGHTS'
import { ServerUserRepository } from '../../typeorm/repository/ServerUser'
import { ROLE_ADMIN } from '../../auth/ROLES'
// eslint-disable-next-line @typescript-eslint/no-var-requires
const sodium = require('sodium-native')
@ -194,37 +197,7 @@ const SecretKeyCryptographyDecrypt = (encryptedMessage: Buffer, encryptionKey: B
@Resolver()
export class UserResolver {
/*
@Authorized()
@Query(() => User)
async verifyLogin(@Ctx() context: any): Promise<User> {
const loginUserRepository = getCustomRepository(LoginUserRepository)
loginUser = loginUserRepository.findByPubkeyHex()
const user = new User(result.data.user)
this.email = json.email
this.firstName = json.first_name
this.lastName = json.last_name
this.username = json.username
this.description = json.description
this.pubkey = json.public_hex
this.language = json.language
this.publisherId = json.publisher_id
this.isAdmin = json.isAdmin
const userSettingRepository = getCustomRepository(UserSettingRepository)
const coinanimation = await userSettingRepository
.readBoolean(userEntity.id, Setting.COIN_ANIMATION)
.catch((error) => {
throw new Error(error)
})
user.coinanimation = coinanimation
user.isAdmin = true // TODO implement
return user
}
*/
@Authorized()
@Authorized([RIGHTS.VERIFY_LOGIN])
@Query(() => User)
@UseMiddleware(klicktippNewsletterStateMiddleware)
async verifyLogin(@Ctx() context: any): Promise<User> {
@ -253,10 +226,12 @@ export class UserResolver {
throw new Error(error)
})
user.coinanimation = coinanimation
user.isAdmin = true // TODO implement
user.isAdmin = context.role === ROLE_ADMIN
return user
}
@Authorized([RIGHTS.LOGIN])
@Query(() => User)
@UseMiddleware(klicktippNewsletterStateMiddleware)
async login(
@ -329,7 +304,11 @@ export class UserResolver {
throw new Error(error)
})
user.coinanimation = coinanimation
user.isAdmin = true // TODO implement
// context.role is not set to the actual role yet on login
const serverUserRepository = await getCustomRepository(ServerUserRepository)
const countServerUsers = await serverUserRepository.count({ email: user.email })
user.isAdmin = countServerUsers > 0
context.setHeaders.push({
key: 'token',
@ -339,6 +318,7 @@ export class UserResolver {
return user
}
@Authorized([RIGHTS.LOGIN_VIA_EMAIL_VERIFICATION_CODE])
@Query(() => LoginViaVerificationCode)
async loginViaEmailVerificationCode(
@Arg('optin') optin: string,
@ -354,7 +334,7 @@ export class UserResolver {
return new LoginViaVerificationCode(result.data)
}
@Authorized()
@Authorized([RIGHTS.LOGOUT])
@Query(() => String)
async logout(): Promise<boolean> {
// TODO: We dont need this anymore, but might need this in the future in oder to invalidate a valid JWT-Token.
@ -365,9 +345,10 @@ export class UserResolver {
return true
}
@Authorized([RIGHTS.CREATE_USER])
@Mutation(() => String)
async createUser(
@Args() { email, firstName, lastName, language, publisherId }: CreateUserArgs,
@Args() { email, firstName, lastName, password, language, publisherId }: CreateUserArgs,
): Promise<string> {
// TODO: wrong default value (should be null), how does graphql work here? Is it an required field?
// default int publisher_id = 0;
@ -377,13 +358,12 @@ export class UserResolver {
language = DEFAULT_LANGUAGE
}
// TODO: Register process
// Validate Password
// if (!isPassword(password)) {
// throw new Error(
// 'Please enter a valid password with at least 8 characters, upper and lower case letters, at least one number and one special character!',
// )
// }
if (!isPassword(password)) {
throw new Error(
'Please enter a valid password with at least 8 characters, upper and lower case letters, at least one number and one special character!',
)
}
// Validate username
// TODO: never true
@ -401,13 +381,11 @@ export class UserResolver {
throw new Error(`User already exists.`)
}
// TODO: Register process
// const passphrase = PassphraseGenerate()
// const keyPair = KeyPairEd25519Create(passphrase) // return pub, priv Key
// const passwordHash = SecretKeyCryptographyCreateKey(email, password) // return short and long hash
// const encryptedPrivkey = SecretKeyCryptographyEncrypt(keyPair[1], passwordHash[1])
const passphrase = PassphraseGenerate()
const keyPair = KeyPairEd25519Create(passphrase) // return pub, priv Key
const passwordHash = SecretKeyCryptographyCreateKey(email, password) // return short and long hash
const emailHash = getEmailHash(email)
const encryptedPrivkey = SecretKeyCryptographyEncrypt(keyPair[1], passwordHash[1])
// Table: login_users
const loginUser = new LoginUser()
@ -416,15 +394,13 @@ export class UserResolver {
loginUser.lastName = lastName
loginUser.username = username
loginUser.description = ''
// TODO: Register process
// loginUser.password = passwordHash[0].readBigUInt64LE() // using the shorthash
loginUser.password = passwordHash[0].readBigUInt64LE() // using the shorthash
loginUser.emailHash = emailHash
loginUser.language = language
loginUser.groupId = 1
loginUser.publisherId = publisherId
// TODO: Register process
// loginUser.pubKey = keyPair[0]
// loginUser.privKey = encryptedPrivkey
loginUser.pubKey = keyPair[0]
loginUser.privKey = encryptedPrivkey
const queryRunner = getConnection().createQueryRunner()
await queryRunner.connect()
@ -436,24 +412,21 @@ export class UserResolver {
throw new Error('insert user failed')
})
// TODO: Register process
// 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;
const loginUserBackup = new LoginUserBackup()
loginUserBackup.userId = loginUserId
loginUserBackup.passphrase = passphrase.join(' ') + ' ' // login server saves trailing space
loginUserBackup.mnemonicType = 2 // ServerConfig::MNEMONIC_BIP0039_SORTED_ORDER;
// TODO: Register process
// 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')
// })
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()
// TODO: Register process
// dbUser.pubkey = keyPair[0]
dbUser.pubkey = keyPair[0]
dbUser.email = email
dbUser.firstName = firstName
dbUser.lastName = lastName
@ -513,6 +486,7 @@ export class UserResolver {
return 'success'
}
@Authorized([RIGHTS.SEND_RESET_PASSWORD_EMAIL])
@Query(() => SendPasswordResetEmailResponse)
async sendResetPasswordEmail(
@Arg('email') email: string,
@ -529,6 +503,7 @@ export class UserResolver {
return new SendPasswordResetEmailResponse(response.data)
}
@Authorized([RIGHTS.RESET_PASSWORD])
@Mutation(() => String)
async resetPassword(
@Args()
@ -546,7 +521,7 @@ export class UserResolver {
return 'success'
}
@Authorized()
@Authorized([RIGHTS.UPDATE_USER_INFOS])
@Mutation(() => Boolean)
async updateUserInfos(
@Args()
@ -655,6 +630,7 @@ export class UserResolver {
return true
}
@Authorized([RIGHTS.CHECK_USERNAME])
@Query(() => Boolean)
async checkUsername(@Args() { username }: CheckUsernameArgs): Promise<boolean> {
// Username empty?
@ -678,6 +654,7 @@ export class UserResolver {
return true
}
@Authorized([RIGHTS.CHECK_EMAIL])
@Query(() => CheckEmailResponse)
@UseMiddleware(klicktippRegistrationMiddleware)
async checkEmail(@Arg('optin') optin: string): Promise<CheckEmailResponse> {
@ -690,7 +667,7 @@ export class UserResolver {
return new CheckEmailResponse(result.data)
}
@Authorized()
@Authorized([RIGHTS.HAS_ELOPAGE])
@Query(() => Boolean)
async hasElopage(@Ctx() context: any): Promise<boolean> {
const userRepository = getCustomRepository(UserRepository)

View File

@ -1,26 +0,0 @@
import jwt, { JwtPayload } from 'jsonwebtoken'
import CONFIG from '../config/'
interface CustomJwtPayload extends JwtPayload {
pubKey: Buffer
}
type DecodedJwt = {
token: string
pubKey: Buffer
}
export default (token: string): DecodedJwt => {
if (!token) throw new Error('401 Unauthorized')
let pubKey = null
try {
const decoded = <CustomJwtPayload>jwt.verify(token, CONFIG.JWT_SECRET)
pubKey = decoded.pubKey
return {
token,
pubKey,
}
} catch (err) {
throw new Error('403.13 - Client certificate revoked')
}
}

View File

@ -1,13 +0,0 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import jwt from 'jsonwebtoken'
import CONFIG from '../config/'
// Generate an Access Token
export default function encode(pubKey: Buffer): string {
const token = jwt.sign({ pubKey }, CONFIG.JWT_SECRET, {
expiresIn: CONFIG.JWT_EXPIRES_IN,
})
return token
}

View File

@ -0,0 +1,5 @@
import { EntityRepository, Repository } from 'typeorm'
import { ServerUser } from '@entity/ServerUser'
@EntityRepository(ServerUser)
export class ServerUserRepository extends Repository<ServerUser> {}

View File

@ -27,7 +27,6 @@
import { LoginElopageBuys } from '@entity/LoginElopageBuys'
import { LoginUser } from '@entity/LoginUser'
import { randomBytes } from 'crypto'
import { UserResolver } from '../graphql/resolver/UserResolver'
export const elopageWebhook = async (req: any, res: any): Promise<void> => {
@ -145,6 +144,7 @@ export const elopageWebhook = async (req: any, res: any): Promise<void> => {
firstName,
lastName,
publisherId: loginElopgaeBuy.publisherId,
password: '123', // TODO remove
})
} catch (error) {
// eslint-disable-next-line no-console

View File

@ -0,0 +1,31 @@
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm'
@Entity('server_users')
export class ServerUser extends BaseEntity {
@PrimaryGeneratedColumn('increment', { unsigned: true })
id: number
@Column({ length: 50 })
username: string
@Column({ type: 'bigint', unsigned: true })
password: BigInt
@Column({ length: 50, unique: true })
email: string
@Column({ length: 20, default: 'admin' })
role: string
@Column({ default: 0 })
activated: number
@Column({ name: 'last_login', default: null, nullable: true })
lastLogin: Date
@Column({ name: 'created', default: () => 'CURRENT_TIMESTAMP' })
created: Date
@Column({ name: 'created', default: () => 'CURRENT_TIMESTAMP' })
modified: Date
}

View File

@ -1,31 +1 @@
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm'
@Entity('server_users')
export class ServerUser extends BaseEntity {
@PrimaryGeneratedColumn('increment', { unsigned: true })
id: number
@Column({ length: 50 })
username: string
@Column({ type: 'bigint', unsigned: true })
password: BigInt
@Column({ length: 50, unique: true })
email: string
@Column({ length: 20, default: 'admin' })
role: string
@Column({ default: 0 })
activated: number
@Column({ name: 'last_login', default: null, nullable: true })
lastLogin: Date
@Column({ name: 'created', default: () => 'CURRENT_TIMESTAMP' })
created: Date
@Column({ name: 'created', default: () => 'CURRENT_TIMESTAMP' })
modified: Date
}
export { ServerUser } from './0001-init_db/ServerUser'

View File

@ -4,6 +4,7 @@ import { LoginEmailOptIn } from './LoginEmailOptIn'
import { LoginUser } from './LoginUser'
import { LoginUserBackup } from './LoginUserBackup'
import { Migration } from './Migration'
import { ServerUser } from './ServerUser'
import { Transaction } from './Transaction'
import { TransactionCreation } from './TransactionCreation'
import { TransactionSendCoin } from './TransactionSendCoin'
@ -18,6 +19,7 @@ export const entities = [
LoginUser,
LoginUserBackup,
Migration,
ServerUser,
Transaction,
TransactionCreation,
TransactionSendCoin,

View File

@ -14,7 +14,6 @@ const addNavigationGuards = (router, store, apollo) => {
// store token on authenticate
router.beforeEach(async (to, from, next) => {
if (to.path === '/authenticate' && to.query.token) {
// TODO verify user in order to get user data
store.commit('token', to.query.token)
const result = await apollo.query({
query: verifyLogin,