Files
opsx-vpn/backend/internal/identityprovider/directory_sync_test.go
T

106 lines
4.8 KiB
Go

package identityprovider
import (
"context"
"io"
"net/http"
"strings"
"testing"
)
func TestReadDirectoryTraversesDepartmentsAndPages(t *testing.T) {
previous := upstreamClient
t.Cleanup(func() { upstreamClient = previous })
calls := map[string]int{}
upstreamClient = &http.Client{Transport: transportFunc(func(req *http.Request) (*http.Response, error) {
if req.Header.Get("Authorization") != "Bearer test-token" {
t.Fatal("missing application token")
}
path := req.URL.Path
calls[path]++
body := `{"code":0,"data":{"items":[]}}`
switch path {
case "/open-apis/contact/v3/scopes":
body = `{"code":0,"data":{"department_ids":["0"],"user_ids":[]}}`
case "/open-apis/contact/v3/departments/0/children":
body = `{"code":0,"data":{"items":[{"open_department_id":"od_child","name":"研发","parent_department_id":"0"}]}}`
case "/open-apis/contact/v3/users":
if req.URL.Query().Get("department_id") == "0" && req.URL.Query().Get("page_token") == "" {
body = `{"code":0,"data":{"items":[{"open_id":"ou_1","name":"甲","department_ids":["0"]}],"has_more":true,"page_token":"next"}}`
} else if req.URL.Query().Get("department_id") == "0" && req.URL.Query().Get("page_token") == "next" {
body = `{"code":0,"data":{"items":[{"open_id":"ou_2","name":"乙"}]}}`
} else if req.URL.Query().Get("department_id") == "od_child" {
body = `{"code":0,"data":{"items":[{"open_id":"ou_1","name":"甲","department_ids":["od_child"]}]}}`
} else {
t.Fatalf("unexpected page: %s", req.URL)
}
case "/open-apis/contact/v3/departments/od_child/children":
default:
t.Fatalf("unexpected endpoint: %s", req.URL)
}
return &http.Response{StatusCode: 200, Body: io.NopCloser(strings.NewReader(body)), Header: make(http.Header)}, nil
})}
snapshot, err := readDirectory(context.Background(), "test-token")
if err != nil || len(snapshot.departments) != 2 || len(snapshot.members) != 2 || calls["/open-apis/contact/v3/users"] != 3 || snapshot.members["ou_1"].Departments != `["0","od_child"]` {
t.Fatalf("unexpected snapshot: %v, %#v, %#v", err, snapshot, calls)
}
}
func TestReadDirectoryFailsClosedOnIncompletePage(t *testing.T) {
previous := upstreamClient
t.Cleanup(func() { upstreamClient = previous })
upstreamClient = &http.Client{Transport: transportFunc(func(req *http.Request) (*http.Response, error) {
body := `{"code":0,"data":{"department_ids":["0"]}}`
if req.URL.Path != "/open-apis/contact/v3/scopes" {
body = `{"code":0,"data":{"items":[],"has_more":true}}`
}
return &http.Response{StatusCode: 200, Body: io.NopCloser(strings.NewReader(body)), Header: make(http.Header)}, nil
})}
if _, err := readDirectory(context.Background(), "token"); err == nil || !strings.Contains(err.Error(), "分页游标") {
t.Fatalf("accepted partial directory: %v", err)
}
}
func TestDirectoryAccessCheckDistinguishesVisibilityAndEndpointPermission(t *testing.T) {
previous := upstreamClient
t.Cleanup(func() { upstreamClient = previous })
visible := false
calls := 0
upstreamClient = &http.Client{Transport: transportFunc(func(req *http.Request) (*http.Response, error) {
calls++
body := `{"code":0,"data":{}}`
switch req.URL.Path {
case "/open-apis/auth/v3/tenant_access_token/internal":
body = `{"code":0,"tenant_access_token":"private-token"}`
case "/open-apis/contact/v3/scopes":
if req.Header.Get("Authorization") != "Bearer private-token" {
t.Fatal("missing scoped tenant token")
}
if visible {
body = `{"code":0,"data":{"department_ids":["0"],"user_ids":["ou_1"]}}`
} else {
body = `{"code":0,"data":{"department_ids":[],"user_ids":["ou_1"]}}`
}
case "/open-apis/contact/v3/departments/0/children":
body = `{"code":99991672,"msg":"private upstream message"}`
case "/open-apis/contact/v3/users":
body = `{"code":0,"data":{"items":[{"name":"private user"}]}}`
default:
t.Fatalf("unexpected endpoint: %s", req.URL)
}
return &http.Response{StatusCode: 200, Body: io.NopCloser(strings.NewReader(body)), Header: make(http.Header)}, nil
})}
result, err := checkDirectoryAccess(context.Background(), "cli_test", "private-secret")
if err != nil || result["visible_departments"] != 0 || result["department_access"] != "unverified" || calls != 2 {
t.Fatalf("false permission conclusion for invisible departments: %v %v calls=%d", result, err, calls)
}
visible = true
result, err = checkDirectoryAccess(context.Background(), "cli_test", "private-secret")
if err != nil || result["department_access"] != "denied" || result["user_list_access"] != "ok" {
t.Fatalf("wrong endpoint probes: %v %v", result, err)
}
if raw := result["department_error"].(string); !strings.Contains(raw, "99991672") || strings.Contains(raw, "private upstream message") || strings.Contains(raw, "private-token") {
t.Fatalf("error leaked upstream data or omitted code: %s", raw)
}
}