fix: require Wayne user ID for role binding

This commit is contained in:
mac
2026-07-17 15:47:32 +08:00
parent d9d93fcd20
commit a10d4a5921
5 changed files with 85 additions and 60 deletions
+14 -13
View File
@@ -24,6 +24,7 @@ var (
)
type WayneRoleBindingRequest struct {
UserID *uint64 `json:"userId,omitempty"`
GroupIDs []uint64 `json:"groupIds,omitempty"`
OperatorUserID *uint64 `json:"operatorUserId,omitempty"`
OperatorName string `json:"operatorName,omitempty"`
@@ -71,20 +72,20 @@ func NewWayneRoleBindingService(cfg config.Config) *WayneRoleBindingService {
}
}
func (s *WayneRoleBindingService) BindNamespace(ctx context.Context, namespaceID uint64, username string, req WayneRoleBindingRequest) (*WayneRoleBindingResult, error) {
return s.call(ctx, http.MethodPut, fmt.Sprintf("/api/v1/internal/namespaces/%d/users/%s/roles", namespaceID, url.PathEscape(username)), username, req)
func (s *WayneRoleBindingService) BindNamespace(ctx context.Context, namespaceID uint64, userID uint64, operatorEmail string, req WayneRoleBindingRequest) (*WayneRoleBindingResult, error) {
return s.call(ctx, http.MethodPut, fmt.Sprintf("/api/v1/internal/namespaces/%d/users/%d/roles", namespaceID, userID), operatorEmail, req)
}
func (s *WayneRoleBindingService) UnbindNamespace(ctx context.Context, namespaceID uint64, username string, req WayneRoleBindingRequest) (*WayneRoleBindingResult, error) {
return s.call(ctx, http.MethodDelete, fmt.Sprintf("/api/v1/internal/namespaces/%d/users/%s/roles", namespaceID, url.PathEscape(username)), username, req)
func (s *WayneRoleBindingService) UnbindNamespace(ctx context.Context, namespaceID uint64, userID uint64, operatorEmail string, req WayneRoleBindingRequest) (*WayneRoleBindingResult, error) {
return s.call(ctx, http.MethodDelete, fmt.Sprintf("/api/v1/internal/namespaces/%d/users/%d/roles", namespaceID, userID), operatorEmail, req)
}
func (s *WayneRoleBindingService) BindApp(ctx context.Context, appID uint64, username string, req WayneRoleBindingRequest) (*WayneRoleBindingResult, error) {
return s.call(ctx, http.MethodPut, fmt.Sprintf("/api/v1/internal/apps/%d/users/%s/roles", appID, url.PathEscape(username)), username, req)
func (s *WayneRoleBindingService) BindApp(ctx context.Context, appID uint64, userID uint64, operatorEmail string, req WayneRoleBindingRequest) (*WayneRoleBindingResult, error) {
return s.call(ctx, http.MethodPut, fmt.Sprintf("/api/v1/internal/apps/%d/users/%d/roles", appID, userID), operatorEmail, req)
}
func (s *WayneRoleBindingService) UnbindApp(ctx context.Context, appID uint64, username string, req WayneRoleBindingRequest) (*WayneRoleBindingResult, error) {
return s.call(ctx, http.MethodDelete, fmt.Sprintf("/api/v1/internal/apps/%d/users/%s/roles", appID, url.PathEscape(username)), username, req)
func (s *WayneRoleBindingService) UnbindApp(ctx context.Context, appID uint64, userID uint64, operatorEmail string, req WayneRoleBindingRequest) (*WayneRoleBindingResult, error) {
return s.call(ctx, http.MethodDelete, fmt.Sprintf("/api/v1/internal/apps/%d/users/%d/roles", appID, userID), operatorEmail, req)
}
func (s *WayneRoleBindingService) ListNamespaces(ctx context.Context) (*WayneRoleBindingResult, error) {
@@ -101,12 +102,11 @@ func (s *WayneRoleBindingService) ListGroups(ctx context.Context, groupType *int
return s.callRaw(ctx, http.MethodGet, internalPath, nil)
}
func (s *WayneRoleBindingService) GetUserRoles(ctx context.Context, username string) (*WayneRoleBindingResult, error) {
username = strings.TrimSpace(username)
if username == "" {
return nil, ErrWayenEmailMissing
func (s *WayneRoleBindingService) GetUserRoles(ctx context.Context, userID uint64) (*WayneRoleBindingResult, error) {
if userID == 0 {
return nil, ErrWayneRoleBindingRequestFailed
}
return s.callRaw(ctx, http.MethodGet, fmt.Sprintf("/api/v1/internal/users/%s/roles", url.PathEscape(username)), nil)
return s.callRaw(ctx, http.MethodGet, fmt.Sprintf("/api/v1/internal/users/%d/roles", userID), nil)
}
func (s *WayneRoleBindingService) NamespaceOperatorPermissions(ctx context.Context, namespaceID uint64, operatorEmail string) (*WayneRoleBindingResult, error) {
@@ -128,6 +128,7 @@ func (s *WayneRoleBindingService) call(ctx context.Context, method, internalPath
req.OperatorUserID = nil
req.OperatorName = operatorEmail
req.UserID = nil
body, err := json.Marshal(req)
if err != nil {
@@ -40,7 +40,9 @@ func TestWayneRoleBindingServiceBindNamespaceSignsAndOverridesOperator(t *testin
operatorUserID := uint64(123)
replace := false
result, err := svc.BindNamespace(context.Background(), 1, "eastsales@qiniu.com", WayneRoleBindingRequest{
targetUserID := uint64(2001)
result, err := svc.BindNamespace(context.Background(), 1, targetUserID, "eastsales@qiniu.com", WayneRoleBindingRequest{
UserID: &targetUserID,
GroupIDs: []uint64{10, 11},
OperatorUserID: &operatorUserID,
OperatorName: "attacker@example.com",
@@ -54,7 +56,7 @@ func TestWayneRoleBindingServiceBindNamespaceSignsAndOverridesOperator(t *testin
if result.StatusCode != http.StatusOK {
t.Fatalf("StatusCode = %d, want 200", result.StatusCode)
}
if requestPath != "/api/v1/internal/namespaces/1/users/eastsales@qiniu.com/roles" {
if requestPath != "/api/v1/internal/namespaces/1/users/2001/roles" {
t.Fatalf("requestPath = %q", requestPath)
}
if payload.OperatorName != "eastsales@qiniu.com" {
@@ -63,6 +65,9 @@ func TestWayneRoleBindingServiceBindNamespaceSignsAndOverridesOperator(t *testin
if payload.OperatorUserID != nil {
t.Fatalf("OperatorUserID should be omitted, got %v", *payload.OperatorUserID)
}
if payload.UserID != nil {
t.Fatalf("UserID should be omitted from Wayne body, got %v", *payload.UserID)
}
if payload.Replace == nil || *payload.Replace {
t.Fatalf("Replace = %v, want false", payload.Replace)
}
@@ -77,23 +82,23 @@ func TestWayneRoleBindingServiceCallsAllDocumentedEndpoints(t *testing.T) {
{
name: "unbind namespace",
call: func(s *WayneRoleBindingService) (*WayneRoleBindingResult, error) {
return s.UnbindNamespace(context.Background(), 1, "eastsales@qiniu.com", WayneRoleBindingRequest{GroupIDs: []uint64{10}})
return s.UnbindNamespace(context.Background(), 1, 2001, "eastsales@qiniu.com", WayneRoleBindingRequest{GroupIDs: []uint64{10}})
},
want: "DELETE /api/v1/internal/namespaces/1/users/eastsales@qiniu.com/roles",
want: "DELETE /api/v1/internal/namespaces/1/users/2001/roles",
},
{
name: "bind app",
call: func(s *WayneRoleBindingService) (*WayneRoleBindingResult, error) {
return s.BindApp(context.Background(), 3, "eastsales@qiniu.com", WayneRoleBindingRequest{GroupIDs: []uint64{20}})
return s.BindApp(context.Background(), 3, 2001, "eastsales@qiniu.com", WayneRoleBindingRequest{GroupIDs: []uint64{20}})
},
want: "PUT /api/v1/internal/apps/3/users/eastsales@qiniu.com/roles",
want: "PUT /api/v1/internal/apps/3/users/2001/roles",
},
{
name: "unbind app",
call: func(s *WayneRoleBindingService) (*WayneRoleBindingResult, error) {
return s.UnbindApp(context.Background(), 3, "eastsales@qiniu.com", WayneRoleBindingRequest{GroupIDs: []uint64{20}})
return s.UnbindApp(context.Background(), 3, 2001, "eastsales@qiniu.com", WayneRoleBindingRequest{GroupIDs: []uint64{20}})
},
want: "DELETE /api/v1/internal/apps/3/users/eastsales@qiniu.com/roles",
want: "DELETE /api/v1/internal/apps/3/users/2001/roles",
},
{
name: "list namespaces",
@@ -120,9 +125,9 @@ func TestWayneRoleBindingServiceCallsAllDocumentedEndpoints(t *testing.T) {
{
name: "get user roles",
call: func(s *WayneRoleBindingService) (*WayneRoleBindingResult, error) {
return s.GetUserRoles(context.Background(), "eastsales@qiniu.com")
return s.GetUserRoles(context.Background(), 2001)
},
want: "GET /api/v1/internal/users/eastsales@qiniu.com/roles",
want: "GET /api/v1/internal/users/2001/roles",
},
{
name: "namespace operator permissions",
@@ -226,7 +231,7 @@ func TestWayneRoleBindingServiceHTTPError(t *testing.T) {
WayneServiceName: "xinfra",
WayneServiceAPISecretKey: "service-secret",
})
result, err := svc.BindApp(context.Background(), 3, "eastsales@qiniu.com", WayneRoleBindingRequest{GroupIDs: []uint64{20}})
result, err := svc.BindApp(context.Background(), 3, 2001, "eastsales@qiniu.com", WayneRoleBindingRequest{GroupIDs: []uint64{20}})
if err == nil {
t.Fatal("expected error")
}