Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ab5a96ca4 |
@@ -47,19 +47,24 @@ func (handler Handler) List(context *gin.Context) {
|
||||
}
|
||||
|
||||
func currentUserID(c *gin.Context) uint64 {
|
||||
value, ok := c.Get("userId")
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
switch id := value.(type) {
|
||||
// #333: go-admin's Authorizator runs on every request with the
|
||||
// IdentityHandler map, which has no "user" entry, so c.Get("userId") is
|
||||
// always 0 there. The JWT "identity" claim is the authenticated user id.
|
||||
switch id := jwt.ExtractClaims(c)["identity"].(type) {
|
||||
case float64:
|
||||
if id > 0 {
|
||||
return uint64(id)
|
||||
}
|
||||
case int:
|
||||
return uint64(id)
|
||||
if id > 0 {
|
||||
return uint64(id)
|
||||
}
|
||||
case int64:
|
||||
return uint64(id)
|
||||
if id > 0 {
|
||||
return uint64(id)
|
||||
}
|
||||
case uint64:
|
||||
return id
|
||||
case float64:
|
||||
return uint64(id)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
package device
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
jwt "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth"
|
||||
"go-admin/app/goauto/models"
|
||||
)
|
||||
|
||||
// #333: mirrors the real go-admin middleware, whose Authorizator sets
|
||||
// userId to 0 on every request while the JWT claims carry the identity.
|
||||
func listDevicesAs(t *testing.T, handler Handler, claims jwt.MapClaims) []DeviceListItem {
|
||||
t.Helper()
|
||||
gin.SetMode(gin.TestMode)
|
||||
engine := gin.New()
|
||||
engine.GET("/devices", func(c *gin.Context) {
|
||||
c.Set(jwt.JwtPayloadKey, claims)
|
||||
c.Set("userId", 0)
|
||||
c.Next()
|
||||
}, handler.List)
|
||||
recorder := httptest.NewRecorder()
|
||||
engine.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/devices?page=1&pageSize=20", nil))
|
||||
if recorder.Code != http.StatusOK {
|
||||
t.Fatalf("status=%d body=%s", recorder.Code, recorder.Body.String())
|
||||
}
|
||||
var body struct {
|
||||
Data DeviceListResponse `json:"data"`
|
||||
}
|
||||
if err := json.Unmarshal(recorder.Body.Bytes(), &body); err != nil {
|
||||
t.Fatalf("decode: %v", err)
|
||||
}
|
||||
return body.Data.Items
|
||||
}
|
||||
|
||||
func seedOwnedDevice(t *testing.T, handler Handler, name string, owner *uint64) models.AgentDevice {
|
||||
t.Helper()
|
||||
device := models.AgentDevice{
|
||||
InstallID: "install-" + name, Name: name, Manufacturer: "test", Model: "test",
|
||||
AndroidVersion: "14", AgentVersion: "1", PDDVersion: "1", CapabilitiesJSON: "[]",
|
||||
Status: models.DeviceStatusOnline, TokenDigest: fmt.Sprintf("digest-%s", name),
|
||||
TokenIssuedAt: time.Now(), OwnerUserID: owner,
|
||||
}
|
||||
if err := handler.DB.Create(&device).Error; err != nil {
|
||||
t.Fatalf("create device: %v", err)
|
||||
}
|
||||
return device
|
||||
}
|
||||
|
||||
func deviceNames(items []DeviceListItem) map[string]bool {
|
||||
names := map[string]bool{}
|
||||
for _, item := range items {
|
||||
names[item.Name] = true
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
func TestDeviceListPurchaserSeesOnlyOwnDevicesFromJWTIdentity(t *testing.T) {
|
||||
handler := Handler{DB: openTestDatabase(t)}
|
||||
two, three := uint64(2), uint64(3)
|
||||
seedOwnedDevice(t, handler, "caigou1-phone", &two)
|
||||
seedOwnedDevice(t, handler, "caigou2-phone", &three)
|
||||
seedOwnedDevice(t, handler, "unowned-phone", nil)
|
||||
|
||||
names := deviceNames(listDevicesAs(t, handler, jwt.MapClaims{"rolekey": "purchaser", "identity": float64(3)}))
|
||||
if len(names) != 1 || !names["caigou2-phone"] {
|
||||
t.Fatalf("purchaser 3 should see only own device, got %v", names)
|
||||
}
|
||||
|
||||
all := deviceNames(listDevicesAs(t, handler, jwt.MapClaims{"rolekey": "admin", "identity": float64(1)}))
|
||||
if len(all) != 3 {
|
||||
t.Fatalf("admin should see all devices, got %v", all)
|
||||
}
|
||||
|
||||
none := listDevicesAs(t, handler, jwt.MapClaims{"rolekey": "purchaser"})
|
||||
if len(none) != 0 {
|
||||
t.Fatalf("purchaser without identity must see no devices, got %d", len(none))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package shopeeproduct
|
||||
|
||||
import (
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
jwt "github.com/go-admin-team/go-admin-core/sdk/pkg/jwtauth"
|
||||
)
|
||||
|
||||
// #333: the real middleware sets userId=0 on every request; the operator used
|
||||
// for owned-device collection lookup and audit must come from the JWT identity.
|
||||
func TestCurrentUserIDReadsJWTIdentityNotUserIDKey(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
cases := []struct {
|
||||
claims jwt.MapClaims
|
||||
want uint64
|
||||
}{
|
||||
{jwt.MapClaims{"rolekey": "purchaser", "identity": float64(3)}, 3},
|
||||
{jwt.MapClaims{"rolekey": "purchaser"}, 0},
|
||||
{jwt.MapClaims{"rolekey": "purchaser", "identity": float64(-1)}, 0},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
c, _ := gin.CreateTestContext(httptest.NewRecorder())
|
||||
c.Set(jwt.JwtPayloadKey, tc.claims)
|
||||
c.Set("userId", 0)
|
||||
if got := currentUserID(c); got != tc.want {
|
||||
t.Fatalf("claims %v: got %d want %d", tc.claims, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -427,22 +427,26 @@ func (handler Handler) service(c *gin.Context) (*Service, bool) {
|
||||
// purchasing roles per #40; role membership itself is enforced by the router
|
||||
// middleware, not here.
|
||||
func currentUserID(c *gin.Context) uint64 {
|
||||
value, exists := c.Get("userId")
|
||||
if !exists {
|
||||
return 0
|
||||
}
|
||||
switch id := value.(type) {
|
||||
// #333: go-admin's Authorizator runs on every request with the
|
||||
// IdentityHandler map, which has no "user" entry, so c.Get("userId") is
|
||||
// always 0 there. The JWT "identity" claim is the authenticated user id.
|
||||
switch id := jwt.ExtractClaims(c)["identity"].(type) {
|
||||
case float64:
|
||||
if id > 0 {
|
||||
return uint64(id)
|
||||
}
|
||||
case int:
|
||||
return uint64(id)
|
||||
if id > 0 {
|
||||
return uint64(id)
|
||||
}
|
||||
case int64:
|
||||
return uint64(id)
|
||||
if id > 0 {
|
||||
return uint64(id)
|
||||
}
|
||||
case uint64:
|
||||
return id
|
||||
case float64:
|
||||
return uint64(id)
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func currentRole(c *gin.Context) string {
|
||||
|
||||
Reference in New Issue
Block a user