Skip to content

Commit a48b448

Browse files
stanleyphukkajla12
andauthored
Allow colons in object id (#108)
* Update warrant object ids to accept colons * Update user and tenant ids to accept colons * Remove unused ObjectSpec and StringToObjectSpec method * Add valid_object_id validation to client provided id attribute for tenant, user, role, permission, pricing-tier, and feature * Update validation error messages for valid_object_type, valid_object_id, and valid_inherit_if validations --------- Co-authored-by: Karan Kajla <[email protected]>
1 parent c24e6d8 commit a48b448

File tree

12 files changed

+57
-101
lines changed

12 files changed

+57
-101
lines changed

pkg/authz/feature/spec.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
)
99

1010
type FeatureSpec struct {
11-
FeatureId string `json:"featureId" validate:"required"`
11+
FeatureId string `json:"featureId" validate:"required,valid_object_id"`
1212
Name *string `json:"name"`
1313
Description *string `json:"description"`
1414
CreatedAt time.Time `json:"createdAt"`

pkg/authz/permission/spec.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
)
99

1010
type PermissionSpec struct {
11-
PermissionId string `json:"permissionId" validate:"required"`
11+
PermissionId string `json:"permissionId" validate:"required,valid_object_id"`
1212
Name *string `json:"name"`
1313
Description *string `json:"description"`
1414
CreatedAt time.Time `json:"createdAt"`

pkg/authz/pricingtier/spec.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
)
99

1010
type PricingTierSpec struct {
11-
PricingTierId string `json:"pricingTierId" validate:"required"`
11+
PricingTierId string `json:"pricingTierId" validate:"required,valid_object_id"`
1212
Name *string `json:"name"`
1313
Description *string `json:"description"`
1414
CreatedAt time.Time `json:"createdAt"`

pkg/authz/role/spec.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
)
99

