feat: authsession service

This commit is contained in:
Ilia Denisov
2026-04-08 16:23:07 +02:00
committed by GitHub
parent 28f04916af
commit 86a68ed9d0
174 changed files with 31732 additions and 112 deletions
@@ -0,0 +1,89 @@
// Package sessionlimit defines the domain decision shape used for active
// device-session limit evaluation.
package sessionlimit
import (
"errors"
"fmt"
)
// Kind identifies the coarse outcome of evaluating the active-session limit.
type Kind string
const (
// KindDisabled reports that no configured limit is currently active.
KindDisabled Kind = "disabled"
// KindAllowed reports that creating the next session is allowed.
KindAllowed Kind = "allowed"
// KindExceeded reports that creating the next session would exceed the
// configured limit.
KindExceeded Kind = "exceeded"
)
// IsKnown reports whether Kind is one of the session-limit outcomes supported
// by the current domain model.
func (k Kind) IsKnown() bool {
switch k {
case KindDisabled, KindAllowed, KindExceeded:
return true
default:
return false
}
}
// Decision stores the result of evaluating one possible next session creation.
type Decision struct {
// Kind reports the coarse decision outcome.
Kind Kind
// ConfiguredLimit stores the active configured limit when one exists.
ConfiguredLimit *int
// ActiveSessionCount stores the current active-session count before create.
ActiveSessionCount int
// NextSessionCount stores the count that would exist after creating the next
// session.
NextSessionCount int
}
// Validate reports whether Decision satisfies the Stage-2 structural
// invariants.
func (d Decision) Validate() error {
if !d.Kind.IsKnown() {
return fmt.Errorf("session-limit decision kind %q is unsupported", d.Kind)
}
if d.ActiveSessionCount < 0 {
return errors.New("session-limit active session count must not be negative")
}
if d.NextSessionCount < 0 {
return errors.New("session-limit next session count must not be negative")
}
if d.NextSessionCount != d.ActiveSessionCount+1 {
return errors.New("session-limit next session count must equal active session count plus one")
}
switch d.Kind {
case KindDisabled:
if d.ConfiguredLimit != nil {
return errors.New("disabled session-limit decision must not contain configured limit")
}
case KindAllowed, KindExceeded:
if d.ConfiguredLimit == nil {
return errors.New("limited session-limit decision must contain configured limit")
}
if *d.ConfiguredLimit <= 0 {
return errors.New("session-limit configured limit must be positive")
}
if d.Kind == KindAllowed && d.NextSessionCount > *d.ConfiguredLimit {
return errors.New("allowed session-limit decision must not exceed configured limit")
}
if d.Kind == KindExceeded && d.NextSessionCount <= *d.ConfiguredLimit {
return errors.New("exceeded session-limit decision must be above configured limit")
}
}
return nil
}
@@ -0,0 +1,128 @@
package sessionlimit
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestKindIsKnown(t *testing.T) {
t.Parallel()
tests := []struct {
name string
value Kind
want bool
}{
{name: "disabled", value: KindDisabled, want: true},
{name: "allowed", value: KindAllowed, want: true},
{name: "exceeded", value: KindExceeded, want: true},
{name: "unknown", value: Kind("unknown"), want: false},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
if got := tt.value.IsKnown(); got != tt.want {
require.Failf(t, "test failed", "IsKnown() = %v, want %v", got, tt.want)
}
})
}
}
func TestDecisionValidate(t *testing.T) {
t.Parallel()
limitTwo := 2
limitThree := 3
tests := []struct {
name string
value Decision
wantErr bool
}{
{
name: "disabled valid",
value: Decision{
Kind: KindDisabled,
ActiveSessionCount: 0,
NextSessionCount: 1,
},
},
{
name: "allowed valid",
value: Decision{
Kind: KindAllowed,
ConfiguredLimit: &limitThree,
ActiveSessionCount: 1,
NextSessionCount: 2,
},
},
{
name: "exceeded valid",
value: Decision{
Kind: KindExceeded,
ConfiguredLimit: &limitTwo,
ActiveSessionCount: 2,
NextSessionCount: 3,
},
},
{
name: "disabled rejects limit",
value: Decision{
Kind: KindDisabled,
ConfiguredLimit: &limitTwo,
ActiveSessionCount: 0,
NextSessionCount: 1,
},
wantErr: true,
},
{
name: "allowed requires limit",
value: Decision{
Kind: KindAllowed,
ActiveSessionCount: 0,
NextSessionCount: 1,
},
wantErr: true,
},
{
name: "allowed rejects overflow",
value: Decision{
Kind: KindAllowed,
ConfiguredLimit: &limitTwo,
ActiveSessionCount: 2,
NextSessionCount: 3,
},
wantErr: true,
},
{
name: "next count must be active plus one",
value: Decision{
Kind: KindDisabled,
ActiveSessionCount: 2,
NextSessionCount: 2,
},
wantErr: true,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
err := tt.value.Validate()
if tt.wantErr && err == nil {
require.FailNow(t, "Validate() returned nil error")
}
if !tt.wantErr && err != nil {
require.Failf(t, "test failed", "Validate() returned error: %v", err)
}
})
}
}