feat: authsession service
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
package shared
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"galaxy/authsession/internal/domain/devicesession"
|
||||
"galaxy/authsession/internal/domain/gatewayprojection"
|
||||
)
|
||||
|
||||
// Session mirrors the frozen internal read-model DTO used by later trusted
|
||||
// transport handlers.
|
||||
type Session struct {
|
||||
// DeviceSessionID is the stable identifier of one device session.
|
||||
DeviceSessionID string
|
||||
|
||||
// UserID is the stable identifier of the session owner.
|
||||
UserID string
|
||||
|
||||
// ClientPublicKey is the base64-encoded raw 32-byte Ed25519 public key of
|
||||
// the device session.
|
||||
ClientPublicKey string
|
||||
|
||||
// Status reports whether the session is active or revoked.
|
||||
Status string
|
||||
|
||||
// CreatedAt is the RFC3339 UTC timestamp at which the session was created.
|
||||
CreatedAt string
|
||||
|
||||
// RevokedAt is the RFC3339 UTC timestamp at which the session was revoked,
|
||||
// when the session is revoked.
|
||||
RevokedAt *string
|
||||
|
||||
// RevokeReasonCode is the machine-readable revoke reason code when the
|
||||
// session is revoked.
|
||||
RevokeReasonCode *string
|
||||
|
||||
// RevokeActorType is the machine-readable revoke actor type when the
|
||||
// session is revoked.
|
||||
RevokeActorType *string
|
||||
|
||||
// RevokeActorID is the optional stable revoke actor identifier when the
|
||||
// session is revoked.
|
||||
RevokeActorID *string
|
||||
}
|
||||
|
||||
// ToSession converts source-of-truth session into the frozen internal read DTO
|
||||
// shape.
|
||||
func ToSession(record devicesession.Session) (Session, error) {
|
||||
if err := record.Validate(); err != nil {
|
||||
return Session{}, fmt.Errorf("map session: %w", err)
|
||||
}
|
||||
|
||||
result := Session{
|
||||
DeviceSessionID: record.ID.String(),
|
||||
UserID: record.UserID.String(),
|
||||
ClientPublicKey: record.ClientPublicKey.String(),
|
||||
Status: string(record.Status),
|
||||
CreatedAt: formatTime(record.CreatedAt),
|
||||
}
|
||||
|
||||
if record.Revocation != nil {
|
||||
revokedAt := formatTime(record.Revocation.At)
|
||||
reasonCode := record.Revocation.ReasonCode.String()
|
||||
actorType := record.Revocation.ActorType.String()
|
||||
result.RevokedAt = &revokedAt
|
||||
result.RevokeReasonCode = &reasonCode
|
||||
result.RevokeActorType = &actorType
|
||||
if record.Revocation.ActorID != "" {
|
||||
actorID := record.Revocation.ActorID
|
||||
result.RevokeActorID = &actorID
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// ToSessions converts every source-of-truth session into the frozen internal
|
||||
// read DTO shape.
|
||||
func ToSessions(records []devicesession.Session) ([]Session, error) {
|
||||
result := make([]Session, 0, len(records))
|
||||
for index, record := range records {
|
||||
mapped, err := ToSession(record)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("map session %d: %w", index, err)
|
||||
}
|
||||
result = append(result, mapped)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// ToGatewayProjectionSnapshot converts source-of-truth session into the
|
||||
// separate gateway-facing projection model.
|
||||
func ToGatewayProjectionSnapshot(record devicesession.Session) (gatewayprojection.Snapshot, error) {
|
||||
if err := record.Validate(); err != nil {
|
||||
return gatewayprojection.Snapshot{}, fmt.Errorf("map gateway projection snapshot: %w", err)
|
||||
}
|
||||
|
||||
snapshot := gatewayprojection.Snapshot{
|
||||
DeviceSessionID: record.ID,
|
||||
UserID: record.UserID,
|
||||
ClientPublicKey: record.ClientPublicKey.String(),
|
||||
Status: gatewayprojection.Status(record.Status),
|
||||
}
|
||||
if record.Revocation != nil {
|
||||
snapshot.RevokedAt = cloneTimePointer(commonTimePointer(record.Revocation.At.UTC()))
|
||||
snapshot.RevokeReasonCode = record.Revocation.ReasonCode
|
||||
snapshot.RevokeActorType = record.Revocation.ActorType
|
||||
snapshot.RevokeActorID = record.Revocation.ActorID
|
||||
}
|
||||
if err := snapshot.Validate(); err != nil {
|
||||
return gatewayprojection.Snapshot{}, fmt.Errorf("map gateway projection snapshot: %w", err)
|
||||
}
|
||||
|
||||
return snapshot, nil
|
||||
}
|
||||
|
||||
func formatTime(value time.Time) string {
|
||||
return value.UTC().Format(time.RFC3339)
|
||||
}
|
||||
|
||||
func commonTimePointer(value time.Time) *time.Time {
|
||||
return &value
|
||||
}
|
||||
|
||||
func cloneTimePointer(value *time.Time) *time.Time {
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
cloned := *value
|
||||
return &cloned
|
||||
}
|
||||
Reference in New Issue
Block a user