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

can navigate to folder and inherit auth #7353

Merged
merged 23 commits into from
May 17, 2024
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
65 changes: 65 additions & 0 deletions packages/insomnia-smoke-test/fixtures/oauth.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,71 @@ __export_format: 4
__export_date: 2022-02-24T01:02:16.537Z
__export_source: insomnia.desktop.app:v2022.1.0-beta.0
resources:
- _id: req_8f029a22e56341748fa92ad14851a7be
parentId: fld_9fd59e794fe5455692a3c33af67f7668
modified: 1715935264320
created: 1715935207910
url: "http://127.0.0.1:4010/oidc/me"
name: Request with Inherited Auth
description: ""
method: GET
body: {}
preRequestScript: ""
parameters: []
headers:
- name: User-Agent
value: insomnia/9.2.0
authentication: {}
metaSortKey: -1715935264244
isPrivate: false
pathParameters: []
settingStoreCookies: true
settingSendCookies: true
settingDisableRenderRequestBody: false
settingEncodeUrl: true
settingRebuildPath: true
settingFollowRedirects: global
_type: request
- _id: fld_9fd59e794fe5455692a3c33af67f7668
parentId: fld_e45987966c224b63b68b09b34687209c
modified: 1715935256495
created: 1715935256495
name: Inherit Auth Folder
description: ""
environment: {}
environmentPropertyOrder: null
metaSortKey: -1715935256495
preRequestScript: ""
postRequestScript: ""
authentication: {}
_type: request_group
- _id: fld_e45987966c224b63b68b09b34687209c
parentId: wrk_392055e2aa29457b9d2904396cd7631f
modified: 1715935142556
created: 1715935030239
name: Folder Level Auth Code
description: ""
environment: {}
environmentPropertyOrder: null
metaSortKey: -1715935030239
preRequestScript: ""
postRequestScript: ""
authentication:
accessTokenUrl: "http://127.0.0.1:4010/oidc/token"
authorizationUrl: "http://127.0.0.1:4010/oidc/auth"
clientId: "authorization_code"
clientSecret: "secret"
disabled: false
grantType: authorization_code
redirectUrl: "http://127.0.0.1:4010/callback"
responseType: id_token
scope: openid offline_access
state: ""
type: oauth2
usePkce: false
credentialsInBody: "false"
tokenPrefix: ""
_type: request_group
- _id: req_54f2824040c847ebaf3ed6d080111b4e
parentId: fld_0e50ba4426bb4540ade91e0525ea1f29
modified: 1645664215605
Expand Down
7 changes: 7 additions & 0 deletions packages/insomnia-smoke-test/tests/smoke/oauth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,13 @@ test('can make oauth2 requests', async ({ app, page }) => {
await expect(statusTag).toContainText('200 OK');
await expect(responseBody).toContainText('"sub": "admin"');

// Inherited Auth from folder
await page.getByLabel('Request Collection').getByTestId('Request with Inherited Auth').press('Enter');
await expect(page.locator('.app')).toContainText('http://127.0.0.1:4010/oidc/me');
await sendButton.click();
await expect(statusTag).toContainText('200 OK');
await expect(responseBody).toContainText('"sub": "admin"');

// Reset the OAuth 2 session from Preferences
if (process.platform === 'darwin') {
await page.keyboard.press('Meta+,');
Expand Down
7 changes: 4 additions & 3 deletions packages/insomnia/src/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,7 @@ const authTypesMap: Record<string, string[]> = {
[AUTH_AWS_IAM]: ['AWS', 'AWS IAM v4'],
[AUTH_ASAP]: ['ASAP', 'Atlassian ASAP'],
[AUTH_NETRC]: ['Netrc', 'Netrc File'],
[AUTH_NONE]: ['None', 'No Auth'],
};

// Sort Orders
Expand Down Expand Up @@ -406,11 +407,11 @@ export function getContentTypeName(contentType?: string | null, useLong = false)
return useLong ? contentTypesMap[CONTENT_TYPE_OTHER][1] : contentTypesMap[CONTENT_TYPE_OTHER][0];
}

export function getAuthTypeName(authType: string, useLong = false) {
if (authTypesMap.hasOwnProperty(authType)) {
export function getAuthTypeName(authType?: string, useLong = false) {
if (authType && authTypesMap.hasOwnProperty(authType)) {
return useLong ? authTypesMap[authType][1] : authTypesMap[authType][0];
} else {
return '';
return 'Auth';
}
}

Expand Down
2 changes: 1 addition & 1 deletion packages/insomnia/src/common/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -565,7 +565,7 @@ export const database = {
}
},

withAncestors: async function<T extends BaseModel>(doc: T | null, types: string[] = allTypes()) {
withAncestors: async function <T extends BaseModel>(doc: T | null, types: string[] = allTypes()) {
if (db._empty) {
return _send<T[]>('withAncestors', ...arguments);
}
Expand Down
5 changes: 3 additions & 2 deletions packages/insomnia/src/common/har.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import { Cookie as ToughCookie } from 'tough-cookie';

import * as models from '../models';
import type { Request } from '../models/request';
import type { RequestGroup } from '../models/request-group';
import type { Response } from '../models/response';
import { isWorkspace } from '../models/workspace';
import { isWorkspace, type Workspace } from '../models/workspace';
import { getAuthHeader } from '../network/authentication';
import * as plugins from '../plugins';
import * as pluginContexts from '../plugins/context/index';
Expand All @@ -26,7 +27,7 @@ export interface ExportRequest {
}

export async function exportHarCurrentRequest(request: Request, response: Response): Promise<Har.Har> {
const ancestors = await database.withAncestors(request, [
const ancestors = await database.withAncestors<Request | RequestGroup | Workspace>(request, [
models.workspace.type,
models.requestGroup.type,
]);
Expand Down
6 changes: 4 additions & 2 deletions packages/insomnia/src/common/send-request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import path from 'path';

import { BaseModel, types as modelTypes } from '../models';
import * as models from '../models';
import type { Request } from '../models/request';
import type { RequestGroup } from '../models/request-group';
import { getBodyBuffer } from '../models/response';
import { Settings } from '../models/settings';
import { isWorkspace } from '../models/workspace';
import { isWorkspace, type Workspace } from '../models/workspace';
import {
responseTransform,
sendCurlAndWriteTimeline,
Expand Down Expand Up @@ -48,7 +50,7 @@ export async function getSendRequestCallbackMemDb(environmentId: string, memDB:
const fetchInsoRequestData = async (requestId: string) => {
const request = await models.request.getById(requestId);
invariant(request, 'failed to find request');
const ancestors = await database.withAncestors(request, [
const ancestors = await database.withAncestors<Request | RequestGroup | Workspace>(request, [
models.request.type,
models.requestGroup.type,
models.workspace.type,
Expand Down
2 changes: 1 addition & 1 deletion packages/insomnia/src/models/grpc-request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export const isGrpcRequest = (model: Pick<BaseModel, 'type'>): model is GrpcRequ
model.type === type
);

export const isGrpcRequestId = (id: string | null) => (
export const isGrpcRequestId = (id?: string | null) => (
id?.startsWith(`${prefix}_`)
);

Expand Down
9 changes: 8 additions & 1 deletion packages/insomnia/src/models/request-group.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { database as db } from '../common/database';
import type { BaseModel } from './index';
import { RequestAuthentication } from './request';

export const name = 'Folder';

Expand All @@ -16,6 +17,9 @@ interface BaseRequestGroup {
environment: Record<string, any>;
environmentPropertyOrder: Record<string, any> | null;
metaSortKey: number;
preRequestScript: string;
postRequestScript: string;
authentication: RequestAuthentication | {};
}

export type RequestGroup = BaseModel & BaseRequestGroup;
Expand All @@ -31,6 +35,9 @@ export function init(): BaseRequestGroup {
environment: {},
environmentPropertyOrder: null,
metaSortKey: -1 * Date.now(),
preRequestScript: '',
postRequestScript: '',
authentication: {},
};
}

Expand Down Expand Up @@ -96,4 +103,4 @@ export async function duplicate(requestGroup: RequestGroup, patch: Partial<Reque
});
}

export const isRequestGroupId = (id: string) => id.startsWith(prefix);
export const isRequestGroupId = (id?: string | null) => id?.startsWith(prefix);
2 changes: 1 addition & 1 deletion packages/insomnia/src/models/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ export const isRequest = (model: Pick<BaseModel, 'type'>): model is Request => (
model.type === type
);

export const isRequestId = (id: string | null) => (
export const isRequestId = (id?: string | null) => (
id?.startsWith(`${prefix}_`)
);

Expand Down
2 changes: 1 addition & 1 deletion packages/insomnia/src/models/websocket-request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const isWebSocketRequest = (model: Pick<BaseModel, 'type'>): model is Web
model.type === type
);

export const isWebSocketRequestId = (id: string | null) => (
export const isWebSocketRequestId = (id?: string | null) => (
id?.startsWith(`${prefix}_`)
);

Expand Down
3 changes: 3 additions & 0 deletions packages/insomnia/src/network/authentication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,3 +189,6 @@ export const _buildBearerHeader = (accessToken: string, prefix?: string) => {

return header;
};
export const isAuthEnabled = (auth?: RequestAuthentication | {}) => (auth && 'disabled' in auth) ? auth.disabled !== true : true;
export const getAuthObjectOrNull = (auth?: RequestAuthentication | {}): RequestAuthentication | null =>
(auth === undefined || Object.keys(auth).length === 0 || !('type' in auth)) ? null : auth;
4 changes: 2 additions & 2 deletions packages/insomnia/src/network/grpc/write-proto-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type { BaseModel } from '../../models';
import * as models from '../../models';
import { isProtoDirectory, ProtoDirectory } from '../../models/proto-directory';
import { isProtoFile, ProtoFile } from '../../models/proto-file';
import { isWorkspace } from '../../models/workspace';
import { isWorkspace, type Workspace } from '../../models/workspace';

interface WriteResult {
filePath: string;
Expand Down Expand Up @@ -40,7 +40,7 @@ const recursiveWriteProtoDirectory = async (

export const writeProtoFile = async (protoFile: ProtoFile): Promise<WriteResult> => {
// Find all ancestors
const ancestors = await db.withAncestors(protoFile, [
const ancestors = await db.withAncestors<ProtoFile | ProtoDirectory | Workspace>(protoFile, [
models.protoDirectory.type,
models.workspace.type,
]);
Expand Down
31 changes: 26 additions & 5 deletions packages/insomnia/src/network/network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,12 @@ import { CaCertificate } from '../models/ca-certificate';
import { ClientCertificate } from '../models/client-certificate';
import { CookieJar } from '../models/cookie-jar';
import { Environment } from '../models/environment';
import { MockRoute } from '../models/mock-route';
import { MockServer } from '../models/mock-server';
import type { Request, RequestAuthentication, RequestParameter } from '../models/request';
import { isRequestGroup, RequestGroup } from '../models/request-group';
import type { Settings } from '../models/settings';
import { isWorkspace } from '../models/workspace';
import { isWorkspace, Workspace } from '../models/workspace';
import * as pluginContexts from '../plugins/context/index';
import * as plugins from '../plugins/index';
import { invariant } from '../utils/invariant';
Expand All @@ -33,15 +36,30 @@ import {
joinUrlAndQueryString,
smartEncodeUrl,
} from '../utils/url/querystring';
import { getAuthHeader, getAuthQueryParams } from './authentication';
import { getAuthHeader, getAuthObjectOrNull, getAuthQueryParams, isAuthEnabled } from './authentication';
import { cancellableCurlRequest, cancellableRunPreRequestScript } from './cancellation';
import { filterClientCertificates } from './certificate';
import { addSetCookiesToToughCookieJar } from './set-cookie-util';

export const getOrInheritAuthentication = ({ request, requestGroups }: { request: Request; requestGroups: RequestGroup[] }): RequestAuthentication | {} => {
const hasValidAuth = getAuthObjectOrNull(request.authentication) && isAuthEnabled(request.authentication);
if (hasValidAuth) {
return request.authentication;
}
const hasParentFolders = requestGroups.length > 0;
const closestParentFolderWithAuth = requestGroups.find(({ authentication }) => getAuthObjectOrNull(authentication) && isAuthEnabled(authentication));
const shouldCheckFolderAuth = hasParentFolders && closestParentFolderWithAuth;
if (shouldCheckFolderAuth) {
// override auth with closest parent folder that has one set
return closestParentFolderWithAuth.authentication;
}
return { type: 'none' };
};

export const fetchRequestData = async (requestId: string) => {
const request = await models.request.getById(requestId);
invariant(request, 'failed to find request');
const ancestors = await db.withAncestors(request, [
const ancestors = await db.withAncestors<Request | RequestGroup | Workspace | MockRoute | MockServer>(request, [
models.request.type,
models.requestGroup.type,
models.workspace.type,
Expand All @@ -50,12 +68,15 @@ export const fetchRequestData = async (requestId: string) => {
]);
const workspaceDoc = ancestors.find(isWorkspace);
invariant(workspaceDoc?._id, 'failed to find workspace');

const workspaceId = workspaceDoc._id;

const workspace = await models.workspace.getById(workspaceId);
invariant(workspace, 'failed to find workspace');
const workspaceMeta = await models.workspaceMeta.getOrCreateByParentId(workspace._id);
// check for authentication overrides in parent folders
const requestGroups = ancestors.filter(isRequestGroup) as RequestGroup[];
request.authentication = getOrInheritAuthentication({ request, requestGroups });
// TODO: add inherit to auth list

// fallback to base environment
const activeEnvironmentId = workspaceMeta.activeEnvironmentId;
Expand Down Expand Up @@ -100,6 +121,7 @@ export const tryToExecutePreRequestScript = async (
// TODO: restart the hidden browser window
}, timeout + 1000);
});

const preRequestPromise = cancellableRunPreRequestScript({
script: request.preRequestScript,
context: {
Expand Down Expand Up @@ -222,7 +244,6 @@ export async function sendCurlAndWriteTimeline(
if (!renderedRequest.settingSendCookies) {
timelineStrings.push(JSON.stringify({ value: 'Disable cookie sending due to user setting', name: 'Text', timestamp: Date.now() }) + '\n');
}

const authHeader = await getAuthHeader(renderedRequest, finalUrl);
const requestOptions = {
requestId,
Expand Down
5 changes: 4 additions & 1 deletion packages/insomnia/src/templating/base-extension.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { database as db } from '../common/database';
import * as models from '../models/index';
import type { Request } from '../models/request';
import type { RequestGroup } from '../models/request-group';
import type { Workspace } from '../models/workspace';
import * as pluginContexts from '../plugins/context';
import { PluginTemplateTag } from './extensions';
import * as templating from './index';
Expand Down Expand Up @@ -119,7 +122,7 @@ export default class BaseExtension {
request: {
getById: models.request.getById,
getAncestors: async (request: any) => {
const ancestors = await db.withAncestors(request, [
const ancestors = await db.withAncestors<Request | RequestGroup | Workspace>(request, [
models.requestGroup.type,
models.workspace.type,
]);
Expand Down