Skip to content

Commit a161ef8

Browse files
feat(core): v2 ERS with proto updates (#2210)
### Proposed Changes * ERS v2 implementation handling new ERS v2 protos that engage newly detached Entity-related proto messages * Improves file/directory organization to help clarify v2 directory structures * Copies all logic and tests over into v2, updates types, updates message/field names * No refactoring or logic changes at all * lint fixes flagged by updated linter on changed files ### Checklist - [x] I have added or updated unit tests - [ ] I have added or updated integration tests (if appropriate) - [ ] I have added or updated documentation ### Testing Instructions --------- Co-authored-by: Ryan Yanulites <[email protected]>
1 parent 363c22d commit a161ef8

21 files changed

+1435
-15
lines changed

docs/grpc/index.html

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/openapi/entityresolution/entity_resolution.swagger.json

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

protocol/go/entityresolution/entity_resolution_grpc.pb.go

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

protocol/go/entityresolution/entityresolutionconnect/entity_resolution.connect.go

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sdk/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ require (
1212
github.com/lestrrat-go/jwx/v2 v2.0.21
1313
github.com/opentdf/platform/lib/fixtures v0.2.10
1414
github.com/opentdf/platform/lib/ocrypto v0.1.9
15-
github.com/opentdf/platform/protocol/go v0.3.2
15+
github.com/opentdf/platform/protocol/go v0.3.3
1616
github.com/stretchr/testify v1.10.0
1717
github.com/testcontainers/testcontainers-go v0.34.0
1818
github.com/xeipuuv/gojsonschema v1.2.0

sdk/go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,8 @@ github.com/opentdf/platform/lib/fixtures v0.2.10 h1:R688b98ctsEiDRlQSvLxmAWT7bXv
116116
github.com/opentdf/platform/lib/fixtures v0.2.10/go.mod h1:wGhclxDeDXf8bp5VAWztT1nY2gWVNGQLd8rWs5wtXV0=
117117
github.com/opentdf/platform/lib/ocrypto v0.1.9 h1:GvgPB7CoK7JmWvsSvJ0hc+RC0wezgcuRpy3q2oYKjdA=
118118
github.com/opentdf/platform/lib/ocrypto v0.1.9/go.mod h1:UTtqh8mvhAYA+sEnaMxpr/406e84L5Q1sAxtKGIXfu4=
119-
github.com/opentdf/platform/protocol/go v0.3.2 h1:WugeSl7RSRM7e7c5jJumZOIW2jr+sMqwDzpGUGyeC5k=
120-
github.com/opentdf/platform/protocol/go v0.3.2/go.mod h1:nErYkgt32GW22CNqSyLO+JE49C3JndI1TsVdF+CUYd4=
119+
github.com/opentdf/platform/protocol/go v0.3.3 h1:ySnnMniOFpxnc4G2EPGQEuEzaYAfLtNhqqIkGv1XXQM=
120+
github.com/opentdf/platform/protocol/go v0.3.3/go.mod h1:nErYkgt32GW22CNqSyLO+JE49C3JndI1TsVdF+CUYd4=
121121
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
122122
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
123123
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=

sdk/sdk.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"github.com/opentdf/platform/lib/ocrypto"
1818
"github.com/opentdf/platform/protocol/go/authorization"
1919
"github.com/opentdf/platform/protocol/go/entityresolution"
20+
entityresolutionV2 "github.com/opentdf/platform/protocol/go/entityresolution/v2"
2021
"github.com/opentdf/platform/protocol/go/policy"
2122
"github.com/opentdf/platform/protocol/go/policy/actions"
2223
"github.com/opentdf/platform/protocol/go/policy/attributes"
@@ -71,6 +72,7 @@ type SDK struct {
7172
Attributes attributes.AttributesServiceClient
7273
Authorization authorization.AuthorizationServiceClient
7374
EntityResoution entityresolution.EntityResolutionServiceClient
75+
EntityResolutionV2 entityresolutionV2.EntityResolutionServiceClient
7476
KeyAccessServerRegistry kasregistry.KeyAccessServerRegistryServiceClient
7577
Namespaces namespaces.NamespaceServiceClient
7678
RegisteredResources registeredresources.RegisteredResourcesServiceClient
@@ -214,6 +216,7 @@ func New(platformEndpoint string, opts ...Option) (*SDK, error) {
214216
KeyAccessServerRegistry: kasregistry.NewKeyAccessServerRegistryServiceClient(platformConn),
215217
Authorization: authorization.NewAuthorizationServiceClient(platformConn),
216218
EntityResoution: entityresolution.NewEntityResolutionServiceClient(ersConn),
219+
EntityResolutionV2: entityresolutionV2.NewEntityResolutionServiceClient(ersConn),
217220
KeyManagement: keymanagement.NewKeyManagementServiceClient(platformConn),
218221
wellknownConfiguration: wellknownconfiguration.NewWellKnownServiceClient(platformConn),
219222
}, nil

service/entityresolution/claims/claims_entity_resolution.go renamed to service/entityresolution/claims/entity_resolution.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package entityresolution
1+
package claims
22

33
import (
44
"context"

service/entityresolution/claims/claims_entity_resolution_test.go renamed to service/entityresolution/claims/entity_resolution_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package entityresolution_test
1+
package claims_test
22

33
import (
44
"testing"
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package claims
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"log/slog"
7+
"strconv"
8+
9+
"connectrpc.com/connect"
10+
"github.com/lestrrat-go/jwx/v2/jwt"
11+
"github.com/opentdf/platform/protocol/go/entity"
12+
entityresolutionV2 "github.com/opentdf/platform/protocol/go/entityresolution/v2"
13+
auth "github.com/opentdf/platform/service/authorization"
14+
"github.com/opentdf/platform/service/logger"
15+
"github.com/opentdf/platform/service/pkg/config"
16+
"github.com/opentdf/platform/service/pkg/serviceregistry"
17+
"go.opentelemetry.io/otel/trace"
18+
"google.golang.org/protobuf/encoding/protojson"
19+
"google.golang.org/protobuf/types/known/anypb"
20+
"google.golang.org/protobuf/types/known/structpb"
21+
)
22+
23+
type EntityResolutionServiceV2 struct {
24+
entityresolutionV2.UnimplementedEntityResolutionServiceServer
25+
logger *logger.Logger
26+
trace.Tracer
27+
}
28+
29+
func RegisterClaimsERS(_ config.ServiceConfig, logger *logger.Logger) (EntityResolutionServiceV2, serviceregistry.HandlerServer) {
30+
claimsSVC := EntityResolutionServiceV2{logger: logger}
31+
return claimsSVC, nil
32+
}
33+
34+
func (s EntityResolutionServiceV2) ResolveEntities(ctx context.Context, req *connect.Request[entityresolutionV2.ResolveEntitiesRequest]) (*connect.Response[entityresolutionV2.ResolveEntitiesResponse], error) {
35+
resp, err := EntityResolution(ctx, req.Msg, s.logger)
36+
return connect.NewResponse(&resp), err
37+
}
38+
39+
func (s EntityResolutionServiceV2) CreateEntityChainsFromTokens(ctx context.Context, req *connect.Request[entityresolutionV2.CreateEntityChainsFromTokensRequest]) (*connect.Response[entityresolutionV2.CreateEntityChainsFromTokensResponse], error) {
40+
ctx, span := s.Tracer.Start(ctx, "CreateEntityChainsFromTokens")
41+
defer span.End()
42+
43+
resp, err := CreateEntityChainsFromTokens(ctx, req.Msg, s.logger)
44+
return connect.NewResponse(&resp), err
45+
}
46+
47+
func CreateEntityChainsFromTokens(
48+
_ context.Context,
49+
req *entityresolutionV2.CreateEntityChainsFromTokensRequest,
50+
_ *logger.Logger,
51+
) (entityresolutionV2.CreateEntityChainsFromTokensResponse, error) {
52+
entityChains := []*entity.EntityChain{}
53+
// for each token in the tokens form an entity chain
54+
for _, tok := range req.GetTokens() {
55+
entities, err := getEntitiesFromToken(tok.GetJwt())
56+
if err != nil {
57+
return entityresolutionV2.CreateEntityChainsFromTokensResponse{}, err
58+
}
59+
entityChains = append(entityChains, &entity.EntityChain{EphemeralId: tok.GetEphemeralId(), Entities: entities})
60+
}
61+
62+
return entityresolutionV2.CreateEntityChainsFromTokensResponse{EntityChains: entityChains}, nil
63+
}
64+
65+
func EntityResolution(_ context.Context,
66+
req *entityresolutionV2.ResolveEntitiesRequest, logger *logger.Logger,
67+
) (entityresolutionV2.ResolveEntitiesResponse, error) {
68+
payload := req.GetEntities()
69+
var resolvedEntities []*entityresolutionV2.EntityRepresentation
70+
71+
for idx, ident := range payload {
72+
entityStruct := &structpb.Struct{}
73+
switch ident.GetEntityType().(type) {
74+
case *entity.Entity_Claims:
75+
claims := ident.GetClaims()
76+
if claims != nil {
77+
err := claims.UnmarshalTo(entityStruct)
78+
if err != nil {
79+
return entityresolutionV2.ResolveEntitiesResponse{}, connect.NewError(connect.CodeInvalidArgument, fmt.Errorf("error unpacking anypb.Any to structpb.Struct: %w", err))
80+
}
81+
}
82+
default:
83+
retrievedStruct, err := entityToStructPb(ident)
84+
if err != nil {
85+
logger.Error("unable to make entity struct", slog.String("error", err.Error()))
86+
return entityresolutionV2.ResolveEntitiesResponse{}, connect.NewError(connect.CodeInternal, fmt.Errorf("unable to make entity struct: %w", err))
87+
}
88+
entityStruct = retrievedStruct
89+
}
90+
// make sure the id field is populated
91+
originialID := ident.GetEphemeralId()
92+
if originialID == "" {
93+
originialID = auth.EntityIDPrefix + strconv.Itoa(idx)
94+
}
95+
resolvedEntities = append(
96+
resolvedEntities,
97+
&entityresolutionV2.EntityRepresentation{
98+
OriginalId: originialID,
99+
AdditionalProps: []*structpb.Struct{entityStruct},
100+
},
101+
)
102+
}
103+
return entityresolutionV2.ResolveEntitiesResponse{EntityRepresentations: resolvedEntities}, nil
104+
}
105+
106+
func getEntitiesFromToken(jwtString string) ([]*entity.Entity, error) {
107+
token, err := jwt.ParseString(jwtString, jwt.WithVerify(false), jwt.WithValidate(false))
108+
if err != nil {
109+
return nil, fmt.Errorf("error parsing jwt: %w", err)
110+
}
111+
112+
claims := token.PrivateClaims()
113+
entities := []*entity.Entity{}
114+
115+
// Convert map[string]interface{} to *structpb.Struct
116+
structClaims, err := structpb.NewStruct(claims)
117+
if err != nil {
118+
return nil, fmt.Errorf("error converting to structpb.Struct: %w", err)
119+
}
120+
121+
// Wrap the struct in an *anypb.Any message
122+
anyClaims, err := anypb.New(structClaims)
123+
if err != nil {
124+
return nil, fmt.Errorf("error wrapping in anypb.Any: %w", err)
125+
}
126+
127+
entities = append(entities, &entity.Entity{
128+
EntityType: &entity.Entity_Claims{Claims: anyClaims},
129+
EphemeralId: "jwtentity-claims",
130+
Category: entity.Entity_CATEGORY_SUBJECT,
131+
})
132+
return entities, nil
133+
}
134+
135+
func entityToStructPb(ident *entity.Entity) (*structpb.Struct, error) {
136+
entityBytes, err := protojson.Marshal(ident)
137+
if err != nil {
138+
return nil, err
139+
}
140+
var entityStruct structpb.Struct
141+
err = entityStruct.UnmarshalJSON(entityBytes)
142+
if err != nil {
143+
return nil, err
144+
}
145+
return &entityStruct, nil
146+
}

0 commit comments

Comments
 (0)