Browse Source

Google Cloud Platform support is working again

master
Leo Lamprecht 7 years ago
parent
commit
665e36eddf
  1. 44
      src/now.js
  2. 349
      src/providers/gcp/login.js

44
src/now.js

@ -28,7 +28,7 @@ const NOW_AUTH_CONFIG_PATH = configFiles.getAuthConfigFilePath()
const GLOBAL_COMMANDS = new Set(['help'])
const main = async (argv_): Promise<number> => {
const main = async (argv_) => {
await checkForUpdates()
const argv = mri(argv_, {
@ -64,7 +64,7 @@ const main = async (argv_): Promise<number> => {
err.message
)
)
return 1
return
}
if (!nowDirExists) {
@ -91,7 +91,7 @@ const main = async (argv_): Promise<number> => {
err.message
)
)
return 1
return
}
let config
@ -107,7 +107,7 @@ const main = async (argv_): Promise<number> => {
err.message
)
)
return 1
return
}
try {
@ -119,7 +119,7 @@ const main = async (argv_): Promise<number> => {
err.message
)
)
return 1
return
}
} else {
config = getDefaultCfg()
@ -133,7 +133,7 @@ const main = async (argv_): Promise<number> => {
err.message
)
)
return 1
return
}
}
@ -149,7 +149,7 @@ const main = async (argv_): Promise<number> => {
err.message
)
)
return 1
return
}
let authConfig = null
@ -165,7 +165,7 @@ const main = async (argv_): Promise<number> => {
err.message
)
)
return 1
return
}
try {
@ -178,7 +178,7 @@ const main = async (argv_): Promise<number> => {
'No `credentials` list found inside'
)
)
return 1
return
}
for (const [i, { provider }] of authConfig.credentials.entries()) {
@ -189,7 +189,7 @@ const main = async (argv_): Promise<number> => {
`Missing \`provider\` key in entry with index ${i}`
)
)
return 1
return
}
if (!(provider in providers)) {
@ -199,7 +199,7 @@ const main = async (argv_): Promise<number> => {
`Unknown provider "${provider}"`
)
)
return 1
return
}
}
} catch (err) {
@ -210,7 +210,7 @@ const main = async (argv_): Promise<number> => {
)}": ` + err.message
)
)
return 1
return
}
} else {
authConfig = getDefaultAuthCfg()
@ -225,7 +225,7 @@ const main = async (argv_): Promise<number> => {
err.message
)
)
return 1
return
}
}
@ -250,7 +250,7 @@ const main = async (argv_): Promise<number> => {
`An unexpected error occurred in config ${subcommand}: ${err.stack}`
)
)
return 1
return
}
}
@ -269,7 +269,7 @@ const main = async (argv_): Promise<number> => {
'Both a directory and a provider are known'
)
)
return 1
return
}
suppliedProvider = targetOrSubcommand
@ -299,12 +299,13 @@ const main = async (argv_): Promise<number> => {
`"${NOW_CONFIG_PATH}" is not a valid provider`
)
)
return 1
return
}
}
}
const provider: Object = providers[suppliedProvider || defaultProvider]
const providerName = suppliedProvider || defaultProvider
const provider: Object = providers[providerName]
let subcommand
@ -324,7 +325,7 @@ const main = async (argv_): Promise<number> => {
'Both a directory and a subcommand are known'
)
)
return 1
return
}
if (subcommandExists) {
@ -353,14 +354,17 @@ const main = async (argv_): Promise<number> => {
}
try {
return provider[subcommand](ctx)
await provider[subcommand](ctx)
} catch (err) {
console.error(
error(
`An unexpected error occurred in provider ${subcommand}: ${err.stack}`
)
)
return 1
}
if (providerName === 'gcp') {
process.exit()
}
}

349
src/providers/gcp/login.js