1010
type RoleSpec struct {
11-
RoleId string `json:"roleId" validate:"required"`
11+
RoleId string `json:"roleId" validate:"required,valid_object_id"`
1212
Name *string `json:"name"`
1313
Description *string `json:"description"`
1414
CreatedAt time.Time `json:"createdAt"`

pkg/authz/tenant/service.go

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ package tenant
22

33
import (
44
"context"
5-
"regexp"
65

76
"github.com/google/uuid"
7+
"github.com/pkg/errors"
88
object "github.com/warrant-dev/warrant/pkg/authz/object"
99
objecttype "github.com/warrant-dev/warrant/pkg/authz/objecttype"
1010
"github.com/warrant-dev/warrant/pkg/event"
@@ -31,13 +31,17 @@ func NewService(env service.Env, repository TenantRepository, eventSvc event.Eve
3131
}
3232

3333
func (svc TenantService) Create(ctx context.Context, tenantSpec TenantSpec) (*TenantSpec, error) {
34-
err := validateOrGenerateTenantIdInSpec(&tenantSpec)
35-
if err != nil {
36-
return nil, err
34+
if tenantSpec.TenantId == "" {
35+
// generate an id for the tenant if one isn't supplied
36+
generatedUUID, err := uuid.NewRandom()
37+
if err != nil {
38+
return nil, errors.New("unable to generate random UUID for tenant")
39+
}
40+
tenantSpec.TenantId = generatedUUID.String()
3741
}
3842

3943
var newTenant Model
40-
err = svc.Env().DB().WithinTransaction(ctx, func(txCtx context.Context) error {
44+
err := svc.Env().DB().WithinTransaction(ctx, func(txCtx context.Context) error {
4145
createdObject, err := svc.ObjectSvc.Create(txCtx, *tenantSpec.ToObjectSpec())
4246
if err != nil {
4347
switch err.(type) {
@@ -152,21 +156,3 @@ func (svc TenantService) DeleteByTenantId(ctx context.Context, tenantId string)
152156

153157
return nil
154158
}
155-
156-
func validateOrGenerateTenantIdInSpec(tenantSpec *TenantSpec) error {
157-
tenantIdRegExp := regexp.MustCompile(`^[a-zA-Z0-9_\-\.@\|]+$`)
158-
if tenantSpec.TenantId != "" {
159-
// Validate tenantId if provided
160-
if !tenantIdRegExp.Match([]byte(tenantSpec.TenantId)) {
161-
return service.NewInvalidParameterError("tenantId", "must be provided and can only contain alphanumeric characters and/or '-', '_', '@', and '|'")
162-
}
163-
} else {
164-
// Generate a TenantId for the tenant if one isn't supplied
165-
generatedUUID, err := uuid.NewRandom()
166-
if err != nil {
167-
return service.NewInternalError("unable to generate random UUID for tenant")
168-
}
169-
tenantSpec.TenantId = generatedUUID.String()
170-
}
171-
return nil
172-
}

pkg/authz/tenant/spec.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
)
99

1010
type TenantSpec struct {
11-
TenantId string `json:"tenantId"`
11+
TenantId string `json:"tenantId" validate:"omitempty,valid_object_id"`
1212
Name *string `json:"name"`
1313
CreatedAt time.Time `json:"createdAt"`
1414
}

pkg/authz/user/service.go

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ package authz
22

33
import (
44
"context"
5-
"regexp"
65

76
"github.com/google/uuid"
7+
"github.com/pkg/errors"
88
object "github.com/warrant-dev/warrant/pkg/authz/object"
99
objecttype "github.com/warrant-dev/warrant/pkg/authz/objecttype"
1010
"github.com/warrant-dev/warrant/pkg/event"
@@ -31,13 +31,17 @@ func NewService(env service.Env, repository UserRepository, eventSvc event.Event
3131
}
3232

3333
func (svc UserService) Create(ctx context.Context, userSpec UserSpec) (*UserSpec, error) {
34-
err := validateOrGenerateUserIdInSpec(&userSpec)
35-
if err != nil {
36-
return nil, err
34+
if userSpec.UserId == "" {
35+
// generate an id for the user if one isn't provided
36+
generatedUUID, err := uuid.NewRandom()
37+
if err != nil {
38+
return nil, errors.New("unable to generate random UUID for user")
39+
}
40+
userSpec.UserId = generatedUUID.String()
3741
}
3842

3943
var newUser Model
40-
err = svc.Env().DB().WithinTransaction(ctx, func(txCtx context.Context) error {
44+
err := svc.Env().DB().WithinTransaction(ctx, func(txCtx context.Context) error {
4145
createdObject, err := svc.ObjectSvc.Create(txCtx, *userSpec.ToObjectSpec())
4246
if err != nil {
4347
switch err.(type) {
@@ -149,21 +153,3 @@ func (svc UserService) DeleteByUserId(ctx context.Context, userId string) error
149153

150154
return err
151155
}
152-
153-
func validateOrGenerateUserIdInSpec(userSpec *UserSpec) error {
154-
userIdRegExp := regexp.MustCompile(`^[a-zA-Z0-9_\-\.@\|]+$`)
155-
if userSpec.UserId != "" {
156-
// Validate userId if provided
157-
if !userIdRegExp.Match([]byte(userSpec.UserId)) {
158-
return service.NewInvalidParameterError("userId", "must be provided and can only contain alphanumeric characters and/or '-', '_', '@', and '|'")
159-
}
160-
} else {
161-
// Generate a UserID for the user if one isn't supplied
162-
generatedUUID, err := uuid.NewRandom()
163-
if err != nil {
164-
return service.NewInternalError("unable to generate random UUID for user")
165-
}
166-
userSpec.UserId = generatedUUID.String()
167-
}
168-
return nil
169-
}

pkg/authz/user/spec.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
)
99

1010
type UserSpec struct {
11-
UserId string `json:"userId"`
11+
UserId string `json:"userId" validate:"omitempty,valid_object_id"`
1212
Email *string `json:"email" validate:"omitempty,email"`
1313
CreatedAt time.Time `json:"createdAt"`
1414
}

pkg/authz/warrant/spec.go

Lines changed: 13 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -25,24 +25,6 @@ type SortOptions struct {
2525
IsAscending bool
2626
}
2727

28-
type ObjectSpec struct {
29-
ObjectType string `json:"objectType" validate:"required,valid_object_type"`
30-
ObjectId string `json:"objectId" validate:"required,valid_object_id"`
31-
}
32-
33-
func StringToObjectSpec(str string) (*ObjectSpec, error) {
34-
objectTypeId := strings.Split(str, ":")
35-
36-
if len(objectTypeId) != 2 {
37-
return nil, fmt.Errorf("invalid object")
38-
}
39-
40-
return &ObjectSpec{
41-
ObjectType: objectTypeId[0],
42-
ObjectId: objectTypeId[1],
43-
}, nil
44-
}
45-
4628
type SubjectSpec struct {
4729
ObjectType string `json:"objectType,omitempty" validate:"required_with=ObjectId,valid_object_type"`
4830
ObjectId string `json:"objectId,omitempty" validate:"required_with=ObjectType,valid_object_id"`
@@ -60,23 +42,25 @@ func (spec *SubjectSpec) String() string {
6042
func StringToSubjectSpec(str string) (*SubjectSpec, error) {
6143
objectRelation := strings.Split(str, "#")
6244
if len(objectRelation) < 2 {
63-
objectTypeId := strings.Split(str, ":")
45+
objectType, objectId, colonFound := strings.Cut(str, ":")
6446

65-
if len(objectTypeId) != 2 {
47+
if !colonFound {
6648
return nil, fmt.Errorf("invalid subject")
6749
}
6850

6951
return &SubjectSpec{
70-
ObjectType: objectTypeId[0],
71-
ObjectId: objectTypeId[1],
52+
ObjectType: objectType,
53+
ObjectId: objectId,
7254
}, nil
7355
}
7456

7557
object := objectRelation[0]
7658
relation := objectRelation[1]
77-
objectTypeId := strings.Split(object, ":")
78-
objectType := objectTypeId[0]
79-
objectId := objectTypeId[1]
59+
60+
objectType, objectId, colonFound := strings.Cut(object, ":")
61+
if !colonFound {
62+
return nil, fmt.Errorf("invalid subject")
63+
}
8064

8165
subjectSpec := &SubjectSpec{
8266
ObjectType: objectType,
@@ -147,8 +131,8 @@ func StringToWarrantSpec(warrantString string) (*WarrantSpec, error) {
147131
return nil, fmt.Errorf("invalid warrant")
148132
}
149133

150-
objectTypeAndObjectId := strings.Split(objectAndRelation[0], ":")
151-
if len(objectTypeAndObjectId) != 2 {
134+
objectType, objectId, colonFound := strings.Cut(objectAndRelation[0], ":")
135+
if !colonFound {
152136
return nil, fmt.Errorf("invalid warrant")
153137
}
154138

@@ -176,8 +160,8 @@ func StringToWarrantSpec(warrantString string) (*WarrantSpec, error) {
176160
}
177161

178162
return &WarrantSpec{
179-
ObjectType: objectTypeAndObjectId[0],
180-
ObjectId: objectTypeAndObjectId[1],
163+
ObjectType: objectType,
164+
ObjectId: objectId,
181165
Relation: objectAndRelation[1],
182166
Subject: subjectSpec,
183167
Context: contextSetSpec,

pkg/service/json.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ func validObjectId(fl validator.FieldLevel) bool {
5858
return true
5959
}
6060

61-
regExp := regexp.MustCompile(`^[a-zA-Z0-9_\-\.@\|]+$`)
61+
regExp := regexp.MustCompile(`^[a-zA-Z0-9_\-\.@\|:]+$`)
6262
return regExp.Match([]byte(value))
6363
}
6464

@@ -183,11 +183,11 @@ func ValidateStruct(obj interface{}) error {
183183
validValues := strings.Join(strings.Split(err.Param(), " "), ", ")
184184
return NewInvalidParameterError(fieldName, fmt.Sprintf("must be one of %s", validValues))
185185
case "valid_object_type", "valid_relation":
186-
return NewInvalidParameterError(fieldName, "must be provided and can only contain lower-case alphanumeric characters and/or '-' and '_'")
186+
return NewInvalidParameterError(fieldName, "can only contain lower-case alphanumeric characters and/or '-' and '_'")
187187
case "valid_object_id":
188-
return NewInvalidParameterError(fieldName, "must be provided and can only contain alphanumeric characters and/or '-', '_', '@', and '|'")
188+
return NewInvalidParameterError(fieldName, "can only contain alphanumeric characters and/or '-', '_', '@', ':', and '|'")
189189
case "valid_inheritif":
190-
return NewInvalidParameterError(fieldName, "must be provided and can only be 'anyOf', 'allOf', 'noneOf', or a valid relation name")
190+
return NewInvalidParameterError(fieldName, "can only be 'anyOf', 'allOf', 'noneOf', or a valid relation name")
191191
default:
192192
return NewInvalidRequestError("Invalid request body")
193193
}

tests/warrants.json

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,15 +67,15 @@
6767
"method": "POST",
6868
"url": "/v1/permissions",
6969
"body": {
70-
"permissionId": "edit-balance-sheet",
70+
"permissionId": "balance-sheet:edit",
7171
"name": "Edit Balance Sheet",
7272
"description": "Grants access to edit the balance sheet."
7373
}
7474
},
7575
"expectedResponse": {
7676
"statusCode": 200,
7777
"body": {
78-
"permissionId": "edit-balance-sheet",
78+
"permissionId": "balance-sheet:edit",
7979
"name": "Edit Balance Sheet",
8080
"description": "Grants access to edit the balance sheet."
8181
}
@@ -116,7 +116,7 @@
116116
"url": "/v1/warrants",
117117
"body": {
118118
"objectType": "permission",
119-
"objectId": "edit-balance-sheet",
119+
"objectId": "balance-sheet:edit",
120120
"relation": "member",
121121
"subject": {
122122
"objectType": "user",
@@ -128,7 +128,7 @@
128128
"statusCode": 200,
129129
"body": {
130130
"objectType": "permission",
131-
"objectId": "edit-balance-sheet",
131+
"objectId": "balance-sheet:edit",
132132
"relation": "member",
133133
"subject": {
134134
"objectType": "user",
@@ -203,7 +203,7 @@
203203
"url": "/v1/warrants",
204204
"body": {
205205
"objectType": "permission",
206-
"objectId": "edit-balance-sheet",
206+
"objectId": "balance-sheet:edit",
207207
"relation": "member",
208208
"subject": {
209209
"objectType": "user",
@@ -238,9 +238,9 @@
238238
"name": "deletePermissionEditBalanceSheet",
239239
"request": {
240240
"method": "DELETE",
241-
"url": "/v1/permissions/edit-balance-sheet",
241+
"url": "/v1/permissions/balance-sheet:edit",
242242
"body": {
243-
"permissionId": "edit-balance-sheet"
243+
"permissionId": "balance-sheet:edit"
244244
}
245245
},
246246
"expectedResponse": {

tests/zz-events.json

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,17 +51,17 @@
5151
"type": "deleted",
5252
"source": "api",
5353
"resourceType": "permission",
54-
"resourceId": "edit-balance-sheet"
54+
"resourceId": "balance-sheet:edit"
5555
},
5656
{
5757
"type": "created",
5858
"source": "api",
5959
"resourceType": "permission",
60-
"resourceId": "edit-balance-sheet",
60+
"resourceId": "balance-sheet:edit",
6161
"meta": {
6262
"description": "Grants access to edit the balance sheet.",
6363
"name": "Edit Balance Sheet",
64-
"permissionId": "edit-balance-sheet"
64+
"permissionId": "balance-sheet:edit"
6565
}
6666
}
6767
],
@@ -114,7 +114,7 @@
114114
"type": "deleted",
115115
"source": "api",
116116
"resourceType": "permission",
117-
"resourceId": "edit-balance-sheet"
117+
"resourceId": "balance-sheet:edit"
118118
},
119119
{
120120
"type": "deleted",
@@ -252,7 +252,7 @@
252252
"type": "access_revoked",
253253
"source": "api",
254254
"objectType": "permission",
255-
"objectId": "edit-balance-sheet",
255+
"objectId": "balance-sheet:edit",
256256
"relation": "member",
257257
"subjectType": "user",
258258
"subjectId": "user-a"
@@ -294,7 +294,7 @@
294294
"type": "access_granted",
295295
"source": "api",
296296
"objectType": "permission",
297-
"objectId": "edit-balance-sheet",
297+
"objectId": "balance-sheet:edit",
298298
"relation": "member",
299299
"subjectType": "user",
300300
"subjectId": "user-a"
@@ -340,7 +340,7 @@
340340
"type": "access_granted",
341341
"source": "api",
342342
"objectType": "permission",
343-
"objectId": "edit-balance-sheet",
343+
"objectId": "balance-sheet:edit",
344344
"relation": "member",
345345
"subjectType": "user",
346346
"subjectId": "user-a"
@@ -562,7 +562,7 @@
562562
"type": "access_revoked",
563563
"source": "api",
564564
"objectType": "permission",
565-
"objectId": "edit-balance-sheet",
565+
"objectId": "balance-sheet:edit",
566566
"relation": "member",
567567
"subjectType": "user",
568568
"subjectId": "user-a"
@@ -589,7 +589,7 @@
589589
"type": "access_granted",
590590
"source": "api",
591591
"objectType": "permission",
592-
"objectId": "edit-balance-sheet",
592+
"objectId": "balance-sheet:edit",
593593
"relation": "member",
594594
"subjectType": "user",
595595
"subjectId": "user-a"

0 commit comments

Comments
 (0)