Skip to content

Commit 2cee6ca

Browse files
pitabwireclaude
andcommitted
fix authorizer bugs and rewrite tests with real Keto server
Rewrote authorizer tests as a testify suite using testcontainers with a real Ory Keto instance instead of httptest mock servers. The integration tests uncovered and fixed three adapter bugs: - WriteTuples sent invalid batch format (Keto PUT accepts single tuples) - Check rejected HTTP 403 responses (Keto returns 403 for denied checks) - ListSubjectRelations used write URI instead of read URI for queries Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent f788b20 commit 2cee6ca

File tree

2 files changed

+538
-1149
lines changed

2 files changed

+538
-1149
lines changed

security/authorizer/client.go

Lines changed: 25 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,6 @@ type ketoSubjectSet struct {
5959
Relation string `json:"relation"`
6060
}
6161

62-
type ketoWriteRequest struct {
63-
RelationTuples []*ketoRelationTuple `json:"relation_tuples,omitempty"`
64-
}
65-
6662
type ketoListResponse struct {
6763
RelationTuples []*ketoRelationTuple `json:"relation_tuples"`
6864
NextPageToken string `json:"next_page_token,omitempty"`
@@ -116,7 +112,8 @@ func (k *ketoAdapter) Check(ctx context.Context, req security.CheckRequest) (sec
116112
return security.CheckResult{}, NewAuthzServiceError("check", err)
117113
}
118114

119-
if resp.StatusCode != http.StatusOK {
115+
// Keto returns 200 for allowed and 403 for denied; both carry a JSON body.
116+
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusForbidden {
120117
body, _ := resp.ToContent(ctx)
121118
return security.CheckResult{}, NewAuthzServiceError("check",
122119
fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(body)))
@@ -190,7 +187,9 @@ func (k *ketoAdapter) WriteTuple(ctx context.Context, tuple security.RelationTup
190187
return k.WriteTuples(ctx, []security.RelationTuple{tuple})
191188
}
192189

193-
// WriteTuples creates multiple relationship tuples atomically.
190+
// WriteTuples creates multiple relationship tuples.
191+
// Keto's PUT endpoint accepts a single tuple per request, so each tuple
192+
// is written individually.
194193
func (k *ketoAdapter) WriteTuples(ctx context.Context, tuples []security.RelationTuple) error {
195194
cfg := k.config
196195

@@ -202,34 +201,28 @@ func (k *ketoAdapter) WriteTuples(ctx context.Context, tuples []security.Relatio
202201
return nil
203202
}
204203

205-
ketoTuples := make([]*ketoRelationTuple, len(tuples))
206-
for i, t := range tuples {
207-
ketoTuples[i] = k.toKetoTuple(t)
208-
}
204+
for _, t := range tuples {
205+
kt := k.toKetoTuple(t)
209206

210-
reqBody := ketoWriteRequest{
211-
RelationTuples: ketoTuples,
212-
}
213-
214-
// Execute request with retries
215-
resp, err := k.httpClient.Invoke(
216-
ctx,
217-
http.MethodPut,
218-
cfg.GetAuthorizationServiceWriteURI()+"/admin/relation-tuples",
219-
reqBody,
220-
nil,
221-
)
222-
if err != nil {
223-
return NewAuthzServiceError("write_tuples", err)
224-
}
207+
resp, err := k.httpClient.Invoke(
208+
ctx,
209+
http.MethodPut,
210+
cfg.GetAuthorizationServiceWriteURI()+"/admin/relation-tuples",
211+
kt,
212+
nil,
213+
)
214+
if err != nil {
215+
return NewAuthzServiceError("write_tuples", err)
216+
}
225217

226-
defer util.CloseAndLogOnError(ctx, resp)
218+
if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusOK &&
219+
resp.StatusCode != http.StatusNoContent {
220+
respBody, _ := resp.ToContent(ctx)
221+
return NewAuthzServiceError("write_tuples",
222+
fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(respBody)))
223+
}
227224

228-
if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusOK &&
229-
resp.StatusCode != http.StatusNoContent {
230-
respBody, _ := resp.ToContent(ctx)
231-
return NewAuthzServiceError("write_tuples",
232-
fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(respBody)))
225+
util.CloseAndLogOnError(ctx, resp)
233226
}
234227

235228
return nil
@@ -346,7 +339,7 @@ func (k *ketoAdapter) ListSubjectRelations(
346339
return []security.RelationTuple{}, nil
347340
}
348341

349-
u, err := url.Parse(cfg.GetAuthorizationServiceWriteURI() + "/relation-tuples")
342+
u, err := url.Parse(cfg.GetAuthorizationServiceReadURI() + "/relation-tuples")
350343
if err != nil {
351344
return nil, NewAuthzServiceError("list_subject_relations", err)
352345
}

0 commit comments

Comments
 (0)