这是indexloc提供的服务,不要输入任何密码
Skip to content
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
7 changes: 4 additions & 3 deletions lib/actions/google/drive/google_drive.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Credentials, OAuth2Client } from "google-auth-library";
import { drive_v3 } from "googleapis";
import * as Hub from "../../../hub";
import Drive = drive_v3.Drive;
export declare class GoogleDriveAction extends Hub.OAuthAction {
export declare class GoogleDriveAction extends Hub.OAuthActionV2 {
name: string;
label: string;
iconName: string;
Expand All @@ -23,9 +23,10 @@ export declare class GoogleDriveAction extends Hub.OAuthAction {
execute(request: Hub.ActionRequest): Promise<Hub.ActionResponse>;
form(request: Hub.ActionRequest): Promise<Hub.ActionForm>;
oauthUrl(redirectUri: string, encryptedState: string): Promise<string>;
oauthFetchInfo(urlParams: {
oauthHandleRedirect(urlParams: {
[key: string]: string;
}, redirectUri: string): Promise<void>;
}, redirectUri: string): Promise<string>;
oauthFetchAccessToken(request: Hub.ActionRequest): Promise<string>;
oauthCheck(request: Hub.ActionRequest): Promise<boolean>;
oauth2Client(redirectUri: string | undefined): OAuth2Client;
sendData(filename: string, request: Hub.ActionRequest, drive: Drive): Promise<GaxiosResponse<drive_v3.Schema$File>>;
Expand Down
53 changes: 43 additions & 10 deletions lib/actions/google/drive/google_drive.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const action_response_1 = require("../../../hub/action_response");
const domain_validator_1 = require("./domain_validator");
const LOG_PREFIX = "[GOOGLE_DRIVE]";
const FOLDERID_REGEX = /\/folders\/(?<folderId>[^\/?]+)/;
class GoogleDriveAction extends Hub.OAuthAction {
class GoogleDriveAction extends Hub.OAuthActionV2 {
constructor() {
super(...arguments);
this.name = "google_drive";
Expand Down Expand Up @@ -218,19 +218,50 @@ class GoogleDriveAction extends Hub.OAuthAction {
});
return url.toString();
}
async oauthFetchInfo(urlParams, redirectUri) {
async oauthHandleRedirect(urlParams, redirectUri) {
const actionCrypto = new Hub.ActionCrypto();
const plaintext = await actionCrypto.decrypt(urlParams.state).catch((err) => {
winston.error("Encryption not correctly configured" + err);
throw err;
});
const tokens = await this.getAccessTokenCredentialsFromCode(redirectUri, urlParams.code);
// Pass back context to Looker
const payload = JSON.parse(plaintext);
await https.post({
url: payload.stateurl,
body: JSON.stringify({ tokens, redirect: redirectUri }),
}).catch((_err) => { winston.error(_err.toString()); });
const statePayload = JSON.parse(plaintext);
if (statePayload.hasOwnProperty("tokenurl")) {
// redirect user back to Looker with context
const newState = {
code: urlParams.code,
redirecturi: redirectUri,
};
const jsonString = JSON.stringify(newState);
const ciphertextBlob = await actionCrypto.encrypt(jsonString).catch((err) => {
winston.error("Encryption not correctly configured");
throw err;
});
return `${statePayload.tokenurl}?state=${ciphertextBlob}`;
}
else {
// Pass back context to Looker
const tokens = await this.getAccessTokenCredentialsFromCode(redirectUri, urlParams.code);
await https.post({
url: statePayload.stateurl,
body: JSON.stringify({ tokens, redirect: redirectUri }),
}).catch((_err) => { winston.error(_err.toString()); });
return "";
}
}
async oauthFetchAccessToken(request) {
if (request.params.state) {
const actionCrypto = new Hub.ActionCrypto();
const plaintext = await actionCrypto.decrypt(request.params.state).catch((err) => {
winston.error("Encryption not correctly configured" + err);
throw err;
});
const state = JSON.parse(plaintext);
const tokens = await this.getAccessTokenCredentialsFromCode(state.redirecturi, state.code);
return JSON.stringify({ tokens, redirect: state.redirecturi });
}
else {
throw new Error("Request is missing state parameter.");
}
}
async oauthCheck(request) {
if (request.params.state_json) {
Expand Down Expand Up @@ -389,8 +420,10 @@ class GoogleDriveAction extends Hub.OAuthAction {
async loginForm(request) {
const form = new Hub.ActionForm();
form.fields = [];
const hasTokenUrl = request.params.hasOwnProperty("state_redir_url");
const state = hasTokenUrl ? { tokenurl: request.params.state_redir_url } : { stateurl: request.params.state_url };
const jsonString = JSON.stringify(state);
const actionCrypto = new Hub.ActionCrypto();
const jsonString = JSON.stringify({ stateurl: request.params.state_url });
const ciphertextBlob = await actionCrypto.encrypt(jsonString).catch((err) => {
winston.error("Encryption not correctly configured");
throw err;
Expand Down
2 changes: 2 additions & 0 deletions lib/hub/action.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export interface Action {
export interface RouteBuilder {
actionUrl(action: Action): string;
formUrl(action: Action): string;
tokenUrl(action: Action): string;
}
export declare abstract class Action {
get hasForm(): boolean;
Expand Down Expand Up @@ -54,6 +55,7 @@ export declare abstract class Action {
supported_download_settings: ActionDownloadSettings[];
icon_data_uri: any;
url: string;
token_url: string;
};
validateAndExecute(request: ActionRequest, queue?: ProcessQueue): Promise<ActionResponse>;
validateAndFetchForm(request: ActionRequest): Promise<ActionForm>;
Expand Down
1 change: 1 addition & 0 deletions lib/hub/action.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class Action {
[_1.ActionDownloadSettings.Push]),
icon_data_uri: this.getImageDataUri(),
url: router.actionUrl(this),
token_url: "",
};
}
async validateAndExecute(request, queue) {
Expand Down
1 change: 1 addition & 0 deletions lib/hub/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export * from "./action_state";
export * from "./action_response";
export * from "./action";
export * from "./oauth_action";
export * from "./oauth_action_v2";
export * from "./delegate_oauth_action";
export * from "./sources";
export * from "./utils";
Expand Down
1 change: 1 addition & 0 deletions lib/hub/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ __exportStar(require("./action_state"), exports);
__exportStar(require("./action_response"), exports);
__exportStar(require("./action"), exports);
__exportStar(require("./oauth_action"), exports);
__exportStar(require("./oauth_action_v2"), exports);
__exportStar(require("./delegate_oauth_action"), exports);
__exportStar(require("./sources"), exports);
__exportStar(require("./utils"), exports);
Expand Down
12 changes: 12 additions & 0 deletions lib/hub/oauth_action_v2.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Action, RouteBuilder } from "./action";
import { ActionRequest } from "./action_request";
export declare abstract class OAuthActionV2 extends Action {
abstract oauthCheck(request: ActionRequest): Promise<boolean>;
abstract oauthUrl(redirectUri: string, encryptedState: string): Promise<string>;
abstract oauthHandleRedirect(urlParams: {
[key: string]: string;
}, redirectUri: string): Promise<string>;
abstract oauthFetchAccessToken(request: ActionRequest): Promise<string>;
asJson(router: RouteBuilder, request: ActionRequest): any;
}
export declare function isOauthActionV2(action: Action): boolean;
17 changes: 17 additions & 0 deletions lib/hub/oauth_action_v2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.OAuthActionV2 = void 0;
exports.isOauthActionV2 = isOauthActionV2;
const action_1 = require("./action");
class OAuthActionV2 extends action_1.Action {
asJson(router, request) {
const json = super.asJson(router, request);
json.uses_oauth = true;
json.token_url = router.tokenUrl(this);
return json;
}
}
exports.OAuthActionV2 = OAuthActionV2;
function isOauthActionV2(action) {
return action instanceof OAuthActionV2;
}
1 change: 1 addition & 0 deletions lib/server/server.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export default class Server implements Hub.RouteBuilder {
constructor();
actionUrl(action: Hub.Action): string;
formUrl(action: Hub.Action): string;
tokenUrl(action: Hub.Action): string;
private oauthRedirectUrl;
/**
* For JSON responses that take a long time without sending any data,
Expand Down
43 changes: 34 additions & 9 deletions lib/server/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ class Server {
this.app.get("/actions/:actionId/oauth", async (req, res) => {
const request = Hub.ActionRequest.fromRequest(req);
const action = await Hub.findAction(req.params.actionId, { lookerVersion: request.lookerVersion });
if ((0, hub_1.isOauthAction)(action)) {
if ((0, hub_1.isOauthAction)(action) || (0, hub_1.isOauthActionV2)(action)) {
const parts = uparse.parse(req.url, true);
const state = parts.query.state;
const url = await action.oauthUrl(this.oauthRedirectUrl(action), state);
Expand All @@ -143,7 +143,7 @@ class Server {
this.app.get("/actions/:actionId/oauth_check", async (req, res) => {
const request = Hub.ActionRequest.fromRequest(req);
const action = await Hub.findAction(req.params.actionId, { lookerVersion: request.lookerVersion });
if ((0, hub_1.isOauthAction)(action)) {
if ((0, hub_1.isOauthAction)(action) || (0, hub_1.isOauthActionV2)(action)) {
const check = action.oauthCheck(request);
res.json(check);
}
Expand All @@ -155,19 +155,41 @@ class Server {
this.app.get("/actions/:actionId/oauth_redirect", async (req, res) => {
const request = Hub.ActionRequest.fromRequest(req);
const action = await Hub.findAction(req.params.actionId, { lookerVersion: request.lookerVersion });
if ((0, hub_1.isOauthAction)(action)) {
try {
try {
if ((0, hub_1.isOauthAction)(action)) {
await action.oauthFetchInfo(req.query, this.oauthRedirectUrl(action));
res.statusCode = 200;
res.send(`<html><script>window.close()</script>><body>You may now close this tab.</body></html>`);
res.send(`<html><script>window.close()</script><body>You may now close this tab.</body></html>`);
}
catch (e) {
this.logPromiseFail(req, res, e);
res.statusCode = 400;
else if ((0, hub_1.isOauthActionV2)(action)) {
const redirUrl = await action.oauthHandleRedirect(req.query, this.oauthRedirectUrl(action));
if (redirUrl === "") {
res.statusCode = 200;
res.send(`<html><script>window.close()</script><body>You may now close this tab.</body></html>`);
}
else {
res.redirect(redirUrl);
}
}
else {
throw "Action does not support OAuth.";
}
}
catch (e) {
this.logPromiseFail(req, res, e);
res.statusCode = 400;
}
});
this.route("/actions/:actionId/oauth_token", async (req, res) => {
const request = Hub.ActionRequest.fromRequest(req);
const action = await Hub.findAction(req.params.actionId, { lookerVersion: request.lookerVersion });
if ((0, hub_1.isOauthActionV2)(action)) {
const jsonPayload = await action.oauthFetchAccessToken(request);
res.type("json");
res.send(jsonPayload);
}
else {
throw "Action does not support OAuth.";
res.statusCode = 404;
}
});
// To provide a health or version check endpoint you should place a status.json file
Expand All @@ -182,6 +204,9 @@ class Server {
formUrl(action) {
return this.absUrl(`/actions/${encodeURIComponent(action.name)}/form`);
}
tokenUrl(action) {
return this.absUrl(`/actions/${encodeURIComponent(action.name)}/oauth_token`);
}
oauthRedirectUrl(action) {
return this.absUrl(`/actions/${encodeURIComponent(action.name)}/oauth_redirect`);
}
Expand Down
54 changes: 44 additions & 10 deletions src/actions/google/drive/google_drive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { DomainValidator } from "./domain_validator"
const LOG_PREFIX = "[GOOGLE_DRIVE]"
const FOLDERID_REGEX = /\/folders\/(?<folderId>[^\/?]+)/

export class GoogleDriveAction extends Hub.OAuthAction {
export class GoogleDriveAction extends Hub.OAuthActionV2 {
name = "google_drive"
label = "Google Drive"
iconName = "google/drive/google_drive.svg"
Expand Down Expand Up @@ -257,21 +257,52 @@ export class GoogleDriveAction extends Hub.OAuthAction {
return url.toString()
}

async oauthFetchInfo(urlParams: { [key: string]: string }, redirectUri: string) {
async oauthHandleRedirect(urlParams: { [key: string]: string }, redirectUri: string) {
const actionCrypto = new Hub.ActionCrypto()
const plaintext = await actionCrypto.decrypt(urlParams.state).catch((err: string) => {
winston.error("Encryption not correctly configured" + err)
throw err
})

const tokens = await this.getAccessTokenCredentialsFromCode(redirectUri, urlParams.code)
const statePayload = JSON.parse(plaintext)
if (statePayload.hasOwnProperty("tokenurl")) {
// redirect user back to Looker with context
const newState = {
code: urlParams.code,
redirecturi: redirectUri,
}
const jsonString = JSON.stringify(newState)
const ciphertextBlob = await actionCrypto.encrypt(jsonString).catch((err: string) => {
winston.error("Encryption not correctly configured")
throw err
})

return `${statePayload.tokenurl}?state=${ciphertextBlob}`
} else {
// Pass back context to Looker
const tokens = await this.getAccessTokenCredentialsFromCode(redirectUri, urlParams.code)
await https.post({
url: statePayload.stateurl,
body: JSON.stringify({tokens, redirect: redirectUri}),
}).catch((_err) => { winston.error(_err.toString()) })
return ""
}
}

// Pass back context to Looker
const payload = JSON.parse(plaintext)
await https.post({
url: payload.stateurl,
body: JSON.stringify({tokens, redirect: redirectUri}),
}).catch((_err) => { winston.error(_err.toString()) })
async oauthFetchAccessToken(request: Hub.ActionRequest) {
if (request.params.state) {
const actionCrypto = new Hub.ActionCrypto()
const plaintext = await actionCrypto.decrypt(request.params.state).catch((err: string) => {
winston.error("Encryption not correctly configured" + err)
throw err
})
const state = JSON.parse(plaintext)

const tokens = await this.getAccessTokenCredentialsFromCode(state.redirecturi, state.code)
return JSON.stringify({tokens, redirect: state.redirecturi})
} else {
throw new Error("Request is missing state parameter.")
}
}

async oauthCheck(request: Hub.ActionRequest) {
Expand Down Expand Up @@ -450,8 +481,11 @@ export class GoogleDriveAction extends Hub.OAuthAction {
const form = new Hub.ActionForm()
form.fields = []

const hasTokenUrl = request.params.hasOwnProperty("state_redir_url")
const state = hasTokenUrl ? {tokenurl: request.params.state_redir_url} : {stateurl: request.params.state_url}
const jsonString = JSON.stringify(state)

const actionCrypto = new Hub.ActionCrypto()
const jsonString = JSON.stringify({stateurl: request.params.state_url})
const ciphertextBlob = await actionCrypto.encrypt(jsonString).catch((err: string) => {
winston.error("Encryption not correctly configured")
throw err
Expand Down
Loading