Skip to content

Commit

Permalink
can navigate to folder and inherit auth (#7353)
Browse files Browse the repository at this point in the history
* can navigate to folder

* add docs tab

* can edit pre request script

* refactor authdropdown to support groups

* can input auth in folders

* can concat scripts

* ensure scripts are scoped

* inherit auth from parent folder

* extract to function

* support inherit

* remove console log

* fix inheritance

* fix types

* remove console.log

* skip pre request for now

* hide response pane

* reload on env modified

* fix oauth1 and support undefined auth

* add auth test

* watch sync for updates

* collapse folder only on icon click

* review feedback

* remove pre-req logic
  • Loading branch information
jackkav committed May 17, 2024
1 parent 9b0654f commit 5e67376
Show file tree
Hide file tree
Showing 34 changed files with 417 additions and 172 deletions.
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

0 comments on commit 5e67376

Please sign in to comment.