This is an automated email from the ASF dual-hosted git repository.
humingcheng pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/servicecomb-service-center.git
The following commit(s) were added to refs/heads/dev by this push:
new 7df8f70f support admin reset self's password without current password
(#1506)
7df8f70f is described below
commit 7df8f70ff076fce5381bb31d0bc390252d1fdcb6
Author: Wanghb1 <[email protected]>
AuthorDate: Tue Jun 24 10:11:07 2025 +0800
support admin reset self's password without current password (#1506)
fix the flaw that admin can change other admins' password
---
go.mod | 2 +-
go.sum | 6 ++--
server/service/rbac/account_service_test.go | 45 +++++++++++------------
server/service/rbac/password.go | 34 +++++++++++-------
server/service/rbac/rbac_test.go | 56 +++++++++++++++++++----------
5 files changed, 87 insertions(+), 56 deletions(-)
diff --git a/go.mod b/go.mod
index b74e81f7..a9d45cad 100644
--- a/go.mod
+++ b/go.mod
@@ -21,7 +21,7 @@ require (
github.com/cloudflare/gokey v0.1.2
github.com/deckarep/golang-set v1.8.0
github.com/elithrar/simple-scrypt v1.3.0
- github.com/go-chassis/cari v0.9.1-0.20250206085436-b02623c62de2
+ github.com/go-chassis/cari v0.9.1-0.20250623123416-20ac4b73bdff
github.com/go-chassis/etcdadpt v0.5.3-0.20240328092602-984e34b756fe
github.com/go-chassis/foundation v0.4.0
github.com/go-chassis/go-archaius v1.5.6
diff --git a/go.sum b/go.sum
index 807c82d2..bc1622bd 100644
--- a/go.sum
+++ b/go.sum
@@ -99,7 +99,7 @@ github.com/armon/go-metrics v0.3.10
h1:FR+drcQStOe+32sYyJYyZ7FIdgoGGBnwLl+flodp8
github.com/armon/go-metrics v0.3.10/go.mod
h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod
h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-radix v1.0.0
h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
-github.com/armon/go-radix v1.0.0/go.mod
h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
+github.com/armon/go-radix v1.0.0/go.mod
h1:TsTFsXBVHVK4HQ+UrFSsQEhBXZGCDqoY+cr+sUq5ZmA=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a
h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod
h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/aws/aws-sdk-go v1.34.28
h1:sscPpn/Ns3i0F4HPEWAVcwdIRaZZCuL7llJ2/60yPIk=
@@ -235,8 +235,8 @@ github.com/go-chassis/cari v0.4.0/go.mod
h1:av/19fqwEP4eOC8unL/z67AAbFDwXUCko6SK
github.com/go-chassis/cari v0.5.0/go.mod
h1:av/19fqwEP4eOC8unL/z67AAbFDwXUCko6SKa4Avrd8=
github.com/go-chassis/cari v0.5.1-0.20210823023004-74041d1363c4/go.mod
h1:av/19fqwEP4eOC8unL/z67AAbFDwXUCko6SKa4Avrd8=
github.com/go-chassis/cari v0.6.0/go.mod
h1:mSDRCOQXGmlD69A6NG0hsv0UP1xbVPtL6HCGI6X1tqs=
-github.com/go-chassis/cari v0.9.1-0.20250206085436-b02623c62de2
h1:XCav5UOU6Vu9G45EIaaAuhqb4zWAGFP3B/fzxo9TBwQ=
-github.com/go-chassis/cari v0.9.1-0.20250206085436-b02623c62de2/go.mod
h1:ibqLyh+Q+1n9PlldW3glD9G+2s/yeSyVMCCkQWKRwuE=
+github.com/go-chassis/cari v0.9.1-0.20250623123416-20ac4b73bdff
h1:UOYq7xuNK460huHGivaD6maJvALuMmv+seVvQ81wRmA=
+github.com/go-chassis/cari v0.9.1-0.20250623123416-20ac4b73bdff/go.mod
h1:ibqLyh+Q+1n9PlldW3glD9G+2s/yeSyVMCCkQWKRwuE=
github.com/go-chassis/etcdadpt v0.5.3-0.20240328092602-984e34b756fe
h1:peLHEt3wzab6nKVcmcu0qkj1+ZXK6D1ymtiyyMBv/XA=
github.com/go-chassis/etcdadpt v0.5.3-0.20240328092602-984e34b756fe/go.mod
h1:HV8OZ1Npu+lttD+pJA5nUxWZR3/SBFetTh7w8nYFkUA=
github.com/go-chassis/foundation v0.2.2-0.20201210043510-9f6d3de40234/go.mod
h1:2PjwqpVwYEVaAldl5A58a08viH8p27pNeYaiE3ZxOBA=
diff --git a/server/service/rbac/account_service_test.go
b/server/service/rbac/account_service_test.go
index 1b902375..7d9f3bc5 100644
--- a/server/service/rbac/account_service_test.go
+++ b/server/service/rbac/account_service_test.go
@@ -22,10 +22,11 @@ import (
"context"
"testing"
- rbacsvc
"github.com/apache/servicecomb-service-center/server/service/rbac"
"github.com/go-chassis/cari/discovery"
"github.com/go-chassis/cari/pkg/errsvc"
+ rbacsvc
"github.com/apache/servicecomb-service-center/server/service/rbac"
+
_ "github.com/apache/servicecomb-service-center/test"
beego "github.com/beego/beego/v2/server/web"
@@ -42,7 +43,7 @@ const (
testPwd1 = "Ab@11111"
)
-func newAccount(name string) *rbac.Account {
+func newAdminAccount(name string) *rbac.Account {
return &rbac.Account{
Name: name,
Password: testPwd0,
@@ -54,24 +55,24 @@ func newAccount(name string) *rbac.Account {
func TestCreateAccount(t *testing.T) {
ctx := context.TODO()
t.Run("create account, should succeed", func(t *testing.T) {
- a := newAccount("TestCreateAccount_create_account")
+ a := newAdminAccount("TestCreateAccount_create_account")
err := rbacsvc.CreateAccount(ctx, a)
assert.Nil(t, err)
})
t.Run("create account twice, should return:
"+rbac.NewError(rbac.ErrAccountConflict, "").Error(), func(t *testing.T) {
name := "TestCreateAccount_create_account_twice"
- a := newAccount(name)
+ a := newAdminAccount(name)
err := rbacsvc.CreateAccount(ctx, a)
assert.Nil(t, err)
- a = newAccount(name)
+ a = newAdminAccount(name)
err = rbacsvc.CreateAccount(ctx, a)
assert.NotNil(t, err)
svcErr := err.(*errsvc.Error)
assert.Equal(t, rbac.ErrAccountConflict, svcErr.Code)
})
t.Run("account has invalid role, should return:
"+rbac.NewError(rbac.ErrAccountHasInvalidRole, "").Error(), func(t *testing.T) {
- a := newAccount("TestCreateAccount_account_has_invalid_role")
+ a :=
newAdminAccount("TestCreateAccount_account_has_invalid_role")
a.Roles = append(a.Roles, "invalid_role")
err := rbacsvc.CreateAccount(ctx, a)
assert.NotNil(t, err)
@@ -80,7 +81,7 @@ func TestCreateAccount(t *testing.T) {
})
t.Run("account has id, should succeed", func(t *testing.T) {
accountName := "TestCreateAccount_account_has_id"
- a := newAccount(accountName)
+ a := newAdminAccount(accountName)
a.ID = "specifyID"
err := rbacsvc.CreateAccount(ctx, a)
assert.NoError(t, err)
@@ -95,7 +96,7 @@ func TestCreateAccount(t *testing.T) {
func TestDeleteAccount(t *testing.T) {
t.Run("delete account, should succeed", func(t *testing.T) {
- a := newAccount("TestDeleteAccount_delete_account")
+ a := newAdminAccount("TestDeleteAccount_delete_account")
err := rbacsvc.CreateAccount(context.TODO(), a)
assert.Nil(t, err)
@@ -121,7 +122,7 @@ func TestDeleteAccount(t *testing.T) {
assert.Equal(t, rbac.ErrForbidOperateBuildInAccount,
svcErr.Code)
})
t.Run("delete self, should return:
"+rbac.NewError(rbac.ErrForbidOperateSelfAccount, "").Error(), func(t
*testing.T) {
- a := newAccount("TestDeleteAccount_delete_self")
+ a := newAdminAccount("TestDeleteAccount_delete_self")
err := rbacsvc.CreateAccount(context.TODO(), a)
assert.Nil(t, err)
claims := map[string]interface{}{
@@ -138,11 +139,11 @@ func TestDeleteAccount(t *testing.T) {
func TestUpdateAccount(t *testing.T) {
t.Run("update account, should succeed", func(t *testing.T) {
name := "TestUpdateAccount_update_account"
- a := newAccount(name)
+ a := newAdminAccount(name)
err := rbacsvc.CreateAccount(context.TODO(), a)
assert.Nil(t, err)
- a = newAccount(name)
+ a = newAdminAccount(name)
a.Roles = []string{rbac.RoleAdmin, rbac.RoleDeveloper}
err = rbacsvc.UpdateAccount(context.TODO(), a.Name, a)
assert.Nil(t, err)
@@ -152,14 +153,14 @@ func TestUpdateAccount(t *testing.T) {
})
t.Run("update no exist account, should return:
"+rbac.NewError(rbac.ErrAccountNotExist, "").Error(), func(t *testing.T) {
name := "TestUpdateAccount_update_no_exist_account"
- a := newAccount(name)
+ a := newAdminAccount(name)
err := rbacsvc.UpdateAccount(context.TODO(), a.Name, a)
assert.NotNil(t, err)
svcErr := err.(*errsvc.Error)
assert.Equal(t, rbac.ErrAccountNotExist, svcErr.Code)
})
t.Run("update root, should return:
"+discovery.NewError(rbac.ErrForbidOperateBuildInAccount, "").Error(), func(t
*testing.T) {
- a := newAccount("root")
+ a := newAdminAccount("root")
err := rbacsvc.UpdateAccount(context.TODO(), a.Name, a)
assert.NotNil(t, err)
svcErr := err.(*errsvc.Error)
@@ -167,7 +168,7 @@ func TestUpdateAccount(t *testing.T) {
})
t.Run("account has invalid role, should return:
"+rbac.NewError(rbac.ErrAccountHasInvalidRole, "").Error(), func(t *testing.T) {
name := "TestUpdateAccount_account_has_invalid_role"
- a := newAccount(name)
+ a := newAdminAccount(name)
err := rbacsvc.CreateAccount(context.TODO(), a)
assert.Nil(t, err)
@@ -179,11 +180,11 @@ func TestUpdateAccount(t *testing.T) {
})
t.Run("roles status empty both, should return:
"+discovery.NewError(discovery.ErrInvalidParams, "").Error(), func(t
*testing.T) {
name := "TestUpdateAccount_roles_status_empty_both"
- a := newAccount(name)
+ a := newAdminAccount(name)
err := rbacsvc.CreateAccount(context.TODO(), a)
assert.Nil(t, err)
- a = newAccount(name)
+ a = newAdminAccount(name)
a.Roles = nil
a.Status = ""
err = rbacsvc.UpdateAccount(context.TODO(), a.Name, a)
@@ -193,11 +194,11 @@ func TestUpdateAccount(t *testing.T) {
})
t.Run("update self, should return:
"+rbac.NewError(rbac.ErrForbidOperateSelfAccount, "").Error(), func(t
*testing.T) {
name := "TestDeleteAccount_update_self"
- a := newAccount(name)
+ a := newAdminAccount(name)
err := rbacsvc.CreateAccount(context.TODO(), a)
assert.Nil(t, err)
- a = newAccount(name)
+ a = newAdminAccount(name)
claims := map[string]interface{}{
rbac.ClaimsUser: a.Name,
}
@@ -211,7 +212,7 @@ func TestUpdateAccount(t *testing.T) {
func TestEditAccount(t *testing.T) {
t.Run("edit no exist account, should return:
"+rbac.NewError(rbac.ErrAccountNotExist, "").Error(), func(t *testing.T) {
- a := newAccount("TestEditAccount_edit_no_exist_account")
+ a := newAdminAccount("TestEditAccount_edit_no_exist_account")
err := rbacsvc.UpdateAccount(context.TODO(), a.Name, a)
assert.NotNil(t, err)
svcErr := err.(*errsvc.Error)
@@ -254,10 +255,10 @@ func TestBatchCreateAccounts(t *testing.T) {
assert.Equal(t, discovery.ErrInvalidParams, svcErr.Code)
})
t.Run("batch create accounts, should succeed", func(t *testing.T) {
- a1 := newAccount("TestBatchCreateAccounts_account_1")
- a2 := newAccount("TestBatchCreateAccounts_account_no_pwd")
+ a1 := newAdminAccount("TestBatchCreateAccounts_account_1")
+ a2 := newAdminAccount("TestBatchCreateAccounts_account_no_pwd")
a2.Password = ""
- a3 := newAccount("TestBatchCreateAccounts_account_invalid_pwd")
+ a3 :=
newAdminAccount("TestBatchCreateAccounts_account_invalid_pwd")
a3.Password = "1"
defer func() {
diff --git a/server/service/rbac/password.go b/server/service/rbac/password.go
index 9631bab9..54373cde 100644
--- a/server/service/rbac/password.go
+++ b/server/service/rbac/password.go
@@ -21,12 +21,13 @@ import (
"context"
"fmt"
+ "github.com/go-chassis/cari/discovery"
+ "github.com/go-chassis/cari/rbac"
+
"github.com/apache/servicecomb-service-center/pkg/log"
"github.com/apache/servicecomb-service-center/pkg/privacy"
"github.com/apache/servicecomb-service-center/pkg/util"
"github.com/apache/servicecomb-service-center/server/service/validator"
- "github.com/go-chassis/cari/discovery"
- "github.com/go-chassis/cari/rbac"
)
func ChangePassword(ctx context.Context, a *rbac.Account) error {
@@ -40,20 +41,29 @@ func ChangePassword(ctx context.Context, a *rbac.Account)
error {
return discovery.NewError(discovery.ErrInternal, err.Error())
}
- // change self password, need to check password mismatch
- if changer.Name == a.Name {
- return changePassword(ctx, a.Name, a.CurrentPassword,
a.Password)
+ // non-admin user can only change self
+ if !changer.HasAdminRole() {
+ if changer.Name == a.Name {
+ return changePassword(ctx, a.Name, a.CurrentPassword,
a.Password)
+ }
+ return discovery.NewError(discovery.ErrForbidden,
ErrNoPermChangeAccount.Error())
}
- // change other user's password, only admin role can do this and no need
- // supply current password
- for _, r := range changer.Roles {
- if r == rbac.RoleAdmin {
- return changePasswordForcibly(ctx, a.Name, a.Password)
- }
+ // admin user can reset self or non-admin without password
+ if a.Name == changer.Name {
+ return changePasswordForcibly(ctx, a.Name, a.Password)
+ }
+
+ curAccount, err := GetAccount(ctx, a.Name)
+ if err != nil {
+ return discovery.NewError(discovery.ErrInternal, err.Error())
+ }
+
+ if !curAccount.HasAdminRole() {
+ return changePasswordForcibly(ctx, a.Name, a.Password)
}
- // other cases, change password is forbidden
+ // cannot change other admins' password
return discovery.NewError(discovery.ErrForbidden,
ErrNoPermChangeAccount.Error())
}
diff --git a/server/service/rbac/rbac_test.go b/server/service/rbac/rbac_test.go
index b424eaeb..3755d09b 100644
--- a/server/service/rbac/rbac_test.go
+++ b/server/service/rbac/rbac_test.go
@@ -24,9 +24,6 @@ import (
_ "github.com/apache/servicecomb-service-center/test"
- "github.com/apache/servicecomb-service-center/pkg/privacy"
- "github.com/apache/servicecomb-service-center/server/config"
- rbacsvc
"github.com/apache/servicecomb-service-center/server/service/rbac"
beego "github.com/beego/beego/v2/server/web"
"github.com/go-chassis/cari/discovery"
"github.com/go-chassis/cari/pkg/errsvc"
@@ -35,6 +32,10 @@ import (
"github.com/go-chassis/go-chassis/v2/security/authr"
"github.com/go-chassis/go-chassis/v2/security/secret"
"github.com/stretchr/testify/assert"
+
+ "github.com/apache/servicecomb-service-center/pkg/privacy"
+ "github.com/apache/servicecomb-service-center/server/config"
+ rbacsvc
"github.com/apache/servicecomb-service-center/server/service/rbac"
)
func init() {
@@ -89,15 +90,16 @@ func TestInitRBAC(t *testing.T) {
assert.NotNil(t, key)
})
- t.Run("change pwd,admin can change any one password", func(t
*testing.T) {
- accountName := "admin_change_other_pwd"
- persisted := newAccount(accountName)
+ t.Run("admin can change non-admin password", func(t *testing.T) {
+ accountName := "admin_change_non_admin_pwd"
+ persisted := newAdminAccount(accountName)
+ persisted.Roles = []string{rbac.RoleDeveloper}
err := rbacsvc.CreateAccount(ctx, persisted)
assert.NoError(t, err)
defer rbacsvc.DeleteAccount(ctx, accountName)
claims := map[string]interface{}{
- rbac.ClaimsUser: "test",
+ rbac.ClaimsUser: "test_admin",
rbac.ClaimsRoles: []interface{}{rbac.RoleAdmin},
}
ctx := context.WithValue(ctx, rbacsvc.CtxRequestClaims, claims)
@@ -107,17 +109,33 @@ func TestInitRBAC(t *testing.T) {
assert.NoError(t, err)
assert.True(t, privacy.SamePassword(a.Password,
"Complicated_password2"))
})
- t.Run("admin change self, must provide current pwd", func(t *testing.T)
{
- accountName := "admin_change_self"
- a := newAccount(accountName)
- a.Roles = []string{rbac.RoleAdmin}
+ t.Run("admin change other admin, should return:
"+discovery.NewError(discovery.ErrForbidden, "").Error(), func(t *testing.T) {
+ accountName := "test"
+ a := newAdminAccount(accountName)
+ err := rbacsvc.CreateAccount(ctx, a)
+ assert.NoError(t, err)
+ defer rbacsvc.DeleteAccount(ctx, accountName)
+
+ claims := map[string]interface{}{
+ rbac.ClaimsUser: "change_other_user_password",
+ rbac.ClaimsRoles: []interface{}{rbac.RoleAdmin},
+ }
+ ctx := context.WithValue(ctx, rbacsvc.CtxRequestClaims, claims)
+ err = rbacsvc.ChangePassword(ctx, &rbac.Account{Name: a.Name,
CurrentPassword: testPwd0, Password: testPwd1})
+ assert.True(t, errsvc.IsErrEqualCode(err,
discovery.ErrForbidden))
+ })
+
+ t.Run("non-admin change self, must provide current pwd", func(t
*testing.T) {
+ accountName := "non_admin_change_self"
+ a := newAdminAccount(accountName)
+ a.Roles = []string{rbac.RoleDeveloper}
err := rbacsvc.CreateAccount(context.TODO(), a)
assert.Nil(t, err)
defer rbacsvc.DeleteAccount(ctx, accountName)
claims := map[string]interface{}{
rbac.ClaimsUser: accountName,
- rbac.ClaimsRoles: []interface{}{rbac.RoleAdmin},
+ rbac.ClaimsRoles: []interface{}{rbac.RoleDeveloper},
}
ctx := context.WithValue(ctx, rbacsvc.CtxRequestClaims, claims)
err = rbacsvc.ChangePassword(ctx, &rbac.Account{Name: a.Name,
CurrentPassword: "", Password: testPwd1})
@@ -126,27 +144,29 @@ func TestInitRBAC(t *testing.T) {
err = rbacsvc.ChangePassword(ctx, &rbac.Account{Name: a.Name,
CurrentPassword: testPwd0, Password: testPwd1})
assert.Nil(t, err)
})
- t.Run("change self password", func(t *testing.T) {
- accountName := "change_self_pwd"
- a := newAccount(accountName)
+
+ t.Run("admin change self password may not provide current pwd", func(t
*testing.T) {
+ accountName := "admin_change_self"
+ a := newAdminAccount(accountName)
err := rbacsvc.CreateAccount(ctx, a)
assert.NoError(t, err)
defer rbacsvc.DeleteAccount(ctx, accountName)
claims := map[string]interface{}{
rbac.ClaimsUser: accountName,
- rbac.ClaimsRoles: []interface{}{rbac.RoleDeveloper},
+ rbac.ClaimsRoles: []interface{}{rbac.RoleAdmin},
}
ctx := context.WithValue(ctx, rbacsvc.CtxRequestClaims, claims)
- err = rbacsvc.ChangePassword(ctx, &rbac.Account{Name: a.Name,
CurrentPassword: testPwd0, Password: testPwd1})
+ err = rbacsvc.ChangePassword(ctx, &rbac.Account{Name: a.Name,
Password: testPwd1})
assert.NoError(t, err)
resp, err := rbacsvc.GetAccount(ctx, a.Name)
assert.NoError(t, err)
assert.True(t, privacy.SamePassword(resp.Password, testPwd1))
})
+
t.Run("no admin account change other user password, should return:
"+discovery.NewError(discovery.ErrForbidden, "").Error(), func(t *testing.T) {
accountName := "test"
- a := newAccount(accountName)
+ a := newAdminAccount(accountName)
defer rbacsvc.DeleteAccount(ctx, accountName)
claims := map[string]interface{}{