Skip to content

Commit

Permalink
Add authentication through main instance
Browse files Browse the repository at this point in the history
  • Loading branch information
ggodlewski committed Jul 2, 2024
1 parent d2a4273 commit 0b216fc
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 91 deletions.
11 changes: 7 additions & 4 deletions .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: Pull request created
on:
pull_request:
branches: [ master ]
types: [submitted, edited, synchronize]
types: [edited, synchronize]

jobs:
test:
Expand Down Expand Up @@ -47,7 +47,7 @@ jobs:
cache: npm

- name: Build action runner
run: docker build -t "wgd-action-runner:pr-${{ github.event.number }}" --build-arg "GIT_SHA=${GITHUB_SHA}" apps/wgd-action-runner
run: docker build -t "wgd-action-runner:pr-${{ github.event.number }}" --build-arg "GIT_SHA=${{ github.sha }}" apps/wgd-action-runner

- name: Build hugo docs
run: |
Expand Down Expand Up @@ -86,11 +86,14 @@ jobs:
-v /home/wikigdrive/env.develop:/usr/src/app/.env \
-v /var/run/docker.sock:/var/run/docker.sock \
-v "/var/www/pr-${{ github.event.number }}.wikigdrive.com:/usr/src/app/dist/hugo" \
-e "GIT_SHA=${GITHUB_SHA}" \
-e "GIT_SHA=${{ github.sha }}" \
-e "ZIPKIN_URL=https://pr-${{ github.event.number }}.wikigdrive.com/zipkin" \
-e "ZIPKIN_SERVICE=pr-${{ github.event.number }}" \
-e "AUTH_DOMAIN=https://dev.wikigdrive.com" \
-e "AUTH_INSTANCE=pr-${{ github.event.number }}" \
-e "DOMAIN=https://pr-${{ github.event.number }}.wikigdrive.com" \
--link=zipkin:zipkin \
"wikigdrive-feature:${GITHUB_SHA}" wikigdrive \
"wikigdrive-feature:${{ github.sha }}" wikigdrive \
--service_account /service_account.json \
--share_email mie-docs-wikigdrive@wikigdrive.iam.gserviceaccount.com \
--workdir /data \
Expand Down
19 changes: 4 additions & 15 deletions src/containers/server/ServerContainer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import rateLimit from 'express-rate-limit';
import compress from 'compression';

import {Container, ContainerConfig, ContainerEngine} from '../../ContainerEngine.ts';
import {FileId} from '../../model/model.ts';
import {saveRunningInstance} from './loadRunningInstance.ts';
import {urlToFolderId} from '../../utils/idParsers.ts';
import {GoogleDriveService} from '../../google/GoogleDriveService.ts';
Expand Down Expand Up @@ -51,15 +50,6 @@ const __dirname = path.dirname(__filename);
const HTML_DIR = __dirname + '/../../../apps/ui';
const MAIN_DIR = __dirname + '/../../..';

interface TreeItem {
id: FileId;
name: string;
mimeType: string;
children?: TreeItem[];
}

export const isHtml = req => req.headers.accept.indexOf('text/html') > -1;

