mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
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:
co-authored by
Steven Masley
parent
060eeed5c3
commit
46fe59f5e7
@@ -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}
|
||||
/>
|
||||
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
"os": "OS: ",
|
||||
"browser": "Browser: ",
|
||||
"onBehalfOf": " on behalf of {{owner}}",
|
||||
"buildReason": "Coder automatically"
|
||||
"buildReason": "Coder automatically",
|
||||
"unknownUser": "an unknown user"
|
||||
}
|
||||
},
|
||||
"paywall": {
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user