forked from baron/baron-sso
158 lines
4.5 KiB
Go
158 lines
4.5 KiB
Go
package handler
|
|
|
|
import (
|
|
"baron-sso-backend/internal/domain"
|
|
"baron-sso-backend/internal/service"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/gofiber/fiber/v2"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/mock"
|
|
)
|
|
|
|
func TestListMySessions_Success(t *testing.T) {
|
|
now := time.Date(2026, 4, 2, 1, 2, 3, 0, time.UTC)
|
|
setDefaultHTTPClientForTest(t, roundTripFunc(func(r *http.Request) (*http.Response, error) {
|
|
if r.URL.Path == "/sessions/whoami" {
|
|
return httpJSONAny(r, http.StatusOK, map[string]any{
|
|
"id": "current-sid",
|
|
"authenticated_at": now.Format(time.RFC3339),
|
|
"identity": map[string]any{
|
|
"id": "user-123",
|
|
"traits": map[string]any{
|
|
"email": "user@example.com",
|
|
"name": "User",
|
|
"role": "user",
|
|
},
|
|
},
|
|
}), nil
|
|
}
|
|
return httpResponse(r, http.StatusNotFound, "not found"), nil
|
|
}))
|
|
|
|
mockKratos := new(MockKratosAdminService)
|
|
mockKratos.On("ListIdentitySessions", mock.Anything, "user-123").Return([]service.KratosSession{
|
|
{
|
|
ID: "current-sid",
|
|
Active: true,
|
|
AuthenticatedAt: now,
|
|
ExpiresAt: now.Add(24 * time.Hour),
|
|
},
|
|
{
|
|
ID: "other-sid",
|
|
Active: true,
|
|
AuthenticatedAt: now.Add(-2 * time.Hour),
|
|
ExpiresAt: now.Add(22 * time.Hour),
|
|
},
|
|
}, nil).Once()
|
|
|
|
auditRepo := &mockAuditRepo{
|
|
logs: []domain.AuditLog{
|
|
{
|
|
UserID: "user-123",
|
|
EventType: "login_success",
|
|
SessionID: "other-sid",
|
|
Timestamp: now.Add(-30 * time.Minute),
|
|
IPAddress: "203.0.113.10",
|
|
UserAgent: "Mozilla/5.0",
|
|
},
|
|
},
|
|
}
|
|
|
|
h := &AuthHandler{
|
|
KratosAdmin: mockKratos,
|
|
AuditRepo: auditRepo,
|
|
}
|
|
|
|
app := fiber.New()
|
|
app.Get("/api/v1/user/sessions", h.ListMySessions)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/api/v1/user/sessions", nil)
|
|
req.Header.Set("Cookie", "ory_kratos_session=valid")
|
|
|
|
resp, err := app.Test(req, -1)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
|
|
|
var body struct {
|
|
Items []struct {
|
|
SessionID string `json:"session_id"`
|
|
IsCurrent bool `json:"is_current"`
|
|
IsActive bool `json:"is_active"`
|
|
IPAddress string `json:"ip_address"`
|
|
UserAgent string `json:"user_agent"`
|
|
} `json:"items"`
|
|
}
|
|
err = json.NewDecoder(resp.Body).Decode(&body)
|
|
assert.NoError(t, err)
|
|
if assert.Len(t, body.Items, 2) {
|
|
assert.Equal(t, "current-sid", body.Items[0].SessionID)
|
|
assert.True(t, body.Items[0].IsCurrent)
|
|
assert.Equal(t, "other-sid", body.Items[1].SessionID)
|
|
assert.True(t, body.Items[1].IsActive)
|
|
assert.Equal(t, "203.0.113.10", body.Items[1].IPAddress)
|
|
assert.Equal(t, "Mozilla/5.0", body.Items[1].UserAgent)
|
|
}
|
|
|
|
mockKratos.AssertExpectations(t)
|
|
}
|
|
|
|
func TestDeleteMySession_Success(t *testing.T) {
|
|
setDefaultHTTPClientForTest(t, roundTripFunc(func(r *http.Request) (*http.Response, error) {
|
|
if r.URL.Path == "/sessions/whoami" {
|
|
return httpJSONAny(r, http.StatusOK, map[string]any{
|
|
"id": "current-sid",
|
|
"authenticated_at": time.Now().UTC().Format(time.RFC3339),
|
|
"identity": map[string]any{
|
|
"id": "user-123",
|
|
"traits": map[string]any{
|
|
"email": "user@example.com",
|
|
"name": "User",
|
|
"role": "user",
|
|
},
|
|
},
|
|
}), nil
|
|
}
|
|
return httpResponse(r, http.StatusNotFound, "not found"), nil
|
|
}))
|
|
|
|
mockKratos := new(MockKratosAdminService)
|
|
mockKratos.On("ListIdentitySessions", mock.Anything, "user-123").Return([]service.KratosSession{
|
|
{ID: "target-sid", Active: true},
|
|
}, nil).Once()
|
|
mockKratos.On("GetSession", mock.Anything, "target-sid").Return(&service.KratosSession{
|
|
ID: "target-sid",
|
|
Active: true,
|
|
}, nil).Once()
|
|
mockKratos.On("DeleteSession", mock.Anything, "target-sid").Return(nil).Once()
|
|
|
|
auditRepo := &mockAuditRepo{}
|
|
h := &AuthHandler{
|
|
KratosAdmin: mockKratos,
|
|
AuditRepo: auditRepo,
|
|
}
|
|
|
|
app := fiber.New()
|
|
app.Delete("/api/v1/user/sessions/:id", h.DeleteMySession)
|
|
|
|
req := httptest.NewRequest(http.MethodDelete, "/api/v1/user/sessions/target-sid", nil)
|
|
req.Header.Set("Cookie", "ory_kratos_session=valid")
|
|
req.Header.Set("User-Agent", "session-test-agent")
|
|
|
|
resp, err := app.Test(req, -1)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
|
if assert.Len(t, auditRepo.logs, 1) {
|
|
assert.Equal(t, "session.revoked", auditRepo.logs[0].EventType)
|
|
assert.Equal(t, "user-123", auditRepo.logs[0].UserID)
|
|
assert.Equal(t, "current-sid", auditRepo.logs[0].SessionID)
|
|
assert.Contains(t, auditRepo.logs[0].Details, "target-sid")
|
|
}
|
|
|
|
mockKratos.AssertExpectations(t)
|
|
}
|