77
88 "github.com/DIMO-Network/cloudevent"
99 "github.com/DIMO-Network/fetch-api/internal/graph/model"
10+ "github.com/DIMO-Network/fetch-api/internal/identity"
1011 "github.com/DIMO-Network/fetch-api/pkg/eventrepo"
1112 "github.com/DIMO-Network/fetch-api/pkg/grpc"
1213 "github.com/DIMO-Network/token-exchange-api/pkg/tokenclaims"
@@ -15,17 +16,17 @@ import (
1516
1617// This file will not be regenerated automatically.
1718//
18- // It serves as dependency injection for your app, add any dependencies you require
19- // here.
19+ // It serves as dependency injection for your app, add any dependencies you require here.
2020
2121// ClaimsContextKey is the context key for token claims (set by JWT middleware).
2222type ClaimsContextKey struct {}
2323
2424type Resolver struct {
25- EventService * eventrepo.Service
26- Buckets []string
27- VehicleAddr common.Address
28- ChainID uint64
25+ EventService * eventrepo.Service
26+ Buckets []string
27+ VehicleAddr common.Address
28+ ChainID uint64
29+ IdentityClient identity.Client
2930}
3031
3132// CheckVehicleRawDataByDID returns an error if the context does not have claims or the token
@@ -34,27 +35,58 @@ func CheckVehicleRawDataByDID(ctx context.Context, did string) error {
3435 if _ , err := cloudevent .DecodeERC721DID (did ); err != nil {
3536 return fmt .Errorf ("invalid DID: %w" , err )
3637 }
38+ return checkRawDataPermissionsForVehicle (ctx , did )
39+ }
40+
41+ // checkRawDataPermissionsForVehicle verifies the context token has access for vehicleDID.
42+ // vehicleDID must already be validated.
43+ // Allowed: GetRawData, or both GetLocationHistory (all-time location) and GetNonLocationHistory.
44+ func checkRawDataPermissionsForVehicle (ctx context.Context , vehicleDID string ) error {
3745 tok , _ := ctx .Value (ClaimsContextKey {}).(* tokenclaims.Token )
3846 if tok == nil {
3947 return fmt .Errorf ("unauthorized: no token claims" )
4048 }
41- if tok .Asset != did {
49+ if tok .Asset != vehicleDID {
4250 return fmt .Errorf ("unauthorized: token does not have access to this vehicle" )
4351 }
44- if ! slices .Contains (tok .Permissions , tokenclaims .PermissionGetRawData ) {
45- return fmt .Errorf ("unauthorized: missing GetRawData privilege" )
52+ hasGetRawData := slices .Contains (tok .Permissions , tokenclaims .PermissionGetRawData )
53+ hasLocationHistory := slices .Contains (tok .Permissions , tokenclaims .PermissionGetLocationHistory )
54+ hasNonLocationHistory := slices .Contains (tok .Permissions , tokenclaims .PermissionGetNonLocationHistory )
55+ hasAllTimeData := hasLocationHistory && hasNonLocationHistory
56+ if ! hasGetRawData && ! hasAllTimeData {
57+ return fmt .Errorf ("unauthorized: token does not have access to this vehicle" )
4658 }
4759 return nil
4860}
4961
50- // requireVehicleOptsByDID validates access by DID and returns search options for the vehicle.
62+ // requireVehicleOptsByDID validates raw-data access and returns search options for the DID.
63+ //
64+ // If did belongs to the vehicle NFT contract (r.VehicleAddr), the token asset must match that
65+ // vehicle DID directly. Otherwise the DID is treated as a device DID: identity-api is called
66+ // to resolve the paired vehicle, and the token must have access to that vehicle. Either way,
67+ // the search subject is always the originally requested DID.
5168func (r * queryResolver ) requireVehicleOptsByDID (ctx context.Context , did string , filter * model.CloudEventFilter ) (* grpc.SearchOptions , error ) {
52- if err := CheckVehicleRawDataByDID (ctx , did ); err != nil {
53- return nil , err
54- }
55- subject , err := cloudevent .DecodeERC721DID (did )
69+ decoded , err := cloudevent .DecodeERC721DID (did )
5670 if err != nil {
5771 return nil , fmt .Errorf ("invalid DID: %w" , err )
5872 }
59- return filterToSearchOptions (filter , subject ), nil
73+
74+ var vehicleDID string
75+ if decoded .ContractAddress == r .VehicleAddr {
76+ vehicleDID = did
77+ } else {
78+ if r .IdentityClient == nil {
79+ return nil , fmt .Errorf ("device DID lookup not configured: no identity client" )
80+ }
81+ vehicleDID , err = r .IdentityClient .GetVehicleDIDForDevice (ctx , did )
82+ if err != nil {
83+ return nil , fmt .Errorf ("failed to resolve vehicle for device DID: %w" , err )
84+ }
85+ }
86+
87+ if err := checkRawDataPermissionsForVehicle (ctx , vehicleDID ); err != nil {
88+ return nil , err
89+ }
90+
91+ return filterToSearchOptions (filter , decoded ), nil
6092}
0 commit comments