mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Introuduce auth header value and retrieval on renderer
Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
6781b4657f
commit
a5dab8549b
11
src/common/auth/channel.ts
Normal file
11
src/common/auth/channel.ts
Normal file
@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { RequestChannel } from "../utils/channel/request-channel-listener-injection-token";
|
||||
|
||||
// This channel retreives the value needed to grant authentication to requests
|
||||
export const lensAuthenticationChannel: RequestChannel<void, string> = {
|
||||
id: "lens-authentication-channel",
|
||||
};
|
||||
9
src/common/vars/auth-header.ts
Normal file
9
src/common/vars/auth-header.ts
Normal file
@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This is the header name that we use for request authentication
|
||||
*/
|
||||
export const lensAuthenticationHeader = "LENS-AUTHENTICATION";
|
||||
@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { lensAuthenticationChannel } from "../../common/auth/channel";
|
||||
import { getRequestChannelListenerInjectable } from "../utils/channel/channel-listeners/listener-tokens";
|
||||
import authHeaderValueInjectable from "./auth-header-value.injectable";
|
||||
|
||||
const lensAuthenticationRequestListener = getRequestChannelListenerInjectable({
|
||||
channel: lensAuthenticationChannel,
|
||||
handler: (di) => {
|
||||
const authHeaderValue = di.inject(authHeaderValueInjectable);
|
||||
|
||||
return () => authHeaderValue;
|
||||
},
|
||||
});
|
||||
|
||||
export default lensAuthenticationRequestListener;
|
||||
13
src/main/lens-proxy/auth-header-value.injectable.ts
Normal file
13
src/main/lens-proxy/auth-header-value.injectable.ts
Normal file
@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import * as uuid from "uuid";
|
||||
|
||||
const authHeaderValueInjectable = getInjectable({
|
||||
id: "auth-header-value",
|
||||
instantiate: () => uuid.v4(),
|
||||
});
|
||||
|
||||
export default authHeaderValueInjectable;
|
||||
@ -5,7 +5,7 @@
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { LensProxy } from "./lens-proxy";
|
||||
import { kubeApiUpgradeRequest } from "./proxy-functions";
|
||||
import routerInjectable from "../router/router.injectable";
|
||||
import routeRequestInjectable from "../router/router.injectable";
|
||||
import httpProxy from "http-proxy";
|
||||
import clusterManagerInjectable from "../cluster/manager.injectable";
|
||||
import shellApiRequestInjectable from "./proxy-functions/shell-api-request/shell-api-request.injectable";
|
||||
@ -13,12 +13,13 @@ import lensProxyPortInjectable from "./lens-proxy-port.injectable";
|
||||
import contentSecurityPolicyInjectable from "../../common/vars/content-security-policy.injectable";
|
||||
import emitAppEventInjectable from "../../common/app-event-bus/emit-event.injectable";
|
||||
import loggerInjectable from "../../common/logger.injectable";
|
||||
import authHeaderValueInjectable from "./auth-header-value.injectable";
|
||||
|
||||
const lensProxyInjectable = getInjectable({
|
||||
id: "lens-proxy",
|
||||
|
||||
instantiate: (di) => new LensProxy({
|
||||
router: di.inject(routerInjectable),
|
||||
routeRequest: di.inject(routeRequestInjectable),
|
||||
proxy: httpProxy.createProxy(),
|
||||
kubeApiUpgradeRequest,
|
||||
shellApiRequest: di.inject(shellApiRequestInjectable),
|
||||
@ -27,6 +28,7 @@ const lensProxyInjectable = getInjectable({
|
||||
contentSecurityPolicy: di.inject(contentSecurityPolicyInjectable),
|
||||
emitAppEvent: di.inject(emitAppEventInjectable),
|
||||
logger: di.inject(loggerInjectable),
|
||||
authHeaderValue: di.inject(authHeaderValueInjectable),
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
@ -7,7 +7,6 @@ import net from "net";
|
||||
import http from "http";
|
||||
import type httpProxy from "http-proxy";
|
||||
import { apiPrefix, apiKubePrefix } from "../../common/vars";
|
||||
import type { Router } from "../router/router";
|
||||
import type { ClusterContextHandler } from "../context-handler/context-handler";
|
||||
import type { Cluster } from "../../common/cluster/cluster";
|
||||
import type { ProxyApiRequestArgs } from "./proxy-functions";
|
||||
@ -16,6 +15,10 @@ import assert from "assert";
|
||||
import type { SetRequired } from "type-fest";
|
||||
import type { EmitAppEvent } from "../../common/app-event-bus/emit-event.injectable";
|
||||
import type { Logger } from "../../common/logger";
|
||||
import type { RouteRequest } from "../router/router.injectable";
|
||||
import { lensAuthenticationHeader } from "../../common/vars/auth-header";
|
||||
import { contentTypes } from "../router/router-content-types";
|
||||
import { writeServerResponseFor } from "../router/write-server-response";
|
||||
|
||||
type GetClusterForRequest = (req: http.IncomingMessage) => Cluster | undefined;
|
||||
|
||||
@ -26,11 +29,12 @@ interface Dependencies {
|
||||
shellApiRequest: (args: ProxyApiRequestArgs) => void | Promise<void>;
|
||||
kubeApiUpgradeRequest: (args: ProxyApiRequestArgs) => void | Promise<void>;
|
||||
emitAppEvent: EmitAppEvent;
|
||||
readonly router: Router;
|
||||
routeRequest: RouteRequest;
|
||||
readonly proxy: httpProxy;
|
||||
readonly lensProxyPort: { set: (portNumber: number) => void };
|
||||
readonly contentSecurityPolicy: string;
|
||||
readonly logger: Logger;
|
||||
readonly authHeaderValue: string;
|
||||
}
|
||||
|
||||
const watchParam = "watch";
|
||||
@ -61,9 +65,9 @@ const disallowedPorts = new Set([
|
||||
]);
|
||||
|
||||
export class LensProxy {
|
||||
protected proxyServer: http.Server;
|
||||
protected readonly proxyServer: http.Server;
|
||||
protected closed = false;
|
||||
protected retryCounters = new Map<string, number>();
|
||||
protected readonly retryCounters = new Map<string, number>();
|
||||
|
||||
constructor(private readonly dependencies: Dependencies) {
|
||||
this.configureProxy(dependencies.proxy);
|
||||
@ -75,6 +79,13 @@ export class LensProxy {
|
||||
this.proxyServer
|
||||
.on("upgrade", (req: ServerIncomingMessage, socket: net.Socket, head: Buffer) => {
|
||||
const cluster = dependencies.getClusterForRequest(req);
|
||||
const authHeader = req.headers[lensAuthenticationHeader.toLowerCase()];
|
||||
|
||||
if (authHeader !== this.dependencies.authHeaderValue) {
|
||||
socket.destroy(new Error("Missing authorization"));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!cluster) {
|
||||
this.dependencies.logger.error(`[LENS-PROXY]: Could not find cluster for upgrade request from url=${req.url}`);
|
||||
@ -210,13 +221,15 @@ export class LensProxy {
|
||||
return proxy;
|
||||
}
|
||||
|
||||
protected async getProxyTarget(req: http.IncomingMessage, contextHandler: ClusterContextHandler): Promise<httpProxy.ServerOptions | void> {
|
||||
protected async getProxyTarget(req: http.IncomingMessage, contextHandler: ClusterContextHandler): Promise<httpProxy.ServerOptions | undefined> {
|
||||
if (req.url?.startsWith(apiKubePrefix)) {
|
||||
delete req.headers.authorization;
|
||||
req.url = req.url.replace(apiKubePrefix, "");
|
||||
|
||||
return contextHandler.getApiTarget(isLongRunningRequest(req.url));
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
protected getRequestId(req: http.IncomingMessage): string {
|
||||
@ -227,16 +240,28 @@ export class LensProxy {
|
||||
|
||||
protected async handleRequest(req: ServerIncomingMessage, res: http.ServerResponse) {
|
||||
const cluster = this.dependencies.getClusterForRequest(req);
|
||||
const writeServerResponse = writeServerResponseFor(res);
|
||||
|
||||
if (cluster) {
|
||||
const proxyTarget = await this.getProxyTarget(req, cluster.contextHandler);
|
||||
|
||||
if (proxyTarget) {
|
||||
const authHeader = req.headers[lensAuthenticationHeader.toLowerCase()];
|
||||
|
||||
if (authHeader !== this.dependencies.authHeaderValue) {
|
||||
writeServerResponse(contentTypes.txt.resultMapper({
|
||||
statusCode: 401,
|
||||
response: "Missing authorization",
|
||||
}));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
return this.dependencies.proxy.web(req, res, proxyTarget);
|
||||
}
|
||||
}
|
||||
|
||||
res.setHeader("Content-Security-Policy", this.dependencies.contentSecurityPolicy);
|
||||
await this.dependencies.router.route(cluster, req, res);
|
||||
await this.dependencies.routeRequest(cluster, req, res);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,73 +5,56 @@
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import type { ServerResponse } from "http";
|
||||
import loggerInjectable from "../../common/logger.injectable";
|
||||
import { object } from "../../common/utils";
|
||||
import { lensAuthenticationHeader } from "../../common/vars/auth-header";
|
||||
import authHeaderValueInjectable from "../lens-proxy/auth-header-value.injectable";
|
||||
import type { LensApiRequest, Route } from "./route";
|
||||
import { contentTypes } from "./router-content-types";
|
||||
import { writeServerResponseFor } from "./write-server-response";
|
||||
|
||||
export type RouteHandler = (request: LensApiRequest<string>, response: ServerResponse) => Promise<void>;
|
||||
export type CreateHandlerForRoute = (route: Route<unknown, string>) => RouteHandler;
|
||||
|
||||
interface LensServerResponse {
|
||||
statusCode: number;
|
||||
content: any;
|
||||
headers: {
|
||||
[name: string]: string;
|
||||
};
|
||||
}
|
||||
|
||||
const writeServerResponseFor = (serverResponse: ServerResponse) => ({
|
||||
statusCode,
|
||||
content,
|
||||
headers,
|
||||
}: LensServerResponse) => {
|
||||
serverResponse.statusCode = statusCode;
|
||||
|
||||
for (const [name, value] of object.entries(headers)) {
|
||||
serverResponse.setHeader(name, value);
|
||||
}
|
||||
|
||||
if (content instanceof Buffer) {
|
||||
serverResponse.write(content);
|
||||
serverResponse.end();
|
||||
} else if (content) {
|
||||
serverResponse.end(content);
|
||||
} else {
|
||||
serverResponse.end();
|
||||
}
|
||||
};
|
||||
|
||||
const createHandlerForRouteInjectable = getInjectable({
|
||||
id: "create-handler-for-route",
|
||||
instantiate: (di): CreateHandlerForRoute => {
|
||||
const logger = di.inject(loggerInjectable);
|
||||
const authHeaderValue = di.inject(authHeaderValueInjectable);
|
||||
|
||||
return (route) => async (request, response) => {
|
||||
const writeServerResponse = writeServerResponseFor(response);
|
||||
|
||||
if (route.requireAuthentication) {
|
||||
const authHeader = request.getHeader(lensAuthenticationHeader);
|
||||
|
||||
if (authHeader !== authHeaderValue) {
|
||||
writeServerResponse(contentTypes.txt.resultMapper({
|
||||
statusCode: 401,
|
||||
response: "Missing authorization",
|
||||
}));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await route.handler(request);
|
||||
|
||||
if (!result) {
|
||||
const mappedResult = contentTypes.txt.resultMapper({
|
||||
writeServerResponse(contentTypes.txt.resultMapper({
|
||||
statusCode: 204,
|
||||
response: undefined,
|
||||
});
|
||||
|
||||
writeServerResponse(mappedResult);
|
||||
}));
|
||||
} else if (!result.proxy) {
|
||||
const contentType = result.contentType || contentTypes.json;
|
||||
|
||||
writeServerResponse(contentType.resultMapper(result));
|
||||
}
|
||||
} catch(error) {
|
||||
const mappedResult = contentTypes.txt.resultMapper({
|
||||
logger.error(`[ROUTER]: route ${route.path}, called with ${request.path}, threw an error`, error);
|
||||
writeServerResponse(contentTypes.txt.resultMapper({
|
||||
statusCode: 500,
|
||||
error: error ? String(error) : "unknown error",
|
||||
});
|
||||
|
||||
logger.error(`[ROUTER]: route ${route.path}, called with ${request.path}, threw an error`, error);
|
||||
writeServerResponse(mappedResult);
|
||||
}));
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
@ -35,6 +35,7 @@ export interface LensApiRequest<Path extends string> {
|
||||
params: InferParamFromPath<Path>;
|
||||
cluster: Cluster | undefined;
|
||||
query: URLSearchParams;
|
||||
getHeader: (key: string) => string | string[] | undefined;
|
||||
raw: {
|
||||
req: http.IncomingMessage;
|
||||
res: http.ServerResponse;
|
||||
@ -65,6 +66,7 @@ export interface RouteHandler<TResponse, Path extends string>{
|
||||
export interface BaseRoutePaths<Path extends string> {
|
||||
path: Path;
|
||||
method: "get" | "post" | "put" | "patch" | "delete";
|
||||
requireAuthentication?: boolean;
|
||||
}
|
||||
|
||||
export interface PayloadValidator<Payload> {
|
||||
@ -77,15 +79,20 @@ export interface ValidatorBaseRoutePaths<Path extends string, Payload> extends B
|
||||
|
||||
export interface Route<TResponse, Path extends string> extends BaseRoutePaths<Path> {
|
||||
handler: RouteHandler<TResponse, Path>;
|
||||
requireAuthentication: boolean;
|
||||
}
|
||||
|
||||
export interface BindHandler<Path extends string> {
|
||||
<TResponse>(handler: RouteHandler<TResponse, Path>): Route<TResponse, Path>;
|
||||
}
|
||||
|
||||
export function route<Path extends string>(parts: BaseRoutePaths<Path>): BindHandler<Path> {
|
||||
export function route<Path extends string>({
|
||||
requireAuthentication = true,
|
||||
...parts
|
||||
}: BaseRoutePaths<Path>): BindHandler<Path> {
|
||||
return (handler) => ({
|
||||
...parts,
|
||||
requireAuthentication,
|
||||
handler,
|
||||
});
|
||||
}
|
||||
@ -98,8 +105,12 @@ export interface BindClusterHandler<Path extends string> {
|
||||
<TResponse>(handler: ClusterRouteHandler<TResponse, Path>): Route<TResponse, Path>;
|
||||
}
|
||||
|
||||
export function clusterRoute<Path extends string>(parts: BaseRoutePaths<Path>): BindClusterHandler<Path> {
|
||||
export function clusterRoute<Path extends string>({
|
||||
requireAuthentication = true,
|
||||
...parts
|
||||
}: BaseRoutePaths<Path>): BindClusterHandler<Path> {
|
||||
return (handler) => ({
|
||||
requireAuthentication,
|
||||
...parts,
|
||||
handler: ({ cluster, ...rest }) => {
|
||||
if (!cluster) {
|
||||
|
||||
@ -2,12 +2,16 @@
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import type { Injectable, InjectionToken } from "@ogre-tools/injectable";
|
||||
import type { DiContainerForInjection, Injectable, InjectionToken } from "@ogre-tools/injectable";
|
||||
import { getInjectable, getInjectionToken } from "@ogre-tools/injectable";
|
||||
import { Router } from "./router";
|
||||
import parseRequestInjectable from "./parse-request.injectable";
|
||||
import type { Route } from "./route";
|
||||
import type { Route, LensApiRequest } from "./route";
|
||||
import createHandlerForRouteInjectable from "./create-handler-for-route.injectable";
|
||||
import Call from "@hapi/call";
|
||||
import type http from "http";
|
||||
import type { Cluster } from "../../common/cluster/cluster";
|
||||
import type { ServerIncomingMessage } from "../lens-proxy/lens-proxy";
|
||||
import type { RouteHandler } from "./create-handler-for-route.injectable";
|
||||
|
||||
export const routeInjectionToken = getInjectionToken<Route<unknown, string>>({
|
||||
id: "route-injection-token",
|
||||
@ -22,14 +26,75 @@ export function getRouteInjectable<T, Path extends string>(
|
||||
});
|
||||
}
|
||||
|
||||
const routerInjectable = getInjectable({
|
||||
id: "router",
|
||||
export type RouteRequest = (cluster: Cluster | undefined, req: ServerIncomingMessage, res: http.ServerResponse) => Promise<boolean>;
|
||||
|
||||
instantiate: (di) => new Router({
|
||||
parseRequest: di.inject(parseRequestInjectable),
|
||||
routes: di.injectMany(routeInjectionToken),
|
||||
createHandlerForRoute: di.inject(createHandlerForRouteInjectable),
|
||||
}),
|
||||
const createRouter = (di: DiContainerForInjection) => {
|
||||
const routes = di.injectMany(routeInjectionToken);
|
||||
const createHandlerForRoute = di.inject(createHandlerForRouteInjectable);
|
||||
const router = new Call.Router<RouteHandler>();
|
||||
|
||||
for (const route of routes) {
|
||||
router.add({ method: route.method, path: route.path }, createHandlerForRoute(route));
|
||||
}
|
||||
|
||||
return router;
|
||||
};
|
||||
|
||||
interface RouterRequestOpts {
|
||||
req: http.IncomingMessage;
|
||||
res: http.ServerResponse;
|
||||
cluster: Cluster | undefined;
|
||||
params: Partial<Record<string, string>>;
|
||||
url: URL;
|
||||
}
|
||||
|
||||
const getRequestWith = (di: DiContainerForInjection) => {
|
||||
const parseRequest = di.inject(parseRequestInjectable);
|
||||
|
||||
return async (opts: RouterRequestOpts): Promise<LensApiRequest<string>> => {
|
||||
const { req, res, url, cluster, params } = opts;
|
||||
const { payload } = await parseRequest(req, null, {
|
||||
parse: true,
|
||||
output: "data",
|
||||
});
|
||||
|
||||
return {
|
||||
cluster,
|
||||
path: url.pathname,
|
||||
raw: {
|
||||
req, res,
|
||||
},
|
||||
query: url.searchParams,
|
||||
payload,
|
||||
params,
|
||||
getHeader: (key) => req.headers[key.toLowerCase()],
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
const routeRequestInjectable = getInjectable({
|
||||
id: "route-request",
|
||||
instantiate: (di): RouteRequest => {
|
||||
const router = createRouter(di);
|
||||
const getRequest = getRequestWith(di);
|
||||
|
||||
return async (cluster, req, res) => {
|
||||
const url = new URL(req.url, "http://localhost");
|
||||
const path = url.pathname;
|
||||
const method = req.method.toLowerCase();
|
||||
const matchingRoute = router.route(method, path);
|
||||
|
||||
if (matchingRoute instanceof Error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const request = await getRequest({ req, res, cluster, url, params: matchingRoute.params });
|
||||
|
||||
await matchingRoute.route(request, res);
|
||||
|
||||
return true;
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default routerInjectable;
|
||||
export default routeRequestInjectable;
|
||||
|
||||
@ -3,9 +3,9 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import routerInjectable, { routeInjectionToken } from "./router.injectable";
|
||||
import type { RouteRequest } from "./router.injectable";
|
||||
import routeRequestInjectable, { routeInjectionToken } from "./router.injectable";
|
||||
import { getDiForUnitTesting } from "../getDiForUnitTesting";
|
||||
import type { Router } from "./router";
|
||||
import type { Cluster } from "../../common/cluster/cluster";
|
||||
import { Request } from "mock-http";
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
@ -24,7 +24,7 @@ import fsInjectable from "../../common/fs/fs.injectable";
|
||||
import { runInAction } from "mobx";
|
||||
|
||||
describe("router", () => {
|
||||
let router: Router;
|
||||
let routeRequest: RouteRequest;
|
||||
let routeHandlerMock: AsyncFnMock<() => any>;
|
||||
|
||||
beforeEach(async () => {
|
||||
@ -51,6 +51,7 @@ describe("router", () => {
|
||||
method: "get",
|
||||
path: "/some-path",
|
||||
handler: routeHandlerMock,
|
||||
requireAuthentication: false,
|
||||
} as Route<any, string>),
|
||||
|
||||
injectionToken: routeInjectionToken,
|
||||
@ -60,7 +61,7 @@ describe("router", () => {
|
||||
di.register(injectable);
|
||||
});
|
||||
|
||||
router = di.inject(routerInjectable);
|
||||
routeRequest = di.inject(routeRequestInjectable);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@ -86,7 +87,7 @@ describe("router", () => {
|
||||
|
||||
clusterStub = {} as Cluster;
|
||||
|
||||
actualPromise = router.route(clusterStub, requestStub, responseStub);
|
||||
actualPromise = routeRequest(clusterStub, requestStub, responseStub);
|
||||
});
|
||||
|
||||
it("calls handler with the request", () => {
|
||||
|
||||
@ -1,72 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import Call from "@hapi/call";
|
||||
import type http from "http";
|
||||
import type { Cluster } from "../../common/cluster/cluster";
|
||||
import type { LensApiRequest, Route } from "./route";
|
||||
import type { ServerIncomingMessage } from "../lens-proxy/lens-proxy";
|
||||
import type { ParseRequest } from "./parse-request.injectable";
|
||||
import type { CreateHandlerForRoute, RouteHandler } from "./create-handler-for-route.injectable";
|
||||
|
||||
export interface RouterRequestOpts {
|
||||
req: http.IncomingMessage;
|
||||
res: http.ServerResponse;
|
||||
cluster: Cluster | undefined;
|
||||
params: Partial<Record<string, string>>;
|
||||
url: URL;
|
||||
}
|
||||
|
||||
interface Dependencies {
|
||||
parseRequest: ParseRequest;
|
||||
createHandlerForRoute: CreateHandlerForRoute;
|
||||
readonly routes: Route<unknown, string>[];
|
||||
}
|
||||
|
||||
export class Router {
|
||||
private readonly router = new Call.Router<RouteHandler>();
|
||||
|
||||
constructor(private readonly dependencies: Dependencies) {
|
||||
for (const route of this.dependencies.routes) {
|
||||
this.router.add({ method: route.method, path: route.path }, this.dependencies.createHandlerForRoute(route));
|
||||
}
|
||||
}
|
||||
|
||||
public async route(cluster: Cluster | undefined, req: ServerIncomingMessage, res: http.ServerResponse): Promise<boolean> {
|
||||
const url = new URL(req.url, "http://localhost");
|
||||
const path = url.pathname;
|
||||
const method = req.method.toLowerCase();
|
||||
const matchingRoute = this.router.route(method, path);
|
||||
|
||||
if (matchingRoute instanceof Error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const request = await this.getRequest({ req, res, cluster, url, params: matchingRoute.params });
|
||||
|
||||
await matchingRoute.route(request, res);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected async getRequest(opts: RouterRequestOpts): Promise<LensApiRequest<string>> {
|
||||
const { req, res, url, cluster, params } = opts;
|
||||
const { payload } = await this.dependencies.parseRequest(req, null, {
|
||||
parse: true,
|
||||
output: "data",
|
||||
});
|
||||
|
||||
return {
|
||||
cluster,
|
||||
path: url.pathname,
|
||||
raw: {
|
||||
req, res,
|
||||
},
|
||||
query: url.searchParams,
|
||||
payload,
|
||||
params,
|
||||
};
|
||||
}
|
||||
}
|
||||
36
src/main/router/write-server-response.ts
Normal file
36
src/main/router/write-server-response.ts
Normal file
@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { ServerResponse } from "http";
|
||||
import { object } from "../../common/utils";
|
||||
|
||||
export interface LensServerResponse {
|
||||
statusCode: number;
|
||||
content: any;
|
||||
headers: {
|
||||
[name: string]: string;
|
||||
};
|
||||
}
|
||||
|
||||
export const writeServerResponseFor = (serverResponse: ServerResponse) => ({
|
||||
statusCode,
|
||||
content,
|
||||
headers,
|
||||
}: LensServerResponse) => {
|
||||
serverResponse.statusCode = statusCode;
|
||||
|
||||
for (const [name, value] of object.entries(headers)) {
|
||||
serverResponse.setHeader(name, value);
|
||||
}
|
||||
|
||||
if (content instanceof Buffer) {
|
||||
serverResponse.write(content);
|
||||
serverResponse.end();
|
||||
} else if (content) {
|
||||
serverResponse.end(content);
|
||||
} else {
|
||||
serverResponse.end();
|
||||
}
|
||||
};
|
||||
@ -17,6 +17,7 @@ const staticFileRouteInjectable = getRouteInjectable({
|
||||
return route({
|
||||
method: "get",
|
||||
path: `/{path*}`,
|
||||
requireAuthentication: false,
|
||||
})(
|
||||
isDevelopment
|
||||
? di.inject(devStaticFileRouteHandlerInjectable)
|
||||
|
||||
33
src/renderer/auth/auth-header-state.injectable.ts
Normal file
33
src/renderer/auth/auth-header-state.injectable.ts
Normal file
@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
|
||||
const authHeaderValueStateInjectable = getInjectable({
|
||||
id: "auth-header-value-state",
|
||||
instantiate: () => {
|
||||
let state: string | undefined = undefined;
|
||||
|
||||
return {
|
||||
get: () =>{
|
||||
if (!state) {
|
||||
throw new Error("Tried to get auth header value before it was initialized");
|
||||
}
|
||||
|
||||
return state;
|
||||
},
|
||||
|
||||
set: (newState: string) => {
|
||||
if (state) {
|
||||
throw new Error("Tried to overwrite existing state of auth header value");
|
||||
}
|
||||
|
||||
state = newState;
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default authHeaderValueStateInjectable;
|
||||
|
||||
13
src/renderer/auth/auth-header.injectable.ts
Normal file
13
src/renderer/auth/auth-header.injectable.ts
Normal file
@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import authHeaderValueStateInjectable from "./auth-header-state.injectable";
|
||||
|
||||
const authHeaderValueInjectable = getInjectable({
|
||||
id: "auth-header-value",
|
||||
instantiate: (di) => di.inject(authHeaderValueStateInjectable).get(),
|
||||
});
|
||||
|
||||
export default authHeaderValueInjectable;
|
||||
27
src/renderer/auth/init.injectable.ts
Normal file
27
src/renderer/auth/init.injectable.ts
Normal file
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { lensAuthenticationChannel } from "../../common/auth/channel";
|
||||
import { beforeFrameStartsInjectionToken } from "../before-frame-starts/before-frame-starts-injection-token";
|
||||
import requestFromChannelInjectable from "../utils/channel/request-from-channel.injectable";
|
||||
import authHeaderValueStateInjectable from "./auth-header-state.injectable";
|
||||
|
||||
const initAuthHeaderValueStateInjectable = getInjectable({
|
||||
id: "init-auth-header-value-state",
|
||||
instantiate: (di) => {
|
||||
const state = di.inject(authHeaderValueStateInjectable);
|
||||
const requestFromChannel = di.inject(requestFromChannelInjectable);
|
||||
|
||||
return {
|
||||
id: "init-auth-header-value-state",
|
||||
run: async () => {
|
||||
state.set(await requestFromChannel(lensAuthenticationChannel));
|
||||
},
|
||||
};
|
||||
},
|
||||
injectionToken: beforeFrameStartsInjectionToken,
|
||||
});
|
||||
|
||||
export default initAuthHeaderValueStateInjectable;
|
||||
Loading…
Reference in New Issue
Block a user