mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge pull request #906 from gradido/pubKey-in-JWT
refactor: Provide pubKey in Resolver Context to Avoid API Calls
This commit is contained in:
commit
e7f68d0da6
@ -15,9 +15,10 @@ export const isAuthorized: AuthChecker<any> = async ({ root, args, context, info
|
||||
`${CONFIG.LOGIN_API_URL}checkSessionState?session_id=${decoded.sessionId}`,
|
||||
)
|
||||
context.sessionId = decoded.sessionId
|
||||
context.setHeaders.push({ key: 'token', value: encode(decoded.sessionId) })
|
||||
context.pubKey = decoded.pubKey
|
||||
context.setHeaders.push({ key: 'token', value: encode(decoded.sessionId, decoded.pubKey) })
|
||||
return result.success
|
||||
}
|
||||
}
|
||||
return false
|
||||
throw new Error('401 Unauthorized')
|
||||
}
|
||||
|
||||
@ -2,9 +2,7 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
|
||||
import { Resolver, Query, Ctx, Authorized } from 'type-graphql'
|
||||
import CONFIG from '../../config'
|
||||
import { Balance } from '../models/Balance'
|
||||
import { apiGet } from '../../apis/HttpRequest'
|
||||
import { User as dbUser } from '../../typeorm/entity/User'
|
||||
import { Balance as dbBalance } from '../../typeorm/entity/Balance'
|
||||
import { calculateDecay } from '../../util/decay'
|
||||
@ -15,12 +13,8 @@ export class BalanceResolver {
|
||||
@Authorized()
|
||||
@Query(() => Balance)
|
||||
async balance(@Ctx() context: any): Promise<Balance> {
|
||||
// get public key for current logged in user
|
||||
const result = await apiGet(CONFIG.LOGIN_API_URL + 'login?session_id=' + context.sessionId)
|
||||
if (!result.success) throw new Error(result.data)
|
||||
|
||||
// load user and balance
|
||||
const userEntity = await dbUser.findByPubkeyHex(result.data.user.public_hex)
|
||||
const userEntity = await dbUser.findByPubkeyHex(context.pubKey)
|
||||
const balanceEntity = await dbBalance.findByUser(userEntity.id)
|
||||
let balance: Balance
|
||||
const now = new Date()
|
||||
@ -39,7 +33,6 @@ export class BalanceResolver {
|
||||
decay_date: now.toString(),
|
||||
})
|
||||
}
|
||||
|
||||
return balance
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,18 +18,14 @@ export class GdtResolver {
|
||||
{ currentPage = 1, pageSize = 5, order = 'DESC' }: GdtTransactionSessionIdInput,
|
||||
@Ctx() context: any,
|
||||
): Promise<GdtEntryList> {
|
||||
// get public key for current logged in user
|
||||
const result = await apiGet(CONFIG.LOGIN_API_URL + 'login?session_id=' + context.sessionId)
|
||||
if (!result.success) throw new Error(result.data)
|
||||
|
||||
// load user
|
||||
const userEntity = await dbUser.findByPubkeyHex(result.data.user.public_hex)
|
||||
const userEntity = await dbUser.findByPubkeyHex(context.pubKey)
|
||||
|
||||
const resultGDT = await apiGet(
|
||||
`${CONFIG.GDT_API_URL}/GdtEntries/listPerEmailApi/${userEntity.email}/${currentPage}/${pageSize}/${order}`,
|
||||
)
|
||||
if (!resultGDT.success) {
|
||||
throw new Error(result.data)
|
||||
throw new Error(resultGDT.data)
|
||||
}
|
||||
|
||||
return new GdtEntryList(resultGDT.data)
|
||||
|
||||
@ -22,6 +22,7 @@ import {
|
||||
klicktippNewsletterStateMiddleware,
|
||||
} from '../../middleware/klicktippMiddleware'
|
||||
import { CheckEmailResponse } from '../models/CheckEmailResponse'
|
||||
|
||||
@Resolver()
|
||||
export class UserResolver {
|
||||
@Query(() => User)
|
||||
@ -35,7 +36,10 @@ export class UserResolver {
|
||||
throw new Error(result.data)
|
||||
}
|
||||
|
||||
context.setHeaders.push({ key: 'token', value: encode(result.data.session_id) })
|
||||
context.setHeaders.push({
|
||||
key: 'token',
|
||||
value: encode(result.data.session_id, result.data.user.public_hex),
|
||||
})
|
||||
|
||||
return new User(result.data.user)
|
||||
}
|
||||
|
||||
@ -1,18 +1,29 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
|
||||
import jwt from 'jsonwebtoken'
|
||||
import jwt, { JwtPayload } from 'jsonwebtoken'
|
||||
import CONFIG from '../config/'
|
||||
|
||||
export default (token: string): any => {
|
||||
if (!token) return new Error('401 Unauthorized')
|
||||
interface CustomJwtPayload extends JwtPayload {
|
||||
sessionId: number
|
||||
pubKey: Buffer
|
||||
}
|
||||
|
||||
type DecodedJwt = {
|
||||
token: string
|
||||
sessionId: number
|
||||
pubKey: Buffer
|
||||
}
|
||||
|
||||
export default (token: string): DecodedJwt => {
|
||||
if (!token) throw new Error('401 Unauthorized')
|
||||
let sessionId = null
|
||||
let pubKey = null
|
||||
try {
|
||||
const decoded = jwt.verify(token, CONFIG.JWT_SECRET)
|
||||
sessionId = decoded.sub
|
||||
const decoded = <CustomJwtPayload>jwt.verify(token, CONFIG.JWT_SECRET)
|
||||
sessionId = decoded.sessionId
|
||||
pubKey = decoded.pubKey
|
||||
return {
|
||||
token,
|
||||
sessionId,
|
||||
pubKey,
|
||||
}
|
||||
} catch (err) {
|
||||
throw new Error('403.13 - Client certificate revoked')
|
||||
|
||||
@ -5,8 +5,8 @@ import jwt from 'jsonwebtoken'
|
||||
import CONFIG from '../config/'
|
||||
|
||||
// Generate an Access Token
|
||||
export default function encode(sessionId: string): string {
|
||||
const token = jwt.sign({ sessionId }, CONFIG.JWT_SECRET, {
|
||||
export default function encode(sessionId: number, pubKey: Buffer): string {
|
||||
const token = jwt.sign({ sessionId, pubKey }, CONFIG.JWT_SECRET, {
|
||||
expiresIn: CONFIG.JWT_EXPIRES_IN,
|
||||
subject: sessionId.toString(),
|
||||
})
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
|
||||
const context = (args: any) => {
|
||||
const authorization = args.req.headers.authorization
|
||||
let token = null
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
|
||||
const plugins = [
|
||||
{
|
||||
requestDidStart() {
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm'
|
||||
|
||||
// import { Group } from "./Group"
|
||||
|
||||
// Moriz: I do not like the idea of having two user tables
|
||||
@Entity('state_users')
|
||||
export class User extends BaseEntity {
|
||||
@PrimaryGeneratedColumn()
|
||||
@ -27,6 +29,9 @@ export class User extends BaseEntity {
|
||||
@Column()
|
||||
disabled: boolean
|
||||
|
||||
// Moriz: I am voting for the data mapper implementation.
|
||||
// see: https://typeorm.io/#/active-record-data-mapper/what-is-the-data-mapper-pattern
|
||||
// We should discuss this ASAP
|
||||
static findByPubkeyHex(pubkeyHex: string): Promise<User> {
|
||||
return this.createQueryBuilder('user')
|
||||
.where('hex(user.pubkey) = :pubkeyHex', { pubkeyHex })
|
||||
|
||||
@ -57,7 +57,7 @@ describe('UserCard_Language', () => {
|
||||
})
|
||||
|
||||
it('has change language as text', () => {
|
||||
expect(wrapper.find('a').text()).toBe('form.changeLanguage')
|
||||
expect(wrapper.find('a').text()).toBe('settings.language.changeLanguage')
|
||||
})
|
||||
|
||||
it('has no select field by default', () => {
|
||||
@ -87,23 +87,23 @@ describe('UserCard_Language', () => {
|
||||
|
||||
describe('change language', () => {
|
||||
it('does not enable the submit button when same language is chosen', () => {
|
||||
wrapper.findAll('option').at(1).setSelected()
|
||||
wrapper.findAll('option').at(0).setSelected()
|
||||
expect(wrapper.find('button[type="submit"]').attributes('disabled')).toBe('disabled')
|
||||
})
|
||||
|
||||
it('enables the submit button when other language is chosen', async () => {
|
||||
await wrapper.findAll('option').at(2).setSelected()
|
||||
await wrapper.findAll('option').at(1).setSelected()
|
||||
expect(wrapper.find('button[type="submit"]').attributes('disabled')).toBe(undefined)
|
||||
})
|
||||
|
||||
it('updates language data in component', async () => {
|
||||
await wrapper.findAll('option').at(2).setSelected()
|
||||
await wrapper.findAll('option').at(1).setSelected()
|
||||
expect(wrapper.vm.language).toBe('en')
|
||||
})
|
||||
|
||||
describe('cancel edit', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.findAll('option').at(2).setSelected()
|
||||
await wrapper.findAll('option').at(1).setSelected()
|
||||
wrapper.find('a').trigger('click')
|
||||
})
|
||||
|
||||
@ -118,7 +118,7 @@ describe('UserCard_Language', () => {
|
||||
|
||||
describe('submit', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.findAll('option').at(2).setSelected()
|
||||
await wrapper.findAll('option').at(1).setSelected()
|
||||
wrapper.find('form').trigger('submit')
|
||||
})
|
||||
|
||||
@ -147,7 +147,7 @@ describe('UserCard_Language', () => {
|
||||
})
|
||||
|
||||
it('toasts a success message', () => {
|
||||
expect(toastSuccessMock).toBeCalledWith('languages.success')
|
||||
expect(toastSuccessMock).toBeCalledWith('settings.language.success')
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -104,7 +104,7 @@ describe('UserCard_Newsletter', () => {
|
||||
})
|
||||
|
||||
it('toasts a success message', () => {
|
||||
expect(toastSuccessMock).toBeCalledWith('setting.newsletterFalse')
|
||||
expect(toastSuccessMock).toBeCalledWith('settings.newsletter.newsletterFalse')
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user