mirror of
https://github.com/fleetdm/fleet
synced 2026-05-21 16:08:47 +00:00
<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** Resolves #37806 Removed `ds.ListActivities` from the legacy datastore and updated code/tests to use the new activity bounded context instead. The changes to `cron.go` and most changes to `mysql/activities_test.go` will eventually be migrated to the activity bounded context. The current changes are an intermediate step. The issues tracked by https://github.com/fleetdm/fleet/issues/38234 will be addressed in additional/parallel PRs shortly. # Checklist for submitter - [x] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. - Done in the previous PR ## Testing - [x] Added/updated automated tests - [x] QA'd all new/changed functionality manually <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Migrated activity retrieval from direct datastore calls to a service-based architecture for improved maintainability and consistency. * Enhanced system context handling for background automation tasks to ensure proper authorization during scheduled operations. * Streamlined activity recording for automated processes with dedicated system identity tracking. * **Tests** * Updated test infrastructure with new helpers for activity service integration across test suites. <sub>✏️ Tip: You can customize this high-level summary in your review settings.</sub> <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Ian Littman <iansltx@gmail.com>
155 lines
4 KiB
Go
155 lines
4 KiB
Go
// Package viewer enables setting and reading the current
|
|
// user contexts
|
|
package viewer
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
|
|
"github.com/fleetdm/fleet/v4/server/fleet"
|
|
"github.com/fleetdm/fleet/v4/server/ptr"
|
|
)
|
|
|
|
type key int
|
|
|
|
const viewerKey key = 0
|
|
|
|
// NewContext creates a new context with the current user information.
|
|
func NewContext(ctx context.Context, v Viewer) context.Context {
|
|
return context.WithValue(ctx, viewerKey, v)
|
|
}
|
|
|
|
// FromContext returns the current user information if present.
|
|
func FromContext(ctx context.Context) (Viewer, bool) {
|
|
v, ok := ctx.Value(viewerKey).(Viewer)
|
|
return v, ok
|
|
}
|
|
|
|
// Viewer holds information about the current
|
|
// user and the user's session
|
|
type Viewer struct {
|
|
User *fleet.User
|
|
Session *fleet.Session
|
|
}
|
|
|
|
// UserID is a helper that enables quick access to the user ID of the current
|
|
// user.
|
|
func (v Viewer) UserID() uint {
|
|
if v.User != nil {
|
|
return v.User.ID
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// Email is a helper that enables quick access to the email of the current
|
|
// user.
|
|
func (v Viewer) Email() string {
|
|
if v.User != nil {
|
|
return v.User.Email
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// FullName is a helper that enables quick access to the full name of the
|
|
// current user.
|
|
func (v Viewer) FullName() string {
|
|
if v.User != nil {
|
|
return v.User.Name
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// SessionID returns the current user's session ID
|
|
func (v Viewer) SessionID() uint {
|
|
if v.Session != nil {
|
|
return v.Session.ID
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// IsUserID returns true if the given user id the same as the user which is
|
|
// represented by this ViewerContext
|
|
func (v Viewer) IsUserID(id uint) bool {
|
|
return v.UserID() == id
|
|
}
|
|
|
|
// IsLoggedIn determines whether or not the current VC is attached to a user
|
|
// account
|
|
func (v Viewer) IsLoggedIn() bool {
|
|
if v.Session != nil {
|
|
// Without having access to a service to call GetInfoAboutSession(id),
|
|
// we can't synchronously check the database here.
|
|
if v.Session.ID != 0 {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// CanPerformActions returns a bool indicating the current user's ability to
|
|
// perform the most basic actions on the site
|
|
func (v Viewer) CanPerformActions() bool {
|
|
if v.User != nil {
|
|
return v.IsLoggedIn() && !v.User.IsAdminForcedPasswordReset()
|
|
}
|
|
return false
|
|
}
|
|
|
|
// CanPerformPasswordReset returns a bool indicating the current user's
|
|
// ability to perform a password reset (in the case they have been required by
|
|
// the admin).
|
|
func (v Viewer) CanPerformPasswordReset() bool {
|
|
if v.User != nil {
|
|
return v.IsLoggedIn() && v.User.IsAdminForcedPasswordReset()
|
|
}
|
|
return false
|
|
}
|
|
|
|
// GetDiagnosticContext implements ctxerr.ErrorContextProvider
|
|
func (v *Viewer) GetDiagnosticContext() map[string]any {
|
|
vdata := map[string]any{
|
|
"is_logged_in": v.IsLoggedIn(),
|
|
}
|
|
if v.User != nil {
|
|
vdata["sso_enabled"] = v.User.SSOEnabled
|
|
}
|
|
return map[string]any{
|
|
"viewer": vdata,
|
|
}
|
|
}
|
|
|
|
// GetTelemetryContext implements ctxerr.ErrorContextProvider
|
|
func (v *Viewer) GetTelemetryContext() map[string]any {
|
|
if v.User == nil {
|
|
return nil
|
|
}
|
|
return map[string]any{
|
|
"user.id": v.User.ID,
|
|
"user.email": maskEmail(v.User.Email),
|
|
}
|
|
}
|
|
|
|
// maskEmail anonymizes an email address for telemetry by showing only
|
|
// the first character of the local part and the full domain.
|
|
// Example: "john.doe@example.com" -> "j***@example.com"
|
|
func maskEmail(email string) string {
|
|
parts := strings.SplitN(email, "@", 2)
|
|
if len(parts) != 2 || len(parts[0]) == 0 {
|
|
return "***"
|
|
}
|
|
return string(parts[0][0]) + "***@" + parts[1]
|
|
}
|
|
|
|
// systemUser is a synthetic user for internal system operations.
|
|
// The Name uses ActivityAutomationAuthor to align with system-initiated activities.
|
|
var systemUser = &fleet.User{
|
|
Name: fleet.ActivityAutomationAuthor,
|
|
GlobalRole: ptr.String(fleet.RoleAdmin),
|
|
}
|
|
|
|
// NewSystemContext returns a context with system-level (admin) privileges.
|
|
// Use this for cron jobs, internal service calls, and other system operations
|
|
// that need to bypass user-based authorization.
|
|
func NewSystemContext(ctx context.Context) context.Context {
|
|
return NewContext(ctx, Viewer{User: systemUser})
|
|
}
|