@ -57,220 +57,219 @@ const serverListen = ({ server, port }) => {
})
}
function login(ctx) {
return new Promise(async resolve => {
let credentialsIndex = ctx.authConfig.credentials.findIndex(
cred => cred.provider === 'gcp'
)
const login = ctx => new Promise(async resolve => {
let credentialsIndex = ctx.authConfig.credentials.findIndex(
cred => cred.provider === 'gcp'
)
if (credentialsIndex !== -1) {
// the user is already logged into gcp
let yes
try {
yes = await promptBool(
info(
`You already have GCP credentials – this will replace them.`,
` Do you want to continue?`
)
if (credentialsIndex !== -1) {
// the user is already logged into gcp
let yes
try {
yes = await promptBool(
info(
`You already have GCP credentials – this will replace them.`,
` Do you want to continue?`
)
} catch (err) {
// promptBool only `reject`s upon user abort
// let's set it to false just to make it clear
yes = false
}
)
} catch (err) {
// promptBool only `reject`s upon user abort
// let's set it to false just to make it clear
yes = false
}
if (!yes) {
console.log(aborted('No changes made.'))
resolve(0)
}
if (!yes) {
console.log(aborted('No changes made.'))
return
}
}
const ports = [...PORTS]
const server = createServer(async function handleRequest(req, res) {
const { query: { error: _error, code } } = parseUrl(req.url, true)
const ports = [...PORTS]
const server = createServer(async function handleRequest(req, res) {
const { query: { error: _error, code } } = parseUrl(req.url, true)
if (!_error && !code) {
// the browser requesting the favicon etc
res.end('')
return
}
res.setHeader('content-type', 'text/html')
res.end(
`<meta charset="UTF-8">` +
`<h2>That's it – you can now return to your terminal!</h2>`
)
if (!_error && !code) {
// the browser requesting the favicon etc
res.end('')
return
}
if (_error) {
// the user didn't give us permission
console.log(aborted(`No changes made.`))
return resolve(1)
}
res.setHeader('content-type', 'text/html')
res.end(
`<meta charset="UTF-8">` +
`<h2>That's it – you can now return to your terminal!</h2>`
)
if (code) {
// that's right after the user gave us permission
// let's exchange the authorization code for an access + refresh codes
if (_error) {
// the user didn't give us permission
console.log(aborted(`No changes made.`))
return
}
const body = formUrlEncode({
code,
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
redirect_uri: `http://${req.headers.host}`,
grant_type: GRANT_TYPE
})
if (code) {
// that's right after the user gave us permission
// let's exchange the authorization code for an access + refresh codes
const opts = {
method: 'POST',
headers: {
'content-type': 'application/x-www-form-urlencoded',
'content-length': body.length // just in case
},
body: body
}
const body = formUrlEncode({
code,
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
redirect_uri: `http://${req.headers.host}`,
grant_type: GRANT_TYPE
})
let accessToken
let expiresIn
let refreshToken
let response
const opts = {
method: 'POST',
headers: {
'content-type': 'application/x-www-form-urlencoded',
'content-length': body.length // just in case
},
body: body
}
try {
response = await fetch(TOKEN_URL, opts)
if (response.status !== 200) {
debug(
`HTTP ${response.status} when trying to exchange the authorization code`,
await response.text()
)
console.log(
error(
`Got unexpected status code from Google: ${response.status}`
)
)
return resolve(1)
}
} catch (err) {
debug(
'unexpected error occurred while making the request to exchange the authorization code',
err.message
)
console.log(
error(
'Unexpected error occurred while authenthing with Google',
err.stack
)
)
return resolve(1)
}
let accessToken
let expiresIn
let refreshToken
let response
try {
const json = await response.json()
accessToken = json.access_token
expiresIn = json.expires_in
refreshToken = json.refresh_token
} catch (err) {
try {
response = await fetch(TOKEN_URL, opts)
if (response.status !== 200) {
debug(
'unexpected error occurred while parsing the JSON from the exchange request',
err.stack,
'got',
`HTTP ${response.status} when trying to exchange the authorization code`,
await response.text()
)
console.log(
error(
'Unexpected error occurred while parsing the JSON response from Google',
err.message
`Got unexpected status code from Google: ${response.status}`
)
)
resolve(1)
return
}
const now = new Date()
// `expires_in` is 3600 seconds
const expiresAt = now.setSeconds(now.getSeconds() + expiresIn)
ctx = saveCredentials({
ctx,
accessToken,
expiresAt,
refreshToken,
credentialsIndex
})
const projects = await listProjects(ctx)
const message = 'Select a project:'
const choices = projects.map(project => {
return {
name: `${project.name} (${project.projectId})`,
value: project.projectId,
short: project.name
}
})
const projectId = await promptList({
message,
choices,
separator: false
})
const { projectId: id, name } = projects.find(
p => p.projectId === projectId
} catch (err) {
debug(
'unexpected error occurred while making the request to exchange the authorization code',
err.message
)
credentialsIndex = ctx.authConfig.credentials.findIndex(
cred => cred.provider === 'gcp'
console.log(
error(
'Unexpected error occurred while authenthing with Google',
err.stack
)
)
ctx.authConfig.credentials[credentialsIndex].project = {
id,
name
}
writeToAuthConfigFile(ctx.authConfig)
return
}
try {
const json = await response.json()
accessToken = json.access_token
expiresIn = json.expires_in
refreshToken = json.refresh_token
} catch (err) {
debug(
'unexpected error occurred while parsing the JSON from the exchange request',
err.stack,
'got',
await response.text()
)
console.log(
ready(
`Credentials and project saved in ${param(humanize(getNowDir()))}.`
error(
'Unexpected error occurred while parsing the JSON response from Google',
err.message
)
)
resolve(1)
return
}
})
let shouldRetry = true
let portToTry = ports.shift()
const now = new Date()
// `expires_in` is 3600 seconds
const expiresAt = now.setSeconds(now.getSeconds() + expiresIn)
ctx = saveCredentials({
ctx,
accessToken,
expiresAt,
refreshToken,
credentialsIndex
})
while (shouldRetry) {
try {
await serverListen({ server, port: portToTry })
shouldRetry = false // done, listening
} catch (err) {
if (ports.length) {
// let's try again
portToTry = ports.shift()
} else {
// we're out of ports to try
shouldRetry = false
const projects = await listProjects(ctx)
const message = 'Select a project:'
const choices = projects.map(project => {
return {
name: `${project.name} (${project.projectId})`,
value: project.projectId,
short: project.name
}
})
const projectId = await promptList({
message,
choices,
separator: false
})
const { projectId: id, name } = projects.find(
p => p.projectId === projectId
)
credentialsIndex = ctx.authConfig.credentials.findIndex(
cred => cred.provider === 'gcp'
)
ctx.authConfig.credentials[credentialsIndex].project = {
id,
name
}
}
if (!server.listening) {
writeToAuthConfigFile(ctx.authConfig)
console.log(
error(
`Make sure you have one of the following TCP ports available:`,
` ${PORTS.join(', ').replace()}`
ready(
`Credentials and project saved in ${param(humanize(getNowDir()))}.`
)
)
return resolve(1)
}
const query = {
client_id: CLIENT_ID,
redirect_uri: `http://localhost:${portToTry}`,
response_type: RESPONSE_TYPE,
scope: SCOPES.join(' '),
access_type: ACCESS_TYPE,
prompt: PROMPT_CONSENT
resolve()
})
let shouldRetry = true
let portToTry = ports.shift()
while (shouldRetry) {
try {
await serverListen({ server, port: portToTry })
shouldRetry = false // done, listening
} catch (err) {
if (ports.length) {
// let's try again
portToTry = ports.shift()
} else {
// we're out of ports to try
shouldRetry = false
}
}
}
opn(USER_URL + '?' + encodeQuery(query))
})
}
if (!server.listening) {
console.log(
error(
`Make sure you have one of the following TCP ports available:`,
` ${PORTS.join(', ').replace()}`
)
)
return
}
const query = {
client_id: CLIENT_ID,
redirect_uri: `http://localhost:${portToTry}`,
response_type: RESPONSE_TYPE,
scope: SCOPES.join(' '),
access_type: ACCESS_TYPE,
prompt: PROMPT_CONSENT
}
opn(USER_URL + '?' + encodeQuery(query))
})
module.exports = login

Loading…
Cancel
Save