Skip to content

Commit 543ec92

Browse files
committed
Unwrap scim errors when possible for patch validation
1 parent 172bf2a commit 543ec92

File tree

3 files changed

+36
-16
lines changed

3 files changed

+36
-16
lines changed

handlers_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,25 @@ func TestServerResourcePatchHandlerInvalidRemoveOp(t *testing.T) {
257257
assertEqualStatusCode(t, http.StatusBadRequest, rr.Code)
258258
}
259259

260+
func TestServerResourcePatchHandlerInvalidRemoveOpNoTarget(t *testing.T) {
261+
req := httptest.NewRequest(http.MethodPatch, "/Groups/0001", strings.NewReader(`{
262+
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
263+
"Operations":[
264+
{
265+
"op":"remove",
266+
"path":""
267+
}
268+
]
269+
}`))
270+
rr := httptest.NewRecorder()
271+
newTestServer(t).ServeHTTP(rr, req)
272+
273+
assertEqualStatusCode(t, http.StatusBadRequest, rr.Code)
274+
var scimErr *errors.ScimError
275+
assertUnmarshalNoError(t, json.Unmarshal(rr.Body.Bytes(), &scimErr))
276+
assertEqualSCIMErrors(t, &errors.ScimErrorNoTarget, scimErr)
277+
}
278+
260279
func TestServerResourcePatchHandlerMapTypeSubAttribute(t *testing.T) {
261280
recorder := httptest.NewRecorder()
262281
newTestServer(t).ServeHTTP(recorder, httptest.NewRequest(http.MethodPatch, "/Users/0001", strings.NewReader(`{

internal/patch/remove.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package patch
22

33
import (
44
"github.com/elimity-com/scim/filter"
5-
"net/http"
65

76
"github.com/elimity-com/scim/errors"
87
"github.com/elimity-com/scim/schema"
@@ -13,10 +12,7 @@ import (
1312
func (v OperationValidator) validateRemove() (interface{}, error) {
1413
// If "path" is unspecified, the operation fails with HTTP status code 400 and a "scimType" error code of "noTarget".
1514
if v.Path == nil {
16-
return nil, &errors.ScimError{
17-
ScimType: errors.ScimTypeNoTarget,
18-
Status: http.StatusBadRequest,
19-
}
15+
return nil, &errors.ScimErrorNoTarget
2016
}
2117

2218
refAttr, err := v.getRefAttribute(v.Path.AttributePath)

resource_type.go

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ package scim
33
import (
44
"bytes"
55
"encoding/json"
6+
"errors"
67
"net/http"
78

8-
"github.com/elimity-com/scim/errors"
9+
scimErrors "github.com/elimity-com/scim/errors"
910
"github.com/elimity-com/scim/internal/patch"
1011
"github.com/elimity-com/scim/optional"
1112
"github.com/elimity-com/scim/schema"
@@ -86,10 +87,10 @@ func (t ResourceType) schemaWithCommon() schema.Schema {
8687
return s
8788
}
8889

89-
func (t ResourceType) validate(raw []byte) (ResourceAttributes, *errors.ScimError) {
90+
func (t ResourceType) validate(raw []byte) (ResourceAttributes, *scimErrors.ScimError) {
9091
var m map[string]interface{}
9192
if err := unmarshal(raw, &m); err != nil {
92-
return ResourceAttributes{}, &errors.ScimErrorInvalidSyntax
93+
return ResourceAttributes{}, &scimErrors.ScimErrorInvalidSyntax
9394
}
9495

9596
attributes, scimErr := t.schemaWithCommon().Validate(m)
@@ -101,7 +102,7 @@ func (t ResourceType) validate(raw []byte) (ResourceAttributes, *errors.ScimErro
101102
extensionField := m[extension.Schema.ID]
102103
if extensionField == nil {
103104
if extension.Required {
104-
return ResourceAttributes{}, &errors.ScimErrorInvalidValue
105+
return ResourceAttributes{}, &scimErrors.ScimErrorInvalidValue
105106
}
106107
continue
107108
}
@@ -118,30 +119,30 @@ func (t ResourceType) validate(raw []byte) (ResourceAttributes, *errors.ScimErro
118119
}
119120

120121
// validatePatch parse and validate PATCH request.
121-
func (t ResourceType) validatePatch(r *http.Request) ([]PatchOperation, *errors.ScimError) {
122+
func (t ResourceType) validatePatch(r *http.Request) ([]PatchOperation, *scimErrors.ScimError) {
122123
data, err := readBody(r)
123124
if err != nil {
124-
return nil, &errors.ScimErrorInvalidSyntax
125+
return nil, &scimErrors.ScimErrorInvalidSyntax
125126
}
126127

127128
var req struct {
128129
Schemas []string
129130
Operations []json.RawMessage
130131
}
131132
if err := unmarshal(data, &req); err != nil {
132-
return nil, &errors.ScimErrorInvalidSyntax
133+
return nil, &scimErrors.ScimErrorInvalidSyntax
133134
}
134135

135136
// The body of each request MUST contain the "schemas" attribute with the URI value of
136137
// "urn:ietf:params:scim:api:messages:2.0:PatchOp".
137138
if len(req.Schemas) != 1 || req.Schemas[0] != "urn:ietf:params:scim:api:messages:2.0:PatchOp" {
138-
return nil, &errors.ScimErrorInvalidValue
139+
return nil, &scimErrors.ScimErrorInvalidValue
139140
}
140141

141142
// The body of an HTTP PATCH request MUST contain the attribute "Operations",
142143
// whose value is an array of one or more PATCH operations.
143144
if len(req.Operations) < 1 {
144-
return nil, &errors.ScimErrorInvalidValue
145+
return nil, &scimErrors.ScimErrorInvalidValue
145146
}
146147

147148
// Evaluation continues until all operations are successfully applied or until an error condition is encountered.
@@ -153,11 +154,15 @@ func (t ResourceType) validatePatch(r *http.Request) ([]PatchOperation, *errors.
153154
t.getSchemaExtensions()...,
154155
)
155156
if err != nil {
156-
return nil, &errors.ScimErrorInvalidPath
157+
return nil, &scimErrors.ScimErrorInvalidPath
157158
}
158159
value, err := validator.Validate()
159160
if err != nil {
160-
return nil, &errors.ScimErrorInvalidValue
161+
var scimErr *scimErrors.ScimError
162+
if errors.As(err, &scimErr) {
163+
return nil, scimErr
164+
}
165+
return nil, &scimErrors.ScimErrorInvalidValue
161166
}
162167
operations = append(operations, PatchOperation{
163168
Op: string(validator.Op),

0 commit comments

Comments
 (0)