feat(auth): support unbinding third-party identities

This commit is contained in:
IanShaw027
2026-04-22 00:53:28 +08:00
parent 89d09838d8
commit d4c0a99114
13 changed files with 355 additions and 15 deletions

View File

@@ -22,6 +22,7 @@ import (
type userHandlerRepoStub struct {
user *service.User
identities []service.UserAuthIdentityRecord
unbound []string
}
func (s *userHandlerRepoStub) Create(context.Context, *service.User) error { return nil }
@@ -116,6 +117,18 @@ func (s *userHandlerRepoStub) ListUserAuthIdentities(context.Context, int64) ([]
copy(out, s.identities)
return out, nil
}
func (s *userHandlerRepoStub) UnbindUserAuthProvider(_ context.Context, _ int64, provider string) error {
s.unbound = append(s.unbound, provider)
filtered := s.identities[:0]
for _, identity := range s.identities {
if identity.ProviderType == provider {
continue
}
filtered = append(filtered, identity)
}
s.identities = append([]service.UserAuthIdentityRecord(nil), filtered...)
return nil
}
func TestUserHandlerUpdateProfileReturnsAvatarURL(t *testing.T) {
gin.SetMode(gin.TestMode)
@@ -428,6 +441,60 @@ func TestUserHandlerBindEmailIdentityReturnsProfileResponse(t *testing.T) {
require.True(t, resp.Data.EmailBound)
}
func TestUserHandlerUnbindIdentityReturnsUpdatedProfile(t *testing.T) {
gin.SetMode(gin.TestMode)
repo := &userHandlerRepoStub{
user: &service.User{
ID: 21,
Email: "identity@example.com",
Username: "identity-user",
Role: service.RoleUser,
Status: service.StatusActive,
},
identities: []service.UserAuthIdentityRecord{
{
ProviderType: "email",
ProviderKey: "email",
ProviderSubject: "identity@example.com",
},
{
ProviderType: "linuxdo",
ProviderKey: "linuxdo",
ProviderSubject: "linuxdo-subject-21",
Metadata: map[string]any{
"username": "linuxdo-handle",
},
},
},
}
handler := NewUserHandler(service.NewUserService(repo, nil, nil, nil), nil, nil, nil)
recorder := httptest.NewRecorder()
c, _ := gin.CreateTestContext(recorder)
c.Request = httptest.NewRequest(http.MethodDelete, "/api/v1/user/account-bindings/linuxdo", nil)
c.Set(string(middleware2.ContextKeyUser), middleware2.AuthSubject{UserID: 21})
c.Params = gin.Params{{Key: "provider", Value: "linuxdo"}}
handler.UnbindIdentity(c)
require.Equal(t, http.StatusOK, recorder.Code)
require.Equal(t, []string{"linuxdo"}, repo.unbound)
var resp struct {
Code int `json:"code"`
Data map[string]any `json:"data"`
}
require.NoError(t, json.Unmarshal(recorder.Body.Bytes(), &resp))
require.Equal(t, 0, resp.Code)
authBindings, ok := resp.Data["auth_bindings"].(map[string]any)
require.True(t, ok)
linuxdoBinding, ok := authBindings["linuxdo"].(map[string]any)
require.True(t, ok)
require.Equal(t, false, linuxdoBinding["bound"])
}
func TestUserHandlerBindEmailIdentityRejectsWrongCurrentPasswordForBoundEmail(t *testing.T) {
gin.SetMode(gin.TestMode)