Skip to content

Commit c304257

Browse files
feat(core-manager): implement basic authentication (#3687)
1 parent 9894e9a commit c304257

File tree

18 files changed

+556
-47
lines changed

18 files changed

+556
-47
lines changed
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
import "jest-extended";
2+
3+
import { Server as HapiServer } from "@hapi/hapi";
4+
import * as Boom from "@hapi/boom";
5+
import * as rpc from "@hapist/json-rpc";
6+
7+
import { Container } from "@packages/core-kernel";
8+
import { Validation } from "@packages/crypto";
9+
import { Sandbox } from "@packages/core-test-framework";
10+
import { Identifiers } from "@packages/core-manager/src/ioc";
11+
import { Server } from "@packages/core-manager/src/server/server";
12+
import { ActionReader } from "@packages/core-manager/src/action-reader";
13+
import { Actions, Plugins } from "@packages/core-manager/src/contracts";
14+
import { rpcResponseHandler } from "@packages/core-manager/src/server/plugins/rpc-response-handler";
15+
import { Assets } from "../../__fixtures__";
16+
17+
let sandbox: Sandbox;
18+
let server: Server;
19+
20+
let mockOnRequest = jest.fn();
21+
22+
// @ts-ignore
23+
let dummyPlugin = {
24+
name: "dummy",
25+
version: "0.0.1",
26+
register: (server: HapiServer) => {
27+
server.ext({
28+
type: "onRequest",
29+
method: mockOnRequest
30+
})
31+
}
32+
}
33+
34+
let mockPluginFactory: Plugins.PluginFactory = {
35+
preparePlugins() {
36+
return [
37+
{
38+
plugin: rpcResponseHandler
39+
},
40+
{
41+
plugin: rpc,
42+
options: {
43+
methods: [
44+
new Assets.DummyAction()
45+
],
46+
processor: {
47+
schema: {
48+
properties: {
49+
id: {
50+
type: ["number", "string"],
51+
},
52+
jsonrpc: {
53+
pattern: "2.0",
54+
type: "string",
55+
},
56+
method: {
57+
type: "string",
58+
},
59+
params: {
60+
type: "object",
61+
},
62+
},
63+
required: ["jsonrpc", "method", "id"],
64+
type: "object",
65+
},
66+
validate(data: object, schema: object) {
67+
try {
68+
const { error } = Validation.validator.validate(schema, data);
69+
return { value: data, error: error ? error : null };
70+
} catch (error) {
71+
return { value: null, error: error.stack };
72+
}
73+
},
74+
},
75+
}
76+
},
77+
{
78+
plugin: dummyPlugin
79+
},
80+
]
81+
}
82+
}
83+
84+
let logger = {
85+
info: jest.fn(),
86+
notice: jest.fn(),
87+
error: jest.fn(),
88+
}
89+
90+
beforeEach(() => {
91+
92+
let dummyAction = new Assets.DummyAction();
93+
94+
let actionReader: Partial<ActionReader> = {
95+
discoverActions(): Actions.Action[] {
96+
return [dummyAction];
97+
}
98+
}
99+
100+
sandbox = new Sandbox();
101+
102+
sandbox.app.bind(Identifiers.HTTP).to(Server).inSingletonScope();
103+
sandbox.app.bind(Identifiers.ActionReader).toConstantValue(actionReader);
104+
sandbox.app.bind(Identifiers.PluginFactory).toConstantValue(mockPluginFactory);
105+
106+
sandbox.app.bind(Container.Identifiers.LogService).toConstantValue(logger);
107+
108+
server = sandbox.app.get<Server>(Identifiers.HTTP);
109+
});
110+
111+
afterEach(async () => {
112+
await server.dispose();
113+
jest.clearAllMocks();
114+
})
115+
116+
describe("RPC Response Handler", () => {
117+
let injectOptions;
118+
119+
beforeEach(() => {
120+
injectOptions = {
121+
method: "POST",
122+
url: "/",
123+
payload: {
124+
jsonrpc: "2.0",
125+
id: "1",
126+
method: "dummy",
127+
params: { id: 123 },
128+
},
129+
headers: {
130+
"content-type": "application/vnd.api+json",
131+
},
132+
};
133+
})
134+
135+
it("should return result", async () => {
136+
await server.initialize("serverName", {})
137+
await server.boot()
138+
139+
mockOnRequest.mockImplementation((request, h) => {
140+
return h.continue;
141+
})
142+
143+
const response = await server.inject(injectOptions);
144+
const parsedResponse: Record<string, any> = { body: response.result, statusCode: response.statusCode };
145+
146+
expect(parsedResponse).toEqual({ body: { id: '1', jsonrpc: '2.0', result: {} }, statusCode: 200 })
147+
});
148+
149+
it("should return status -32001 if 401 response code", async () => {
150+
await server.initialize("serverName", {})
151+
await server.boot()
152+
153+
mockOnRequest.mockImplementation((request, h) => {
154+
return Boom.unauthorized();
155+
})
156+
157+
const response = await server.inject(injectOptions);
158+
const parsedResponse: Record<string, any> = { body: response.result, statusCode: response.statusCode };
159+
160+
expect(parsedResponse).toEqual({
161+
body: {
162+
jsonrpc: '2.0',
163+
error: { code: -32001, message: 'These credentials do not match our records' },
164+
id: null
165+
},
166+
statusCode: 200
167+
})
168+
});
169+
170+
it("should return status -32003 if 403 response code", async () => {
171+
await server.initialize("serverName", {})
172+
await server.boot()
173+
174+
mockOnRequest.mockImplementation((request, h) => {
175+
return Boom.forbidden();
176+
})
177+
178+
const response = await server.inject(injectOptions);
179+
const parsedResponse: Record<string, any> = { body: response.result, statusCode: response.statusCode };
180+
181+
expect(parsedResponse).toEqual({
182+
body: {
183+
jsonrpc: '2.0',
184+
error: { code: -32003, message: 'Forbidden' },
185+
id: null
186+
},
187+
statusCode: 200
188+
})
189+
});
190+
191+
it("should return status -32603 if unhandled response code", async () => {
192+
await server.initialize("serverName", {})
193+
await server.boot()
194+
195+
mockOnRequest.mockImplementation((request, h) => {
196+
return Boom.notImplemented();
197+
})
198+
199+
const response = await server.inject(injectOptions);
200+
const parsedResponse: Record<string, any> = { body: response.result, statusCode: response.statusCode };
201+
202+
expect(parsedResponse).toEqual({
203+
body: {
204+
jsonrpc: '2.0',
205+
error: { code: -32603, message: 'Internal server error' },
206+
id: null
207+
},
208+
statusCode: 200
209+
})
210+
});
211+
})

__tests__/unit/core-manager/server-boot-dispose.test.ts renamed to __tests__/unit/core-manager/server/server-boot-dispose.test.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
11
import "jest-extended";
22

3-
// @ts-ignore
4-
import { Server as HapiServer } from "@hapi/hapi";
5-
63
import { Container } from "@packages/core-kernel";
74
import { Sandbox } from "@packages/core-test-framework";
85
import { Identifiers } from "@packages/core-manager/src/ioc";
9-
import { Server } from "@packages/core-manager/src/server";
6+
import { Server } from "@packages/core-manager/src/server/server";
107
import { ActionReader } from "@packages/core-manager/src/action-reader";
11-
import { PluginFactory } from "@packages/core-manager/src/plugins/plugin-factory";
8+
import { PluginFactory } from "@packages/core-manager/src/server/plugins/plugin-factory";
129
import { defaults } from "@packages/core-manager/src/defaults";
1310

1411
let sandbox: Sandbox;
@@ -41,11 +38,15 @@ jest.mock("@hapi/hapi", () => {
4138
})
4239

4340
beforeEach(() => {
41+
pluginsConfiguration.basicAuthentication.enabled = false
42+
4443
sandbox = new Sandbox();
4544

4645
sandbox.app.bind(Identifiers.HTTP).to(Server).inSingletonScope();
4746
sandbox.app.bind(Identifiers.ActionReader).to(ActionReader).inSingletonScope();
4847
sandbox.app.bind(Identifiers.PluginFactory).to(PluginFactory).inSingletonScope();
48+
sandbox.app.bind(Identifiers.BasicCredentialsValidator).toConstantValue({});
49+
4950
sandbox.app.bind(Container.Identifiers.LogService).toConstantValue(logger);
5051
sandbox.app.bind(Container.Identifiers.PluginConfiguration).toConstantValue({
5152
get: jest.fn().mockReturnValue(pluginsConfiguration)

0 commit comments

Comments
 (0)