Skip to content

Commit

Permalink
Merge pull request #383 from KxSystems/ee-ssl
Browse files Browse the repository at this point in the history
Accept self-signed ssl certificates and add https for Insights
  • Loading branch information
ecmel authored Jul 23, 2024
2 parents 45c8d1c + ef48c45 commit 032c031
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 43 deletions.
70 changes: 44 additions & 26 deletions src/classes/insightsConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ import axios, { AxiosRequestConfig } from "axios";
import { ProgressLocation, window } from "vscode";
import * as url from "url";
import { MetaInfoType, MetaObject, MetaObjectPayload } from "../models/meta";
import { getCurrentToken } from "../services/kdbInsights/codeFlowLogin";
import {
getCurrentToken,
getHttpsAgent,
} from "../services/kdbInsights/codeFlowLogin";
import { InsightsNode } from "../services/kdbTreeProvider";
import { GetDataObjectPayload } from "../models/data";
import { isCompressed, uncompress } from "../ipc/c";
Expand Down Expand Up @@ -54,6 +57,7 @@ export class InsightsConnection {
this.node.details.server,
this.node.details.alias,
this.node.details.realm || "insights",
!!this.node.details.insecure,
).then(async (token) => {
this.connected = token ? true : false;
if (token) {
Expand All @@ -74,28 +78,40 @@ export class InsightsConnection {
//will be added the feature to retrieve server objects from insights
}

private async getOptions() {
const token = await getCurrentToken(
this.node.details.server,
this.node.details.alias,
this.node.details.realm || "insights",
!!this.node.details.insecure,
);

if (token === undefined) {
tokenUndefinedError(this.connLabel);
return undefined;
}

const options: AxiosRequestConfig = {
headers: { Authorization: `Bearer ${token.accessToken}` },
httpsAgent: getHttpsAgent(this.node.details.insecure),
};

return options;
}

public async getMeta(): Promise<MetaObjectPayload | undefined> {
if (this.connected) {
const metaUrl = new url.URL(
ext.insightsServiceGatewayUrls.meta,
this.node.details.server,
);

const token = await getCurrentToken(
this.node.details.server,
this.node.details.alias,
this.node.details.realm || "insights",
);
const options = await this.getOptions();

if (token === undefined) {
tokenUndefinedError(this.connLabel);
if (options === undefined) {
return undefined;
}

const options = {
headers: { Authorization: `Bearer ${token.accessToken}` },
};

const metaResponse = await axios.post(metaUrl.toString(), {}, options);
const meta: MetaObject = metaResponse.data;
this.meta = meta;
Expand All @@ -110,21 +126,13 @@ export class InsightsConnection {
ext.insightsAuthUrls.configURL,
this.node.details.server,
);
const token = await getCurrentToken(
this.node.details.server,
this.node.details.alias,
this.node.details.realm || "insights",
);

if (token === undefined) {
tokenUndefinedError(this.connLabel);
const options = await this.getOptions();

if (options === undefined) {
return undefined;
}

const options = {
headers: { Authorization: `Bearer ${token.accessToken}` },
};

const configResponse = await axios.get(configUrl.toString(), options);
this.config = configResponse.data;
this.getInsightsVersion();
Expand Down Expand Up @@ -209,6 +217,7 @@ export class InsightsConnection {
this.node.details.server,
this.node.details.alias,
this.node.details.realm || "insights",
!!this.node.details.insecure,
);
if (token === undefined) {
tokenUndefinedError(this.connLabel);
Expand All @@ -226,6 +235,7 @@ export class InsightsConnection {
data: body,
headers: headers,
responseType: "arraybuffer",
httpsAgent: getHttpsAgent(this.node.details.insecure),
};
const results = await window.withProgress(
{
Expand Down Expand Up @@ -315,6 +325,7 @@ export class InsightsConnection {
this.node.details.server,
this.node.details.alias,
this.node.details.realm || "insights",
!!this.node.details.insecure,
);

if (token === undefined) {
Expand All @@ -326,12 +337,13 @@ export class InsightsConnection {
if (username === undefined || username.preferred_username === "") {
invalidUsernameJWT(this.connLabel);
}
const headers = {
const headers: AxiosRequestConfig = {
headers: {
Authorization: `Bearer ${token.accessToken}`,
username: username.preferred_username!,
json: true,
},
httpsAgent: getHttpsAgent(this.node.details.insecure),
};
const body = {
output: variableName,
Expand Down Expand Up @@ -399,6 +411,7 @@ export class InsightsConnection {
this.node.details.server,
this.node.details.alias,
this.node.details.realm || "insights",
!!this.node.details.insecure,
);
if (token === undefined) {
tokenUndefinedError(this.connLabel);
Expand Down Expand Up @@ -434,7 +447,10 @@ export class InsightsConnection {

progress.report({ message: "Query is executing..." });
const spRes = await axios
.post(scratchpadURL.toString(), body, { headers })
.post(scratchpadURL.toString(), body, {
headers,
httpsAgent: getHttpsAgent(this.node.details.insecure),
})
.then((response: any) => {
kdbOutputLog(`[SCRATCHPAD] Status: ${response.status}`, "INFO");
if (isTableView && !response.data.error) {
Expand Down Expand Up @@ -470,6 +486,7 @@ export class InsightsConnection {
this.node.details.server,
this.node.details.alias,
this.node.details.realm || "insights",
!!this.node.details.insecure,
);

if (token === undefined) {
Expand All @@ -482,12 +499,13 @@ export class InsightsConnection {
invalidUsernameJWT(this.connLabel);
return false;
}
const headers = {
const headers: AxiosRequestConfig = {
headers: {
Authorization: `Bearer ${token.accessToken}`,
username: username.preferred_username!,
json: true,
},
httpsAgent: getHttpsAgent(this.node.details.insecure),
};
return await window.withProgress(
{
Expand Down
10 changes: 8 additions & 2 deletions src/commands/serverCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,21 +94,27 @@ export async function addInsightsConnection(insightsData: InsightDetails) {
return;
} else {
const key = insightsData.alias;
let server = insightsData.server || "";
if (!/^https?:\/\//i.exec(server)) {
server = "https://" + server;
}
if (insights === undefined) {
insights = {
key: {
auth: true,
alias: insightsData.alias,
server: insightsData.server!,
server,
realm: insightsData.realm,
insecure: insightsData.insecure,
},
};
} else {
insights[key] = {
auth: true,
alias: insightsData.alias,
server: insightsData.server!,
server,
realm: insightsData.realm,
insecure: insightsData.insecure,
};
}

Expand Down
1 change: 1 addition & 0 deletions src/models/insights.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface InsightDetails {
server: string;
auth: boolean;
realm?: string;
insecure?: boolean;
}

export interface Insights {
Expand Down
41 changes: 31 additions & 10 deletions src/services/kdbInsights/codeFlowLogin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
* specific language governing permissions and limitations under the License.
*/

import axios from "axios";
import axios, { AxiosRequestConfig } from "axios";
import * as crypto from "crypto";
import * as fs from "fs-extra";
import * as http from "http";
Expand All @@ -21,6 +21,7 @@ import * as url from "url";
import { ext } from "../../extensionVariables";
import { Uri, env } from "vscode";
import { pickPort } from "pick-port";
import https from "https";

interface IDeferred<T> {
resolve: (result: T | Promise<T>) => void;
Expand Down Expand Up @@ -55,14 +56,22 @@ export interface IToken {
refreshToken: string;
}

export function getHttpsAgent(insecure: boolean | undefined) {
return new https.Agent({ rejectUnauthorized: !insecure });
}

const defaultTimeout = 3 * 60 * 1000; // 3 min
const closeTimeout = 10 * 1000; // 10 sec

const commonRequestParams = {
client_id: "insights-app",
};

export async function signIn(insightsUrl: string, realm: string) {
export async function signIn(
insightsUrl: string,
realm: string,
insecure: boolean,
) {
const { server, codePromise } = createServer();

try {
Expand All @@ -82,7 +91,7 @@ export async function signIn(insightsUrl: string, realm: string) {
await env.openExternal(Uri.parse(authorizationUrl.toString()));

const code = await codePromise;
return await getToken(insightsUrl, realm, code);
return await getToken(insightsUrl, realm, insecure, code);
} finally {
setImmediate(() => server.close());
}
Expand All @@ -91,6 +100,7 @@ export async function signIn(insightsUrl: string, realm: string) {
export async function signOut(
insightsUrl: string,
realm: string,
insecure: boolean,
token: string,
): Promise<void> {
const queryParams = queryString({
Expand All @@ -100,8 +110,9 @@ export async function signOut(
const body = {
body: queryParams,
};
const headers = {
const headers: AxiosRequestConfig = {
headers: { "Content-Type": "application/x-www-form-urlencoded" },
httpsAgent: getHttpsAgent(insecure),
};
const requestUrl = getRevokeUrl(insightsUrl, realm);

Expand All @@ -113,9 +124,10 @@ export async function signOut(
export async function refreshToken(
insightsUrl: string,
realm: string,
insecure: boolean,
token: string,
): Promise<IToken | undefined> {
return await tokenRequest(insightsUrl, realm, {
return await tokenRequest(insightsUrl, realm, insecure, {
grant_type: ext.insightsGrantType.refreshToken,
refresh_token: token,
});
Expand All @@ -126,6 +138,7 @@ export async function getCurrentToken(
serverName: string,
serverAlias: string,
realm: string,
insecure: boolean,
): Promise<IToken | undefined> {
if (serverName === "" || serverAlias === "") {
return undefined;
Expand All @@ -137,9 +150,14 @@ export async function getCurrentToken(
if (existingToken !== undefined) {
const storedToken: IToken = JSON.parse(existingToken);
if (new Date(storedToken.accessTokenExpirationDate) < new Date()) {
token = await refreshToken(serverName, realm, storedToken.refreshToken);
token = await refreshToken(
serverName,
realm,
insecure,
storedToken.refreshToken,
);
if (token === undefined) {
token = await signIn(serverName, realm);
token = await signIn(serverName, realm, insecure);
ext.context.secrets.store(serverAlias, JSON.stringify(token));
}
ext.context.secrets.store(serverAlias, JSON.stringify(token));
Expand All @@ -148,7 +166,7 @@ export async function getCurrentToken(
return storedToken;
}
} else {
token = await signIn(serverName, realm);
token = await signIn(serverName, realm, insecure);
ext.context.secrets.store(serverAlias, JSON.stringify(token));
}
return token;
Expand All @@ -158,9 +176,10 @@ export async function getCurrentToken(
async function getToken(
insightsUrl: string,
realm: string,
insecure: boolean,
code: string,
): Promise<IToken | undefined> {
return await tokenRequest(insightsUrl, realm, {
return await tokenRequest(insightsUrl, realm, insecure, {
code,
grant_type: ext.insightsGrantType.authorizationCode,
});
Expand All @@ -169,15 +188,17 @@ async function getToken(
async function tokenRequest(
insightsUrl: string,
realm: string,
insecure: boolean,
params: any,
): Promise<IToken | undefined> {
const queryParams = queryString(params);
const headers = {
const headers: AxiosRequestConfig = {
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
timeout: closeTimeout,
signal: AbortSignal.timeout(closeTimeout),
httpsAgent: getHttpsAgent(insecure),
};

const requestUrl = getTokenUrl(insightsUrl, realm);
Expand Down
10 changes: 10 additions & 0 deletions src/webview/components/kdbNewConnectionView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export class KdbNewConnectionView extends LitElement {
server: "",
auth: true,
realm: "",
insecure: false,
};
this.bundledServer = {
serverName: "127.0.0.1",
Expand Down Expand Up @@ -410,6 +411,15 @@ export class KdbNewConnectionView extends LitElement {
<details>
<summary>Advanced</summary>
${this.renderRealm()}
<div class="row mt-1">
<vscode-checkbox
.checked="${this.insightsServer.insecure}"
@change="${(event: Event) => {
this.insightsServer.insecure = (
event.target as HTMLInputElement
).checked;
}}">Accept insecure SSL certifcates</vscode-checkbox>
</div>
</details>
</div>
</div>
Expand Down
Loading

0 comments on commit 032c031

Please sign in to comment.