Skip to content

Commit

Permalink
improvement(enterprise): better error message on login 401 errors
Browse files Browse the repository at this point in the history
  • Loading branch information
eysi09 authored and thsig committed Mar 29, 2021
1 parent d96e490 commit b84239b
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 12 deletions.
22 changes: 17 additions & 5 deletions core/src/commands/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,23 @@ export class LoginCommand extends Command {

// The Enterprise API is missing from the Garden class for commands with noProject
// so we initialize it here.
const enterpriseApi = await EnterpriseApi.factory({ log, currentDirectory, skipLogging: true })
if (enterpriseApi) {
log.info({ msg: `You're already logged in to Garden Enteprise.` })
enterpriseApi.close()
return {}
try {
const enterpriseApi = await EnterpriseApi.factory({ log, currentDirectory, skipLogging: true })
if (enterpriseApi) {
log.info({ msg: `You're already logged in to Garden Enteprise.` })
enterpriseApi.close()
return {}
}
} catch (err) {
if (err?.detail?.statusCode === 401) {
const msg = dedent`
Looks like your session token is invalid. If you were previously logged into a different instance
of Garden Enterprise, log out first before logging in.
`
log.warn({ msg, symbol: "warning" })
log.info("")
}
throw err
}

const config = await getEnterpriseConfig(currentDirectory)
Expand Down
20 changes: 14 additions & 6 deletions core/src/enterprise/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { IncomingHttpHeaders } from "http"

import { got, GotHeaders, GotHttpError } from "../util/http"
import { findProjectConfig } from "../config/base"
import { CommandError, EnterpriseApiError, RuntimeError } from "../exceptions"
import { CommandError, EnterpriseApiError } from "../exceptions"
import { LogEntry } from "../logger/log-entry"
import { gardenEnv } from "../constants"
import { ClientAuthToken } from "../db/entities/client-auth-token"
Expand Down Expand Up @@ -268,7 +268,8 @@ export class EnterpriseApi {

private async refreshToken(token: ClientAuthToken) {
try {
const res = await this.get<any>("token/refresh", {
let res: any
res = await this.get<any>("token/refresh", {
Cookie: `rt=${token?.refreshToken}`,
})

Expand All @@ -290,7 +291,11 @@ export class EnterpriseApi {
await EnterpriseApi.saveAuthToken(this.log, tokenObj)
} catch (err) {
this.log.debug({ msg: `Failed to refresh the token.` })
throw new RuntimeError(`An error occurred while verifying client auth token with platform: ${err.message}`, {})
const detail = is401Error(err) ? { statusCode: err.response.statusCode } : {}
throw new EnterpriseApiError(
`An error occurred while verifying client auth token with Garden Enterprise: ${err.message}`,
detail
)
}
}

Expand Down Expand Up @@ -355,15 +360,18 @@ export class EnterpriseApi {
async checkClientAuthToken(): Promise<boolean> {
let valid = false
try {
this.log.debug(`Checking client auth token with platform: ${this.domain}/token/verify`)
this.log.debug(`Checking client auth token with Garden Enterprise: ${this.domain}/token/verify`)
await this.get("token/verify")
valid = true
} catch (err) {
if (!is401Error(err)) {
throw new RuntimeError(`An error occurred while verifying client auth token with platform: ${err.message}`, {})
throw new EnterpriseApiError(
`An error occurred while verifying client auth token with Garden Enterprise: ${err.message}`,
{}
)
}
}
this.log.debug(`Checked client auth token with platform - valid: ${valid}`)
this.log.debug(`Checked client auth token with Garden Enterprise - valid: ${valid}`)
return valid
}
}
41 changes: 40 additions & 1 deletion core/test/unit/src/commands/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ import stripAnsi from "strip-ansi"
import { makeDummyGarden } from "../../../../src/cli/cli"
import { Garden } from "../../../../src"
import { ClientAuthToken } from "../../../../src/db/entities/client-auth-token"
import { randomString } from "../../../../src/util/string"
import { dedent, randomString } from "../../../../src/util/string"
import { EnterpriseApi } from "../../../../src/enterprise/api"
import { LogLevel } from "../../../../src/logger/log-node"
import { gardenEnv } from "../../../../src/constants"
import { EnterpriseApiError } from "../../../../src/exceptions"

function makeCommandParams(garden: Garden) {
const log = garden.log
Expand Down Expand Up @@ -168,6 +169,44 @@ describe("LoginCommand", () => {
)
})

it("should throw and print a helpful message on 401 errors", async () => {
const postfix = randomString()
const testToken = {
token: `dummy-token-${postfix}`,
refreshToken: `dummy-refresh-token-${postfix}`,
tokenValidity: 60,
}

const command = new LoginCommand()
const garden = await makeDummyGarden(getDataDir("test-projects", "login", "has-domain-and-id"), {
noEnterprise: false,
commandInfo: { name: "foo", args: {}, opts: {} },
})

await EnterpriseApi.saveAuthToken(garden.log, testToken)
td.replace(EnterpriseApi.prototype, "checkClientAuthToken", async () => false)
td.replace(EnterpriseApi.prototype, "refreshToken", async () => {
throw new EnterpriseApiError("bummer", { statusCode: 401 })
})

const savedToken = await ClientAuthToken.findOne()
expect(savedToken).to.exist
expect(savedToken!.token).to.eql(testToken.token)
expect(savedToken!.refreshToken).to.eql(testToken.refreshToken)

await expectError(
() => command.action(makeCommandParams(garden)),
(err) => expect(stripAnsi(err.message)).to.match(/bummer/)
)

const logOutput = getLogMessages(garden.log, (entry) => entry.level <= LogLevel.info).join("\n")

expect(logOutput).to.include(dedent`
Looks like your session token is invalid. If you were previously logged into a different instance
of Garden Enterprise, log out first before logging in.
`)
})

context("GARDEN_AUTH_TOKEN set in env", () => {
const saveEnv = gardenEnv.GARDEN_AUTH_TOKEN
before(() => {
Expand Down

0 comments on commit b84239b

Please sign in to comment.