function getDurationInMilliseconds(start) {
const NS_PER_SEC = 1e9;
const NS_TO_MS = 1e6;
Expand Down Expand Up @@ -153,7 +143,7 @@ export class ServerContainer extends Container {

async initAuth(app) {
app.use('/auth/logout', authenticateOptionally(this.logger));
app.post('/auth/logout', async (req, res, next) => {
app.post('/auth/logout', async (req, res) => {
if (req.user?.google_access_token) {
const authClient = new UserAuthClient(process.env.GOOGLE_AUTH_CLIENT_ID, process.env.GOOGLE_AUTH_CLIENT_SECRET);
await authClient.revokeToken(req.user.google_access_token);
Expand All @@ -165,17 +155,16 @@ export class ServerContainer extends Container {

app.get('/auth/:driveId', async (req, res, next) => {
try {
const hostname = req.header('host');
const protocol = hostname.indexOf('localhost') > -1 ? 'http://' : 'https://';
const serverUrl = protocol + hostname;
const serverUrl = process.env.AUTH_DOMAIN || process.env.DOMAIN;
const driveId = urlToFolderId(req.params.driveId);
const redirectTo = req.query.redirectTo;
const popupWindow = req.query.popupWindow;

const state = new URLSearchParams(filterParams({
driveId: driveId !== 'none' ? (driveId || '') : '',
redirectTo,
popupWindow: popupWindow === 'true' ? 'true' : ''
popupWindow: popupWindow === 'true' ? 'true' : '',
instance: process.env.AUTH_INSTANCE
})).toString();

const authClient = new UserAuthClient(process.env.GOOGLE_AUTH_CLIENT_ID, process.env.GOOGLE_AUTH_CLIENT_SECRET);
Expand Down
34 changes: 21 additions & 13 deletions src/containers/server/auth.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import jsonwebtoken from 'jsonwebtoken';
import {decrypt, encrypt} from '../../google/GoogleAuthService';
import {GoogleDriveService} from '../../google/GoogleDriveService';
import {Logger} from 'winston';
import type {Request, Response} from 'express';
import {UserAuthClient} from '../../google/AuthClient';
import {FolderRegistryContainer} from '../folder_registry/FolderRegistryContainer';
import {urlToFolderId} from '../../utils/idParsers';
import {initJob, JobManagerContainer} from '../job/JobManagerContainer';
import {Logger} from 'winston';
import {decrypt, encrypt} from '../../google/GoogleAuthService.ts';
import {GoogleDriveService} from '../../google/GoogleDriveService.ts';
import {UserAuthClient} from '../../google/AuthClient.ts';
import {FolderRegistryContainer} from '../folder_registry/FolderRegistryContainer.ts';
import {urlToFolderId} from '../../utils/idParsers.ts';
import {initJob, JobManagerContainer} from '../job/JobManagerContainer.ts';

export class AuthError extends Error {
public status: number;
Expand All @@ -21,7 +21,7 @@ export class AuthError extends Error {

export function redirError(req: Request, msg: string) {
const err = new AuthError(msg + ' for: ' + req.originalUrl, 401);
const [empty, driveId] = req.path.split('/');
const [, driveId] = req.path.split('/');

const redirectTo: string = req.headers['redirect-to'] ? req.headers['redirect-to'].toString() : '';
if (redirectTo && redirectTo.startsWith('/') && redirectTo.indexOf('//') === -1) {
Expand Down Expand Up @@ -177,22 +177,30 @@ function sanitizeRedirect(redirectTo: string) {
return `/drive/${folderId}`;
}

export async function getAuth(req, res: Response, next) {
export async function getAuth(req: Request, res: Response, next) {
try {
const hostname = req.header('host');
const protocol = hostname.indexOf('localhost') > -1 ? 'http://' : 'https://';
const serverUrl = protocol + hostname;

const state = new URLSearchParams(req.query.state.toString());

if (!process.env.AUTH_INSTANCE) { // main auth host
const instance = state.get('instance');
if (instance && instance.match(/^pr-\d+$/)) {
res.redirect(`https://${instance}.wikigdrive.com${req.originalUrl}`);
return;
}
}

const driveId = urlToFolderId(state.get('driveId'));
const folderRegistryContainer = <FolderRegistryContainer>this.engine.getContainer('folder_registry');

const shareDrive = !!state.get('shareDrive');
if (driveId && shareDrive) {
const googleDriveService = new GoogleDriveService(this.logger, null);
const authClient = new UserAuthClient(process.env.GOOGLE_AUTH_CLIENT_ID, process.env.GOOGLE_AUTH_CLIENT_SECRET);
await authClient.authorizeResponseCode(req.query.code, `${serverUrl}/auth`);
await authClient.authorizeResponseCode(req.query.code.toString(), `${serverUrl}/auth`);

await googleDriveService.shareDrive(await authClient.getAccessToken(), driveId, this.params.share_email);

Expand All @@ -204,7 +212,7 @@ export async function getAuth(req, res: Response, next) {
const uploadDrive = !!state.get('uploadDrive');
if (driveId && uploadDrive) {
const authClient = new UserAuthClient(process.env.GOOGLE_AUTH_CLIENT_ID, process.env.GOOGLE_AUTH_CLIENT_SECRET);
await authClient.authorizeResponseCode(req.query.code, `${serverUrl}/auth`);
await authClient.authorizeResponseCode(req.query.code.toString(), `${serverUrl}/auth`);

const jobManagerContainer = <JobManagerContainer>this.engine.getContainer('job_manager');
await jobManagerContainer.schedule(driveId, {
Expand All @@ -226,7 +234,7 @@ export async function getAuth(req, res: Response, next) {
const redirectTo = sanitizeRedirect(state.get('redirectTo'));

const authClient = new UserAuthClient(process.env.GOOGLE_AUTH_CLIENT_ID, process.env.GOOGLE_AUTH_CLIENT_SECRET);
await authClient.authorizeResponseCode(req.query.code, `${serverUrl}/auth`);
await authClient.authorizeResponseCode(req.query.code.toString(), `${serverUrl}/auth`);
const googleDriveService = new GoogleDriveService(this.logger, null);
const googleUser: GoogleUser = await authClient.getUser(await authClient.getAccessToken());

Expand Down Expand Up @@ -257,7 +265,7 @@ export async function getAuth(req, res: Response, next) {
} catch (err) {
if (err.message.indexOf('invalid_grant') > -1) {
if (req.query.state) {
const state = new URLSearchParams(req.query.state);
const state = new URLSearchParams(req.query.state.toString());
const redirectTo = state.get('redirectTo');
res.redirect(redirectTo || '/');

Check warning

Code scanning / CodeQL

Server-side URL redirect Medium

Untrusted URL redirection depends on a
user-provided value
.
} else {
Expand Down
9 changes: 4 additions & 5 deletions src/containers/server/routes/Controller.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import type { Router } from 'express';
import type * as express from 'express';
import SwaggerDocService from './SwaggerDocService';
import winston from 'winston';
import {instrumentAndWrap} from '../../../telemetry';
import SwaggerDocService from './SwaggerDocService.ts';
import {instrumentAndWrap} from '../../../telemetry.ts';
// import SwaggerDocService from '../api-docs.api/SwaggerDocService';

export const HttpStatus = {
Expand Down Expand Up @@ -104,8 +103,8 @@ export class ErrorHandler implements ControllerCallContext {

export interface ControllerRoute {
errorHandlers: ErrorHandler[];
inputFilters: RouteFilter<any>[];
outputFilters: RouteFilter<any>[];
inputFilters: RouteFilter<unknown>[];
outputFilters: RouteFilter<unknown>[];
roles: string[];
method?: string;
routePath?: string;
Expand Down
29 changes: 14 additions & 15 deletions src/containers/server/routes/DriveUiController.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import {Logger} from 'winston';
import {
Controller,
RouteErrorHandler,
RouteGet,
RouteParamQuery,
RouteResponse
} from './Controller';
import {FileContentService} from '../../../utils/FileContentService';
import {Logger} from 'winston';
import {ShareErrorHandler} from './FolderController';
import {filterParams} from '../../../google/driveFetch';
import {GoogleDriveService} from '../../../google/GoogleDriveService';
import {GoogleApiContainer} from '../../google_api/GoogleApiContainer';
import {MarkdownTreeProcessor} from '../../transform/MarkdownTreeProcessor';
import {UserConfigService} from '../../google_folder/UserConfigService';
import {UserAuthClient} from '../../../google/AuthClient';
import {getContentFileService} from '../../transform/utils';
} from './Controller.ts';
import {FileContentService} from '../../../utils/FileContentService.ts';
import {ShareErrorHandler} from './FolderController.ts';
import {filterParams} from '../../../google/driveFetch.ts';
import {GoogleDriveService} from '../../../google/GoogleDriveService.ts';
import {GoogleApiContainer} from '../../google_api/GoogleApiContainer.ts';
import {MarkdownTreeProcessor} from '../../transform/MarkdownTreeProcessor.ts';
import {UserConfigService} from '../../google_folder/UserConfigService.ts';
import {UserAuthClient} from '../../../google/AuthClient.ts';
import {getContentFileService} from '../../transform/utils.ts';

export class DriveUiController extends Controller {

Expand All @@ -31,7 +31,6 @@ export class DriveUiController extends Controller {
throw new Error('No state query parameter');
}
const obj = JSON.parse(state);
const userId = obj.userId;
const action = obj.action;
const ids = obj.ids;

Expand All @@ -44,8 +43,7 @@ export class DriveUiController extends Controller {
const drives = await this.googleApiContainer.listDrives();
const driveIds = drives.map(drive => drive.id);

const googleFile = await googleDriveService.getFile(auth, fileId);
let dir = googleFile;
let dir = await googleDriveService.getFile(auth, fileId);
while (dir.parentId) {
dir = await googleDriveService.getFile(auth, dir.parentId);
}
Expand Down Expand Up @@ -94,10 +92,11 @@ export class DriveUiController extends Controller {
@RouteErrorHandler(new ShareErrorHandler())
@RouteResponse('stream')
async getInstall() {
const serverUrl = process.env.DOMAIN;
const serverUrl = process.env.AUTH_DOMAIN || process.env.DOMAIN;

const state = new URLSearchParams(filterParams({
driveui: 1,
instance: process.env.AUTH_INSTANCE
// driveId: driveId !== 'none' ? (driveId || '') : '',
// redirectTo
})).toString();
Expand Down
36 changes: 18 additions & 18 deletions src/containers/server/routes/FolderController.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
import type * as express from 'express';
import {Logger} from 'winston';
import {
Controller, ErrorHandler,
RouteErrorHandler,
RouteParamPath, RouteParamMethod,
RouteResponse,
RouteUse, RouteParamBody
} from './Controller';
import {MimeTypes} from '../../../model/GoogleFile';
import {AuthConfig} from '../../../model/AccountJson';
import {FileContentService} from '../../../utils/FileContentService';
import type * as express from 'express';
import {TreeItem} from '../../../model/TreeItem';
import {UserConfigService} from '../../google_folder/UserConfigService';
import {DirectoryScanner, isTextFileName} from '../../transform/DirectoryScanner';
import {GitChange, GitScanner} from '../../../git/GitScanner';
import {MarkdownTreeProcessor} from '../../transform/MarkdownTreeProcessor';
import {Logger} from 'winston';
import {clearCachedChanges} from '../../job/JobManagerContainer';
import {getContentFileService} from '../../transform/utils';
import {LocalLog} from '../../transform/LocalLog';
import {ContainerEngine} from '../../../ContainerEngine';
import {FolderRegistryContainer} from '../../folder_registry/FolderRegistryContainer';
import {GoogleTreeProcessor} from '../../google_folder/GoogleTreeProcessor';
import {FileId} from '../../../model/model';
} from './Controller.ts';
import {MimeTypes} from '../../../model/GoogleFile.ts';
import {AuthConfig} from '../../../model/AccountJson.ts';
import {FileContentService} from '../../../utils/FileContentService.ts';
import {TreeItem} from '../../../model/TreeItem.ts';
import {UserConfigService} from '../../google_folder/UserConfigService.ts';
import {DirectoryScanner, isTextFileName} from '../../transform/DirectoryScanner.ts';
import {GitChange, GitScanner} from '../../../git/GitScanner.ts';
import {MarkdownTreeProcessor} from '../../transform/MarkdownTreeProcessor.ts';
import {clearCachedChanges} from '../../job/JobManagerContainer.ts';
import {getContentFileService} from '../../transform/utils.ts';
import {LocalLog} from '../../transform/LocalLog.ts';
import {ContainerEngine} from '../../../ContainerEngine.ts';
import {FolderRegistryContainer} from '../../folder_registry/FolderRegistryContainer.ts';
import {GoogleTreeProcessor} from '../../google_folder/GoogleTreeProcessor.ts';
import {FileId} from '../../../model/model.ts';

export const extToMime = {
'js': 'application/javascript',
Expand Down
33 changes: 17 additions & 16 deletions src/containers/server/routes/GoogleDriveController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@ import {
RouteParamPath,
RouteParamUser,
RouteResponse
} from './Controller';
import {FileContentService} from '../../../utils/FileContentService';
import {addPreviewUrl, getCachedChanges, outputDirectory, ShareErrorHandler} from './FolderController';
import {UserConfigService} from '../../google_folder/UserConfigService';
import {MarkdownTreeProcessor} from '../../transform/MarkdownTreeProcessor';
import {getContentFileService} from '../../transform/utils';
import {GoogleTreeProcessor} from '../../google_folder/GoogleTreeProcessor';
import {UserAuthClient} from '../../../google/AuthClient';
import {filterParams} from '../../../google/driveFetch';
import {GoogleDriveService} from '../../../google/GoogleDriveService';
import {redirError} from '../auth';
} from './Controller.ts';
import {FileContentService} from '../../../utils/FileContentService.ts';
import {addPreviewUrl, getCachedChanges, outputDirectory, ShareErrorHandler} from './FolderController.ts';
import {UserConfigService} from '../../google_folder/UserConfigService.ts';
import {MarkdownTreeProcessor} from '../../transform/MarkdownTreeProcessor.ts';
import {getContentFileService} from '../../transform/utils.ts';
import {GoogleTreeProcessor} from '../../google_folder/GoogleTreeProcessor.ts';
import {UserAuthClient} from '../../../google/AuthClient.ts';
import {filterParams} from '../../../google/driveFetch.ts';
import {GoogleDriveService} from '../../../google/GoogleDriveService.ts';
import {redirError} from '../auth.ts';

export class GoogleDriveController extends Controller {

Expand All @@ -25,11 +25,12 @@ export class GoogleDriveController extends Controller {

@RouteGet('/:driveId/share')
async getShare(@RouteParamUser() user, @RouteParamPath('driveId') driveId: string) {
const serverUrl = process.env.DOMAIN;
const serverUrl = process.env.AUTH_DOMAIN || process.env.DOMAIN;

const state = new URLSearchParams(filterParams({
shareDrive: 1,
driveId: driveId !== 'none' ? (driveId || '') : ''
driveId: driveId !== 'none' ? (driveId || '') : '',
instance: process.env.AUTH_INSTANCE
})).toString();

const authClient = new UserAuthClient(process.env.GOOGLE_AUTH_CLIENT_ID, process.env.GOOGLE_AUTH_CLIENT_SECRET);
Expand All @@ -43,11 +44,12 @@ export class GoogleDriveController extends Controller {

@RouteGet('/:driveId/upload')
async getUpload(@RouteParamUser() user, @RouteParamPath('driveId') driveId: string) {
const serverUrl = process.env.DOMAIN;
const serverUrl = process.env.AUTH_DOMAIN || process.env.DOMAIN;

const state = new URLSearchParams(filterParams({
uploadDrive: 1,
driveId: driveId !== 'none' ? (driveId || '') : ''
driveId: driveId !== 'none' ? (driveId || '') : '',
instance: process.env.AUTH_INSTANCE
})).toString();

const authClient = new UserAuthClient(process.env.GOOGLE_AUTH_CLIENT_ID, process.env.GOOGLE_AUTH_CLIENT_SECRET);
Expand Down Expand Up @@ -104,7 +106,6 @@ export class GoogleDriveController extends Controller {
} catch (err) {
if (err.status === 401) {
throw redirError(this.req, err.message);
return;
}
}

Expand Down
Loading

0 comments on commit 0b216fc

Please sign in to comment.