refactored isAuthorized & implemented a new role & rights based system

This commit is contained in:
Ulf Gebhardt 2021-11-20 19:33:21 +01:00
parent 963bff1c0b
commit 44365bccf1
Signed by: ulfgebhardt
GPG Key ID: DA6B843E748679C9
7 changed files with 106 additions and 7 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,
]

View File

@ -0,0 +1,23 @@
export enum RIGHTS {
LOGIN = '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',
}

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

@ -0,0 +1,24 @@
import { INALIENABLE_RIGHTS } from './INALIENABLE_RIGHTS'
import { RIGHTS } from './RIGHTS'
import { Role } from './Role'
// TODO from database
export const ROLES = [
new Role('unauthorized', INALIENABLE_RIGHTS), // inalienable rights
new Role('user', [
...INALIENABLE_RIGHTS,
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,
]),
new Role('admin', Object.values(RIGHTS)), // all rights
]

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

@ -0,0 +1,11 @@
import { RIGHTS } from './RIGHTS'
export class Role {
id: string
rights: RIGHTS[]
constructor(id: string, rights: RIGHTS[]) {
this.id = id
this.rights = rights
}
}

View File

@ -0,0 +1,6 @@
import { RIGHTS } from './RIGHTS'
import { Role } from './Role'
export const hasRight = (right: RIGHTS, role: Role): boolean => {
return role.rights.includes(right)
}

View File

@ -2,19 +2,36 @@
import { AuthChecker } from 'type-graphql' import { AuthChecker } from 'type-graphql'
import decode from '../../jwt/decode' import { decode, encode } from '../../auth/JWT'
import encode from '../../jwt/encode' import { ROLES } from '../../auth/ROLES'
import { hasRight } from '../../auth/hasRight'
import { RIGHTS } from '../../auth/RIGHTS'
const isAuthorized: AuthChecker<any> = async ( const isAuthorized: AuthChecker<any> = async ({ context }, rights) => {
{ /* root, args, */ context /*, info */ } /*, roles */, context.role = ROLES[0] // unauthorized user
) => {
// Do we have a token?
if (context.token) { if (context.token) {
const decoded = decode(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') context.pubKey = Buffer.from(decoded.pubKey).toString('hex')
// set new header token
context.setHeaders.push({ key: 'token', value: encode(decoded.pubKey) }) context.setHeaders.push({ key: 'token', value: encode(decoded.pubKey) })
return true // TODO - load from database dynamically & admin - maybe encode this in the token to prevent many database requests
context.role = ROLES[1] // logged in user
} }
throw new Error('401 Unauthorized')
// check for correct rights
const missingRights = (<RIGHTS[]>rights).filter((right) => !hasRight(right, context.role))
if (missingRights.length !== 0) {
throw new Error('401 Unauthorized')
}
return true
} }
export default isAuthorized export default isAuthorized