254 lines
8.8 KiB
Go
254 lines
8.8 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/1024XEngineer/xinfra/server/internal/config"
|
|
"github.com/1024XEngineer/xinfra/server/internal/wayne"
|
|
)
|
|
|
|
func TestWayneRoleBindingServiceBindNamespaceSignsAndOverridesOperator(t *testing.T) {
|
|
var requestPath string
|
|
var payload WayneRoleBindingRequest
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
requestPath = r.URL.RequestURI()
|
|
body := readTestBody(t, r)
|
|
if !wayne.Verify("service-secret", r.Header.Get(wayne.HeaderSignature), r.Method, r.URL.RequestURI(), r.Header.Get(wayne.HeaderTimestamp), r.Header.Get(wayne.HeaderNonce), body) {
|
|
t.Fatalf("invalid signature headers: %#v body=%s", r.Header, string(body))
|
|
}
|
|
if err := json.Unmarshal(body, &payload); err != nil {
|
|
t.Fatalf("invalid request body: %v", err)
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_, _ = w.Write([]byte(`{"data":{"changed":true}}`))
|
|
}))
|
|
defer server.Close()
|
|
|
|
svc := NewWayneRoleBindingService(config.Config{
|
|
WayneInternalAPIBaseURL: server.URL,
|
|
WayneServiceName: "xinfra",
|
|
WayneServiceAPISecretKey: "service-secret",
|
|
})
|
|
svc.now = func() time.Time { return time.Unix(1721000000, 0) }
|
|
|
|
operatorUserID := uint64(123)
|
|
replace := false
|
|
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",
|
|
Replace: &replace,
|
|
RequestID: "req-001",
|
|
Reason: "grant",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("BindNamespace() error = %v", err)
|
|
}
|
|
if result.StatusCode != http.StatusOK {
|
|
t.Fatalf("StatusCode = %d, want 200", result.StatusCode)
|
|
}
|
|
if requestPath != "/api/v1/internal/namespaces/1/users/2001/roles" {
|
|
t.Fatalf("requestPath = %q", requestPath)
|
|
}
|
|
if payload.OperatorName != "eastsales@qiniu.com" {
|
|
t.Fatalf("OperatorName = %q, want token email", payload.OperatorName)
|
|
}
|
|
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)
|
|
}
|
|
}
|
|
|
|
func TestWayneRoleBindingServiceCallsAllDocumentedEndpoints(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
call func(*WayneRoleBindingService) (*WayneRoleBindingResult, error)
|
|
want string
|
|
}{
|
|
{
|
|
name: "unbind namespace",
|
|
call: func(s *WayneRoleBindingService) (*WayneRoleBindingResult, error) {
|
|
return s.UnbindNamespace(context.Background(), 1, 2001, "eastsales@qiniu.com", WayneRoleBindingRequest{GroupIDs: []uint64{10}})
|
|
},
|
|
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, 2001, "eastsales@qiniu.com", WayneRoleBindingRequest{GroupIDs: []uint64{20}})
|
|
},
|
|
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, 2001, "eastsales@qiniu.com", WayneRoleBindingRequest{GroupIDs: []uint64{20}})
|
|
},
|
|
want: "DELETE /api/v1/internal/apps/3/users/2001/roles",
|
|
},
|
|
{
|
|
name: "list namespaces",
|
|
call: func(s *WayneRoleBindingService) (*WayneRoleBindingResult, error) {
|
|
return s.ListNamespaces(context.Background())
|
|
},
|
|
want: "GET /api/v1/internal/namespaces",
|
|
},
|
|
{
|
|
name: "list namespace groups",
|
|
call: func(s *WayneRoleBindingService) (*WayneRoleBindingResult, error) {
|
|
groupType := 1
|
|
return s.ListGroups(context.Background(), &groupType)
|
|
},
|
|
want: "GET /api/v1/internal/groups?type=1",
|
|
},
|
|
{
|
|
name: "list all groups",
|
|
call: func(s *WayneRoleBindingService) (*WayneRoleBindingResult, error) {
|
|
return s.ListGroups(context.Background(), nil)
|
|
},
|
|
want: "GET /api/v1/internal/groups",
|
|
},
|
|
{
|
|
name: "get user roles",
|
|
call: func(s *WayneRoleBindingService) (*WayneRoleBindingResult, error) {
|
|
return s.GetUserRoles(context.Background(), 2001)
|
|
},
|
|
want: "GET /api/v1/internal/users/2001/roles",
|
|
},
|
|
{
|
|
name: "namespace operator permissions",
|
|
call: func(s *WayneRoleBindingService) (*WayneRoleBindingResult, error) {
|
|
return s.NamespaceOperatorPermissions(context.Background(), 1, "eastsales@qiniu.com")
|
|
},
|
|
want: "GET /api/v1/internal/namespaces/1/operator-permissions?operatorName=eastsales%40qiniu.com",
|
|
},
|
|
{
|
|
name: "app operator permissions",
|
|
call: func(s *WayneRoleBindingService) (*WayneRoleBindingResult, error) {
|
|
return s.AppOperatorPermissions(context.Background(), 3, "eastsales@qiniu.com")
|
|
},
|
|
want: "GET /api/v1/internal/apps/3/operator-permissions?operatorName=eastsales%40qiniu.com",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
var got string
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
got = r.Method + " " + r.URL.RequestURI()
|
|
_, _ = w.Write([]byte(`{"data":{"changed":true}}`))
|
|
}))
|
|
defer server.Close()
|
|
|
|
svc := NewWayneRoleBindingService(config.Config{
|
|
WayneInternalAPIBaseURL: server.URL,
|
|
WayneServiceName: "xinfra",
|
|
WayneServiceAPISecretKey: "service-secret",
|
|
})
|
|
if _, err := tt.call(svc); err != nil {
|
|
t.Fatalf("call error = %v", err)
|
|
}
|
|
if got != tt.want {
|
|
t.Fatalf("got endpoint %q, want %q", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestWayneRoleBindingServiceQuerySignsEmptyBodyAndQueryURI(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
body := readTestBody(t, r)
|
|
if len(body) != 0 {
|
|
t.Fatalf("GET body length = %d, want 0", len(body))
|
|
}
|
|
if r.URL.RequestURI() != "/api/v1/internal/groups?type=1" {
|
|
t.Fatalf("RequestURI = %q", r.URL.RequestURI())
|
|
}
|
|
if !wayne.Verify("service-secret", r.Header.Get(wayne.HeaderSignature), r.Method, r.URL.RequestURI(), r.Header.Get(wayne.HeaderTimestamp), r.Header.Get(wayne.HeaderNonce), body) {
|
|
t.Fatalf("invalid GET signature headers: %#v", r.Header)
|
|
}
|
|
_, _ = w.Write([]byte(`{"data":[]}`))
|
|
}))
|
|
defer server.Close()
|
|
|
|
svc := NewWayneRoleBindingService(config.Config{
|
|
WayneInternalAPIBaseURL: server.URL,
|
|
WayneServiceName: "xinfra",
|
|
WayneServiceAPISecretKey: "service-secret",
|
|
})
|
|
groupType := 1
|
|
if _, err := svc.ListGroups(context.Background(), &groupType); err != nil {
|
|
t.Fatalf("ListGroups() error = %v", err)
|
|
}
|
|
}
|
|
|
|
func TestWayneRoleBindingServiceOperatorPermissionsSignsQueryURI(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
body := readTestBody(t, r)
|
|
if r.URL.RequestURI() != "/api/v1/internal/namespaces/1/operator-permissions?operatorName=eastsales%40qiniu.com" {
|
|
t.Fatalf("RequestURI = %q", r.URL.RequestURI())
|
|
}
|
|
if !wayne.Verify("service-secret", r.Header.Get(wayne.HeaderSignature), r.Method, r.URL.RequestURI(), r.Header.Get(wayne.HeaderTimestamp), r.Header.Get(wayne.HeaderNonce), body) {
|
|
t.Fatalf("invalid operator permissions signature headers: %#v", r.Header)
|
|
}
|
|
_, _ = w.Write([]byte(`{"data":{"permissions":{"create":true,"update":true,"delete":false}}}`))
|
|
}))
|
|
defer server.Close()
|
|
|
|
svc := NewWayneRoleBindingService(config.Config{
|
|
WayneInternalAPIBaseURL: server.URL,
|
|
WayneServiceName: "xinfra",
|
|
WayneServiceAPISecretKey: "service-secret",
|
|
})
|
|
if _, err := svc.NamespaceOperatorPermissions(context.Background(), 1, "eastsales@qiniu.com"); err != nil {
|
|
t.Fatalf("NamespaceOperatorPermissions() error = %v", err)
|
|
}
|
|
}
|
|
|
|
func TestWayneRoleBindingServiceHTTPError(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusForbidden)
|
|
_, _ = w.Write([]byte(`{"code":403,"msg":"denied"}`))
|
|
}))
|
|
defer server.Close()
|
|
|
|
svc := NewWayneRoleBindingService(config.Config{
|
|
WayneInternalAPIBaseURL: server.URL,
|
|
WayneServiceName: "xinfra",
|
|
WayneServiceAPISecretKey: "service-secret",
|
|
})
|
|
result, err := svc.BindApp(context.Background(), 3, 2001, "eastsales@qiniu.com", WayneRoleBindingRequest{GroupIDs: []uint64{20}})
|
|
if err == nil {
|
|
t.Fatal("expected error")
|
|
}
|
|
if result == nil || result.StatusCode != http.StatusForbidden {
|
|
t.Fatalf("result = %#v, want 403", result)
|
|
}
|
|
if !strings.Contains(err.Error(), "denied") {
|
|
t.Fatalf("error = %q, want denied body", err.Error())
|
|
}
|
|
}
|
|
|
|
func readTestBody(t *testing.T, r *http.Request) []byte {
|
|
t.Helper()
|
|
body, err := io.ReadAll(r.Body)
|
|
if err != nil {
|
|
t.Fatalf("read body: %v", err)
|
|
}
|
|
return body
|
|
}
|