1
1
"use strict"
2
2
3
- import axios from "axios"
3
+ import axios , { AxiosResponse } from "axios"
4
4
import { getAuthenticatedUser } from "coder/site/src/api/api"
5
+ import { readFileSync } from "fs"
5
6
import * as module from "module"
6
7
import * as vscode from "vscode"
7
8
import { Commands } from "./commands"
8
9
import { Remote } from "./remote"
9
10
import { Storage } from "./storage"
11
+ import * as os from "os"
10
12
import { WorkspaceQuery , WorkspaceProvider } from "./workspacesProvider"
13
+ import path from "path"
11
14
12
15
export async function activate ( ctx : vscode . ExtensionContext ) : Promise < void > {
13
16
const output = vscode . window . createOutputChannel ( "Coder" )
@@ -19,8 +22,8 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
19
22
20
23
vscode . window . registerTreeDataProvider ( "myWorkspaces" , myWorkspacesProvider )
21
24
vscode . window . registerTreeDataProvider ( "allWorkspaces" , allWorkspacesProvider )
22
- await addGlobalVpnHeaders ( )
23
- addAxiosInterceptor ( )
25
+ await initGlobalVpnHeaders ( storage )
26
+ addAxiosInterceptor ( storage )
24
27
getAuthenticatedUser ( )
25
28
. then ( async ( user ) => {
26
29
if ( user ) {
@@ -118,43 +121,94 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
118
121
} )
119
122
}
120
123
}
121
- function addAxiosInterceptor ( ) : void {
124
+ function addAxiosInterceptor ( storage : Storage ) : void {
122
125
axios . interceptors . response . use (
123
- ( res ) => {
124
- if ( typeof res . data === "object" ) {
125
- return res
126
+ ; ( res ) => {
127
+ if ( isVpnTokenInvalid ( res ) ) {
128
+ getVpnHeaderFromUser (
129
+ "seems like the Vpn Token provided is either invalid or expired, please provide a new token" ,
130
+ ) . then ( ( token ) => {
131
+ storage . setVpnHeaderToken ( token )
132
+ } )
126
133
} else {
127
- vscode . window . showErrorMessage ( JSON . stringify ( "vpn token expired or missing" ) )
128
- }
129
- } ,
130
- ( error ) => {
131
- if ( error . status === 403 ) {
132
- vscode . window . showErrorMessage ( JSON . stringify ( "vpn token not valid, make sure you added a correct token" ) )
134
+ return res
133
135
}
134
136
} ,
137
+ ( error ) => {
138
+ if ( isVpnTokenInvalidOnError ( error ) ) {
139
+ getVpnHeaderFromUser (
140
+ "seems like the Vpn Token provided is either invalid or expired, please provide a new token" ,
141
+ ) . then ( ( token ) => {
142
+ storage . setVpnHeaderToken ( token )
143
+ } )
144
+ // vscode.window.showErrorMessage(JSON.stringify("vpn token not valid, make sure you added a correct token"))
145
+ }
146
+ return error
147
+ } ,
135
148
)
136
149
}
137
- async function addGlobalVpnHeaders ( ) : Promise < void > {
150
+ async function initGlobalVpnHeaders ( storage : Storage ) : Promise < void > {
138
151
//find if global vpn headers are needed
139
152
type VpnHeaderConfig = {
140
153
headerName : string
141
- value : string
142
- requiredOnStartup : boolean
154
+ token : string
155
+ tokenFile : string
143
156
}
144
- const vpnHeaderConf = vscode . workspace . getConfiguration ( "coder" ) . get < VpnHeaderConfig > ( "vpnHeader" ) || undefined
145
- if ( ! vpnHeaderConf || vpnHeaderConf . requiredOnStartup === false ) {
157
+ const vpnHeaderConf = vscode . workspace . getConfiguration ( "coder" ) . get < VpnHeaderConfig > ( "vpnHeader" )
158
+ if ( ! vpnHeaderConf ) {
146
159
return
147
160
}
148
- const { headerName } = vpnHeaderConf
149
- //read global headers from user prompt
150
- const vpnToken = await vscode . window . showInputBox ( {
161
+ const { headerName, tokenFile, token } = vpnHeaderConf
162
+ if ( ! headerName ) {
163
+ throw Error (
164
+ "vpn header name was not defined in extension setting, please make sure to set `coder.vpnHeader.headerName`" ,
165
+ )
166
+ }
167
+ const maybeVpnHeaderToken = ( await storage . getVpnHeaderToken ( ) ) || token || readVpnHeaderTokenFromFile ( tokenFile )
168
+ if ( maybeVpnHeaderToken ) {
169
+ storage . setVpnHeaderToken ( maybeVpnHeaderToken )
170
+ axios . defaults . headers . common [ headerName ] = maybeVpnHeaderToken
171
+ } else {
172
+ //default to read global headers from user prompt
173
+ const vpnToken = await getVpnHeaderFromUser (
174
+ "you need to add your vpn access token to be able to run api calls to coder " ,
175
+ )
176
+
177
+ if ( vpnToken ) {
178
+ storage . setVpnHeaderToken ( vpnToken )
179
+ axios . defaults . headers . common [ headerName ] = vpnToken
180
+ } else {
181
+ throw Error (
182
+ "you must provide a vpn token, either by user prompt, path to file holding the token, or explicitly as conf argument " ,
183
+ )
184
+ }
185
+ }
186
+ }
187
+
188
+ async function getVpnHeaderFromUser ( message : string ) : Promise < string | undefined > {
189
+ return await vscode . window . showInputBox ( {
151
190
title : "VpnToken" ,
152
- prompt : "you need to add your vpn access token to be able to run queries" ,
191
+ prompt : message ,
153
192
placeHolder : "put your token here" ,
154
193
// value: ,
155
194
} )
195
+ }
156
196
157
- if ( vpnToken ) {
158
- axios . defaults . headers . common [ headerName ] = vpnToken
197
+ function readVpnHeaderTokenFromFile ( filepath : string ) : string | undefined {
198
+ if ( ! filepath ) {
199
+ return
159
200
}
201
+ if ( filepath . startsWith ( "~" ) ) {
202
+ return readFileSync ( path . join ( os . homedir ( ) , filepath . slice ( 1 ) ) , "utf-8" )
203
+ } else {
204
+ return readFileSync ( filepath , "utf-8" )
205
+ }
206
+ }
207
+ function isVpnTokenInvalid ( res : AxiosResponse < any , any > ) : boolean {
208
+ //if token expired or missing the vpn will return 200 OK with the actual html page to get you to reauthenticate
209
+ // , this will result in "data" not being an object but a string containing the html
210
+ return typeof res . data !== "object"
211
+ }
212
+ function isVpnTokenInvalidOnError ( error : any ) : boolean {
213
+ return error . isAxiosError && error . response . status === 403
160
214
}
0 commit comments