forked from baron/baron-sso
사용자 활성 세션 조회·종료 API 추가
This commit is contained in:
@@ -27,6 +27,21 @@ type KratosIdentity struct {
|
||||
UpdatedAt time.Time `json:"updated_at,omitempty"`
|
||||
}
|
||||
|
||||
type KratosSessionDevice struct {
|
||||
UserAgent string `json:"user_agent,omitempty"`
|
||||
IPAddress string `json:"ip_address,omitempty"`
|
||||
}
|
||||
|
||||
type KratosSession struct {
|
||||
ID string `json:"id"`
|
||||
Active bool `json:"active"`
|
||||
AuthenticatedAt time.Time `json:"authenticated_at,omitempty"`
|
||||
ExpiresAt time.Time `json:"expires_at,omitempty"`
|
||||
IssuedAt time.Time `json:"issued_at,omitempty"`
|
||||
Identity *KratosIdentity `json:"identity,omitempty"`
|
||||
Devices []KratosSessionDevice `json:"devices,omitempty"`
|
||||
}
|
||||
|
||||
type KratosAdminService interface {
|
||||
ListIdentities(ctx context.Context) ([]KratosIdentity, error)
|
||||
FindIdentityIDByIdentifier(ctx context.Context, identifier string) (string, error)
|
||||
@@ -34,6 +49,9 @@ type KratosAdminService interface {
|
||||
UpdateIdentity(ctx context.Context, identityID string, traits map[string]interface{}, state string) (*KratosIdentity, error)
|
||||
UpdateIdentityPassword(ctx context.Context, identityID, newPassword string) error
|
||||
DeleteIdentity(ctx context.Context, identityID string) error
|
||||
ListIdentitySessions(ctx context.Context, identityID string) ([]KratosSession, error)
|
||||
GetSession(ctx context.Context, sessionID string) (*KratosSession, error)
|
||||
DeleteSession(ctx context.Context, sessionID string) error
|
||||
}
|
||||
|
||||
type kratosAdminService struct {
|
||||
@@ -239,6 +257,85 @@ func (s *kratosAdminService) UpdateIdentityPassword(ctx context.Context, identit
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *kratosAdminService) ListIdentitySessions(ctx context.Context, identityID string) ([]KratosSession, error) {
|
||||
endpoint := fmt.Sprintf("%s/admin/identities/%s/sessions", strings.TrimRight(s.AdminURL, "/"), identityID)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := s.httpClient().Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusNotFound {
|
||||
return nil, nil
|
||||
}
|
||||
if resp.StatusCode >= 300 {
|
||||
body, _ := io.ReadAll(io.LimitReader(resp.Body, 2048))
|
||||
return nil, fmt.Errorf("kratos admin list identity sessions failed status=%d body=%s", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
var sessions []KratosSession
|
||||
if err := json.NewDecoder(resp.Body).Decode(&sessions); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sessions, nil
|
||||
}
|
||||
|
||||
func (s *kratosAdminService) GetSession(ctx context.Context, sessionID string) (*KratosSession, error) {
|
||||
endpoint := fmt.Sprintf("%s/admin/sessions/%s", strings.TrimRight(s.AdminURL, "/"), sessionID)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := s.httpClient().Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusNotFound {
|
||||
return nil, nil
|
||||
}
|
||||
if resp.StatusCode >= 300 {
|
||||
body, _ := io.ReadAll(io.LimitReader(resp.Body, 2048))
|
||||
return nil, fmt.Errorf("kratos admin get session failed status=%d body=%s", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
var session KratosSession
|
||||
if err := json.NewDecoder(resp.Body).Decode(&session); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &session, nil
|
||||
}
|
||||
|
||||
func (s *kratosAdminService) DeleteSession(ctx context.Context, sessionID string) error {
|
||||
endpoint := fmt.Sprintf("%s/admin/sessions/%s", strings.TrimRight(s.AdminURL, "/"), sessionID)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodDelete, endpoint, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := s.httpClient().Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusNotFound {
|
||||
return nil
|
||||
}
|
||||
if resp.StatusCode >= 300 {
|
||||
body, _ := io.ReadAll(io.LimitReader(resp.Body, 2048))
|
||||
return fmt.Errorf("kratos admin delete session failed status=%d body=%s", resp.StatusCode, string(body))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func hashPasswordForKratosAdmin(password string) (string, error) {
|
||||
hashed, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
|
||||
@@ -110,3 +110,23 @@ func (m *MockKratosAdminServiceShared) UpdateIdentityPassword(ctx context.Contex
|
||||
func (m *MockKratosAdminServiceShared) DeleteIdentity(ctx context.Context, identityID string) error {
|
||||
return m.Called(ctx, identityID).Error(0)
|
||||
}
|
||||
|
||||
func (m *MockKratosAdminServiceShared) ListIdentitySessions(ctx context.Context, identityID string) ([]KratosSession, error) {
|
||||
args := m.Called(ctx, identityID)
|
||||
if args.Get(0) == nil {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
return args.Get(0).([]KratosSession), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockKratosAdminServiceShared) GetSession(ctx context.Context, sessionID string) (*KratosSession, error) {
|
||||
args := m.Called(ctx, sessionID)
|
||||
if args.Get(0) == nil {
|
||||
return nil, args.Error(1)
|
||||
}
|
||||
return args.Get(0).(*KratosSession), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockKratosAdminServiceShared) DeleteSession(ctx context.Context, sessionID string) error {
|
||||
return m.Called(ctx, sessionID).Error(0)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user