Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: remove {} from OpenApiRequest.openapi type #520

Merged
merged 3 commits into from
Jan 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/framework/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export type Format = {
};

export type Serializer = {
format: string,
format: string;
serialize: (o: unknown) => string;
};

Expand Down Expand Up @@ -436,7 +436,7 @@ export interface OpenApiRequestMetadata {
}

export interface OpenApiRequest extends Request {
openapi?: OpenApiRequestMetadata | {};
openapi?: OpenApiRequestMetadata;
}

export type OpenApiRequestHandler = (
Expand Down
13 changes: 12 additions & 1 deletion src/middlewares/openapi.metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { pathToRegexp } from 'path-to-regexp';
import { Response, NextFunction } from 'express';
import { OpenApiContext } from '../framework/openapi.context';
import {
MethodNotAllowed,
NotFound,
OpenApiRequest,
OpenApiRequestHandler,
OpenApiRequestMetadata,
Expand All @@ -24,6 +26,12 @@ export function applyOpenApiMetadata(
const matched = lookupRoute(req);
if (matched) {
const { expressRoute, openApiRoute, pathParams, schema } = matched;
if (!schema) {
throw new MethodNotAllowed({
path: req.path,
message: `${req.method} method not allowed`,
});
}
req.openapi = {
expressRoute: expressRoute,
openApiRoute: openApiRoute,
Expand All @@ -36,7 +44,10 @@ export function applyOpenApiMetadata(
(<any>req.openapi)._responseSchema = (<any>matched)._responseSchema;
}
} else if (openApiContext.isManagedRoute(path)) {
req.openapi = {};
throw new NotFound({
path: req.path,
message: 'not found',
});
}
next();
};
Expand Down
14 changes: 0 additions & 14 deletions src/middlewares/openapi.request.validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,21 +63,7 @@ export class RequestValidator {

const openapi = <OpenApiRequestMetadata>req.openapi;
const path = openapi.expressRoute;
if (!path) {
throw new NotFound({
path: req.path,
message: 'not found',
});
}

const reqSchema = openapi.schema;
if (!reqSchema) {
throw new MethodNotAllowed({
path: req.path,
message: `${req.method} method not allowed`,
});
}

// cache middleware by combining method, path, and contentType
const contentType = ContentType.from(req);
const contentTypeKey = contentType.equivalents()[0] ?? 'not_provided';
Expand Down
182 changes: 83 additions & 99 deletions src/middlewares/openapi.security.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,28 +38,6 @@ export function security(
}

const openapi = <OpenApiRequestMetadata>req.openapi;
const expressRoute = openapi.expressRoute;
if (!expressRoute) {
return next(
new NotFound({
path: req.path,
message: 'not found',
}),
);
}

const pathSchema = openapi.schema;
if (!pathSchema) {
// add openapi metadata to make this case more clear
// its not obvious that missig schema means methodNotAllowed
return next(
new MethodNotAllowed({
path: req.path,
message: `${req.method} method not allowed`,
}),
);
}

// use the local security object or fallbac to api doc's security or undefined
const securities: OpenAPIV3.SecurityRequirementObject[] =
openapi.schema.security ?? apiDoc.security;
Expand All @@ -82,54 +60,54 @@ export function security(
securitySchemes,
securityHandlers,
securities,
).executeHandlers(req);
).executeHandlers(req);

// TODO handle AND'd and OR'd security
// This assumes OR only! i.e. at least one security passed authentication
let firstError: SecurityHandlerResult = null;
let firstError: SecurityHandlerResult = null;
let success = false;

function checkErrorWithOr(res) {
return res.forEach(r => {
if (r.success) {
success = true;
} else if (!firstError) {
firstError = r;
}
})
return res.forEach((r) => {
if (r.success) {
success = true;
} else if (!firstError) {
firstError = r;
}
});
}

function checkErrorsWithAnd(res) {
let allSuccess = false;

res.forEach(r => {
if (!r.success) {
allSuccess = false;
if (!firstError) {
firstError = r;
}
} else if (!firstError) {
allSuccess = true;
}
})

if (allSuccess) {
success = true;
let allSuccess = false;

res.forEach((r) => {
if (!r.success) {
allSuccess = false;
if (!firstError) {
firstError = r;
}
} else if (!firstError) {
allSuccess = true;
}
});

if (allSuccess) {
success = true;
}
}

results.forEach(result => {
if (Array.isArray(result) && result.length > 1) {
checkErrorsWithAnd(result);
} else {
checkErrorWithOr(result);
}
results.forEach((result) => {
if (Array.isArray(result) && result.length > 1) {
checkErrorsWithAnd(result);
} else {
checkErrorWithOr(result);
}
});

if (success) {
next();
next();
} else {
throw firstError;
throw firstError;
}
} catch (e) {
const message = e?.error?.message || 'unauthorized';
Expand Down Expand Up @@ -177,50 +155,56 @@ class SecuritySchemes {
// anonumous security
return [{ success: true }];
}
return Promise.all(Object.keys(s).map(async (securityKey) => {
var _a, _b, _c;
try {
const scheme = this.securitySchemes[securityKey];
const handler = (_b = (_a = this.securityHandlers) === null || _a === void 0 ? void 0 : _a[securityKey]) !== null && _b !== void 0 ? _b : fallbackHandler;
const scopesTmp = s[securityKey];
const scopes = Array.isArray(scopesTmp) ? scopesTmp : [];
if (!scheme) {
const message = `components.securitySchemes.${securityKey} does not exist`;
throw new InternalServerError({ message });
}
if (!scheme.hasOwnProperty('type')) {
const message = `components.securitySchemes.${securityKey} must have property 'type'`;
throw new InternalServerError({ message });
}
if (!handler) {
const message = `a security handler for '${securityKey}' does not exist`;
throw new InternalServerError({ message });
}
new AuthValidator(req, scheme, scopes).validate();
// expected handler results are:
// - throw exception,
// - return true,
// - return Promise<true>,
// - return false,
// - return Promise<false>
// everything else should be treated as false
const securityScheme = <OpenAPIV3.SecuritySchemeObject>scheme;
const success = await handler(req, scopes, securityScheme);
if (success === true) {
return { success };
}
else {
throw Error();
return Promise.all(
Object.keys(s).map(async (securityKey) => {
var _a, _b, _c;
try {
const scheme = this.securitySchemes[securityKey];
const handler =
(_b =
(_a = this.securityHandlers) === null || _a === void 0
? void 0
: _a[securityKey]) !== null && _b !== void 0
? _b
: fallbackHandler;
const scopesTmp = s[securityKey];
const scopes = Array.isArray(scopesTmp) ? scopesTmp : [];
if (!scheme) {
const message = `components.securitySchemes.${securityKey} does not exist`;
throw new InternalServerError({ message });
}
if (!scheme.hasOwnProperty('type')) {
const message = `components.securitySchemes.${securityKey} must have property 'type'`;
throw new InternalServerError({ message });
}
if (!handler) {
const message = `a security handler for '${securityKey}' does not exist`;
throw new InternalServerError({ message });
}
new AuthValidator(req, scheme, scopes).validate();
// expected handler results are:
// - throw exception,
// - return true,
// - return Promise<true>,
// - return false,
// - return Promise<false>
// everything else should be treated as false
const securityScheme = <OpenAPIV3.SecuritySchemeObject>scheme;
const success = await handler(req, scopes, securityScheme);
if (success === true) {
return { success };
} else {
throw Error();
}
} catch (e) {
return {
success: false,
status: (_c = e.status) !== null && _c !== void 0 ? _c : 401,
error: e,
};
}
}
catch (e) {
return {
success: false,
status: (_c = e.status) !== null && _c !== void 0 ? _c : 401,
error: e,
};
}
}));
}),
);
});
return Promise.all(promises);
}
Expand Down