UNPKG

wechaty-grpc

Version:
112 lines 4.11 kB
import util from 'util'; import fs from 'fs'; import { Status as GrpcServerStatus } from '@grpc/grpc-js/build/src/constants'; import { grpc, puppet, } from '../../src/mod'; import { puppetServerImpl, } from '../../tests/puppet-server-impl.js'; import { StatusBuilder, Metadata, } from '@grpc/grpc-js'; function monkeyPatchMetadataFromHttp2Headers(MetadataClass) { const fromHttp2Headers = MetadataClass.fromHttp2Headers; MetadataClass.fromHttp2Headers = function (headers) { const metadata = fromHttp2Headers.call(MetadataClass, headers); if (metadata.get('authorization').length <= 0) { const authority = headers[':authority']; const authorization = `Wechaty ${authority}`; metadata.set('authorization', authorization); } return metadata; }; } /** * Huan(202108): wrap the following handle calls with authorization: * - handleUnaryCall * - handleClientStreamingCall * - handleServerStreamingCall * - handleBidiStreamingCall * * See: * https://grpc.io/docs/guides/auth/#with-server-authentication-ssltls-and-a-custom-header-with-token */ function authHandler(validToken, handler) { console.info('wrapAuthHandler', handler.name); return function (call, cb) { // console.info('wrapAuthHandler internal') const authorization = call.metadata.get('authorization')[0]; // console.info('authorization', authorization) let errMsg = ''; if (typeof authorization === 'string') { if (authorization.startsWith('Wechaty ')) { const token = authorization.substring(8 /* 'Wechaty '.length */); if (token === validToken) { return handler(call, cb); } else { errMsg = `Invalid Wechaty TOKEN "${token}"`; } } else { const type = authorization.split(/\s+/)[0]; errMsg = `Invalid authorization type: "${type}"`; } } else { errMsg = 'No Authorization found.'; } /** * Not authorized */ const error = new StatusBuilder() .withCode(GrpcServerStatus.UNAUTHENTICATED) .withDetails(errMsg) .withMetadata(call.metadata) .build(); if (cb) { cb(error); } else if ('emit' in call) { call.emit('error', error); } else { throw new Error('no callback and call is not emit-able'); } }; } const wechatyAuthToken = (validToken) => (puppetServer) => { for (const [key, val] of Object.entries(puppetServer)) { puppetServer[key] = authHandler(validToken, val); } return puppetServer; }; monkeyPatchMetadataFromHttp2Headers(Metadata); const puppetServerExample = { ...puppetServerImpl, ding: (call, callback) => { const data = call.request.getData(); console.info(`ding(${data})`); console.info('authorization:', call.metadata.getMap()['authorization']); callback(null, new puppet.DingResponse()); }, }; async function main() { const puppetServerExampleWithAuth = wechatyAuthToken('__token__')(puppetServerExample); const server = new grpc.Server(); server.addService(puppet.PuppetService, puppetServerExampleWithAuth); const serverBindPromise = util.promisify(server.bindAsync.bind(server)); void fs; const rootCerts = fs.readFileSync('root-ca.crt'); void rootCerts; const keyCertPairs = [{ cert_chain: fs.readFileSync('server.crt'), private_key: fs.readFileSync('server.key'), }]; // const checkClientCertificate = false const port = await serverBindPromise('0.0.0.0:8788', // grpc.ServerCredentials.createInsecure(), grpc.ServerCredentials.createSsl(null, keyCertPairs) //, checkClientCertificate), ); console.info('Listen on port:', port); server.start(); return 0; } main() .catch(console.error); //# sourceMappingURL=server.js.map