Add permission management

Signed-off-by: trivernis <trivernis@protonmail.com>
pull/3/head
trivernis 4 years ago
parent b61dfa8810
commit 96916b0fd1
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

12
package-lock.json generated

@ -864,6 +864,18 @@
"zen-observable-ts": "^0.8.21" "zen-observable-ts": "^0.8.21"
} }
}, },
"apollo-server": {
"version": "2.17.0",
"resolved": "https://registry.npmjs.org/apollo-server/-/apollo-server-2.17.0.tgz",
"integrity": "sha512-vVMu+VqjmsB6yk5iNTb/AXM6EJGd2uwzrcTAbwqvGI7GqCYDRZlGBwrQCjOU/jT/EPWdNRWks/qhJYiQMeVXSg==",
"requires": {
"apollo-server-core": "^2.17.0",
"apollo-server-express": "^2.17.0",
"express": "^4.0.0",
"graphql-subscriptions": "^1.0.0",
"graphql-tools": "^4.0.0"
}
},
"apollo-server-caching": { "apollo-server-caching": {
"version": "0.5.2", "version": "0.5.2",
"resolved": "https://registry.npmjs.org/apollo-server-caching/-/apollo-server-caching-0.5.2.tgz", "resolved": "https://registry.npmjs.org/apollo-server-caching/-/apollo-server-caching-0.5.2.tgz",

@ -31,6 +31,7 @@
"@types/crc": "^3.4.0" "@types/crc": "^3.4.0"
}, },
"dependencies": { "dependencies": {
"apollo-server": "^2.17.0",
"apollo-server-express": "^2.17.0", "apollo-server-express": "^2.17.0",
"crc": "^3.8.0", "crc": "^3.8.0",
"dotenv": "^8.2.0", "dotenv": "^8.2.0",

@ -7,5 +7,5 @@ export enum Method {
GetRoles = 0x52_4f_4c_45, GetRoles = 0x52_4f_4c_45,
GetRolePermissions = 0x50_45_52_4d, GetRolePermissions = 0x50_45_52_4d,
CreateRole = 0x43_52_4f_4c, CreateRole = 0x43_52_4f_4c,
CreatePermissionn = 0x43_50_45_52 CreatePermissions = 0x43_50_45_52
} }

@ -0,0 +1,17 @@
/* eslint no-unused-vars: 0 */
export enum Permission {
ReadBike = 'BIKE_READ',
WriteBike = 'BIKE_WRITE',
}
// Permissions where the creation will be requested on startup
export const requiredPermissions = [
{
name: Permission.ReadBike,
description: 'Allows to read of bike information'
},
{
name: Permission.WriteBike,
description: 'Allows the modification of bike information'
}
]

@ -3,3 +3,9 @@ export type ValidateTokenResponse = [boolean, number]
// Info in the form of [name, binary, description, request_structure][] // Info in the form of [name, binary, description, request_structure][]
export type GetInfoResponse = [string, string, string][] export type GetInfoResponse = [string, string, string][]
// Role array in the form of [id, name, description][]
export type GetRolesResponse = [number, string, string][]
// Permissions map where each roleId maps to an array of permissions
export type GetRolesPermissionsResponse = {[id: string]: [number, string, string][]}

@ -3,7 +3,8 @@ import { Socket } from 'net'
import { PromiseSocket } from 'promise-socket' import { PromiseSocket } from 'promise-socket'
import { RPCMessage } from './message' import { RPCMessage } from './message'
import { Method } from './method' import { Method } from './method'
import { GetInfoResponse, ValidateTokenResponse } from './responses' import { GetInfoResponse, GetRolesPermissionsResponse, GetRolesResponse, ValidateTokenResponse } from './responses'
import { Permission, requiredPermissions } from './permission'
/** /**
* fetches datafrom user server, especially validates user tokens * fetches datafrom user server, especially validates user tokens
@ -23,12 +24,22 @@ export class UserServerAPI extends DataSource {
} }
} }
/**
* Returns the information about all available rpc methods
*/
async getInfo (): Promise<GetInfoResponse> { async getInfo (): Promise<GetInfoResponse> {
const response = await this.send<GetInfoResponse>(new RPCMessage(Method.Info, null)) const response = await this.send<GetInfoResponse>(new RPCMessage(Method.Info, null))
return response.data return response.data
} }
/**
* Creates required API permissions
*/
async createDefinedPermissions () {
await this.send<any>(new RPCMessage(Method.CreatePermissions, { permissions: requiredPermissions }))
}
/** /**
* validates user token * validates user token
*/ */
@ -41,6 +52,32 @@ export class UserServerAPI extends DataSource {
} }
} }
/**
* Returns a list of roles the user is assigned to
* @param token
*/
async getUserRoles (token: String): Promise<GetRolesResponse> {
const response = await this.send<any>(new RPCMessage(Method.GetRoles, { token }))
return response.data
}
/**
* Returns all permissions of the user
* @param token
*/
async getUserPermissions (token: String): Promise<string[]> {
const roles = await this.getUserRoles(token)
const roleIds = roles.map(role => role[0])
const permissionsResponse = await this.send<GetRolesPermissionsResponse>(new RPCMessage(Method.GetRolePermissions, { roles: roleIds }))
const permissions: string[] = []
for (const id of roleIds) {
permissions.push(...permissionsResponse.data[id].map(entry => entry[1]))
}
return permissions
}
/** /**
* Connects to the socket and returns the client * Connects to the socket and returns the client
*/ */

