@@ -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-
6662type 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.
194193func (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