Browse Source

sesame function

master
peter 4 years ago
parent
commit
217df18585
6 changed files with 78 additions and 17 deletions
  1. 1
    1
      package.json
  2. 11
    6
      src/Backend.ts
  3. 1
    1
      src/Frontend.ts
  4. 4
    2
      src/Types.ts
  5. 18
    7
      src/Utils.ts
  6. 43
    0
      test/Test.ts

+ 1
- 1
package.json View File

@@ -1,6 +1,6 @@
1 1
 {
2 2
   "name": "rpclibrary",
3
-  "version": "1.4.7",
3
+  "version": "1.5.0",
4 4
   "description": "rpclibrary is a websocket on steroids!",
5 5
   "main": "./js/Index.js",
6 6
   "repository": {

+ 11
- 6
src/Backend.ts View File

@@ -20,6 +20,7 @@ export class RPCServer<
20 20
     private closeHandler:T.CloseHandler
21 21
     private errorHandler: T.ErrorHandler
22 22
     private connectionHandler: T.ConnectionHandler
23
+    private sesame? : T.SesameFunction
23 24
 
24 25
     /**
25 26
      * @throws On RPC with no name
@@ -30,11 +31,16 @@ export class RPCServer<
30 31
     constructor(
31 32
         private port:number,
32 33
         private exporters: I.RPCExporter<T.RPCInterface<InterfaceT>, keyof InterfaceT, SubResType>[] = [],
33
-        private conf: T.ServerConf =  {}
34
+        conf: T.ServerConf =  {}
34 35
     ){
35 36
         
36 37
         if(!conf.visibility) this.visibility = "127.0.0.1"        
37 38
         
39
+        if(conf.sesame){
40
+            console.log("Setting Sesame")
41
+            this.sesame = U.makeSesameFunction(conf.sesame)
42
+        }
43
+
38 44
         this.errorHandler = (socket:I.Socket) => (error:any) => { 
39 45
             if(conf.errorHandler) conf.errorHandler(socket, error)
40 46
             socket.destroy(); 
@@ -42,12 +48,12 @@ export class RPCServer<
42 48
         }
43 49
         
44 50
         this.closeHandler = (socket:I.Socket) => { 
45
-            if(!conf.closeHandler) console.log("Socket on port "+socket.port+" closing") 
51
+            if(!conf.closeHandler) console.log("Connection on port "+socket.port+" closing") 
46 52
             else conf.closeHandler(socket)
47 53
         }
48 54
         
49 55
         this.connectionHandler = (socket:I.Socket) => { 
50
-            if(!conf.connectionHandler) console.log("New websocket connection on port "+socket.port)
56
+            if(!conf.connectionHandler) console.log("New connection on port "+socket.port)
51 57
             else conf.connectionHandler(socket)
52 58
         }
53 59
 
@@ -74,15 +80,14 @@ export class RPCServer<
74 80
             })
75 81
             this.ws.listen(this.port, this.visibility)
76 82
         }catch(e){
77
-            //@ts-ignore
78
-            this.errorHandler(undefined)("Unable to connect to socket")
83
+            this.errorHandler(this.io, e)
79 84
         }
80 85
     }
81 86
 
82 87
     protected initRPCs(socket:I.Socket){
83 88
         socket.hook('info', () => rpcInfos)
84 89
         const rpcInfos:T.ExtendedRpcInfo[] = [
85
-            ...this.exporters.flatMap(exporter => U.rpcHooker(socket, exporter, this.conf.sesame))
90
+            ...this.exporters.flatMap(exporter => U.rpcHooker(socket, exporter, this.sesame))
86 91
         ]
87 92
     }
88 93
 

+ 1
- 1
src/Frontend.ts View File

@@ -134,7 +134,7 @@ export class RPCSocket implements I.Socket{
134 134
         const argParams = fnArgs.map(stripAfterEquals).join(",")
135 135
         if(!sesame)
136 136
             return eval( '( () => async ('+headerArgs+') => { return await this.socket.call("'+fnName+'", '+argParams+')} )()' )
137
-        else   
137
+        else 
138 138
             return eval( '( () => async ('+headerArgs+') => { return await this.socket.call("'+fnName+'", "'+sesame+'", '+argParams+')} )()' )
139 139
     }
140 140
 

+ 4
- 2
src/Types.ts View File

@@ -7,8 +7,10 @@ export type Visibility = "127.0.0.1" | "0.0.0.0"
7 7
 export type ConnectionHandler = (socket:I.Socket) => void
8 8
 export type ErrorHandler = (socket:I.Socket, error:any) => void
9 9
 export type CloseHandler = (socket:I.Socket) =>  void
10
+export type SesameFunction = (sesame : string) => boolean
11
+
10 12
 export type SesameConf = {
11
-    sesame?: string
13
+    sesame?: string | SesameFunction
12 14
 }
13 15
 export type ServerConf = {
14 16
     connectionHandler?: ConnectionHandler
@@ -65,7 +67,7 @@ export type BaseInfo = {
65 67
 
66 68
 export type HookInfo<SubresT = {}> = BaseInfo & { 
67 69
     type: 'Hook', 
68
-    generator: (socket?:I.Socket) => (...args:any) => SubresT
70
+    generator: (socket?:I.Socket) => (...args:any[]) => SubresT
69 71
 }
70 72
 
71 73
 export type CallInfo = BaseInfo & {

+ 18
- 7
src/Utils.ts View File

@@ -10,7 +10,8 @@ import { SubscriptionResponse } from "./Types";
10 10
  * @param owner The owning RPC group's name
11 11
  * @throws Error on RPC without name property
12 12
  */
13
-export const rpcToRpcinfo = <SubResT = {}>(rpc : T.RPC<any, any, SubResT>, owner: string, sesame?:string):T.RpcInfo => {
13
+export const rpcToRpcinfo = <SubResT = {}>(rpc : T.RPC<any, any, SubResT>, owner: string, sesame?:T.SesameFunction):T.RpcInfo => {
14
+    
14 15
     switch (typeof rpc){
15 16
         case  "object":
16 17
             if(rpc['call']){
@@ -19,7 +20,7 @@ export const rpcToRpcinfo = <SubResT = {}>(rpc : T.RPC<any, any, SubResT>, owner
19 20
                     argNames: extractArgs(rpc['call']),
20 21
                     type: "Call",
21 22
                     name: rpc.name,
22
-                    call: sesame?async (_sesame, ...args) => {if(sesame === _sesame) return await rpc['call'].apply({}, args)}:rpc['call'],
23
+                    call: sesame?async (_sesame, ...args) => {if(sesame(_sesame)) return await rpc['call'].apply({}, args)}:rpc['call'], // check & remove sesame 
23 24
                 }
24 25
             }else{
25 26
                 const generator = hookGenerator(<T.HookRPC<any, any, any>>rpc, sesame)
@@ -44,7 +45,7 @@ RPC did not provide a name.
44 45
                 argNames: extractArgs(rpc),
45 46
                 type: "Call",
46 47
                 name: rpc.name,
47
-                call: async(...args) => (<Function>rpc).apply({}, args),
48
+                call: sesame?async (_sesame, ...args) => {if(sesame(_sesame)) return await rpc.apply({}, args)}:rpc, // check & remove sesame 
48 49
             }
49 50
     }
50 51
     throw new Error("Bad socketIORPC type "+ typeof rpc)
@@ -56,7 +57,7 @@ RPC did not provide a name.
56 57
  * @param exporter The exporter
57 58
  * @param makeUnique @default true Attach a suffix to RPC names
58 59
  */
59
-export function rpcHooker<SubResT = {}>(socket: I.Socket, exporter:I.RPCExporter<any, any, SubResT>, sesame?:string, makeUnique = true):T.ExtendedRpcInfo[]{
60
+export function rpcHooker<SubResT = {}>(socket: I.Socket, exporter:I.RPCExporter<any, any, SubResT>, sesame?:T.SesameFunction, makeUnique = true):T.ExtendedRpcInfo[]{
60 61
     const owner = exporter.name
61 62
     const RPCs = [...exporter.exportRPCs()]
62 63
 
@@ -85,15 +86,15 @@ export function rpcHooker<SubResT = {}>(socket: I.Socket, exporter:I.RPCExporter
85 86
  * @param rpc The RPC to transform
86 87
  * @returns A {@link HookFunction}
87 88
  */
88
-const hookGenerator = (rpc:T.HookRPC<any, any, any>, sesame?:string): T.HookInfo['generator'] => { 
89
+const hookGenerator = (rpc:T.HookRPC<any, any, any>, sesame?:T.SesameFunction): T.HookInfo['generator'] => { 
89 90
     const argsArr = extractArgs(rpc.hook)
90 91
     if(sesame){
91 92
         const _sesame = argsArr.shift()
92
-        if(sesame !== _sesame){
93
+        if(!sesame(_sesame!)){
93 94
             throw new Error('Bad sesame')
94 95
         }
95 96
     }
96
-    argsArr.pop()
97
+    argsArr.pop() //remove 'callback' from the end
97 98
     const args = argsArr.join(',')
98 99
 
99 100
     return eval(`(socket) => async (`+args+`) => { 
@@ -131,4 +132,14 @@ export function makeSubResponse<T extends {} = {}>(extension:T):SubscriptionResp
131 132
         uuid: uuidv4(),
132 133
         ...extension
133 134
     }
135
+}
136
+
137
+export function makeSesameFunction (sesame : T.SesameFunction | string) : T.SesameFunction {
138
+    if(typeof sesame === 'function'){
139
+        return sesame
140
+    }
141
+
142
+    return (testSesame : string) => {
143
+        return testSesame === sesame 
144
+    }
134 145
 }

+ 43
- 0
test/Test.ts View File

@@ -223,4 +223,47 @@ describe('It should do unhook', () => {
223 223
                 done(new Error("Results did not match: "+[r1,r2,r3,r4]))
224 224
         })
225 225
     })
226
+})
227
+
228
+type SesameTestIfc = { test: { checkCandy: ()=>Promise<string>} }
229
+
230
+describe('Sesame should unlock the socket', () => {
231
+    let candy = "OK"
232
+    let client: RPCSocket & SesameTestIfc
233
+    let server: RPCServer<{topic: string}, SesameTestIfc> 
234
+
235
+    before(async() => {
236
+        server = new RPCServer<{ topic: string }, SesameTestIfc>(20004, [{
237
+            name: "test",
238
+            exportRPCs: () => [
239
+                async function checkCandy():Promise<string> { return candy },
240
+            ]}
241
+        ],{
242
+            sesame: (_sesame) => _sesame === 'sesame!' 
243
+        })
244
+        const sock = new RPCSocket(20004, "localhost")
245
+        client = await sock.connect<SesameTestIfc>('sesame!')
246
+    })
247
+
248
+    after(() => {
249
+        client.destroy()
250
+        server.destroy()
251
+    })
252
+
253
+    it('should not work without sesame', (done) => {
254
+        const sock = new RPCSocket(20004, "localhost")
255
+        sock.connect<SesameTestIfc>( /* no sesame */).then(async (c) => {
256
+            c.test.checkCandy().then(d => {
257
+                if(d === candy)
258
+                    done("should not be able to get candy")
259
+                done()
260
+            }).finally(() => {
261
+                sock.destroy()
262
+            })
263
+        })
264
+    })
265
+
266
+    it('should work with sesame', (done) => {
267
+        client.test.checkCandy().then(c => done())
268
+    })
226 269
 })

Loading…
Cancel
Save