mirror of
https://github.com/coder/coder.git
synced 2025-07-03 16:13:58 +00:00
chore: add sql filter to fetching audit logs (#14070)
* chore: add sql filter to fetching audit logs * use sqlc.embed for audit logs * fix sql query matcher
This commit is contained in:
@ -182,17 +182,17 @@ func (api *API) convertAuditLogs(ctx context.Context, dblogs []database.GetAudit
|
||||
}
|
||||
|
||||
func (api *API) convertAuditLog(ctx context.Context, dblog database.GetAuditLogsOffsetRow) codersdk.AuditLog {
|
||||
ip, _ := netip.AddrFromSlice(dblog.Ip.IPNet.IP)
|
||||
ip, _ := netip.AddrFromSlice(dblog.AuditLog.Ip.IPNet.IP)
|
||||
|
||||
diff := codersdk.AuditDiff{}
|
||||
_ = json.Unmarshal(dblog.Diff, &diff)
|
||||
_ = json.Unmarshal(dblog.AuditLog.Diff, &diff)
|
||||
|
||||
var user *codersdk.User
|
||||
if dblog.UserUsername.Valid {
|
||||
// Leaving the organization IDs blank for now; not sure they are useful for
|
||||
// the audit query anyway?
|
||||
sdkUser := db2sdk.User(database.User{
|
||||
ID: dblog.UserID,
|
||||
ID: dblog.AuditLog.UserID,
|
||||
Email: dblog.UserEmail.String,
|
||||
Username: dblog.UserUsername.String,
|
||||
CreatedAt: dblog.UserCreatedAt.Time,
|
||||
@ -211,7 +211,7 @@ func (api *API) convertAuditLog(ctx context.Context, dblog database.GetAuditLogs
|
||||
}
|
||||
|
||||
var (
|
||||
additionalFieldsBytes = []byte(dblog.AdditionalFields)
|
||||
additionalFieldsBytes = []byte(dblog.AuditLog.AdditionalFields)
|
||||
additionalFields audit.AdditionalFields
|
||||
err = json.Unmarshal(additionalFieldsBytes, &additionalFields)
|
||||
)
|
||||
@ -224,7 +224,7 @@ func (api *API) convertAuditLog(ctx context.Context, dblog database.GetAuditLogs
|
||||
WorkspaceOwner: "unknown",
|
||||
}
|
||||
|
||||
dblog.AdditionalFields, err = json.Marshal(resourceInfo)
|
||||
dblog.AuditLog.AdditionalFields, err = json.Marshal(resourceInfo)
|
||||
api.Logger.Error(ctx, "marshal additional fields", slog.Error(err))
|
||||
}
|
||||
|
||||
@ -239,30 +239,30 @@ func (api *API) convertAuditLog(ctx context.Context, dblog database.GetAuditLogs
|
||||
}
|
||||
|
||||
alog := codersdk.AuditLog{
|
||||
ID: dblog.ID,
|
||||
RequestID: dblog.RequestID,
|
||||
Time: dblog.Time,
|
||||
ID: dblog.AuditLog.ID,
|
||||
RequestID: dblog.AuditLog.RequestID,
|
||||
Time: dblog.AuditLog.Time,
|
||||
// OrganizationID is deprecated.
|
||||
OrganizationID: dblog.OrganizationID,
|
||||
OrganizationID: dblog.AuditLog.OrganizationID,
|
||||
IP: ip,
|
||||
UserAgent: dblog.UserAgent.String,
|
||||
ResourceType: codersdk.ResourceType(dblog.ResourceType),
|
||||
ResourceID: dblog.ResourceID,
|
||||
ResourceTarget: dblog.ResourceTarget,
|
||||
ResourceIcon: dblog.ResourceIcon,
|
||||
Action: codersdk.AuditAction(dblog.Action),
|
||||
UserAgent: dblog.AuditLog.UserAgent.String,
|
||||
ResourceType: codersdk.ResourceType(dblog.AuditLog.ResourceType),
|
||||
ResourceID: dblog.AuditLog.ResourceID,
|
||||
ResourceTarget: dblog.AuditLog.ResourceTarget,
|
||||
ResourceIcon: dblog.AuditLog.ResourceIcon,
|
||||
Action: codersdk.AuditAction(dblog.AuditLog.Action),
|
||||
Diff: diff,
|
||||
StatusCode: dblog.StatusCode,
|
||||
AdditionalFields: dblog.AdditionalFields,
|
||||
StatusCode: dblog.AuditLog.StatusCode,
|
||||
AdditionalFields: dblog.AuditLog.AdditionalFields,
|
||||
User: user,
|
||||
Description: auditLogDescription(dblog),
|
||||
ResourceLink: resourceLink,
|
||||
IsDeleted: isDeleted,
|
||||
}
|
||||
|
||||
if dblog.OrganizationID != uuid.Nil {
|
||||
if dblog.AuditLog.OrganizationID != uuid.Nil {
|
||||
alog.Organization = &codersdk.MinimalOrganization{
|
||||
ID: dblog.OrganizationID,
|
||||
ID: dblog.AuditLog.OrganizationID,
|
||||
Name: dblog.OrganizationName,
|
||||
DisplayName: dblog.OrganizationDisplayName,
|
||||
Icon: dblog.OrganizationIcon,
|
||||
@ -276,32 +276,32 @@ func auditLogDescription(alog database.GetAuditLogsOffsetRow) string {
|
||||
b := strings.Builder{}
|
||||
// NOTE: WriteString always returns a nil error, so we never check it
|
||||
_, _ = b.WriteString("{user} ")
|
||||
if alog.StatusCode >= 400 {
|
||||
if alog.AuditLog.StatusCode >= 400 {
|
||||
_, _ = b.WriteString("unsuccessfully attempted to ")
|
||||
_, _ = b.WriteString(string(alog.Action))
|
||||
_, _ = b.WriteString(string(alog.AuditLog.Action))
|
||||
} else {
|
||||
_, _ = b.WriteString(codersdk.AuditAction(alog.Action).Friendly())
|
||||
_, _ = b.WriteString(codersdk.AuditAction(alog.AuditLog.Action).Friendly())
|
||||
}
|
||||
|
||||
// API Key resources (used for authentication) do not have targets and follow the below format:
|
||||
// "User {logged in | logged out | registered}"
|
||||
if alog.ResourceType == database.ResourceTypeApiKey &&
|
||||
(alog.Action == database.AuditActionLogin || alog.Action == database.AuditActionLogout || alog.Action == database.AuditActionRegister) {
|
||||
if alog.AuditLog.ResourceType == database.ResourceTypeApiKey &&
|
||||
(alog.AuditLog.Action == database.AuditActionLogin || alog.AuditLog.Action == database.AuditActionLogout || alog.AuditLog.Action == database.AuditActionRegister) {
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// We don't display the name (target) for git ssh keys. It's fairly long and doesn't
|
||||
// make too much sense to display.
|
||||
if alog.ResourceType == database.ResourceTypeGitSshKey {
|
||||
if alog.AuditLog.ResourceType == database.ResourceTypeGitSshKey {
|
||||
_, _ = b.WriteString(" the ")
|
||||
_, _ = b.WriteString(codersdk.ResourceType(alog.ResourceType).FriendlyString())
|
||||
_, _ = b.WriteString(codersdk.ResourceType(alog.AuditLog.ResourceType).FriendlyString())
|
||||
return b.String()
|
||||
}
|
||||
|
||||
_, _ = b.WriteString(" ")
|
||||
_, _ = b.WriteString(codersdk.ResourceType(alog.ResourceType).FriendlyString())
|
||||
_, _ = b.WriteString(codersdk.ResourceType(alog.AuditLog.ResourceType).FriendlyString())
|
||||
|
||||
if alog.ResourceType == database.ResourceTypeConvertLogin {
|
||||
if alog.AuditLog.ResourceType == database.ResourceTypeConvertLogin {
|
||||
_, _ = b.WriteString(" to")
|
||||
}
|
||||
|
||||
@ -311,9 +311,9 @@ func auditLogDescription(alog database.GetAuditLogsOffsetRow) string {
|
||||
}
|
||||
|
||||
func (api *API) auditLogIsResourceDeleted(ctx context.Context, alog database.GetAuditLogsOffsetRow) bool {
|
||||
switch alog.ResourceType {
|
||||
switch alog.AuditLog.ResourceType {
|
||||
case database.ResourceTypeTemplate:
|
||||
template, err := api.Database.GetTemplateByID(ctx, alog.ResourceID)
|
||||
template, err := api.Database.GetTemplateByID(ctx, alog.AuditLog.ResourceID)
|
||||
if err != nil {
|
||||
if xerrors.Is(err, sql.ErrNoRows) {
|
||||
return true
|
||||
@ -322,7 +322,7 @@ func (api *API) auditLogIsResourceDeleted(ctx context.Context, alog database.Get
|
||||
}
|
||||
return template.Deleted
|
||||
case database.ResourceTypeUser:
|
||||
user, err := api.Database.GetUserByID(ctx, alog.ResourceID)
|
||||
user, err := api.Database.GetUserByID(ctx, alog.AuditLog.ResourceID)
|
||||
if err != nil {
|
||||
if xerrors.Is(err, sql.ErrNoRows) {
|
||||
return true
|
||||
@ -331,7 +331,7 @@ func (api *API) auditLogIsResourceDeleted(ctx context.Context, alog database.Get
|
||||
}
|
||||
return user.Deleted
|
||||
case database.ResourceTypeWorkspace:
|
||||
workspace, err := api.Database.GetWorkspaceByID(ctx, alog.ResourceID)
|
||||
workspace, err := api.Database.GetWorkspaceByID(ctx, alog.AuditLog.ResourceID)
|
||||
if err != nil {
|
||||
if xerrors.Is(err, sql.ErrNoRows) {
|
||||
return true
|
||||
@ -340,7 +340,7 @@ func (api *API) auditLogIsResourceDeleted(ctx context.Context, alog database.Get
|
||||
}
|
||||
return workspace.Deleted
|
||||
case database.ResourceTypeWorkspaceBuild:
|
||||
workspaceBuild, err := api.Database.GetWorkspaceBuildByID(ctx, alog.ResourceID)
|
||||
workspaceBuild, err := api.Database.GetWorkspaceBuildByID(ctx, alog.AuditLog.ResourceID)
|
||||
if err != nil {
|
||||
if xerrors.Is(err, sql.ErrNoRows) {
|
||||
return true
|
||||
@ -357,7 +357,7 @@ func (api *API) auditLogIsResourceDeleted(ctx context.Context, alog database.Get
|
||||
}
|
||||
return workspace.Deleted
|
||||
case database.ResourceTypeOauth2ProviderApp:
|
||||
_, err := api.Database.GetOAuth2ProviderAppByID(ctx, alog.ResourceID)
|
||||
_, err := api.Database.GetOAuth2ProviderAppByID(ctx, alog.AuditLog.ResourceID)
|
||||
if xerrors.Is(err, sql.ErrNoRows) {
|
||||
return true
|
||||
} else if err != nil {
|
||||
@ -365,7 +365,7 @@ func (api *API) auditLogIsResourceDeleted(ctx context.Context, alog database.Get
|
||||
}
|
||||
return false
|
||||
case database.ResourceTypeOauth2ProviderAppSecret:
|
||||
_, err := api.Database.GetOAuth2ProviderAppSecretByID(ctx, alog.ResourceID)
|
||||
_, err := api.Database.GetOAuth2ProviderAppSecretByID(ctx, alog.AuditLog.ResourceID)
|
||||
if xerrors.Is(err, sql.ErrNoRows) {
|
||||
return true
|
||||
} else if err != nil {
|
||||
@ -378,17 +378,17 @@ func (api *API) auditLogIsResourceDeleted(ctx context.Context, alog database.Get
|
||||
}
|
||||
|
||||
func (api *API) auditLogResourceLink(ctx context.Context, alog database.GetAuditLogsOffsetRow, additionalFields audit.AdditionalFields) string {
|
||||
switch alog.ResourceType {
|
||||
switch alog.AuditLog.ResourceType {
|
||||
case database.ResourceTypeTemplate:
|
||||
return fmt.Sprintf("/templates/%s",
|
||||
alog.ResourceTarget)
|
||||
alog.AuditLog.ResourceTarget)
|
||||
|
||||
case database.ResourceTypeUser:
|
||||
return fmt.Sprintf("/users?filter=%s",
|
||||
alog.ResourceTarget)
|
||||
alog.AuditLog.ResourceTarget)
|
||||
|
||||
case database.ResourceTypeWorkspace:
|
||||
workspace, getWorkspaceErr := api.Database.GetWorkspaceByID(ctx, alog.ResourceID)
|
||||
workspace, getWorkspaceErr := api.Database.GetWorkspaceByID(ctx, alog.AuditLog.ResourceID)
|
||||
if getWorkspaceErr != nil {
|
||||
return ""
|
||||
}
|
||||
@ -397,13 +397,13 @@ func (api *API) auditLogResourceLink(ctx context.Context, alog database.GetAudit
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("/@%s/%s",
|
||||
workspaceOwner.Username, alog.ResourceTarget)
|
||||
workspaceOwner.Username, alog.AuditLog.ResourceTarget)
|
||||
|
||||
case database.ResourceTypeWorkspaceBuild:
|
||||
if len(additionalFields.WorkspaceName) == 0 || len(additionalFields.BuildNumber) == 0 {
|
||||
return ""
|
||||
}
|
||||
workspaceBuild, getWorkspaceBuildErr := api.Database.GetWorkspaceBuildByID(ctx, alog.ResourceID)
|
||||
workspaceBuild, getWorkspaceBuildErr := api.Database.GetWorkspaceBuildByID(ctx, alog.AuditLog.ResourceID)
|
||||
if getWorkspaceBuildErr != nil {
|
||||
return ""
|
||||
}
|
||||
@ -419,10 +419,10 @@ func (api *API) auditLogResourceLink(ctx context.Context, alog database.GetAudit
|
||||
workspaceOwner.Username, additionalFields.WorkspaceName, additionalFields.BuildNumber)
|
||||
|
||||
case database.ResourceTypeOauth2ProviderApp:
|
||||
return fmt.Sprintf("/deployment/oauth2-provider/apps/%s", alog.ResourceID)
|
||||
return fmt.Sprintf("/deployment/oauth2-provider/apps/%s", alog.AuditLog.ResourceID)
|
||||
|
||||
case database.ResourceTypeOauth2ProviderAppSecret:
|
||||
secret, err := api.Database.GetOAuth2ProviderAppSecretByID(ctx, alog.ResourceID)
|
||||
secret, err := api.Database.GetOAuth2ProviderAppSecretByID(ctx, alog.AuditLog.ResourceID)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
Reference in New Issue
Block a user