diff --git a/backend/src/auth/CustomJwtPayload.ts b/backend/src/auth/CustomJwtPayload.ts new file mode 100644 index 000000000..2b52c3cea --- /dev/null +++ b/backend/src/auth/CustomJwtPayload.ts @@ -0,0 +1,5 @@ +import { JwtPayload } from 'jsonwebtoken' + +export interface CustomJwtPayload extends JwtPayload { + pubKey: Buffer +} diff --git a/backend/src/auth/INALIENABLE_RIGHTS.ts b/backend/src/auth/INALIENABLE_RIGHTS.ts new file mode 100644 index 000000000..eb367d643 --- /dev/null +++ b/backend/src/auth/INALIENABLE_RIGHTS.ts @@ -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, +] diff --git a/backend/src/auth/JWT.ts b/backend/src/auth/JWT.ts new file mode 100644 index 000000000..06c6507b8 --- /dev/null +++ b/backend/src/auth/JWT.ts @@ -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 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 +} diff --git a/backend/src/auth/RIGHTS.ts b/backend/src/auth/RIGHTS.ts new file mode 100644 index 000000000..fa750239e --- /dev/null +++ b/backend/src/auth/RIGHTS.ts @@ -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', +} diff --git a/backend/src/auth/ROLES.ts b/backend/src/auth/ROLES.ts new file mode 100644 index 000000000..ada6a2cef --- /dev/null +++ b/backend/src/auth/ROLES.ts @@ -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] diff --git a/backend/src/auth/Role.ts b/backend/src/auth/Role.ts new file mode 100644 index 000000000..a2f13ec20 --- /dev/null +++ b/backend/src/auth/Role.ts @@ -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) + } +} diff --git a/backend/src/graphql/arg/CreateUserArgs.ts b/backend/src/graphql/arg/CreateUserArgs.ts index 0d63e76bb..3a8914200 100644 --- a/backend/src/graphql/arg/CreateUserArgs.ts +++ b/backend/src/graphql/arg/CreateUserArgs.ts @@ -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 diff --git a/backend/src/graphql/directive/isAuthorized.ts b/backend/src/graphql/directive/isAuthorized.ts index 6245ef8ba..19cd7bcdb 100644 --- a/backend/src/graphql/directive/isAuthorized.ts +++ b/backend/src/graphql/directive/isAuthorized.ts @@ -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 = async ( - { /* root, args, */ context /*, info */ } /*, roles */, -) => { +const isAuthorized: AuthChecker = 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).filter((right) => !context.role.hasRight(right)) + if (missingRights.length !== 0) { + throw new Error('401 Unauthorized') + } + + return true } export default isAuthorized diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index 9af50faad..4ae259087 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -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 { const loginUserRepository = getCustomRepository(LoginUserRepository) diff --git a/backend/src/graphql/resolver/BalanceResolver.ts b/backend/src/graphql/resolver/BalanceResolver.ts index e067b4d68..e368c4dc2 100644 --- a/backend/src/graphql/resolver/BalanceResolver.ts +++ b/backend/src/graphql/resolver/BalanceResolver.ts @@ -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 { // load user and balance diff --git a/backend/src/graphql/resolver/CommunityResolver.ts b/backend/src/graphql/resolver/CommunityResolver.ts index 84d252064..5c9d46f34 100644 --- a/backend/src/graphql/resolver/CommunityResolver.ts +++ b/backend/src/graphql/resolver/CommunityResolver.ts @@ -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 { return new Community({ @@ -17,6 +19,7 @@ export class CommunityResolver { }) } + @Authorized([RIGHTS.COMMUNITIES]) @Query(() => [Community]) async communities(): Promise { if (CONFIG.PRODUCTION) diff --git a/backend/src/graphql/resolver/GdtResolver.ts b/backend/src/graphql/resolver/GdtResolver.ts index b4f9a512b..9110eb76b 100644 --- a/backend/src/graphql/resolver/GdtResolver.ts +++ b/backend/src/graphql/resolver/GdtResolver.ts @@ -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 { diff --git a/backend/src/graphql/resolver/KlicktippResolver.ts b/backend/src/graphql/resolver/KlicktippResolver.ts index e90d43a1f..fdffb940a 100644 --- a/backend/src/graphql/resolver/KlicktippResolver.ts +++ b/backend/src/graphql/resolver/KlicktippResolver.ts @@ -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 { return await getKlickTippUser(email) } - @Authorized() + @Authorized([RIGHTS.GET_KLICKTIPP_TAG_MAP]) @Query(() => String) async getKlicktippTagMap(): Promise { return await getKlicktippTagMap() } - @Authorized() + @Authorized([RIGHTS.UNSUBSCRIBE_NEWSLETTER]) @Mutation(() => Boolean) async unsubscribeNewsletter(@Arg('email') email: string): Promise { return await unsubscribe(email) } - @Authorized() + @Authorized([RIGHTS.SUBSCRIBE_NEWSLETTER]) @Mutation(() => Boolean) async subscribeNewsletter( @Args() { email, language }: SubscribeNewsletterArgs, diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index ae9e318ae..b2f4b4db5 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -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 { @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, diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 235b6c90f..7f5f7dc43 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -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 { - 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 { @@ -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 { // 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 { // 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 { // 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 { @@ -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 { const userRepository = getCustomRepository(UserRepository) diff --git a/backend/src/jwt/decode.ts b/backend/src/jwt/decode.ts deleted file mode 100644 index 6f09276b0..000000000 --- a/backend/src/jwt/decode.ts +++ /dev/null @@ -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 = jwt.verify(token, CONFIG.JWT_SECRET) - pubKey = decoded.pubKey - return { - token, - pubKey, - } - } catch (err) { - throw new Error('403.13 - Client certificate revoked') - } -} diff --git a/backend/src/jwt/encode.ts b/backend/src/jwt/encode.ts deleted file mode 100644 index ef062ad3a..000000000 --- a/backend/src/jwt/encode.ts +++ /dev/null @@ -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 -} diff --git a/backend/src/typeorm/repository/ServerUser.ts b/backend/src/typeorm/repository/ServerUser.ts new file mode 100644 index 000000000..59d7a09f4 --- /dev/null +++ b/backend/src/typeorm/repository/ServerUser.ts @@ -0,0 +1,5 @@ +import { EntityRepository, Repository } from 'typeorm' +import { ServerUser } from '@entity/ServerUser' + +@EntityRepository(ServerUser) +export class ServerUserRepository extends Repository {} diff --git a/backend/src/webhook/elopage.ts b/backend/src/webhook/elopage.ts index 90cdb159f..9e2ddeb81 100644 --- a/backend/src/webhook/elopage.ts +++ b/backend/src/webhook/elopage.ts @@ -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 => { @@ -145,6 +144,7 @@ export const elopageWebhook = async (req: any, res: any): Promise => { firstName, lastName, publisherId: loginElopgaeBuy.publisherId, + password: '123', // TODO remove }) } catch (error) { // eslint-disable-next-line no-console diff --git a/database/entity/0001-init_db/ServerUser.ts b/database/entity/0001-init_db/ServerUser.ts new file mode 100644 index 000000000..e776093ac --- /dev/null +++ b/database/entity/0001-init_db/ServerUser.ts @@ -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 +} diff --git a/database/entity/ServerUser.ts b/database/entity/ServerUser.ts index e776093ac..495513823 100644 --- a/database/entity/ServerUser.ts +++ b/database/entity/ServerUser.ts @@ -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' diff --git a/database/entity/index.ts b/database/entity/index.ts index 5e4e98118..53a6a14bf 100644 --- a/database/entity/index.ts +++ b/database/entity/index.ts @@ -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, diff --git a/frontend/src/routes/guards.js b/frontend/src/routes/guards.js index dc8df4f13..0788cb9d8 100644 --- a/frontend/src/routes/guards.js +++ b/frontend/src/routes/guards.js @@ -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,