diff --git a/docs/grpc/index.html b/docs/grpc/index.html
index 3e393d4b72..f4fe1703dc 100644
--- a/docs/grpc/index.html
+++ b/docs/grpc/index.html
@@ -2400,6 +2400,13 @@
Entity
|
+
+ | client_id |
+ string |
+ |
+ |
+
+
diff --git a/docs/openapi/authorization/authorization.swagger.json b/docs/openapi/authorization/authorization.swagger.json
index 49d54aeaf0..25f72e53bb 100644
--- a/docs/openapi/authorization/authorization.swagger.json
+++ b/docs/openapi/authorization/authorization.swagger.json
@@ -175,6 +175,9 @@
},
"custom": {
"$ref": "#/definitions/authorizationEntityCustom"
+ },
+ "clientId": {
+ "type": "string"
}
},
"title": "PE (Person Entity) or NPE (Non-Person Entity)"
diff --git a/examples/cmd/encrypt.go b/examples/cmd/encrypt.go
index 4df936d706..ca2dc8495c 100644
--- a/examples/cmd/encrypt.go
+++ b/examples/cmd/encrypt.go
@@ -46,7 +46,7 @@ func encrypt(cmd *cobra.Command, args []string) error {
defer tdfFile.Close()
tdf, err := client.CreateTDF(tdfFile, strReader,
- //sdk.WithDataAttributes("https://example.com/attributes/1", "https://example.com/attributes/2"),
+ //sdk.WithDataAttributes("https://example.com/attr/attr1/value/value1"),
sdk.WithKasInformation(
sdk.KASInfo{
URL: "http://localhost:8080",
diff --git a/go.mod b/go.mod
index bc51371af2..f3ef323771 100644
--- a/go.mod
+++ b/go.mod
@@ -16,7 +16,6 @@ require (
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1
github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa
github.com/jackc/pgx/v5 v5.5.5
- github.com/jarcoal/httpmock v1.3.1
github.com/lestrrat-go/jwx/v2 v2.0.21
github.com/miekg/pkcs11 v1.1.1
github.com/open-policy-agent/opa v0.62.1
@@ -28,7 +27,6 @@ require (
github.com/stretchr/testify v1.9.0
github.com/testcontainers/testcontainers-go v0.28.0
github.com/valyala/fasthttp v1.52.0
- github.com/virtru/access-pdp v1.11.0
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225
golang.org/x/oauth2 v0.18.0
google.golang.org/grpc v1.62.1
@@ -139,7 +137,6 @@ require (
go.opentelemetry.io/otel/sdk v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
- go.uber.org/zap v1.27.0 // indirect
golang.org/x/mod v0.15.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.18.0 // indirect
diff --git a/go.sum b/go.sum
index 3313510322..a96f07f104 100644
--- a/go.sum
+++ b/go.sum
@@ -190,8 +190,6 @@ github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw=
github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
-github.com/jarcoal/httpmock v1.3.1 h1:iUx3whfZWVf3jT01hQTO/Eo5sAYtB2/rqaUuOtpInww=
-github.com/jarcoal/httpmock v1.3.1/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg=
github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 h1:rp+c0RAYOWj8l6qbCUTSiRLG/iKnW3K3/QfPPuSsBt4=
github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak=
github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4=
@@ -232,8 +230,6 @@ github.com/marcboeker/go-duckdb v1.5.6 h1:5+hLUXRuKlqARcnW4jSsyhCwBRlu4FGjM0UTf2
github.com/marcboeker/go-duckdb v1.5.6/go.mod h1:wm91jO2GNKa6iO9NTcjXIRsW+/ykPoJbQcHSXhdAl28=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
-github.com/maxatome/go-testdeep v1.12.0 h1:Ql7Go8Tg0C1D/uMMX59LAoYK7LffeJQ6X2T04nTH68g=
-github.com/maxatome/go-testdeep v1.12.0/go.mod h1:lPZc/HAcJMP92l7yI6TRz1aZN5URwUBUAfUNvrclaNM=
github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY=
github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg=
github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM=
@@ -363,8 +359,6 @@ github.com/valyala/fasthttp v1.52.0 h1:wqBQpxH71XW0e2g+Og4dzQM8pk34aFYlA1Ga8db7g
github.com/valyala/fasthttp v1.52.0/go.mod h1:hf5C4QnVMkNXMspnsUlfM3WitlgYflyhHYoKol/szxQ=
github.com/vertica/vertica-sql-go v1.3.3 h1:fL+FKEAEy5ONmsvya2WH5T8bhkvY27y/Ik3ReR2T+Qw=
github.com/vertica/vertica-sql-go v1.3.3/go.mod h1:jnn2GFuv+O2Jcjktb7zyc4Utlbu9YVqpHH/lx63+1M4=
-github.com/virtru/access-pdp v1.11.0 h1:JmMUo5EExOg99TKNQ6AdwUDLdBwVvSpjxUVVJxJ0RR4=
-github.com/virtru/access-pdp v1.11.0/go.mod h1:7OkDvrJX9qtzZ8KYFv7uvbp3IuhJZBqjVaPcH+Irnc0=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
@@ -402,12 +396,8 @@ go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
-go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
-go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
-go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
-go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
diff --git a/internal/entitlements/pdp.go b/internal/entitlements/pdp.go
index 4098277245..1b515fdf51 100644
--- a/internal/entitlements/pdp.go
+++ b/internal/entitlements/pdp.go
@@ -13,8 +13,15 @@ func OpaInput(entity *authorization.Entity, sms map[string]*attributes.GetAttrib
inputUnstructured["attribute_mappings"] = sms
// Entity
ea := make(map[string]interface{})
- ea["id"] = entity.Id
- ea["email_address"] = entity.GetEmailAddress()
+ ea["id"] = entity.GetId()
+ switch entity.GetEntityType().(type) {
+ case *authorization.Entity_ClientId:
+ ea["client_id"] = entity.GetClientId()
+ case *authorization.Entity_EmailAddress:
+ ea["email_address"] = entity.GetEmailAddress()
+ case *authorization.Entity_Jwt:
+ ea["jwt"] = entity.GetJwt()
+ }
inputUnstructured["entity"] = ea
inputUnstructured["idp"] = config
diff --git a/internal/idpplugin/keycloak_plugin.go b/internal/idpplugin/keycloak_plugin.go
index e79790b074..393f3a845a 100644
--- a/internal/idpplugin/keycloak_plugin.go
+++ b/internal/idpplugin/keycloak_plugin.go
@@ -31,9 +31,9 @@ type KeyCloakConnector struct {
func EntityResolution(ctx context.Context,
req *authorization.IdpPluginRequest, config *authorization.IdpConfig) (*authorization.IdpPluginResponse, error) {
- slog.Info(fmt.Sprintf("req: %+v", req))
- slog.Info(fmt.Sprintf("config: %+v", config))
- jsonString, err := json.Marshal(config.Config.AsMap())
+ // note this only logs when run in test not when running in the OPE engine.
+ slog.DebugContext(ctx, "EntityResolution", "req", fmt.Sprintf("%+v", req), "config", fmt.Sprintf("%+v", config))
+ jsonString, err := json.Marshal(config.GetConfig().AsMap())
if err != nil {
slog.Error("Error marshalling keycloak config!", "error", err)
return nil, err
@@ -52,26 +52,55 @@ func EntityResolution(ctx context.Context,
payload := req.GetEntities()
var resolvedEntities []*authorization.IdpEntityRepresentation
- slog.Debug("EntityResolution invoked with", "payload", payload)
+ slog.DebugContext(ctx, "EntityResolution invoked", "payload", payload)
- for i, ident := range payload {
- slog.Debug("Lookup", "entity", ident.GetEntityType())
+ for _, ident := range payload {
+ slog.DebugContext(ctx, "Lookup", "entity", ident.GetEntityType())
var keycloakEntities []*gocloak.User
var getUserParams gocloak.GetUsersParams
-
exactMatch := true
- switch ident.EntityType.(type) {
+ switch ident.GetEntityType().(type) {
+ case *authorization.Entity_ClientId:
+ slog.DebugContext(ctx, "GetClient", "client_id", ident.GetClientId())
+ clientID := ident.GetClientId()
+ clients, err := connector.client.GetClients(ctx, connector.token.AccessToken, kcConfig.Realm, gocloak.GetClientsParams{
+ ClientID: &clientID,
+ })
+ if err != nil {
+ slog.Error(err.Error())
+ return &authorization.IdpPluginResponse{},
+ status.Error(codes.Internal, services.ErrGetRetrievalFailed)
+ }
+ var jsonEntities []*structpb.Struct
+ for _, client := range clients {
+ json, err := typeToGenericJSONMap(client)
+ if err != nil {
+ slog.Error("Error serializing entity representation!", "error", err)
+ return &authorization.IdpPluginResponse{},
+ status.Error(codes.Internal, services.ErrCreationFailed)
+ }
+ var mystruct, struct_err = structpb.NewStruct(json)
+ if struct_err != nil {
+ slog.Error("Error making struct!", "error", err)
+ return &authorization.IdpPluginResponse{},
+ status.Error(codes.Internal, services.ErrCreationFailed)
+ }
+ jsonEntities = append(jsonEntities, mystruct)
+ }
+ resolvedEntities = append(
+ resolvedEntities,
+ &authorization.IdpEntityRepresentation{
+ OriginalId: ident.GetId(),
+ AdditionalProps: jsonEntities,
+ },
+ )
+ return &authorization.IdpPluginResponse{
+ EntityRepresentations: resolvedEntities,
+ }, nil
case *authorization.Entity_EmailAddress:
- getUserParams = gocloak.GetUsersParams{Email: func() *string { t := payload[i].GetEmailAddress(); return &t }(), Exact: &exactMatch}
+ getUserParams = gocloak.GetUsersParams{Email: func() *string { t := ident.GetEmailAddress(); return &t }(), Exact: &exactMatch}
case *authorization.Entity_UserName:
- getUserParams = gocloak.GetUsersParams{Username: func() *string { t := payload[i].GetUserName(); return &t }(), Exact: &exactMatch}
- // case "":
- // return &authorization.IdpPluginResponse{},
- // status.Error(codes.InvalidArgument, services.ErrNotFound)
- default:
- typeErr := fmt.Errorf("Unsupported/unknown type for entity %s", ident.String())
- return &authorization.IdpPluginResponse{},
- status.Error(codes.InvalidArgument, typeErr.Error())
+ getUserParams = gocloak.GetUsersParams{Username: func() *string { t := ident.GetUserName(); return &t }(), Exact: &exactMatch}
}
users, err := connector.client.GetUsers(ctx, connector.token.AccessToken, kcConfig.Realm, getUserParams)
@@ -93,7 +122,7 @@ func EntityResolution(ctx context.Context,
ctx,
connector.token.AccessToken,
kcConfig.Realm,
- gocloak.GetGroupsParams{Search: func() *string { t := payload[i].GetEmailAddress(); return &t }()},
+ gocloak.GetGroupsParams{Search: func() *string { t := ident.GetEmailAddress(); return &t }()},
)
if groupErr != nil {
slog.Error("Error getting group", "group", groupErr)
@@ -153,7 +182,7 @@ func EntityResolution(ctx context.Context,
OriginalId: ident.GetId(),
AdditionalProps: jsonEntities},
)
- slog.Debug("Entities", "resolved", fmt.Sprintf("%+v", resolvedEntities))
+ slog.DebugContext(ctx, "Entities", "resolved", fmt.Sprintf("%+v", resolvedEntities))
}
return &authorization.IdpPluginResponse{
diff --git a/internal/idpplugin/keycloak_plugin_test.go b/internal/idpplugin/keycloak_plugin_test.go
index d3bd396520..92c4bbe490 100644
--- a/internal/idpplugin/keycloak_plugin_test.go
+++ b/internal/idpplugin/keycloak_plugin_test.go
@@ -7,6 +7,7 @@ import (
"io"
"net/http"
"net/http/httptest"
+ "os"
"strings"
"testing"
@@ -76,13 +77,15 @@ func test_server_resp(t *testing.T, w http.ResponseWriter, r *http.Request, k st
}
}
func test_server(t *testing.T, userSearchQueryAndResp map[string]string, groupSearchQueryAndResp map[string]string,
- groupByIdAndResponse map[string]string, groupMemberQueryAndResponse map[string]string) *httptest.Server {
+ groupByIdAndResponse map[string]string, groupMemberQueryAndResponse map[string]string, clientsSearchQueryAndResp map[string]string) *httptest.Server {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/realms/tdf/protocol/openid-connect/token" {
_, err := io.WriteString(w, token_resp)
if err != nil {
t.Error(err)
}
+ } else if r.URL.Path == "/admin/realms/tdf/clients" {
+ test_server_resp(t, w, r, r.URL.RawQuery, clientsSearchQueryAndResp)
} else if r.URL.Path == "/admin/realms/tdf/users" {
test_server_resp(t, w, r, r.URL.RawQuery, userSearchQueryAndResp)
} else if r.URL.Path == "/admin/realms/tdf/groups" && groupSearchQueryAndResp != nil {
@@ -101,12 +104,44 @@ func test_server(t *testing.T, userSearchQueryAndResp map[string]string, groupSe
return server
}
+func Test_KCEntityResolutionByClientId(t *testing.T) {
+
+ var validBody []*authorization.Entity
+ validBody = append(validBody, &authorization.Entity{Id: "1234", EntityType: &authorization.Entity_ClientId{ClientId: "opentdf"}})
+
+ var ctxb = context.Background()
+
+ var req = authorization.IdpPluginRequest{}
+ req.Entities = validBody
+ csqr := map[string]string{
+ "clientId=opentdf": by_email_bob_resp,
+ }
+ server := test_server(t, nil, nil, nil, nil, csqr)
+ defer server.Close()
+ var kcconfig = test_keycloakConfig(server)
+ var kcConfigInterface map[string]interface{}
+ inrec, err := json.Marshal(kcconfig)
+ assert.Nil(t, err)
+
+ require.NoError(t, json.Unmarshal(inrec, &kcConfigInterface))
+ kcConfigStruct, err := structpb.NewStruct(kcConfigInterface)
+ var resp, reserr = idpplugin.EntityResolution(ctxb, &req, &authorization.IdpConfig{
+ Config: kcConfigStruct,
+ })
+
+ assert.Nil(t, reserr)
+ _ = json.NewEncoder(os.Stdout).Encode(resp)
+ var entity_representations = resp.GetEntityRepresentations()
+ assert.NotNil(t, entity_representations)
+ assert.Equal(t, 1, len(entity_representations))
+}
+
func Test_KCEntityResolutionByEmail(t *testing.T) {
server := test_server(t, map[string]string{
"email=bob%40sample.org&exact=true": by_email_bob_resp,
"email=alice%40sample.org&exact=true": by_email_alice_resp,
- }, nil, nil, nil)
+ }, nil, nil, nil, nil)
defer server.Close()
var validBody []*authorization.Entity
@@ -150,7 +185,7 @@ func Test_KCEntityResolutionByUsername(t *testing.T) {
server := test_server(t, map[string]string{
"exact=true&username=bob.smith": by_username_bob_resp,
"exact=true&username=alice.smith": by_username_alice_resp,
- }, nil, nil, nil)
+ }, nil, nil, nil, nil)
defer server.Close()
// validBody := `{"entity_identifiers": [{"type": "username","identifier": "bob.smith"}]}`
@@ -200,7 +235,8 @@ func Test_KCEntityResolutionByGroupEmail(t *testing.T) {
"group1-uuid": group_resp,
}, map[string]string{
"group1-uuid": group_submember_resp,
- })
+ },
+ nil)
defer server.Close()
var validBody []*authorization.Entity
@@ -246,7 +282,7 @@ func Test_KCEntityResolutionNotFoundError(t *testing.T) {
"group1-uuid": group_resp,
}, map[string]string{
"group1-uuid": group_submember_resp,
- })
+ }, nil)
defer server.Close()
var validBody []*authorization.Entity
diff --git a/internal/server/server.go b/internal/server/server.go
index 31b943b18b..25556ac1ce 100644
--- a/internal/server/server.go
+++ b/internal/server/server.go
@@ -48,7 +48,7 @@ type Config struct {
HSM security.HSMConfig `yaml:"hsm"`
TLS TLSConfig `yaml:"tls"`
WellKnownConfigRegister func(namespace string, config any) error
- Port int `yaml:"port" default:"9000"`
+ Port int `yaml:"port" default:"8080"`
Host string `yaml:"host,omitempty"`
}
diff --git a/opentdf-example-no-kas.yaml b/opentdf-example-no-kas.yaml
index 295c1657a0..3c39cf29c0 100644
--- a/opentdf-example-no-kas.yaml
+++ b/opentdf-example-no-kas.yaml
@@ -43,7 +43,7 @@ server:
auth:
enabled: false
audience: "http://localhost:8080"
- issuer: http://localhost:8888/auth/realms/opentdf
+ issuer: http://localhost:8888/auth/realms/tdf
clients:
- "opentdf"
grpc:
diff --git a/policies/entitlements/README.md b/policies/entitlements/README.md
new file mode 100644
index 0000000000..159b92c25d
--- /dev/null
+++ b/policies/entitlements/README.md
@@ -0,0 +1,11 @@
+# OpenTDF Platform OPA rego policies
+
+## entitlements.rego
+
+This is the default rego policy that will parse a JWT,
+traverse the subject mappings, and return the entitlements.
+
+## entitlements-keycloak.rego
+
+This is a rego policy for calling Keycloak to get an entity representation,
+traverse the subject mappings, and return the entitlements.
diff --git a/policies/entitlements/entitlements-keycloak.rego b/policies/entitlements/entitlements-keycloak.rego
new file mode 100644
index 0000000000..339667c875
--- /dev/null
+++ b/policies/entitlements/entitlements-keycloak.rego
@@ -0,0 +1,57 @@
+package opentdf.entitlements
+
+import rego.v1
+
+ idp_config = {"config": {
+ "url": input.idp.url,
+ "realm": input.idp.realm,
+ "clientid": input.idp.client,
+ "clientsecret": input.idp.secret,
+ "legacykeycloak": input.idp.legacy,
+ }}
+
+idp_request := {"entities": [{
+ "id": input.entity.id,
+ "emailAddress": input.entity.email_address,
+ "clientId": input.entity.client_id,
+}]}
+
+attributes := [attribute |
+ # external entity
+ response := keycloak.resolve.entities(idp_request, idp_config)
+ entity_representations := response.entityRepresentations
+ some entity_representation in entity_representations
+
+ # mappings
+ some subject_mapping in input.attribute_mappings[attribute].value.subject_mappings
+ some subject_set in subject_mapping.subject_condition_set.subject_sets
+ some condition_group in subject_set.condition_groups
+ condition_group_evaluate(entity_representation.additionalProps, condition_group.boolean_operator, condition_group.conditions)
+]
+
+# condition_group
+condition_group_evaluate(payload, boolean_operator, conditions) if {
+ # AND
+ boolean_operator == 1
+ some condition in conditions
+ condition_evaluate(payload[condition.subject_external_field], condition.operator, condition.subject_external_values)
+} else if {
+ # OR
+ boolean_operator == 2
+ payload[key]
+ some condition in conditions
+ condition_evaluate(payload[condition.subject_external_field], condition.operator, condition.subject_external_values)
+}
+
+# condition
+condition_evaluate(property_values, operator, values) if {
+ # IN
+ operator == 1
+ some property_value in property_values
+ property_value in values
+} else if {
+ # NOT IN
+ operator == 2
+ some property_value in property_values
+ not property_value in values
+}
diff --git a/policies/entitlements/entitlements.rego b/policies/entitlements/entitlements.rego
index ab687175c1..218e0ff71d 100644
--- a/policies/entitlements/entitlements.rego
+++ b/policies/entitlements/entitlements.rego
@@ -2,64 +2,43 @@ package opentdf.entitlements
import rego.v1
-idp_config = {"config": {
- "url": input.idp.url,
- "realm": input.idp.realm,
- "clientid": input.idp.client,
- "clientsecret": input.idp.secret,
- "legacykeycloak": input.idp.legacy,
-}}
-idp_request = {"entities": [{
- "id": input.entity.id,
- "emailAddress": input.entity.email_address,
-}]}
-
-
attributes := [attribute |
- # external entity
- response := keycloak.resolve.entities(idp_request, idp_config)
- entity_representations := response.entityRepresentations
- some entity_representation in entity_representations
- some prop in entity_representation.additionalProps
-
- # mapppings
- some subject_mapping in input.attribute_mappings[attribute].value.subject_mappings
- some subject_set in subject_mapping.subject_condition_set.subject_sets
+ # JWT
+ # This statement invokes the built-in function `io.jwt.decode` passing the
+ # parsed bearer_token as a parameter. The `io.jwt.decode` function returns an
+ # array: [header, payload, signature]
+ [_, payload, _] := io.jwt.decode(input.entity.jwt)
+
+ # mappings
+ some subject_mapping in input.attribute_mappings[attribute].value.subject_mappings
+ some subject_set in subject_mapping.subject_condition_set.subject_sets
some condition_group in subject_set.condition_groups
- cgbool_evaluate(prop.attributes, condition_group.boolean_operator, condition_group.conditions)
+ condition_group_evaluate(payload, condition_group.boolean_operator, condition_group.conditions)
]
# condition_group
-cgbool_evaluate(external_property, boolean_operator, conditions) if {
+condition_group_evaluate(payload, boolean_operator, conditions) if {
# AND
boolean_operator == 1
- external_property[key]
some condition in conditions
- condition.subject_external_field == key
- external_property[key] in condition.subject_external_values
+ condition_evaluate(payload[condition.subject_external_field], condition.operator, condition.subject_external_values)
} else if {
# OR
boolean_operator == 2
- external_property[key]
+ payload[key]
some condition in conditions
- condition.subject_external_field == key
- cbool_evaluate(external_property[key], condition.operator, condition.subject_external_values)
+ condition_evaluate(payload[condition.subject_external_field], condition.operator, condition.subject_external_values)
}
# condition
-cbool_evaluate(properties, operator, values) if {
- # AND
+condition_evaluate(property_values, operator, values) if {
+ # IN
operator == 1
- some property in properties
- some value in values
- property == value
+ some property_value in property_values
+ property_value in values
} else if {
- # OR
+ # NOT IN
operator == 2
- some property in properties
- some value in values
- property == value
+ some property_value in property_values
+ not property_value in values
}
-
-# get IdP entity
-resolve_entities := keycloak.resolve.entities(idp_request, idp_config)
diff --git a/policies/entitlements/input.json b/policies/entitlements/input.json
index ecc741ed70..ef40a4a137 100644
--- a/policies/entitlements/input.json
+++ b/policies/entitlements/input.json
@@ -1,28 +1,85 @@
{
- "entity": {
- "claims": [
- "ec11",
- "ec12",
- "ec13"
- ],
- "email_address": "a@a.af",
- "id": "email_address:\"a@a.af\""
- },
- "subjectset": {
- "id": "abc",
- "condition_groups": [
- {
- "conditions": [
+ "attribute_mappings": {
+ "https://brightonhealthclinic.org/attr/healthrecordtype/value/basicpatientinfo": {
+ "attribute": {
+ "id": "46fd500e-6839-4cc0-8b29-75665bf98e3a",
+ "namespace": {
+ "id": "f1f12166-8b22-47d6-829a-66e68b533eb2",
+ "name": "brightonhealthclinic.org"
+ },
+ "name": "healthrecordtype",
+ "rule": 2,
+ "values": [
{
- "subject_attribute": "https://example.com/attr/attr1/value/value1",
- "operator": 1,
- "subject_values": [
- "ec11"
- ]
+ "id": "356b7dd3-6abb-453c-8354-6915705fabcb",
+ "value": "basicpatientinfo",
+ "fqn": "https://brightonhealthclinic.org/attr/healthrecordtype/value/basicpatientinfo",
+ "active": {
+ "value": true
+ }
}
],
- "boolean_type": 1
+ "active": {
+ "value": true
+ },
+ "metadata": {}
+ },
+ "value": {
+ "id": "356b7dd3-6abb-453c-8354-6915705fabcb",
+ "value": "basicpatientinfo",
+ "fqn": "https://brightonhealthclinic.org/attr/healthrecordtype/value/basicpatientinfo",
+ "active": {
+ "value": true
+ },
+ "subject_mappings": [
+ {
+ "id": "5fb9f643-b7ea-4d53-8b23-f2b61a7ca38a",
+ "subject_condition_set": {
+ "id": "eb688795-fa0a-4014-8f4e-0f770c7bdab6",
+ "subject_sets": [
+ {
+ "condition_groups": [
+ {
+ "conditions": [
+ {
+ "subject_external_field": "groups",
+ "operator": 1,
+ "subject_external_values": [
+ "/medical"
+ ]
+ },
+ {
+ "subject_external_field": "roles",
+ "operator": 1,
+ "subject_external_values": [
+ "nurse",
+ "doctor"
+ ]
+ }
+ ],
+ "boolean_operator": 1
+ }
+ ]
+ }
+ ],
+ "metadata": {}
+ },
+ "actions": [
+ {
+ "Value": {
+ "Standard": 1
+ }
+ }
+ ],
+ "metadata": {}
+ }
+ ]
}
- ]
- }
+ }
+ },
+ "entity": {
+ "client_id": "opentdf-sdk",
+ "id": ""
+ },
+ "idp": null
}
diff --git a/protocol/go/authorization/authorization.pb.go b/protocol/go/authorization/authorization.pb.go
index 39ff837486..2214498bdb 100644
--- a/protocol/go/authorization/authorization.pb.go
+++ b/protocol/go/authorization/authorization.pb.go
@@ -89,6 +89,7 @@ type Entity struct {
// *Entity_Jwt
// *Entity_Claims
// *Entity_Custom
+ // *Entity_ClientId
EntityType isEntity_EntityType `protobuf_oneof:"entity_type"`
}
@@ -180,6 +181,13 @@ func (x *Entity) GetCustom() *EntityCustom {
return nil
}
+func (x *Entity) GetClientId() string {
+ if x, ok := x.GetEntityType().(*Entity_ClientId); ok {
+ return x.ClientId
+ }
+ return ""
+}
+
type isEntity_EntityType interface {
isEntity_EntityType()
}
@@ -208,6 +216,10 @@ type Entity_Custom struct {
Custom *EntityCustom `protobuf:"bytes,7,opt,name=custom,proto3,oneof"`
}
+type Entity_ClientId struct {
+ ClientId string `protobuf:"bytes,8,opt,name=client_id,json=clientId,proto3,oneof"`
+}
+
func (*Entity_EmailAddress) isEntity_EntityType() {}
func (*Entity_UserName) isEntity_EntityType() {}
@@ -220,6 +232,8 @@ func (*Entity_Claims) isEntity_EntityType() {}
func (*Entity_Custom) isEntity_EntityType() {}
+func (*Entity_ClientId) isEntity_EntityType() {}
+
// Entity type for custom entities beyond the standard types
type EntityCustom struct {
state protoimpl.MessageState
@@ -889,7 +903,7 @@ var file_authorization_authorization_proto_rawDesc = []byte{
0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x14, 0x70, 0x6f, 0x6c,
0x69, 0x63, 0x79, 0x2f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74,
- 0x6f, 0x22, 0x96, 0x02, 0x0a, 0x06, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x0e, 0x0a, 0x02,
+ 0x6f, 0x22, 0xb5, 0x02, 0x0a, 0x06, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x0e, 0x0a, 0x02,
0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x25, 0x0a, 0x0d,
0x65, 0x6d, 0x61, 0x69, 0x6c, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20,
0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0c, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x41, 0x64, 0x64, 0x72,
@@ -905,119 +919,121 @@ var file_authorization_authorization_proto_rawDesc = []byte{
0x61, 0x69, 0x6d, 0x73, 0x12, 0x35, 0x0a, 0x06, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x18, 0x07,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x43, 0x75, 0x73, 0x74, 0x6f,
- 0x6d, 0x48, 0x00, 0x52, 0x06, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x42, 0x0d, 0x0a, 0x0b, 0x65,
- 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x22, 0x42, 0x0a, 0x0c, 0x45, 0x6e,
- 0x74, 0x69, 0x74, 0x79, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x12, 0x32, 0x0a, 0x09, 0x65, 0x78,
- 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e,
- 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
- 0x41, 0x6e, 0x79, 0x52, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x50,
- 0x0a, 0x0b, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x0e, 0x0a,
- 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x31, 0x0a,
- 0x08, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32,
- 0x15, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e,
- 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x08, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x69, 0x65, 0x73,
- 0x22, 0xcf, 0x01, 0x0a, 0x0f, 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71,
- 0x75, 0x65, 0x73, 0x74, 0x12, 0x28, 0x0a, 0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18,
- 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41,
- 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x3f,
- 0x0a, 0x0d, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x18,
- 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a,
- 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x43, 0x68, 0x61, 0x69,
- 0x6e, 0x52, 0x0c, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x12,
- 0x51, 0x0a, 0x13, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x61, 0x74, 0x74, 0x72,
- 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x61,
- 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x73,
- 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x12,
+ 0x6d, 0x48, 0x00, 0x52, 0x06, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x12, 0x1d, 0x0a, 0x09, 0x63,
+ 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00,
+ 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x42, 0x0d, 0x0a, 0x0b, 0x65, 0x6e,
+ 0x74, 0x69, 0x74, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x22, 0x42, 0x0a, 0x0c, 0x45, 0x6e, 0x74,
+ 0x69, 0x74, 0x79, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x12, 0x32, 0x0a, 0x09, 0x65, 0x78, 0x74,
+ 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67,
+ 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41,
+ 0x6e, 0x79, 0x52, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x50, 0x0a,
+ 0x0b, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x0e, 0x0a, 0x02,
+ 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x31, 0x0a, 0x08,
+ 0x65, 0x6e, 0x74, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15,
+ 0x2e, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x45,
+ 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x08, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x69, 0x65, 0x73, 0x22,
+ 0xcf, 0x01, 0x0a, 0x0f, 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75,
+ 0x65, 0x73, 0x74, 0x12, 0x28, 0x0a, 0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01,
+ 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x63,
+ 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x3f, 0x0a,
+ 0x0d, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x02,
+ 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x43, 0x68, 0x61, 0x69, 0x6e,
+ 0x52, 0x0c, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x51,
+ 0x0a, 0x13, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69,
+ 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x73, 0x6f,
+ 0x75, 0x72, 0x63, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x12, 0x72,
+ 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65,
+ 0x73, 0x22, 0xce, 0x02, 0x0a, 0x10, 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65,
+ 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79,
+ 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
+ 0x0d, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x64, 0x12, 0x34,
+ 0x0a, 0x16, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x69,
+ 0x62, 0x75, 0x74, 0x65, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14,
0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74,
- 0x65, 0x73, 0x22, 0xce, 0x02, 0x0a, 0x10, 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x52,
- 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x65, 0x6e, 0x74, 0x69, 0x74,
- 0x79, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
- 0x52, 0x0d, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x64, 0x12,
- 0x34, 0x0a, 0x16, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x61, 0x74, 0x74, 0x72,
- 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
- 0x14, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75,
- 0x74, 0x65, 0x73, 0x49, 0x64, 0x12, 0x26, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18,
- 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41,
- 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x44, 0x0a,
- 0x08, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32,
- 0x28, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e,
- 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
- 0x2e, 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x64, 0x65, 0x63, 0x69, 0x73,
- 0x69, 0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x6f, 0x62, 0x6c, 0x69, 0x67, 0x61, 0x74, 0x69, 0x6f,
- 0x6e, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x62, 0x6c, 0x69, 0x67, 0x61,
- 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x4c, 0x0a, 0x08, 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f,
- 0x6e, 0x12, 0x18, 0x0a, 0x14, 0x44, 0x45, 0x43, 0x49, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e,
- 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x44,
- 0x45, 0x43, 0x49, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x44, 0x45, 0x4e, 0x59, 0x10, 0x01, 0x12, 0x13,
- 0x0a, 0x0f, 0x44, 0x45, 0x43, 0x49, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x45, 0x52, 0x4d, 0x49,
- 0x54, 0x10, 0x02, 0x22, 0x62, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x44, 0x65, 0x63, 0x69, 0x73, 0x69,
- 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4b, 0x0a, 0x11, 0x64, 0x65,
- 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x18,
- 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a,
- 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65,
- 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x10, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x52,
- 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x22, 0x66, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x44, 0x65,
- 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
- 0x4e, 0x0a, 0x12, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x70,
- 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61, 0x75,
- 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x63, 0x69,
- 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x11, 0x64, 0x65,
- 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x22,
- 0x92, 0x01, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x6d, 0x65,
- 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x31, 0x0a, 0x08, 0x65, 0x6e,
- 0x74, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61,
- 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x45, 0x6e, 0x74,
- 0x69, 0x74, 0x79, 0x52, 0x08, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x3b, 0x0a,
- 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x61,
- 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x73,
- 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x48, 0x00,
- 0x52, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x88, 0x01, 0x01, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x73,
- 0x63, 0x6f, 0x70, 0x65, 0x22, 0x63, 0x0a, 0x12, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x45, 0x6e,
- 0x74, 0x69, 0x74, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x65, 0x6e,
- 0x74, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65,
- 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x64, 0x12, 0x30, 0x0a, 0x14, 0x61, 0x74, 0x74, 0x72, 0x69,
- 0x62, 0x75, 0x74, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x66, 0x71, 0x6e, 0x73, 0x18,
- 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65,
- 0x56, 0x61, 0x6c, 0x75, 0x65, 0x46, 0x71, 0x6e, 0x73, 0x22, 0x45, 0x0a, 0x11, 0x52, 0x65, 0x73,
- 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x30,
- 0x0a, 0x14, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75,
- 0x65, 0x5f, 0x66, 0x71, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x74,
- 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x46, 0x71, 0x6e, 0x73,
- 0x22, 0x60, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x6d, 0x65,
- 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x0c, 0x65,
- 0x6e, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
- 0x0b, 0x32, 0x21, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f,
- 0x6e, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x6d,
- 0x65, 0x6e, 0x74, 0x73, 0x52, 0x0c, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x6d, 0x65, 0x6e,
- 0x74, 0x73, 0x32, 0x86, 0x02, 0x0a, 0x14, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61,
- 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x72, 0x0a, 0x0c, 0x47,
- 0x65, 0x74, 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x22, 0x2e, 0x61, 0x75,
- 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x44,
- 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
- 0x23, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e,
- 0x47, 0x65, 0x74, 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70,
- 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x22, 0x11, 0x2f, 0x76,
- 0x31, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12,
- 0x7a, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x6d, 0x65, 0x6e,
- 0x74, 0x73, 0x12, 0x25, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69,
- 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x6d, 0x65, 0x6e,
- 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x61, 0x75, 0x74, 0x68,
- 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x74,
- 0x69, 0x74, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
- 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x65,
- 0x6e, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x42, 0xb2, 0x01, 0x0a, 0x11,
- 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f,
- 0x6e, 0x42, 0x12, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e,
- 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
- 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x64, 0x66, 0x2f, 0x70, 0x6c, 0x61, 0x74,
- 0x66, 0x6f, 0x72, 0x6d, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x67, 0x6f,
- 0x2f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0xa2, 0x02,
- 0x03, 0x41, 0x58, 0x58, 0xaa, 0x02, 0x0d, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61,
- 0x74, 0x69, 0x6f, 0x6e, 0xca, 0x02, 0x0d, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61,
- 0x74, 0x69, 0x6f, 0x6e, 0xe2, 0x02, 0x19, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61,
- 0x74, 0x69, 0x6f, 0x6e, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
- 0xea, 0x02, 0x0d, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e,
- 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+ 0x65, 0x73, 0x49, 0x64, 0x12, 0x26, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03,
+ 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x63,
+ 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x44, 0x0a, 0x08,
+ 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x28,
+ 0x2e, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x44,
+ 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e,
+ 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69,
+ 0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x6f, 0x62, 0x6c, 0x69, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x62, 0x6c, 0x69, 0x67, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x4c, 0x0a, 0x08, 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e,
+ 0x12, 0x18, 0x0a, 0x14, 0x44, 0x45, 0x43, 0x49, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x53,
+ 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x44, 0x45,
+ 0x43, 0x49, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x44, 0x45, 0x4e, 0x59, 0x10, 0x01, 0x12, 0x13, 0x0a,
+ 0x0f, 0x44, 0x45, 0x43, 0x49, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x45, 0x52, 0x4d, 0x49, 0x54,
+ 0x10, 0x02, 0x22, 0x62, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f,
+ 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4b, 0x0a, 0x11, 0x64, 0x65, 0x63,
+ 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x18, 0x01,
+ 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71,
+ 0x75, 0x65, 0x73, 0x74, 0x52, 0x10, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65,
+ 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x22, 0x66, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x44, 0x65, 0x63,
+ 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e,
+ 0x0a, 0x12, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f,
+ 0x6e, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61, 0x75, 0x74,
+ 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x63, 0x69, 0x73,
+ 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x11, 0x64, 0x65, 0x63,
+ 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x22, 0x92,
+ 0x01, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x6d, 0x65, 0x6e,
+ 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x31, 0x0a, 0x08, 0x65, 0x6e, 0x74,
+ 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x45, 0x6e, 0x74, 0x69,
+ 0x74, 0x79, 0x52, 0x08, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x3b, 0x0a, 0x05,
+ 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x73, 0x6f,
+ 0x75, 0x72, 0x63, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x48, 0x00, 0x52,
+ 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x88, 0x01, 0x01, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x73, 0x63,
+ 0x6f, 0x70, 0x65, 0x22, 0x63, 0x0a, 0x12, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x45, 0x6e, 0x74,
+ 0x69, 0x74, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x65, 0x6e, 0x74,
+ 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e,
+ 0x74, 0x69, 0x74, 0x79, 0x49, 0x64, 0x12, 0x30, 0x0a, 0x14, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62,
+ 0x75, 0x74, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x66, 0x71, 0x6e, 0x73, 0x18, 0x02,
+ 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56,
+ 0x61, 0x6c, 0x75, 0x65, 0x46, 0x71, 0x6e, 0x73, 0x22, 0x45, 0x0a, 0x11, 0x52, 0x65, 0x73, 0x6f,
+ 0x75, 0x72, 0x63, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x30, 0x0a,
+ 0x14, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65,
+ 0x5f, 0x66, 0x71, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x61, 0x74, 0x74,
+ 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x46, 0x71, 0x6e, 0x73, 0x22,
+ 0x60, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x6d, 0x65, 0x6e,
+ 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x0c, 0x65, 0x6e,
+ 0x74, 0x69, 0x74, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b,
+ 0x32, 0x21, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x6d, 0x65,
+ 0x6e, 0x74, 0x73, 0x52, 0x0c, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74,
+ 0x73, 0x32, 0x86, 0x02, 0x0a, 0x14, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x72, 0x0a, 0x0c, 0x47, 0x65,
+ 0x74, 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x22, 0x2e, 0x61, 0x75, 0x74,
+ 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65,
+ 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23,
+ 0x2e, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x47,
+ 0x65, 0x74, 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
+ 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x22, 0x11, 0x2f, 0x76, 0x31,
+ 0x2f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x7a,
+ 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74,
+ 0x73, 0x12, 0x25, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74,
+ 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x6f,
+ 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x69,
+ 0x74, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
+ 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x22, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x6e,
+ 0x74, 0x69, 0x74, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x42, 0xb2, 0x01, 0x0a, 0x11, 0x63,
+ 0x6f, 0x6d, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x42, 0x12, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50,
+ 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x64, 0x66, 0x2f, 0x70, 0x6c, 0x61, 0x74, 0x66,
+ 0x6f, 0x72, 0x6d, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x67, 0x6f, 0x2f,
+ 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0xa2, 0x02, 0x03,
+ 0x41, 0x58, 0x58, 0xaa, 0x02, 0x0d, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0xca, 0x02, 0x0d, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0xe2, 0x02, 0x19, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea,
+ 0x02, 0x0d, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x62,
+ 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@@ -1222,6 +1238,7 @@ func file_authorization_authorization_proto_init() {
(*Entity_Jwt)(nil),
(*Entity_Claims)(nil),
(*Entity_Custom)(nil),
+ (*Entity_ClientId)(nil),
}
file_authorization_authorization_proto_msgTypes[7].OneofWrappers = []interface{}{}
type x struct{}
diff --git a/services/authorization/authorization.proto b/services/authorization/authorization.proto
index 05cfd8ebde..ddf9ae42a8 100644
--- a/services/authorization/authorization.proto
+++ b/services/authorization/authorization.proto
@@ -18,6 +18,7 @@ message Entity {
string jwt = 5;
google.protobuf.Any claims = 6;
EntityCustom custom = 7;
+ string client_id = 8;
}
}
diff --git a/services/kas/access/accessPdp.go b/services/kas/access/accessPdp.go
index 2e775123e9..b8102491ad 100644
--- a/services/kas/access/accessPdp.go
+++ b/services/kas/access/accessPdp.go
@@ -5,90 +5,79 @@ import (
"errors"
"log/slog"
- attrs "github.com/virtru/access-pdp/attributes"
- accessPdp "github.com/virtru/access-pdp/pdp"
+ "github.com/opentdf/platform/protocol/go/authorization"
+ "github.com/opentdf/platform/protocol/go/policy"
+ otdf "github.com/opentdf/platform/sdk"
)
const (
- ErrPolicyDissemInvalid = Error("policy dissem invalid")
- ErrDecisionUnexpected = Error("access policy decision unexpected")
+ ErrPolicyDissemInvalid = Error("policy dissem invalid")
+ ErrDecisionUnexpected = Error("authorization decision unexpected")
+ ErrDecisionCountUnexpected = Error("authorization decision count unexpected")
)
-func canAccess(ctx context.Context, entityID string, policy Policy, claims ClaimsObject, attrDefs []attrs.AttributeDefinition) (bool, error) {
- dissemAccess, err := checkDissems(policy.Body.Dissem, entityID)
- if err != nil {
- return false, err
- }
- attrAccess, err := checkAttributes(ctx, policy.Body.DataAttributes, claims.Entitlements, attrDefs)
- if err != nil {
- return false, err
+func canAccess(ctx context.Context, entity authorization.Entity, policy Policy, sdk *otdf.SDK) (bool, error) {
+ if len(policy.Body.Dissem) > 0 {
+ dissemAccess, err := checkDissems(policy.Body.Dissem, entity)
+ if err != nil {
+ return false, err
+ }
+ return dissemAccess, nil
}
- if dissemAccess && attrAccess {
- return true, nil
- } else {
- return false, nil
+ if policy.Body.DataAttributes != nil {
+ attrAccess, err := checkAttributes(ctx, policy.Body.DataAttributes, entity, sdk)
+ if err != nil {
+ return false, err
+ }
+ return attrAccess, nil
}
+ // if no dissem and no attributes then allow
+ return true, nil
}
-func checkDissems(dissems []string, entityID string) (bool, error) {
- if entityID == "" {
+func checkDissems(dissems []string, ent authorization.Entity) (bool, error) {
+ if ent.GetEmailAddress() == "" {
return false, ErrPolicyDissemInvalid
}
- if len(dissems) == 0 || contains(dissems, entityID) {
+ if len(dissems) == 0 || contains(dissems, ent.GetEmailAddress()) {
return true, nil
}
return false, nil
}
-func checkAttributes(ctx context.Context, dataAttrs []Attribute, entitlements []Entitlement, attrDefs []attrs.AttributeDefinition) (bool, error) {
- // convert data and entitty attrs to attrs.AttributeInstance
- dataAttrInstances, err := convertAttrsToAttrInstances(dataAttrs)
- if err != nil {
- return false, err
+func checkAttributes(ctx context.Context, dataAttrs []Attribute, ent authorization.Entity, sdk *otdf.SDK) (bool, error) {
+ ec := authorization.EntityChain{Entities: make([]*authorization.Entity, 0)}
+ ec.Entities = append(ec.Entities, &ent)
+ ras := []*authorization.ResourceAttribute{{
+ AttributeValueFqns: make([]string, 0),
+ }}
+ for _, attr := range dataAttrs {
+ ras[0].AttributeValueFqns = append(ras[0].GetAttributeValueFqns(), attr.URI)
}
- entityAttrMap, err := convertEntitlementsToEntityAttrMap(entitlements)
- if err != nil {
- return false, err
+ in := authorization.GetDecisionsRequest{
+ DecisionRequests: []*authorization.DecisionRequest{
+ {
+ Actions: []*policy.Action{
+ {Value: &policy.Action_Standard{Standard: policy.Action_STANDARD_ACTION_DECRYPT}},
+ },
+ EntityChains: []*authorization.EntityChain{&ec},
+ ResourceAttributes: ras,
+ },
+ },
}
-
- accessPDP := accessPdp.NewAccessPDPWithSlog(slog.Default())
-
- decisions, err := accessPDP.DetermineAccess(dataAttrInstances, entityAttrMap, attrDefs, &ctx)
+ dr, err := sdk.Authorization.GetDecisions(ctx, &in)
if err != nil {
- slog.WarnContext(ctx, "Error recieved from accessPDP", "err", err)
+ slog.ErrorContext(ctx, "Error received from GetDecisions", "err", err)
return false, errors.Join(ErrDecisionUnexpected, err)
}
- // check the decisions
- for _, decision := range decisions {
- if !decision.Access {
- return false, nil
- }
- }
- return true, nil
-}
-
-func convertAttrsToAttrInstances(attributes []Attribute) ([]attrs.AttributeInstance, error) {
- instances := make([]attrs.AttributeInstance, len(attributes))
- for i, attr := range attributes {
- instance, err := attrs.ParseInstanceFromURI(attr.URI)
- if err != nil {
- return nil, errors.Join(ErrPolicyDataAttributeParse, err)
- }
- instances[i] = instance
+ if len(dr.DecisionResponses) != 1 {
+ slog.ErrorContext(ctx, ErrDecisionCountUnexpected.Error(), "count", len(dr.DecisionResponses))
+ return false, ErrDecisionCountUnexpected
}
- return instances, nil
-}
-
-func convertEntitlementsToEntityAttrMap(entitlements []Entitlement) (map[string][]attrs.AttributeInstance, error) {
- entityAttrMap := make(map[string][]attrs.AttributeInstance)
- for _, entitlement := range entitlements {
- instances, err := convertAttrsToAttrInstances(entitlement.EntityAttributes)
- if err != nil {
- return nil, err
- }
- entityAttrMap[entitlement.EntityID] = instances
+ if dr.DecisionResponses[0].Decision == authorization.DecisionResponse_DECISION_PERMIT {
+ return true, nil
}
- return entityAttrMap, nil
+ return false, nil
}
func contains(s []string, e string) bool {
diff --git a/services/kas/access/accessPdp_test.go b/services/kas/access/accessPdp_test.go
index 9b3795d393..15cd502f33 100644
--- a/services/kas/access/accessPdp_test.go
+++ b/services/kas/access/accessPdp_test.go
@@ -2,44 +2,17 @@ package access
import (
"context"
+ "github.com/google/uuid"
+ "github.com/opentdf/platform/protocol/go/authorization"
+ "github.com/opentdf/platform/sdk"
"testing"
-
- uuid "github.com/google/uuid"
- attrs "github.com/virtru/access-pdp/attributes"
)
var c = context.Background()
-// ######## Dissem tests ################
-
-func TestWildcardDissemSuccess(t *testing.T) {
- var entityID string = "email2@example.com"
-
- testPolicy := Policy{
- UUID: uuid.New(),
- Body: PolicyBody{
- DataAttributes: []Attribute{},
- Dissem: []string{},
- },
- }
+var osdk, _ = sdk.New("", sdk.WithClientCredentials("myid", "mysecret", nil))
- testClaims := ClaimsObject{
- PublicKey: "test-public-key",
- ClientPublicSigningKey: "test-client-public-signing-key",
- SchemaVersion: "test-schema",
- Entitlements: []Entitlement{},
- }
-
- testDefinitions := []attrs.AttributeDefinition{}
-
- output, err := canAccess(c, entityID, testPolicy, testClaims, testDefinitions)
- if err != nil {
- t.Error(err)
- }
- if !output {
- t.Errorf("Output %v not equal to expected %v", output, true)
- }
-}
+// ######## Dissem tests ################
func TestDissemSuccess(t *testing.T) {
var entityID string = "email2@example.com"
@@ -53,17 +26,11 @@ func TestDissemSuccess(t *testing.T) {
"email3@example.com"},
},
}
-
- testClaims := ClaimsObject{
- PublicKey: "test-public-key",
- ClientPublicSigningKey: "test-client-public-signing-key",
- SchemaVersion: "test-schema",
- Entitlements: []Entitlement{},
+ entity := authorization.Entity{
+ Id: "0",
+ EntityType: &authorization.Entity_EmailAddress{EmailAddress: entityID},
}
-
- testDefinitions := []attrs.AttributeDefinition{}
-
- output, err := canAccess(c, entityID, testPolicy, testClaims, testDefinitions)
+ output, err := canAccess(c, entity, testPolicy, osdk)
if err != nil {
t.Error(err)
}
@@ -83,309 +50,11 @@ func TestDissemFailure(t *testing.T) {
"email3@example.com"},
},
}
-
- testClaims := ClaimsObject{
- PublicKey: "test-public-key",
- ClientPublicSigningKey: "test-client-public-signing-key",
- SchemaVersion: "test-schema",
- Entitlements: []Entitlement{},
- }
-
- testDefinitions := []attrs.AttributeDefinition{}
-
- output, err := canAccess(c, entityID, testPolicy, testClaims, testDefinitions)
- if err != nil {
- t.Error(err)
- }
- if output {
- t.Errorf("Output %v not equal to expected %v", output, false)
- }
-}
-
-// ######## All Of tests ################
-
-func TestAllOfSuccess(t *testing.T) {
- var entityID string = "email2@example.com"
-
- testPolicy := Policy{
- UUID: uuid.New(),
- Body: PolicyBody{
- DataAttributes: []Attribute{
- {URI: "https://example.com/attr/Test1/value/A", Name: "Test1"},
- },
- Dissem: []string{},
- },
- }
-
- testClaims := ClaimsObject{
- PublicKey: "test-public-key",
- ClientPublicSigningKey: "test-client-public-signing-key",
- SchemaVersion: "test-schema",
- Entitlements: []Entitlement{
- {
- EntityID: "email2@example.com",
- EntityAttributes: []Attribute{
- {URI: "https://example.com/attr/Test1/value/A", Name: "Test1"},
- {URI: "https://example2.com/attr/Test2/value/B", Name: "Test2"},
- {URI: "https://example3.com/attr/Test3/value/C", Name: "Test3"},
- },
- },
- },
- }
-
- testDefinitions := []attrs.AttributeDefinition{
- {
- Authority: "https://example.com",
- Name: "Test1",
- Rule: "allOf",
- Order: []string{"A", "B", "C"},
- },
- }
-
- output, err := canAccess(c, entityID, testPolicy, testClaims, testDefinitions)
- if err != nil {
- t.Error(err)
- }
- if !output {
- t.Errorf("Output %v not equal to expected %v", output, true)
- }
-}
-
-func TestAllOfFailure(t *testing.T) {
- var entityID string = "email2@example.com"
-
- testPolicy := Policy{
- UUID: uuid.New(),
- Body: PolicyBody{
- DataAttributes: []Attribute{
- {URI: "https://example.com/attr/Test1/value/A", Name: "Test1"},
- {URI: "https://example.com/attr/Test1/value/B", Name: "Test1"},
- },
- Dissem: []string{},
- },
- }
-
- testClaims := ClaimsObject{
- PublicKey: "test-public-key",
- ClientPublicSigningKey: "test-client-public-signing-key",
- SchemaVersion: "test-schema",
- Entitlements: []Entitlement{
- {
- EntityID: "email2@example.com",
- EntityAttributes: []Attribute{
- {URI: "https://example.com/attr/Test1/value/A", Name: "Test1"},
- {URI: "https://example2.com/attr/Test2/value/B", Name: "Test2"},
- {URI: "https://example3.com/attr/Test3/value/C", Name: "Test3"},
- },
- },
- },
- }
-
- testDefinitions := []attrs.AttributeDefinition{
- {
- Authority: "https://example.com",
- Name: "Test1",
- Rule: "allOf",
- Order: []string{"A", "B", "C"},
- },
- }
-
- output, err := canAccess(c, entityID, testPolicy, testClaims, testDefinitions)
- if err != nil {
- t.Error(err)
- }
- if output {
- t.Errorf("Output %v not equal to expected %v", output, false)
- }
-}
-
-// ######## Any Of tests ################
-
-func TestAnyOfSuccess(t *testing.T) {
- var entityID string = "email2@example.com"
-
- testPolicy := Policy{
- UUID: uuid.New(),
- Body: PolicyBody{
- DataAttributes: []Attribute{
- {URI: "https://example3.com/attr/Test3/value/A", Name: "Test3"},
- {URI: "https://example3.com/attr/Test3/value/C", Name: "Test3"},
- },
- Dissem: []string{},
- },
- }
-
- testClaims := ClaimsObject{
- PublicKey: "test-public-key",
- ClientPublicSigningKey: "test-client-public-signing-key",
- SchemaVersion: "test-schema",
- Entitlements: []Entitlement{
- {
- EntityID: "email2@example.com",
- EntityAttributes: []Attribute{
- {URI: "https://example.com/attr/Test1/value/A", Name: "Test1"},
- {URI: "https://example2.com/attr/Test2/value/B", Name: "Test2"},
- {URI: "https://example3.com/attr/Test3/value/C", Name: "Test3"},
- },
- },
- },
- }
-
- testDefinitions := []attrs.AttributeDefinition{
- {
- Authority: "https://example3.com",
- Name: "Test3",
- Rule: "anyOf",
- Order: []string{"A", "B", "C"},
- },
- }
-
- output, err := canAccess(c, entityID, testPolicy, testClaims, testDefinitions)
- if err != nil {
- t.Error(err)
- }
- if !output {
- t.Errorf("Output %v not equal to expected %v", output, true)
- }
-}
-
-func TestAnyOfFailure(t *testing.T) {
- var entityID string = "email2@example.com"
-
- testPolicy := Policy{
- UUID: uuid.New(),
- Body: PolicyBody{
- DataAttributes: []Attribute{
- {URI: "https://example3.com/attr/Test3/value/A", Name: "Test3"},
- {URI: "https://example3.com/attr/Test3/value/B", Name: "Test3"},
- },
- Dissem: []string{},
- },
- }
-
- testClaims := ClaimsObject{
- PublicKey: "test-public-key",
- ClientPublicSigningKey: "test-client-public-signing-key",
- SchemaVersion: "test-schema",
- Entitlements: []Entitlement{
- {
- EntityID: "email2@example.com",
- EntityAttributes: []Attribute{
- {URI: "https://example.com/attr/Test1/value/A", Name: "Test1"},
- {URI: "https://example2.com/attr/Test2/value/B", Name: "Test2"},
- {URI: "https://example3.com/attr/Test3/value/C", Name: "Test3"},
- },
- },
- },
- }
-
- testDefinitions := []attrs.AttributeDefinition{
- {
- Authority: "https://example3.com",
- Name: "Test3",
- Rule: "anyOf",
- Order: []string{"A", "B", "C"},
- },
- }
-
- output, err := canAccess(c, entityID, testPolicy, testClaims, testDefinitions)
- if err != nil {
- t.Error(err)
- }
- if output {
- t.Errorf("Output %v not equal to expected %v", output, false)
- }
-}
-
-// ######## Hierarchy tests ################
-
-func TestHierarchySuccess(t *testing.T) {
- var entityID string = "email2@example.com"
-
- testPolicy := Policy{
- UUID: uuid.New(),
- Body: PolicyBody{
- DataAttributes: []Attribute{
- {URI: "https://example2.com/attr/Test2/value/C", Name: "Test2"},
- },
- Dissem: []string{},
- },
- }
-
- testClaims := ClaimsObject{
- PublicKey: "test-public-key",
- ClientPublicSigningKey: "test-client-public-signing-key",
- SchemaVersion: "test-schema",
- Entitlements: []Entitlement{
- {
- EntityID: "email2@example.com",
- EntityAttributes: []Attribute{
- {URI: "https://example.com/attr/Test1/value/A", Name: "Test1"},
- {URI: "https://example2.com/attr/Test2/value/B", Name: "Test2"},
- {URI: "https://example3.com/attr/Test3/value/C", Name: "Test3"},
- },
- },
- },
- }
-
- testDefinitions := []attrs.AttributeDefinition{
- {
- Authority: "https://example2.com",
- Name: "Test2",
- Rule: "hierarchy",
- Order: []string{"A", "B", "C"},
- },
+ entity := authorization.Entity{
+ Id: "0",
+ EntityType: &authorization.Entity_EmailAddress{EmailAddress: entityID},
}
-
- output, err := canAccess(c, entityID, testPolicy, testClaims, testDefinitions)
- if err != nil {
- t.Error(err)
- }
- if !output {
- t.Errorf("Output %v not equal to expected %v", output, true)
- }
-}
-
-func TestHierarchyFailure(t *testing.T) {
- var entityID string = "email2@example.com"
-
- testPolicy := Policy{
- UUID: uuid.New(),
- Body: PolicyBody{
- DataAttributes: []Attribute{
- {URI: "https://example2.com/attr/Test2/value/A", Name: "Test2"},
- {URI: "https://example2.com/attr/Test2/value/B", Name: "Test2"},
- },
- Dissem: []string{},
- },
- }
-
- testClaims := ClaimsObject{
- PublicKey: "test-public-key",
- ClientPublicSigningKey: "test-client-public-signing-key",
- SchemaVersion: "test-schema",
- Entitlements: []Entitlement{
- {
- EntityID: "email2@example.com",
- EntityAttributes: []Attribute{
- {URI: "https://example.com/attr/Test1/value/A", Name: "Test1"},
- {URI: "https://example2.com/attr/Test2/value/B", Name: "Test2"},
- {URI: "https://example3.com/attr/Test3/value/C", Name: "Test3"},
- },
- },
- },
- }
-
- testDefinitions := []attrs.AttributeDefinition{
- {
- Authority: "https://example2.com",
- Name: "Test2",
- Rule: "hierarchy",
- Order: []string{"A", "B", "C"},
- },
- }
-
- output, err := canAccess(c, entityID, testPolicy, testClaims, testDefinitions)
+ output, err := canAccess(c, entity, testPolicy, osdk)
if err != nil {
t.Error(err)
}
@@ -393,344 +62,3 @@ func TestHierarchyFailure(t *testing.T) {
t.Errorf("Output %v not equal to expected %v", output, false)
}
}
-
-// ######## Dissem Attribute combination ############
-
-func TestAttrDissemSuccess(t *testing.T) {
- var entityID string = "email2@example.com"
-
- testPolicy := Policy{
- UUID: uuid.New(),
- Body: PolicyBody{
- DataAttributes: []Attribute{
- {URI: "https://example.com/attr/Test1/value/A", Name: "Test1"},
- },
- Dissem: []string{"email1@example.com",
- "email2@example.com",
- "email3@example.com"},
- },
- }
-
- testClaims := ClaimsObject{
- PublicKey: "test-public-key",
- ClientPublicSigningKey: "test-client-public-signing-key",
- SchemaVersion: "test-schema",
- Entitlements: []Entitlement{
- {
- EntityID: "email2@example.com",
- EntityAttributes: []Attribute{
- {URI: "https://example.com/attr/Test1/value/A", Name: "Test1"},
- {URI: "https://example2.com/attr/Test2/value/B", Name: "Test2"},
- {URI: "https://example3.com/attr/Test3/value/C", Name: "Test3"},
- },
- },
- },
- }
-
- testDefinitions := []attrs.AttributeDefinition{
- {
- Authority: "https://example.com",
- Name: "Test1",
- Rule: "allOf",
- Order: []string{"A", "B", "C"},
- },
- }
-
- output, err := canAccess(c, entityID, testPolicy, testClaims, testDefinitions)
- if err != nil {
- t.Error(err)
- }
- if !output {
- t.Errorf("Output %v not equal to expected %v", output, true)
- }
-}
-
-func TestAttrDissemFailure1(t *testing.T) {
- var entityID string = "email2@example.com"
-
- testPolicy := Policy{
- UUID: uuid.New(),
- Body: PolicyBody{
- DataAttributes: []Attribute{
- {URI: "https://example.com/attr/Test1/value/A", Name: "Test1"},
- },
- Dissem: []string{"email1@example.com",
- "email3@example.com"},
- },
- }
-
- testClaims := ClaimsObject{
- PublicKey: "test-public-key",
- ClientPublicSigningKey: "test-client-public-signing-key",
- SchemaVersion: "test-schema",
- Entitlements: []Entitlement{
- {
- EntityID: "email2@example.com",
- EntityAttributes: []Attribute{
- {URI: "https://example.com/attr/Test1/value/A", Name: "Test1"},
- {URI: "https://example2.com/attr/Test2/value/B", Name: "Test2"},
- {URI: "https://example3.com/attr/Test3/value/C", Name: "Test3"},
- },
- },
- },
- }
-
- testDefinitions := []attrs.AttributeDefinition{
- {
- Authority: "https://example.com",
- Name: "Test1",
- Rule: "allOf",
- Order: []string{"A", "B", "C"},
- },
- }
-
- output, err := canAccess(c, entityID, testPolicy, testClaims, testDefinitions)
- if err != nil {
- t.Error(err)
- }
- if output {
- t.Errorf("Output %v not equal to expected %v", output, false)
- }
-}
-
-func TestAttrDissemFailure2(t *testing.T) {
- var entityID string = "email2@example.com"
-
- testPolicy := Policy{
- UUID: uuid.New(),
- Body: PolicyBody{
- DataAttributes: []Attribute{
- {URI: "https://example.com/attr/Test1/value/A", Name: "Test1"},
- {URI: "https://example.com/attr/Test1/value/B", Name: "Test1"},
- },
- Dissem: []string{"email1@example.com",
- "email2@example.com",
- "email3@example.com"},
- },
- }
-
- testClaims := ClaimsObject{
- PublicKey: "test-public-key",
- ClientPublicSigningKey: "test-client-public-signing-key",
- SchemaVersion: "test-schema",
- Entitlements: []Entitlement{
- {
- EntityID: "email2@example.com",
- EntityAttributes: []Attribute{
- {URI: "https://example.com/attr/Test1/value/A", Name: "Test1"},
- {URI: "https://example2.com/attr/Test2/value/B", Name: "Test2"},
- {URI: "https://example3.com/attr/Test3/value/C", Name: "Test3"},
- },
- },
- },
- }
-
- testDefinitions := []attrs.AttributeDefinition{
- {
- Authority: "https://example.com",
- Name: "Test1",
- Rule: "allOf",
- Order: []string{"A", "B", "C"},
- },
- }
-
- output, err := canAccess(c, entityID, testPolicy, testClaims, testDefinitions)
- if err != nil {
- t.Error(err)
- }
- if output {
- t.Errorf("Output %v not equal to expected %v", output, false)
- }
-}
-
-func TestAttrDissemFailure3(t *testing.T) {
- var entityID string = ""
-
- testPolicy := Policy{
- UUID: uuid.New(),
- Body: PolicyBody{
- DataAttributes: []Attribute{
- {URI: "https://example.com/attr/Test1/value/A", Name: "Test1"},
- {URI: "https://example.com/attr/Test1/value/B", Name: "Test1"},
- },
- Dissem: []string{"email1@example.com",
- "email2@example.com",
- "email3@example.com"},
- },
- }
-
- testClaims := ClaimsObject{
- PublicKey: "test-public-key",
- ClientPublicSigningKey: "test-client-public-signing-key",
- SchemaVersion: "test-schema",
- Entitlements: []Entitlement{
- {
- EntityID: "email2@example.com",
- EntityAttributes: []Attribute{
- {URI: "https://example.com/attr/Test1/value/A", Name: "Test1"},
- {URI: "https://example2.com/attr/Test2/value/B", Name: "Test2"},
- {URI: "https://example3.com/attr/Test3/value/C", Name: "Test3"},
- },
- },
- },
- }
-
- testDefinitions := []attrs.AttributeDefinition{
- {
- Authority: "https://example.com",
- Name: "Test1",
- Rule: "allOf",
- Order: []string{"A", "B", "C"},
- },
- }
-
- output, err := canAccess(c, entityID, testPolicy, testClaims, testDefinitions)
- if err != ErrPolicyDissemInvalid {
- t.Errorf("Output %v not equal to expected %v", output, ErrPolicyDissemInvalid)
- }
- if output {
- t.Errorf("Output %v not equal to expected %v", output, false)
- }
-}
-
-func TestAttrDissemFailure4(t *testing.T) {
- var entityID string = "email2@example.com"
-
- testPolicy := Policy{
- UUID: uuid.New(),
- Body: PolicyBody{
- DataAttributes: []Attribute{
- {URI: "", Name: "Test1"},
- },
- Dissem: []string{"email1@example.com",
- "email2@example.com",
- "email3@example.com"},
- },
- }
-
- testClaims := ClaimsObject{
- PublicKey: "test-public-key",
- ClientPublicSigningKey: "test-client-public-signing-key",
- SchemaVersion: "test-schema",
- Entitlements: []Entitlement{
- {
- EntityID: "email2@example.com",
- EntityAttributes: []Attribute{
- {URI: "https://example.com/attr/Test1/value/A", Name: "Test1"},
- {URI: "https://example2.com/attr/Test2/value/B", Name: "Test2"},
- {URI: "https://example3.com/attr/Test3/value/C", Name: "Test3"},
- },
- },
- },
- }
-
- testDefinitions := []attrs.AttributeDefinition{
- {
- Authority: "https://example.com",
- Name: "Test1",
- Rule: "allOf",
- Order: []string{"A", "B", "C"},
- },
- }
-
- output, err := canAccess(c, entityID, testPolicy, testClaims, testDefinitions)
-
- if output != false {
- t.Errorf("Expected false, but got %v", err)
- }
-
- if err == nil {
- t.Errorf("Expected error, but got %v", err)
- }
-}
-
-func TestAttrDissemFailure5(t *testing.T) {
- var entityID string = "email2@example.com"
-
- testPolicy := Policy{
- UUID: uuid.New(),
- Body: PolicyBody{
- DataAttributes: []Attribute{
- {URI: "https://example.com/attr/Test1/value/A", Name: "Test1"},
- },
- Dissem: []string{"email1@example.com",
- "email2@example.com",
- "email3@example.com"},
- },
- }
-
- testClaims := ClaimsObject{
- PublicKey: "test-public-key",
- ClientPublicSigningKey: "test-client-public-signing-key",
- SchemaVersion: "test-schema",
- Entitlements: []Entitlement{
- {
- EntityID: "email2@example.com",
- EntityAttributes: []Attribute{
- {URI: "", Name: "Test1"},
- },
- },
- },
- }
-
- testDefinitions := []attrs.AttributeDefinition{
- {
- Authority: "https://example.com",
- Name: "Test1",
- Rule: "allOf",
- Order: []string{"A", "B", "C"},
- },
- }
-
- output, err := canAccess(c, entityID, testPolicy, testClaims, testDefinitions)
-
- if output != false {
- t.Errorf("Expected false, but got %v", err)
- }
-
- if err == nil {
- t.Errorf("Expected error, but got %v", err)
- }
-}
-
-func TestAttrDissemFailure6(t *testing.T) {
- var entityID string = "email2@example.com"
-
- testPolicy := Policy{
- UUID: uuid.New(),
- Body: PolicyBody{
- DataAttributes: []Attribute{
- {URI: "https://example.com/attr/Test1/value/A", Name: "Test1"},
- },
- Dissem: []string{"email1@example.com",
- "email2@example.com",
- "email3@example.com"},
- },
- }
-
- testClaims := ClaimsObject{
- PublicKey: "test-public-key",
- ClientPublicSigningKey: "test-client-public-signing-key",
- SchemaVersion: "test-schema",
- Entitlements: []Entitlement{
- {
- EntityID: "email2@example.com",
- EntityAttributes: []Attribute{
- {URI: "https://example.com/attr/Test1/value/A", Name: "Test1"},
- },
- },
- },
- }
-
- testDefinitions := []attrs.AttributeDefinition{}
-
- output, err := canAccess(c, entityID, testPolicy, testClaims, testDefinitions)
-
- if output != false {
- t.Errorf("Expected false, but got %v", err)
- }
-
- if err == nil {
- t.Errorf("Expected error, but got %v", err)
- }
-}
diff --git a/services/kas/access/fetchAttributes.go b/services/kas/access/fetchAttributes.go
deleted file mode 100644
index 9483b76bac..0000000000
--- a/services/kas/access/fetchAttributes.go
+++ /dev/null
@@ -1,89 +0,0 @@
-package access
-
-import (
- "context"
- "encoding/json"
- "errors"
- "fmt"
- "io"
- "log/slog"
- "net/http"
- "net/url"
-
- "github.com/virtru/access-pdp/attributes"
-)
-
-const (
- ErrAttributeDefinitionsUnmarshal = Error("attribute definitions unmarshal")
- ErrAttributeDefinitionsServiceCall = Error("attribute definitions service call unexpected")
-)
-
-func ResolveAttributeAuthority(s string) (*url.URL, error) {
- u, err := url.Parse(s)
- if err != nil {
- slog.Error("invalid attribute authority", "err", err)
- return nil, errors.Join(ErrConfig, err)
- }
- if u.Host == "" || (u.Scheme != "http" && u.Scheme != "https") {
- slog.Error("invalid attribute authority", "url", u)
- return nil, ErrConfig
- }
- r, err := u.Parse("v1/attrName")
- if err != nil {
- panic(err)
- }
- return r, nil
-}
-
-func (p *Provider) fetchAttributes(ctx context.Context, namespaces []string) ([]attributes.AttributeDefinition, error) {
- var definitions []attributes.AttributeDefinition
- for _, ns := range namespaces {
- attrDefs, err := p.fetchAttributesForNamespace(ctx, ns)
- if err != nil {
- slog.ErrorContext(ctx, "unable to fetch attributes for namespace", "err", err, "namespace", ns)
- return nil, err
- }
- definitions = append(definitions, attrDefs...)
- }
- return definitions, nil
-}
-
-func (p *Provider) fetchAttributesForNamespace(ctx context.Context, namespace string) ([]attributes.AttributeDefinition, error) {
- slog.DebugContext(ctx, "Fetching", "namespace", namespace)
- req, err := http.NewRequestWithContext(ctx, http.MethodGet, p.AttributeSvc.String(), nil)
- if err != nil {
- slog.ErrorContext(ctx, "unable to create http request to attributes service", "namespace", namespace, "attributeHost", p.AttributeSvc)
- return nil, errors.Join(ErrAttributeDefinitionsServiceCall, err)
- }
-
- req.Header.Set("Content-Type", "application/json")
-
- q := req.URL.Query()
- q.Add("authority", namespace)
- req.URL.RawQuery = q.Encode()
- var httpClient http.Client
- resp, err := httpClient.Do(req)
- if err != nil {
- slog.ErrorContext(ctx, "failed http request to attributes service", "err", err, "namespace", namespace, "req.URL", req.URL)
- return nil, errors.Join(ErrAttributeDefinitionsServiceCall, err)
- }
- defer func(Body io.ReadCloser) {
- err := Body.Close()
- if err != nil {
- slog.ErrorContext(ctx, "failed to close http request to attributes service", "err", err, "namespace", namespace, "req.URL", req.URL)
- }
- }(resp.Body)
- if resp.StatusCode != http.StatusOK {
- err := fmt.Errorf("status code %v %v", resp.StatusCode, http.StatusText(resp.StatusCode))
- return nil, errors.Join(ErrAttributeDefinitionsServiceCall, err)
- }
-
- var definitions []attributes.AttributeDefinition
- err = json.NewDecoder(resp.Body).Decode(&definitions)
- if err != nil {
- slog.ErrorContext(ctx, "failed to parse response from attributes service", "err", err, "namespace", namespace, "req.URL", req.URL)
- return nil, errors.Join(ErrAttributeDefinitionsUnmarshal, err)
- }
-
- return definitions, nil
-}
diff --git a/services/kas/access/fetchAttributes_test.go b/services/kas/access/fetchAttributes_test.go
deleted file mode 100644
index d491f3511c..0000000000
--- a/services/kas/access/fetchAttributes_test.go
+++ /dev/null
@@ -1,195 +0,0 @@
-package access
-
-import (
- "context"
- "net/http"
- "testing"
-
- "github.com/jarcoal/httpmock"
- "github.com/virtru/access-pdp/attributes"
-)
-
-type WrongAttributeDefinition struct {
- Wrong string `json:"wrong"`
- Type string `json:"type"`
- Of string `json:"of"`
- Response string `json:"response"`
-}
-
-func mockAttrProvider() *Provider {
- u, err := ResolveAttributeAuthority("http://localhost:65432/api/attributes/")
- if err != nil {
- panic(err)
- }
- return &Provider{
- AttributeSvc: u,
- }
-}
-
-func TestResolveAttributeService(t *testing.T) {
- p, err := ResolveAttributeAuthority("")
- if p != nil || err == nil {
- t.Errorf("empty ATTR_AUTHORITY_HOST should fail p=[%s], err=[%s]", p, err)
- }
-
- p, err = ResolveAttributeAuthority("http://localhost")
- if p.String() != "http://localhost/v1/attrName" || err != nil {
- t.Errorf("simple ATTR_AUTHORITY_HOST should not fail p=[%s], err=[%s]", p, err)
- }
-
- p, err = ResolveAttributeAuthority("http://localhost/api/attributes/")
- if p.String() != "http://localhost/api/attributes/v1/attrName" || err != nil {
- t.Errorf("ATTR_AUTHORITY_HOST with path should not fail p=[%s], err=[%s]", p, err)
- }
-}
-
-func TestFetchAttributesSuccess(t *testing.T) {
- httpmock.Activate()
- defer httpmock.DeactivateAndReset()
-
- ctx := context.Background()
- namespaces := []string{"namespace1", "namespace2"}
-
- mockDefinitions := []attributes.AttributeDefinition{
- {
- Authority: "namespace1",
- Name: "attribute1",
- Rule: "rule1",
- State: "active",
- Order: []string{"value1", "value2", "value3"},
- },
- {
- Authority: "namespace2",
- Name: "attribute2",
- Rule: "rule2",
- Order: []string{"valueA", "valueB", "valueC"},
- },
- }
-
- httpmock.RegisterResponder(http.MethodGet, "http://localhost:65432/api/attributes/v1/attrName",
- func(req *http.Request) (*http.Response, error) {
- authority := req.URL.Query().Get("authority")
-
- if authority == "namespace1" {
- resp, err := httpmock.NewJsonResponse(200, mockDefinitions[:1])
- return resp, err
- }
-
- // namespace2
- resp, err := httpmock.NewJsonResponse(200, mockDefinitions[1:])
- return resp, err
- },
- )
-
- p := mockAttrProvider()
- output, err := p.fetchAttributes(ctx, namespaces)
-
- if err != nil {
- t.Error(err)
- }
-
- if len(output) != len(mockDefinitions) {
- t.Errorf("Output %v not equal to expected %v", output, mockDefinitions)
- }
-}
-
-func TestFetchAttributesFailure(t *testing.T) {
- httpmock.Activate()
- defer httpmock.DeactivateAndReset()
-
- ctx := context.Background()
- namespaces := []string{"namespace1", "namespace2"}
-
- mockWrongResponse := WrongAttributeDefinition{
- Wrong: "mock",
- Type: "mock",
- Of: "mock",
- Response: "mock",
- }
-
- httpmock.RegisterResponder(http.MethodGet, "http://localhost:65432/api/attributes/v1/attrName",
- func(req *http.Request) (*http.Response, error) {
- resp, err := httpmock.NewJsonResponse(200, mockWrongResponse)
- return resp, err
- },
- )
-
- p := mockAttrProvider()
- output, err := p.fetchAttributes(ctx, namespaces)
-
- t.Log(err)
- t.Log(output)
-
- if len(output) != 0 {
- t.Errorf("Output %v not equal to expected %v", len(output), 0)
- }
-
- if err == nil {
- t.Errorf("Error expected, but got %v", err)
- }
-}
-
-func TestFetchAttributesFailure1(t *testing.T) {
- httpmock.Activate()
- defer httpmock.DeactivateAndReset()
-
- ctx := context.Background()
- namespaces := []string{"namespace1", "namespace2"}
-
- httpmock.RegisterResponder(http.MethodGet, "http://localhost:65432/api/attributes/v1/attrName",
- func(req *http.Request) (*http.Response, error) {
- return httpmock.NewStringResponse(500, ""), nil
- },
- )
-
- p := mockAttrProvider()
- output, err := p.fetchAttributes(ctx, namespaces)
-
- if err == nil {
- t.Error("Should throw an error")
- }
-
- if len(output) != 0 {
- t.Errorf("Output %v not equal to expected %v", len(output), 0)
- }
-}
-
-func TestFetchAttributesFailure2(t *testing.T) {
- httpmock.Activate()
- defer httpmock.DeactivateAndReset()
-
- ctx := context.Background()
- namespaces := []string{"namespace1", "namespace2"}
-
- httpmock.RegisterResponder(http.MethodGet, "http://localhost:65432/api/attributes/v1/attrName",
- func(req *http.Request) (*http.Response, error) {
- return nil, Error("Mock http client error")
- },
- )
-
- p := mockAttrProvider()
- output, err := p.fetchAttributes(ctx, namespaces)
-
- if err == nil {
- t.Error("Should throw an error")
- }
-
- if len(output) != 0 {
- t.Errorf("Output %v not equal to expected %v", len(output), 0)
- }
-}
-
-func TestFetchAttributesForNamespaceFailure(t *testing.T) {
- namespaces := []string{"namespace1", "namespace2"}
-
- p := mockAttrProvider()
- output, err := p.fetchAttributes(context.Background(), namespaces)
-
- if err == nil {
- t.Error("Should throw an error")
- }
-
- if len(output) != 0 {
- t.Errorf("Output %v not equal to expected %v", len(output), 0)
- }
-}
diff --git a/services/kas/access/policy.go b/services/kas/access/policy.go
index 1c08f991b1..3d50bc650f 100644
--- a/services/kas/access/policy.go
+++ b/services/kas/access/policy.go
@@ -1,14 +1,7 @@
package access
import (
- "errors"
-
"github.com/google/uuid"
- attrs "github.com/virtru/access-pdp/attributes"
-)
-
-const (
- ErrPolicyDataAttributeParse = Error("policy data attribute invalid")
)
type Policy struct {
@@ -20,26 +13,3 @@ type PolicyBody struct {
DataAttributes []Attribute `json:"dataAttributes"`
Dissem []string `json:"dissem"`
}
-
-func getNamespacesFromAttributes(body PolicyBody) ([]string, error) {
- // extract the namespace from an attribute uri
- var dataAttributes = body.DataAttributes
- namespaces := make(map[string]bool)
- for _, attr := range dataAttributes {
- instance, err := attrs.ParseInstanceFromURI(attr.URI)
- if err != nil {
- return nil, errors.Join(ErrPolicyDataAttributeParse, err)
- }
- namespaces[instance.Authority] = true
- }
-
- // get unique
- keys := make([]string, len(namespaces))
- index := 0
- for key := range namespaces {
- keys[index] = key
- index++
- }
-
- return keys, nil
-}
diff --git a/services/kas/access/policy_test.go b/services/kas/access/policy_test.go
index 953294851a..ded273abee 100644
--- a/services/kas/access/policy_test.go
+++ b/services/kas/access/policy_test.go
@@ -1,46 +1,5 @@
package access
-import (
- "testing"
-)
-
-func TestGetNamespacesFromAttributesSuccess(t *testing.T) {
- testBody := PolicyBody{
- DataAttributes: []Attribute{
- {URI: "https://example.com/attr/Test1/value/A", Name: "TestAttr1"},
- {URI: "https://example2.com/attr/Test2/value/B", Name: "TestAttr2"},
- {URI: "https://example.com/attr/Test3/value/C", Name: "TestAttr3"},
- },
- Dissem: []string{},
- }
- expectedResult := []string{"https://example2.com", "https://example.com"}
- output, err := getNamespacesFromAttributes(testBody)
- if err != nil {
- t.Error(err)
- }
- if !sameStringSlice(output, expectedResult) {
- t.Errorf("Output %q not equal to expected %q", output, expectedResult)
- }
-}
-
-func TestGetNamespacesFromAttributesFailure(t *testing.T) {
- testBody := PolicyBody{
- DataAttributes: []Attribute{
- {URI: "", Name: "TestAttr1"},
- },
- Dissem: []string{},
- }
- output, err := getNamespacesFromAttributes(testBody)
-
- if len(output) != 0 {
- t.Errorf("Output %v not equal to expected %v", len(output), 0)
- }
-
- if err == nil {
- t.Errorf("Should throw an error, but got %v", err)
- }
-}
-
func sameStringSlice(x, y []string) bool {
if len(x) != len(y) {
return false
diff --git a/services/kas/access/provider.go b/services/kas/access/provider.go
index 09a2bcbbf9..279f523f17 100644
--- a/services/kas/access/provider.go
+++ b/services/kas/access/provider.go
@@ -1,6 +1,7 @@
package access
import (
+ otdf "github.com/opentdf/platform/sdk"
"net/url"
"github.com/opentdf/platform/internal/security"
@@ -17,7 +18,7 @@ const (
type Provider struct {
kaspb.AccessServiceServer
URI url.URL `json:"uri"`
- AttributeSvc *url.URL
+ SDK *otdf.SDK
Session security.HSMSession
OIDCVerifier *oidc.IDTokenVerifier
}
diff --git a/services/kas/access/rewrap.go b/services/kas/access/rewrap.go
index 4cedfd22dc..d72095a55a 100644
--- a/services/kas/access/rewrap.go
+++ b/services/kas/access/rewrap.go
@@ -19,6 +19,7 @@ import (
"encoding/pem"
"errors"
"fmt"
+ "github.com/opentdf/platform/protocol/go/authorization"
"io"
"log/slog"
"strings"
@@ -52,8 +53,8 @@ type customClaimsBody struct {
}
type customClaimsHeader struct {
- EntityID string `json:"sub"`
- ClientID string `json:"clientId"`
+ Subject string `json:"sub"`
+ ClientID string `json:"client_id"`
TDFClaims ClaimsObject `json:"tdf_claims"`
}
@@ -127,6 +128,7 @@ type verifiedRequest struct {
publicKey crypto.PublicKey
requestBody *RequestBody
cl *customClaimsHeader
+ bearerToken string
}
func (p *Provider) verifyBearerAndParseRequestBody(ctx context.Context, in *kaspb.RewrapRequest) (*verifiedRequest, error) {
@@ -195,9 +197,9 @@ func (p *Provider) verifyBearerAndParseRequestBody(ctx context.Context, in *kasp
}
switch clientPublicKey.(type) {
case *rsa.PublicKey:
- return &verifiedRequest{clientPublicKey, &requestBody, &cl}, nil
+ return &verifiedRequest{clientPublicKey, &requestBody, &cl, in.Bearer}, nil
case *ecdsa.PublicKey:
- return &verifiedRequest{clientPublicKey, &requestBody, &cl}, nil
+ return &verifiedRequest{clientPublicKey, &requestBody, &cl, in.Bearer}, nil
}
slog.WarnContext(ctx, fmt.Sprintf("clientPublicKey not a supported key, was [%T]", clientPublicKey))
return nil, err400("clientPublicKey unsupported type")
@@ -281,21 +283,20 @@ func (p *Provider) tdf3Rewrap(ctx context.Context, body *verifiedRequest) (*kasp
}
slog.DebugContext(ctx, "extracting policy", "requestBody.policy", body.requestBody.Policy)
- namespaces, err := getNamespacesFromAttributes(policy.Body)
- if err != nil {
- slog.WarnContext(ctx, "Could not get namespaces from policy!", "err", err)
- return nil, err403("forbidden")
- }
-
- slog.DebugContext(ctx, "Fetching attributes", "policy.namespaces", namespaces, "policy.body", policy.Body)
- definitions, err := p.fetchAttributes(ctx, namespaces)
- if err != nil {
- slog.ErrorContext(ctx, "Could not fetch attribute definitions from attributes service!", "err", err)
- return nil, err503("attribute server request failure")
+ // changed to ClientID from Subject
+ ent := authorization.Entity{
+ EntityType: &authorization.Entity_Jwt{
+ Jwt: body.bearerToken,
+ },
+ }
+ if body.cl.ClientID != "" {
+ ent = authorization.Entity{
+ EntityType: &authorization.Entity_ClientId{
+ ClientId: body.cl.ClientID,
+ },
+ }
}
- slog.DebugContext(ctx, "fetch attributes", "definitions", definitions)
-
- access, err := canAccess(ctx, body.cl.EntityID, *policy, body.cl.TDFClaims, definitions)
+ access, err := canAccess(ctx, ent, *policy, p.SDK)
if err != nil {
slog.WarnContext(ctx, "Could not perform access decision!", "err", err)
diff --git a/services/kas/access/rewrap_test.go b/services/kas/access/rewrap_test.go
index 5effedd74f..64ae675a8f 100644
--- a/services/kas/access/rewrap_test.go
+++ b/services/kas/access/rewrap_test.go
@@ -295,7 +295,7 @@ func signedMockJWT(signer *rsa.PrivateKey) string {
panic(err)
}
cl := customClaimsHeader{
- EntityID: "testuser1",
+ Subject: "testuser1",
ClientID: "testonly",
TDFClaims: standardClaims(),
}
@@ -323,7 +323,7 @@ func jwtWrongIssuer() string {
panic(err)
}
cl := customClaimsHeader{
- EntityID: "testuser1",
+ Subject: "testuser1",
ClientID: "testonly",
TDFClaims: standardClaims(),
}
diff --git a/services/kas/kas.go b/services/kas/kas.go
index c23da77040..27ff89cb28 100644
--- a/services/kas/kas.go
+++ b/services/kas/kas.go
@@ -62,6 +62,7 @@ func NewRegistration() serviceregistry.Registration {
slog.Error("hsm not enabled")
panic(fmt.Errorf("hsm not enabled"))
}
+ // FIXME msg="mismatched key access url" keyAccessURL=http://localhost:9000 kasURL=https://:9000
kasURLString := "https://" + srp.OTDF.HTTPServer.Addr
kasURI, err := url.Parse(kasURLString)
if err != nil {
@@ -70,7 +71,7 @@ func NewRegistration() serviceregistry.Registration {
p := access.Provider{
URI: *kasURI,
- AttributeSvc: nil,
+ SDK: srp.SDK,
Session: *hsm,
OIDCVerifier: loadIdentityProvider(),
}