@ -16,6 +16,7 @@ async function authenticate (req: any, res: any, next: any) {
const token = req.headers.authorization?.replace('Bearer ', '') const token = req.headers.authorization?.replace('Bearer ', '')
if (token) { if (token) {
if (await userAPI.validateToken(token)) { if (await userAPI.validateToken(token)) {
req.permissions = await userAPI.getUserPermissions(token)
next() next()
} else { } else {
res.status(401) res.status(401)
@ -40,7 +41,10 @@ const server = new ApolloServer({
dataSources: () => ({ dataSources: () => ({
cargoBikeAPI: new CargoBikeAPI(), cargoBikeAPI: new CargoBikeAPI(),
userAPI userAPI
}) }),
context: (req: any) => {
return req
}
}) })
const app = express() const app = express()
@ -50,6 +54,7 @@ app.get(/\/graphql?&.*query=/, authenticate)
server.applyMiddleware({ app }) server.applyMiddleware({ app })
console.log(__dirname) console.log(__dirname)
app.listen(4000, () => { app.listen(4000, async () => {
console.log('Server listening on port 4000') console.log('Server listening on port 4000')
await userAPI.createDefinedPermissions()
}) })

@ -1,10 +1,23 @@
import { Permission } from '../datasources/userserver/permission'
import { GraphQLError } from 'graphql'
export default { export default {
Query: { Query: {
CargobikeById: (_: any, { id, token }:{id: any, token: string}, { dataSources }:{dataSources: any}) => CargobikeById: (_: any, { id, token }:{id: any, token: string}, { dataSources, req }:{dataSources: any, req: any }) => {
dataSources.cargoBikeAPI.findCargoBikeById({ id, token }) if (req.permissions.includes(Permission.ReadBike)) {
return dataSources.cargoBikeAPI.findCargoBikeById({ id, token })
} else {
throw new GraphQLError('Insufficient Permissions')
}
}
}, },
Mutation: { Mutation: {
addBike: (_:any, { id, token, name }:{id: any, token: string, name:string}, { dataSources }:{dataSources: any }) => addBike: (_: any, { id, token, name }:{id: any, token: string, name:string}, { dataSources, req }:{dataSources: any, req: any }) => {
dataSources.cargoBikeAPI.updateBike({ id, token, name }) if (req.permissions.includes(Permission.WriteBike)) {
return dataSources.cargoBikeAPI.updateBike({ id, token, name })
} else {
throw new GraphQLError('Insufficient Permissions')
}
}
} }
} }

Loading…
Cancel
Save