Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions internal/api/certificate_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
pm "github.com/manchtools/power-manage/sdk/gen/go/pm/v1"
"github.com/manchtools/power-manage/server/internal/ca"
"github.com/manchtools/power-manage/server/internal/eventtypes"
"github.com/manchtools/power-manage/server/internal/eventtypes/payloads"
"github.com/manchtools/power-manage/server/internal/store"
db "github.com/manchtools/power-manage/server/internal/store/generated"
)
Expand Down Expand Up @@ -82,13 +83,15 @@ func (h *CertificateHandler) RenewCertificate(ctx context.Context, req *connect.
}

// Emit DeviceCertRenewed event (projection handler already exists in migration 001)
fingerprint := newCert.Fingerprint
notAfterStr := newCert.NotAfter.Format(time.RFC3339)
if err := h.store.AppendEvent(ctx, store.Event{
StreamType: "device",
StreamID: deviceID,
EventType: string(eventtypes.DeviceCertRenewed),
Data: map[string]any{
"cert_fingerprint": newCert.Fingerprint,
"cert_not_after": newCert.NotAfter.Format(time.RFC3339),
Data: payloads.DeviceCertRenewed{
CertFingerprint: &fingerprint,
CertNotAfter: &notAfterStr,
},
ActorType: "device",
ActorID: deviceID,
Expand Down
34 changes: 19 additions & 15 deletions internal/api/device_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/manchtools/power-manage/server/internal/auth"
"github.com/manchtools/power-manage/server/internal/crypto"
"github.com/manchtools/power-manage/server/internal/eventtypes"
"github.com/manchtools/power-manage/server/internal/eventtypes/payloads"
"github.com/manchtools/power-manage/server/internal/middleware"
"github.com/manchtools/power-manage/server/internal/search"
"github.com/manchtools/power-manage/server/internal/store"
Expand Down Expand Up @@ -215,9 +216,9 @@ func (h *DeviceHandler) SetDeviceLabel(ctx context.Context, req *connect.Request
StreamType: "device",
StreamID: req.Msg.Id,
EventType: string(eventtypes.DeviceLabelSet),
Data: map[string]any{
"key": req.Msg.Key,
"value": req.Msg.Value,
Data: payloads.DeviceLabelSet{
Key: &req.Msg.Key,
Value: &req.Msg.Value,
},
ActorType: "user",
ActorID: userCtx.ID,
Expand Down Expand Up @@ -260,8 +261,8 @@ func (h *DeviceHandler) RemoveDeviceLabel(ctx context.Context, req *connect.Requ
StreamType: "device",
StreamID: req.Msg.Id,
EventType: string(eventtypes.DeviceLabelRemoved),
Data: map[string]any{
"key": req.Msg.Key,
Data: payloads.DeviceLabelRemoved{
Key: &req.Msg.Key,
},
ActorType: "user",
ActorID: userCtx.ID,
Expand Down Expand Up @@ -375,12 +376,13 @@ func (h *DeviceHandler) AssignDevice(ctx context.Context, req *connect.Request[p
return nil, apiErrorCtx(ctx, ErrInternal, connect.CodeInternal, "failed to get user")
}

uid := userID
if err := appendEvent(ctx, h.store, h.logger, store.Event{
StreamType: "device",
StreamID: req.Msg.DeviceId,
EventType: string(eventtypes.DeviceAssigned),
Data: map[string]any{
"user_id": userID,
Data: payloads.DeviceUserAssignment{
UserID: &uid,
},
ActorType: "user",
ActorID: userCtx.ID,
Expand All @@ -403,12 +405,13 @@ func (h *DeviceHandler) AssignDevice(ctx context.Context, req *connect.Request[p
return nil, apiErrorCtx(ctx, ErrInternal, connect.CodeInternal, "failed to get user group")
}

gid := groupID
if err := appendEvent(ctx, h.store, h.logger, store.Event{
StreamType: "device",
StreamID: req.Msg.DeviceId,
EventType: string(eventtypes.DeviceGroupAssigned),
Data: map[string]any{
"group_id": groupID,
Data: payloads.DeviceGroupAssignment{
GroupID: &gid,
},
ActorType: "user",
ActorID: userCtx.ID,
Expand Down Expand Up @@ -480,8 +483,8 @@ func (h *DeviceHandler) UnassignDevice(ctx context.Context, req *connect.Request
StreamType: "device",
StreamID: req.Msg.DeviceId,
EventType: string(eventtypes.DeviceUnassigned),
Data: map[string]any{
"user_id": req.Msg.UserId,
Data: payloads.DeviceUserAssignment{
UserID: &req.Msg.UserId,
},
ActorType: "user",
ActorID: userCtx.ID,
Expand All @@ -494,8 +497,8 @@ func (h *DeviceHandler) UnassignDevice(ctx context.Context, req *connect.Request
StreamType: "device",
StreamID: req.Msg.DeviceId,
EventType: string(eventtypes.DeviceGroupUnassigned),
Data: map[string]any{
"group_id": req.Msg.GroupId,
Data: payloads.DeviceGroupAssignment{
GroupID: &req.Msg.GroupId,
},
ActorType: "user",
ActorID: userCtx.ID,
Expand Down Expand Up @@ -533,12 +536,13 @@ func (h *DeviceHandler) SetDeviceSyncInterval(ctx context.Context, req *connect.
}

// Emit DeviceSyncIntervalSet event
syncInterval := req.Msg.SyncIntervalMinutes
if err := appendEvent(ctx, h.store, h.logger, store.Event{
StreamType: "device",
StreamID: req.Msg.Id,
EventType: string(eventtypes.DeviceSyncIntervalSet),
Data: map[string]any{
"sync_interval_minutes": req.Msg.SyncIntervalMinutes,
Data: payloads.DeviceSyncIntervalSet{
SyncIntervalMinutes: &syncInterval,
},
ActorType: "user",
ActorID: userCtx.ID,
Expand Down
40 changes: 28 additions & 12 deletions internal/api/registration_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
pm "github.com/manchtools/power-manage/sdk/gen/go/pm/v1"
"github.com/manchtools/power-manage/server/internal/ca"
"github.com/manchtools/power-manage/server/internal/eventtypes"
"github.com/manchtools/power-manage/server/internal/eventtypes/payloads"
"github.com/manchtools/power-manage/server/internal/store"
)

Expand Down Expand Up @@ -193,21 +194,36 @@ func (h *RegistrationHandler) Register(ctx context.Context, req *connect.Request
return nil, apiErrorCtx(ctx, ErrInternal, connect.CodeInternal, "failed to issue certificate")
}

// Build event data for device registration
eventData := map[string]any{
"hostname": req.Msg.Hostname,
"agent_version": req.Msg.AgentVersion,
"cert_fingerprint": cert.Fingerprint,
"cert_not_after": cert.NotAfter.Format(time.RFC3339),
"registration_token_id": token.ID,
"cert_pem": string(cert.CertPEM),
"ca_cert_pem": string(h.ca.CACertPEM()),
// Build event data for device registration. CertPEM + CACertPEM
// ride along so future replays can recover the cert bytes from
// the event log; the projector ignores them.
hostname := req.Msg.Hostname
agentVersion := req.Msg.AgentVersion
certFingerprint := cert.Fingerprint
// Preserve the legacy RFC 3339-string serialisation (no
// sub-second precision) so wire bytes stay identical to the
// pre-typed-payload emission. The projector parses the string
// back into a time.Time via parseOptionalRFC3339, which accepts
// both RFC 3339 and RFC 3339Nano.
certNotAfterStr := cert.NotAfter.Format(time.RFC3339)
registrationTokenID := token.ID
certPEM := string(cert.CertPEM)
caCertPEM := string(h.ca.CACertPEM())
deviceData := payloads.DeviceRegistered{
Hostname: &hostname,
AgentVersion: &agentVersion,
CertFingerprint: &certFingerprint,
CertNotAfter: &certNotAfterStr,
RegistrationTokenID: &registrationTokenID,
CertPEM: &certPEM,
CACertPEM: &caCertPEM,
}

// Auto-assign device to token owner if the token has an owner
if token.OwnerID != nil && *token.OwnerID != "" {
eventData["assigned_user_id"] = *token.OwnerID
logger.Info("auto-assigning device to token owner", "owner_id", *token.OwnerID)
ownerID := *token.OwnerID
deviceData.AssignedUserID = &ownerID
logger.Info("auto-assigning device to token owner", "owner_id", ownerID)
}

// Consume the token FIRST to prevent race conditions with one-time tokens.
Expand Down Expand Up @@ -238,7 +254,7 @@ func (h *RegistrationHandler) Register(ctx context.Context, req *connect.Request
StreamType: "device",
StreamID: deviceID,
EventType: string(eventtypes.DeviceRegistered),
Data: eventData,
Data: deviceData,
ActorType: "system",
ActorID: "registration",
}); err != nil {
Expand Down
24 changes: 13 additions & 11 deletions internal/api/system_actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/manchtools/power-manage/server/internal/actionparams"
"github.com/manchtools/power-manage/server/internal/ca"
"github.com/manchtools/power-manage/server/internal/eventtypes"
"github.com/manchtools/power-manage/server/internal/eventtypes/payloads"
"github.com/manchtools/power-manage/server/internal/store"
db "github.com/manchtools/power-manage/server/internal/store/generated"
)
Expand Down Expand Up @@ -529,18 +530,19 @@ func (m *SystemActionManager) createSystemAction(ctx context.Context, name strin
// assignActionToUser emits an AssignmentCreated event.
func (m *SystemActionManager) assignActionToUser(ctx context.Context, actionID, userID string) error {
assignmentID := newULID()

mode := int32(0) // REQUIRED
sortOrder := int32(0)
return m.store.AppendEvent(ctx, store.Event{
StreamType: "assignment",
StreamID: assignmentID,
EventType: string(eventtypes.AssignmentCreated),
Data: map[string]any{
"source_type": "action",
"source_id": actionID,
"target_type": "user",
"target_id": userID,
"mode": 0, // REQUIRED
"sort_order": 0,
Data: payloads.AssignmentCreated{
SourceType: "action",
SourceID: actionID,
TargetType: "user",
TargetID: userID,
Mode: &mode,
SortOrder: &sortOrder,
},
ActorType: "system",
ActorID: "system",
Expand Down Expand Up @@ -586,9 +588,9 @@ func (m *SystemActionManager) linkSystemAction(ctx context.Context, userID, fiel
StreamType: "user",
StreamID: userID,
EventType: string(eventtypes.UserSystemActionLinked),
Data: map[string]any{
"field": field,
"action_id": actionID,
Data: payloads.UserSystemActionLinked{
Field: &field,
ActionID: &actionID,
},
ActorType: "system",
ActorID: "system",
Expand Down
Loading
Loading