Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
afe3ad1
feat(authz): GetEntitlementsV2 protos and gencode
jakedoublev Apr 28, 2025
c547edc
fix copilot-flagged comment
jakedoublev Apr 28, 2025
8843729
proto fixes
jakedoublev Apr 29, 2025
65fd691
put back auth service change accidentally committed
jakedoublev Apr 29, 2025
288592d
latest changes
jakedoublev Apr 29, 2025
999a876
draft v2 protos
jakedoublev Apr 29, 2025
50d8436
put back original authz protos
jakedoublev Apr 29, 2025
6993846
Merge branch 'main' into feat/entitle-actions-protos
jakedoublev Apr 30, 2025
88c25d2
undo change to original authn proto file
ryanulit Apr 30, 2025
b3c41f3
rename reg res fqn to use value
ryanulit Apr 30, 2025
13cf017
add entity chain comment
ryanulit Apr 30, 2025
ec22eb2
Merge branch 'main' into feat/entitle-actions-protos
ryanulit Apr 30, 2025
decdd36
Merge branch 'main' into feat/entitle-actions-protos
ryanulit Apr 30, 2025
7e9e22b
rename bulk methods to use same prefix
ryanulit Apr 30, 2025
2364f25
update protos per meeting outcome
jakedoublev Apr 30, 2025
c25a26f
cleanup
jakedoublev Apr 30, 2025
8d3b5ca
Merge branch 'main' into feat/entitle-actions-protos
jakedoublev May 1, 2025
fe76f4b
Merge branch 'main' into feat/entitle-actions-protos
jakedoublev May 4, 2025
03651a8
ERS should take new authv2 entity proto
jakedoublev May 5, 2025
5dad8b5
Merge remote-tracking branch 'origin' into feat/entitle-actions-protos
jakedoublev May 5, 2025
999428f
auth v2 service scaffold and versioned registration/sdk
ryanulit May 6, 2025
095d515
add proper deprecated comments
ryanulit May 6, 2025
aade0d6
remove non-proto changes moved to separate branch
ryanulit May 6, 2025
76390ab
Merge remote-tracking branch 'origin' into feat/entitle-actions-protos
jakedoublev May 6, 2025
8833bcb
Merge branch 'main' into feat/entitle-actions-protos
jakedoublev May 7, 2025
944a7a8
drop scope from GetEntitlements
jakedoublev May 8, 2025
ab1fc4f
comment clarity
jakedoublev May 8, 2025
120ba08
Merge branch 'main' into feat/entitle-actions-protos
jakedoublev May 9, 2025
038cf7d
Merge branch 'main' into feat/entitle-actions-protos
jakedoublev May 13, 2025
6cfb545
update protos
jakedoublev May 13, 2025
8be0ff6
improve protovalidate rules
jakedoublev May 13, 2025
0fdab47
improve protovalidate rules
jakedoublev May 13, 2025
be2118d
GetDecisionByTokenMultiResource addition
jakedoublev May 13, 2025
066cb55
deprecated comments
jakedoublev May 13, 2025
f003f41
use v1 messages in v2 authz where it makes sense (token and entity ch…
jakedoublev May 13, 2025
75b37bd
rm extraneous resource ID
jakedoublev May 13, 2025
d4444b8
pare back responses, proto comments, better validation
jakedoublev May 13, 2025
0ddaeea
ERS v2 after PR review discussion
jakedoublev May 13, 2025
62ce30d
feat(core): entityresolutionv2 service engaging auth v2 entity types
jakedoublev May 13, 2025
2317e79
feat(core): auth v2 with breaking changes to auth v1 protos (#2211)
jakedoublev May 14, 2025
8ee0b3b
make no changes at all to authz v1
jakedoublev May 14, 2025
1c5a1a9
proto comments
jakedoublev May 14, 2025
67bc23c
field names
jakedoublev May 14, 2025
1782f18
entity identifier proto name
jakedoublev May 14, 2025
7f50caa
proto comments
jakedoublev May 14, 2025
96d86d7
fix misnumbered index called out by copilot
jakedoublev May 14, 2025
33d98c7
rm empty commits to unchanged proto files
jakedoublev May 14, 2025
33dc394
validation PR suggestion
jakedoublev May 14, 2025
8156172
Merge branch 'feat/entitle-actions-protos' into feat/ers-v2
jakedoublev May 14, 2025
f7c0e93
claims v2
jakedoublev May 14, 2025
a48f091
keycloak v2
jakedoublev May 14, 2025
02fa5a2
lint fixes
jakedoublev May 14, 2025
b62da08
more lint fixes
jakedoublev May 14, 2025
6cd80ce
more lint fixes
jakedoublev May 14, 2025
ee61d6f
add deprecation warning to v1 protos
jakedoublev May 15, 2025
ed406b7
Merge remote-tracking branch 'origin' into feat/entitle-actions-protos
jakedoublev May 15, 2025
ea18fa0
Merge branch 'feat/entitle-actions-protos' into feat/ers-v2
jakedoublev May 15, 2025
57f3137
Merge remote-tracking branch 'origin' into feat/ers-v2
jakedoublev May 15, 2025
f1bfd25
Merge remote-tracking branch 'origin' into feat/ers-v2
jakedoublev May 15, 2025
63c29a8
bump protocol/go in service module
jakedoublev May 15, 2025
11010b6
bump protocol/go in sdk module
jakedoublev May 15, 2025
91d68f8
fix tests referencing updated proto field name
jakedoublev May 15, 2025
c6713b0
fix service registry counts with two ERS service versions
jakedoublev May 15, 2025
264b969
rm debug print statement in test
jakedoublev May 15, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/grpc/index.html

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions protocol/go/entityresolution/entity_resolution_grpc.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion sdk/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ require (
github.com/lestrrat-go/jwx/v2 v2.0.21
github.com/opentdf/platform/lib/fixtures v0.2.10
github.com/opentdf/platform/lib/ocrypto v0.1.9
github.com/opentdf/platform/protocol/go v0.3.2
github.com/opentdf/platform/protocol/go v0.3.3
github.com/stretchr/testify v1.10.0
github.com/testcontainers/testcontainers-go v0.34.0
github.com/xeipuuv/gojsonschema v1.2.0
Expand Down
4 changes: 2 additions & 2 deletions sdk/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,8 @@ github.com/opentdf/platform/lib/fixtures v0.2.10 h1:R688b98ctsEiDRlQSvLxmAWT7bXv
github.com/opentdf/platform/lib/fixtures v0.2.10/go.mod h1:wGhclxDeDXf8bp5VAWztT1nY2gWVNGQLd8rWs5wtXV0=
github.com/opentdf/platform/lib/ocrypto v0.1.9 h1:GvgPB7CoK7JmWvsSvJ0hc+RC0wezgcuRpy3q2oYKjdA=
github.com/opentdf/platform/lib/ocrypto v0.1.9/go.mod h1:UTtqh8mvhAYA+sEnaMxpr/406e84L5Q1sAxtKGIXfu4=
github.com/opentdf/platform/protocol/go v0.3.2 h1:WugeSl7RSRM7e7c5jJumZOIW2jr+sMqwDzpGUGyeC5k=
github.com/opentdf/platform/protocol/go v0.3.2/go.mod h1:nErYkgt32GW22CNqSyLO+JE49C3JndI1TsVdF+CUYd4=
github.com/opentdf/platform/protocol/go v0.3.3 h1:ySnnMniOFpxnc4G2EPGQEuEzaYAfLtNhqqIkGv1XXQM=
github.com/opentdf/platform/protocol/go v0.3.3/go.mod h1:nErYkgt32GW22CNqSyLO+JE49C3JndI1TsVdF+CUYd4=
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
Expand Down
3 changes: 3 additions & 0 deletions sdk/sdk.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/opentdf/platform/lib/ocrypto"
"github.com/opentdf/platform/protocol/go/authorization"
"github.com/opentdf/platform/protocol/go/entityresolution"
entityresolutionV2 "github.com/opentdf/platform/protocol/go/entityresolution/v2"
"github.com/opentdf/platform/protocol/go/policy"
"github.com/opentdf/platform/protocol/go/policy/actions"
"github.com/opentdf/platform/protocol/go/policy/attributes"
Expand Down Expand Up @@ -71,6 +72,7 @@ type SDK struct {
Attributes attributes.AttributesServiceClient
Authorization authorization.AuthorizationServiceClient
EntityResoution entityresolution.EntityResolutionServiceClient
EntityResolutionV2 entityresolutionV2.EntityResolutionServiceClient
KeyAccessServerRegistry kasregistry.KeyAccessServerRegistryServiceClient
Namespaces namespaces.NamespaceServiceClient
RegisteredResources registeredresources.RegisteredResourcesServiceClient
Expand Down Expand Up @@ -214,6 +216,7 @@ func New(platformEndpoint string, opts ...Option) (*SDK, error) {
KeyAccessServerRegistry: kasregistry.NewKeyAccessServerRegistryServiceClient(platformConn),
Authorization: authorization.NewAuthorizationServiceClient(platformConn),
EntityResoution: entityresolution.NewEntityResolutionServiceClient(ersConn),
EntityResolutionV2: entityresolutionV2.NewEntityResolutionServiceClient(ersConn),
KeyManagement: keymanagement.NewKeyManagementServiceClient(platformConn),
wellknownConfiguration: wellknownconfiguration.NewWellKnownServiceClient(platformConn),
}, nil
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package entityresolution
package claims

import (
"context"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package entityresolution_test
package claims_test

import (
"testing"
Expand Down
146 changes: 146 additions & 0 deletions service/entityresolution/claims/v2/entity_resolution.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package claims

import (
"context"
"fmt"
"log/slog"
"strconv"

"connectrpc.com/connect"
"github.com/lestrrat-go/jwx/v2/jwt"
"github.com/opentdf/platform/protocol/go/entity"
entityresolutionV2 "github.com/opentdf/platform/protocol/go/entityresolution/v2"
auth "github.com/opentdf/platform/service/authorization"
"github.com/opentdf/platform/service/logger"
"github.com/opentdf/platform/service/pkg/config"
"github.com/opentdf/platform/service/pkg/serviceregistry"
"go.opentelemetry.io/otel/trace"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/types/known/anypb"
"google.golang.org/protobuf/types/known/structpb"
)

type EntityResolutionServiceV2 struct {
entityresolutionV2.UnimplementedEntityResolutionServiceServer
logger *logger.Logger
trace.Tracer
}

func RegisterClaimsERS(_ config.ServiceConfig, logger *logger.Logger) (EntityResolutionServiceV2, serviceregistry.HandlerServer) {
claimsSVC := EntityResolutionServiceV2{logger: logger}
return claimsSVC, nil
}

func (s EntityResolutionServiceV2) ResolveEntities(ctx context.Context, req *connect.Request[entityresolutionV2.ResolveEntitiesRequest]) (*connect.Response[entityresolutionV2.ResolveEntitiesResponse], error) {
resp, err := EntityResolution(ctx, req.Msg, s.logger)
return connect.NewResponse(&resp), err
}

func (s EntityResolutionServiceV2) CreateEntityChainsFromTokens(ctx context.Context, req *connect.Request[entityresolutionV2.CreateEntityChainsFromTokensRequest]) (*connect.Response[entityresolutionV2.CreateEntityChainsFromTokensResponse], error) {
ctx, span := s.Tracer.Start(ctx, "CreateEntityChainsFromTokens")
defer span.End()

resp, err := CreateEntityChainsFromTokens(ctx, req.Msg, s.logger)
return connect.NewResponse(&resp), err
}

func CreateEntityChainsFromTokens(
_ context.Context,
req *entityresolutionV2.CreateEntityChainsFromTokensRequest,
_ *logger.Logger,
) (entityresolutionV2.CreateEntityChainsFromTokensResponse, error) {
entityChains := []*entity.EntityChain{}
// for each token in the tokens form an entity chain
for _, tok := range req.GetTokens() {
entities, err := getEntitiesFromToken(tok.GetJwt())
if err != nil {
return entityresolutionV2.CreateEntityChainsFromTokensResponse{}, err
}
entityChains = append(entityChains, &entity.EntityChain{EphemeralId: tok.GetEphemeralId(), Entities: entities})
}

return entityresolutionV2.CreateEntityChainsFromTokensResponse{EntityChains: entityChains}, nil
}

func EntityResolution(_ context.Context,
req *entityresolutionV2.ResolveEntitiesRequest, logger *logger.Logger,
) (entityresolutionV2.ResolveEntitiesResponse, error) {
payload := req.GetEntities()
var resolvedEntities []*entityresolutionV2.EntityRepresentation

for idx, ident := range payload {
entityStruct := &structpb.Struct{}
switch ident.GetEntityType().(type) {
case *entity.Entity_Claims:
claims := ident.GetClaims()
if claims != nil {
err := claims.UnmarshalTo(entityStruct)
if err != nil {
return entityresolutionV2.ResolveEntitiesResponse{}, connect.NewError(connect.CodeInvalidArgument, fmt.Errorf("error unpacking anypb.Any to structpb.Struct: %w", err))
}
}
default:
retrievedStruct, err := entityToStructPb(ident)
if err != nil {
logger.Error("unable to make entity struct", slog.String("error", err.Error()))
return entityresolutionV2.ResolveEntitiesResponse{}, connect.NewError(connect.CodeInternal, fmt.Errorf("unable to make entity struct: %w", err))
}
entityStruct = retrievedStruct
}
// make sure the id field is populated
originialID := ident.GetEphemeralId()
if originialID == "" {
originialID = auth.EntityIDPrefix + strconv.Itoa(idx)
}
resolvedEntities = append(
resolvedEntities,
&entityresolutionV2.EntityRepresentation{
OriginalId: originialID,
AdditionalProps: []*structpb.Struct{entityStruct},
},
)
}
return entityresolutionV2.ResolveEntitiesResponse{EntityRepresentations: resolvedEntities}, nil
}

func getEntitiesFromToken(jwtString string) ([]*entity.Entity, error) {
token, err := jwt.ParseString(jwtString, jwt.WithVerify(false), jwt.WithValidate(false))
if err != nil {
return nil, fmt.Errorf("error parsing jwt: %w", err)
}

claims := token.PrivateClaims()
entities := []*entity.Entity{}

// Convert map[string]interface{} to *structpb.Struct
structClaims, err := structpb.NewStruct(claims)
if err != nil {
return nil, fmt.Errorf("error converting to structpb.Struct: %w", err)
}

// Wrap the struct in an *anypb.Any message
anyClaims, err := anypb.New(structClaims)
if err != nil {
return nil, fmt.Errorf("error wrapping in anypb.Any: %w", err)
}

entities = append(entities, &entity.Entity{
EntityType: &entity.Entity_Claims{Claims: anyClaims},
EphemeralId: "jwtentity-claims",
Category: entity.Entity_CATEGORY_SUBJECT,
})
return entities, nil
}

func entityToStructPb(ident *entity.Entity) (*structpb.Struct, error) {
entityBytes, err := protojson.Marshal(ident)
if err != nil {
return nil, err
}
var entityStruct structpb.Struct
err = entityStruct.UnmarshalJSON(entityBytes)
if err != nil {
return nil, err
}
return &entityStruct, nil
}
116 changes: 116 additions & 0 deletions service/entityresolution/claims/v2/entity_resolution_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package claims_test

import (
"testing"

"github.com/opentdf/platform/protocol/go/entity"
entityresolutionV2 "github.com/opentdf/platform/protocol/go/entityresolution/v2"
claims "github.com/opentdf/platform/service/entityresolution/claims/v2"
"github.com/opentdf/platform/service/logger"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/types/known/anypb"
"google.golang.org/protobuf/types/known/structpb"
)

const samplejwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6ImhlbGxvd29ybGQiLCJpYXQiOjE1MTYyMzkwMjJ9.EAOittOMzKENEAs44eaMuZe-xas7VNVsgBxhwmxYiIw"

func Test_ClientResolveEntity(t *testing.T) {
var validBody []*entity.Entity
validBody = append(validBody, &entity.Entity{EphemeralId: "1234", EntityType: &entity.Entity_ClientId{ClientId: "random"}})

req := entityresolutionV2.ResolveEntitiesRequest{}
req.Entities = validBody

resp, reserr := claims.EntityResolution(t.Context(), &req, logger.CreateTestLogger())

require.NoError(t, reserr)

entityRepresentations := resp.GetEntityRepresentations()
assert.NotNil(t, entityRepresentations)
assert.Len(t, entityRepresentations, 1)

assert.Equal(t, "1234", entityRepresentations[0].GetOriginalId())
assert.Len(t, entityRepresentations[0].GetAdditionalProps(), 1)
propMap := entityRepresentations[0].GetAdditionalProps()[0].AsMap()
assert.Equal(t, "random", propMap["clientId"])
assert.Equal(t, "1234", propMap["ephemeralId"])
}

func Test_EmailResolveEntity(t *testing.T) {
var validBody []*entity.Entity
validBody = append(validBody, &entity.Entity{EphemeralId: "1234", EntityType: &entity.Entity_EmailAddress{EmailAddress: "random"}})

req := entityresolutionV2.ResolveEntitiesRequest{}
req.Entities = validBody

resp, reserr := claims.EntityResolution(t.Context(), &req, logger.CreateTestLogger())

require.NoError(t, reserr)

entityRepresentations := resp.GetEntityRepresentations()
assert.NotNil(t, entityRepresentations)
assert.Len(t, entityRepresentations, 1)

assert.Equal(t, "1234", entityRepresentations[0].GetOriginalId())
assert.Len(t, entityRepresentations[0].GetAdditionalProps(), 1)
propMap := entityRepresentations[0].GetAdditionalProps()[0].AsMap()
assert.Equal(t, "random", propMap["emailAddress"])
assert.Equal(t, "1234", propMap["ephemeralId"])
}

func Test_ClaimsResolveEntity(t *testing.T) {
customclaims := map[string]interface{}{
"foo": "bar",
"baz": 42,
}
// Convert map[string]interface{} to *structpb.Struct
structClaims, err := structpb.NewStruct(customclaims)
require.NoError(t, err)

// Wrap the struct in an *anypb.Any
anyClaims, err := anypb.New(structClaims)
require.NoError(t, err)

var validBody []*entity.Entity
validBody = append(validBody, &entity.Entity{EphemeralId: "1234", EntityType: &entity.Entity_Claims{Claims: anyClaims}})

req := entityresolutionV2.ResolveEntitiesRequest{}
req.Entities = validBody

resp, reserr := claims.EntityResolution(t.Context(), &req, logger.CreateTestLogger())

require.NoError(t, reserr)

entityRepresentations := resp.GetEntityRepresentations()
assert.NotNil(t, entityRepresentations)
assert.Len(t, entityRepresentations, 1)

assert.Equal(t, "1234", entityRepresentations[0].GetOriginalId())
assert.Len(t, entityRepresentations[0].GetAdditionalProps(), 1)
propMap := entityRepresentations[0].GetAdditionalProps()[0].AsMap()
assert.Equal(t, "bar", propMap["foo"])
assert.EqualValues(t, 42, propMap["baz"])
}

func Test_JWTToEntityChainClaims(t *testing.T) {
validBody := []*entity.Token{{Jwt: samplejwt}}

resp, reserr := claims.CreateEntityChainsFromTokens(t.Context(), &entityresolutionV2.CreateEntityChainsFromTokensRequest{Tokens: validBody}, logger.CreateTestLogger())

require.NoError(t, reserr)

assert.Len(t, resp.GetEntityChains(), 1)
assert.Len(t, resp.GetEntityChains()[0].GetEntities(), 1)
assert.IsType(t, &entity.Entity_Claims{}, resp.GetEntityChains()[0].GetEntities()[0].GetEntityType())
assert.Equal(t, entity.Entity_CATEGORY_SUBJECT, resp.GetEntityChains()[0].GetEntities()[0].GetCategory())

var unpackedStruct structpb.Struct
err := resp.GetEntityChains()[0].GetEntities()[0].GetClaims().UnmarshalTo(&unpackedStruct)
require.NoError(t, err)

// Convert structpb.Struct to map[string]interface{}
claimsMap := unpackedStruct.AsMap()

assert.Equal(t, "helloworld", claimsMap["name"])
}
Loading
Loading