Skip to content

Commit 5f3a950

Browse files
committed
feat(scim): update documentation
1 parent a2659a0 commit 5f3a950

7 files changed

Lines changed: 42 additions & 54 deletions

File tree

internal/api/scim/core/attribute.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,13 @@ func (a *Attribute) AsCaseExact() *Attribute {
4242
return a
4343
}
4444

45-
// Suggesting sets "canonicalValues", the values a client may send for this
46-
// attribute, e.g. "work" and "home".
45+
// Suggesting sets "canonicalValues", per RFC 7643, Section 7.
4746
func (a *Attribute) Suggesting(values ...string) *Attribute {
4847
a.CanonicalValues = values
4948
return a
5049
}
5150

52-
// Referencing sets "referenceTypes", the resource types a reference attribute
53-
// may point at, either by name or as ReferenceExternal or ReferenceURI.
51+
// Referencing sets "referenceTypes", per RFC 7643, Section 7.
5452
func (a *Attribute) Referencing(referenceTypes ...ReferenceType) *Attribute {
5553
a.ReferenceTypes = referenceTypes
5654
return a

internal/api/scim/core/core.go

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
// Package core implements the SCIM 2.0 core schema defined in RFC 7643.
22
package core
33

4-
// SchemaURI identifies a SCIM schema
4+
// SchemaURI identifies a SCIM schema, per RFC 7643, Section 3.
55
type SchemaURI string
66

7-
// ResourceTypeName names a resource type
7+
// ResourceTypeName names a resource type, per RFC 7643, Section 6.
88
type ResourceTypeName string
99

1010
func (n ResourceTypeName) Reference() ReferenceType { return ReferenceType(n) }
1111

12-
// ReferenceType is a value of "referenceTypes", per RFC 7643, Section 7:
13-
// a resource type name, or the reference-only "external" / "uri".
12+
// ReferenceType is a "referenceTypes" value, per RFC 7643, Section 7.
1413
type ReferenceType string
1514

1615
// AttributeType is the data type of an attribute, per RFC 7643, Section 7.
@@ -26,7 +25,7 @@ const (
2625
TypeComplex AttributeType = "complex"
2726
)
2827

29-
// Mutability states when an attribute may be (re)defined.
28+
// Mutability states when an attribute may be (re)defined, per RFC 7643, Section 7.
3029
type Mutability string
3130

3231
const (
@@ -36,7 +35,7 @@ const (
3635
MutabilityWriteOnly Mutability = "writeOnly"
3736
)
3837

39-
// Returned states when an attribute is included in a response.
38+
// Returned states when an attribute is included in a response, per RFC 7643, Section 7.
4039
type Returned string
4140

4241
const (
@@ -46,7 +45,7 @@ const (
4645
ReturnedRequest Returned = "request"
4746
)
4847

49-
// Uniqueness states how the service provider enforces uniqueness.
48+
// Uniqueness states how the service provider enforces uniqueness, per RFC 7643, Section 7.
5049
type Uniqueness string
5150

5251
const (

internal/api/scim/models.go

Lines changed: 0 additions & 23 deletions
This file was deleted.

internal/api/scim/protocol/error.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ func (e *Error) Error() string {
5151
return message
5252
}
5353

54-
// StatusCode is the HTTP status the Error was built from, or 500 if unparsable.
5554
func (e *Error) StatusCode() int {
5655
status, err := strconv.Atoi(e.Status)
5756
if err != nil {
@@ -60,7 +59,6 @@ func (e *Error) StatusCode() int {
6059
return status
6160
}
6261

63-
// Is reports whether target is an Error of the same status and scimType.
6462
func (e *Error) Is(target error) bool {
6563
other, ok := target.(*Error)
6664
return ok && other.Status == e.Status && other.ScimType == e.ScimType

internal/api/scim/protocol/protocol.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
// MediaType is the SCIM media type registered in RFC 7644, Section 8.1.
1212
const MediaType = "application/scim+json"
1313

14-
// Send writes obj as a SCIM response.
1514
func Send(w http.ResponseWriter, status int, obj any) error {
1615
var body []byte
1716
if obj != nil {

internal/api/scim/protocol/search_request.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,9 @@ type Limits struct {
4646
MaxCount int
4747
}
4848

49-
// DefaultLimits are the bounds a provider gets until it states its own.
5049
var DefaultLimits = Limits{DefaultCount: 100, MaxCount: 100}
5150

52-
// ParseSearchRequest reads the query parameters of RFC 7644, Section 3.4.2,
53-
// holding the client to this provider's Limits.
51+
// ParseSearchRequest reads the query parameters of RFC 7644, Section 3.4.2.
5452
func (l Limits) ParseSearchRequest(values url.Values) (*SearchRequest, error) {
5553
startIndex, err := intParam(values, "startIndex", 1)
5654
if err != nil {

internal/api/scim/user_repository.go

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"fmt"
88
"strconv"
99
"strings"
10+
"time"
1011

1112
"github.com/gofrs/uuid"
1213
"github.com/jackc/pgconn"
@@ -29,6 +30,24 @@ const countUsers = `SELECT COUNT(*) FROM scim_users WHERE sso_provider_id = ? AN
2930

3031
const listUsers = `SELECT id, resource, active, created_at, updated_at FROM scim_users WHERE sso_provider_id = ? AND deleted_at IS NULL ORDER BY %s LIMIT ? OFFSET ?`
3132

33+
type scimUser struct {
34+
ID string `db:"id"`
35+
Resource []byte `db:"resource"`
36+
Active bool `db:"active"`
37+
CreatedAt time.Time `db:"created_at"`
38+
UpdatedAt time.Time `db:"updated_at"`
39+
}
40+
41+
func (scimUser) TableName() string {
42+
return "scim_users"
43+
}
44+
45+
func (u *scimUser) ResourceID() string { return u.ID }
46+
47+
func (u *scimUser) Timestamps() (created, updated time.Time) {
48+
return u.CreatedAt, u.UpdatedAt
49+
}
50+
3251
type userRepository struct {
3352
db *storage.Connection
3453
baseURL string
@@ -41,20 +60,6 @@ func NewUserRepository(db *storage.Connection, baseURL string) Repository[*core.
4160
}
4261
}
4362

44-
func (r *userRepository) Get(ctx context.Context, id string) (*core.User, error) {
45-
var rows []scimUser
46-
47-
err := r.db.WithContext(ctx).RawQuery("SELECT id, resource, active, created_at, updated_at FROM scim_users WHERE sso_provider_id = ? AND deleted_at IS NULL AND id = ?", r.tenant(ctx), id).All(&rows)
48-
if err != nil {
49-
return nil, fmt.Errorf("scim: reading user: %w", err)
50-
}
51-
52-
if len(rows) == 0 {
53-
return nil, ErrNotFound
54-
}
55-
return r.mapFrom(&rows[0])
56-
}
57-
5863
func (r *userRepository) List(ctx context.Context, query *protocol.SearchRequest) ([]*core.User, int, error) {
5964
if query.Filter != "" {
6065
return nil, 0, protocol.ErrInvalidFilter("filtering is not supported")
@@ -93,6 +98,20 @@ func (r *userRepository) List(ctx context.Context, query *protocol.SearchRequest
9398
return users, total, nil
9499
}
95100

101+
func (r *userRepository) Get(ctx context.Context, id string) (*core.User, error) {
102+
var rows []scimUser
103+
104+
err := r.db.WithContext(ctx).RawQuery("SELECT id, resource, active, created_at, updated_at FROM scim_users WHERE sso_provider_id = ? AND deleted_at IS NULL AND id = ?", r.tenant(ctx), id).All(&rows)
105+
if err != nil {
106+
return nil, fmt.Errorf("scim: reading user: %w", err)
107+
}
108+
109+
if len(rows) == 0 {
110+
return nil, ErrNotFound
111+
}
112+
return r.mapFrom(&rows[0])
113+
}
114+
96115
func (r *userRepository) Create(ctx context.Context, user *core.User) (*core.User, error) {
97116
resource, err := r.toResource(user)
98117
if err != nil {

0 commit comments

Comments
 (0)