feat: audit login (#5925)

* added migration for api key resource

* sort of working

* auditing login

* passing  the correct user id

* added and fixed tests

* gen documentation

* formatting and lint

* lint

* audit Github oauth and write tests

* audit oauth and write  tests

* added defer fn for login error auditing

* fixed test

* feat: audit logout (#5998)

* Update coderd/userauth.go

Co-authored-by: Steven Masley <Emyrk@users.noreply.github.com>

* fix test

* bypassing diff generation if login/logout

* lint

---------

Co-authored-by: Steven Masley <Emyrk@users.noreply.github.com>
This commit is contained in:
Kira Pilot
2023-02-06 15:12:50 -05:00
committed by GitHub
co-authored by Steven Masley
parent 060eeed5c3
commit 46fe59f5e7
29 changed files with 679 additions and 259 deletions
+10 -1
View File
@@ -1047,10 +1047,19 @@ export type APIKeyScope = "all" | "application_connect"
export const APIKeyScopes: APIKeyScope[] = ["all", "application_connect"]
// From codersdk/audit.go
export type AuditAction = "create" | "delete" | "start" | "stop" | "write"
export type AuditAction =
| "create"
| "delete"
| "login"
| "logout"
| "start"
| "stop"
| "write"
export const AuditActions: AuditAction[] = [
"create",
"delete",
"login",
"logout",
"start",
"stop",
"write",
@@ -2,8 +2,12 @@ import {
MockAuditLog,
MockAuditLogWithWorkspaceBuild,
MockWorkspaceCreateAuditLogForDifferentOwner,
MockAuditLogSuccessfulLogin,
MockAuditLogUnsuccessfulLoginKnownUser,
MockAuditLogUnsuccessfulLoginUnknownUser,
} from "testHelpers/entities"
import { AuditLogDescription } from "./AuditLogDescription"
import { AuditLogRow } from "./AuditLogRow"
import { render } from "../../testHelpers/renderHelpers"
import { screen } from "@testing-library/react"
@@ -59,4 +63,22 @@ describe("AuditLogDescription", () => {
),
).toBeDefined()
})
it("renders the correct string for successful login", async () => {
render(<AuditLogRow auditLog={MockAuditLogSuccessfulLogin} />)
expect(getByTextContent(`TestUser logged in`)).toBeDefined()
const statusPill = screen.getByRole("status")
expect(statusPill).toHaveTextContent("201")
})
it("renders the correct string for unsuccessful login for a known user", async () => {
render(<AuditLogRow auditLog={MockAuditLogUnsuccessfulLoginKnownUser} />)
expect(getByTextContent(`TestUser logged in`)).toBeDefined()
const statusPill = screen.getByRole("status")
expect(statusPill).toHaveTextContent("401")
})
it("renders the correct string for unsuccessful login for an unknown user", async () => {
render(<AuditLogRow auditLog={MockAuditLogUnsuccessfulLoginUnknownUser} />)
expect(getByTextContent(`an unknown user logged in`)).toBeDefined()
const statusPill = screen.getByRole("status")
expect(statusPill).toHaveTextContent("401")
})
})
@@ -12,7 +12,9 @@ export const AuditLogDescription: FC<{ auditLog: AuditLog }> = ({
const { t } = i18next
let target = auditLog.resource_target.trim()
let user = auditLog.user?.username.trim()
let user = auditLog.user
? auditLog.user.username.trim()
: t("auditLog:table.logRow.unknownUser")
if (auditLog.resource_type === "workspace_build") {
// audit logs with a resource_type of workspace build use workspace name as a target
@@ -22,7 +24,7 @@ export const AuditLogDescription: FC<{ auditLog: AuditLog }> = ({
auditLog.additional_fields?.build_reason &&
auditLog.additional_fields?.build_reason !== "initiator"
? t("auditLog:table.logRow.buildReason")
: auditLog.user?.username.trim()
: user
}
// SSH key entries have no links
@@ -93,7 +93,7 @@ export const AuditLogRow: React.FC<AuditLogRowProps> = ({
className={styles.fullWidth}
>
<UserAvatar
username={auditLog.user?.username ?? ""}
username={auditLog.user?.username ?? "?"}
avatarURL={auditLog.user?.avatar_url}
/>
+2 -1
View File
@@ -13,7 +13,8 @@
"os": "OS: ",
"browser": "Browser: ",
"onBehalfOf": " on behalf of {{owner}}",
"buildReason": "Coder automatically"
"buildReason": "Coder automatically",
"unknownUser": "an unknown user"
}
},
"paywall": {
+20
View File
@@ -1182,6 +1182,26 @@ export const MockAuditLogGitSSH: TypesGen.AuditLog = {
},
}
export const MockAuditLogSuccessfulLogin: TypesGen.AuditLog = {
...MockAuditLog,
resource_type: "api_key",
resource_target: "",
action: "login",
status_code: 201,
description: "{user} logged in",
}
export const MockAuditLogUnsuccessfulLoginKnownUser: TypesGen.AuditLog = {
...MockAuditLogSuccessfulLogin,
status_code: 401,
}
export const MockAuditLogUnsuccessfulLoginUnknownUser: TypesGen.AuditLog = {
...MockAuditLogSuccessfulLogin,
status_code: 401,
user: undefined,
}
export const MockWorkspaceQuota: TypesGen.WorkspaceQuota = {
credits_consumed: 0,
budget: 100,