Skip to content

Commit

Permalink
fix(api): namespace creation not permitted for user
Browse files Browse the repository at this point in the history
  • Loading branch information
haller33 authored and gustavosbarreto committed Oct 4, 2024
1 parent d124588 commit 6fe009b
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 11 deletions.
2 changes: 2 additions & 0 deletions api/services/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ func (s *service) AuthUser(ctx context.Context, req *requests.UserAuth, sourceIP
Tenant: tenantID,
Role: role,
Token: token,
MaxNamespaces: user.MaxNamespaces,
}

return res, 0, "", nil
Expand Down Expand Up @@ -351,6 +352,7 @@ func (s *service) CreateUserToken(ctx context.Context, req *requests.CreateUserT
Tenant: namespace.TenantID,
Role: memberInfo.Role.String(),
Token: token,
MaxNamespaces: user.MaxNamespaces,
}, nil
}

Expand Down
6 changes: 6 additions & 0 deletions api/services/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ var (
ErrAuthInvalid = errors.New("auth invalid", ErrLayer, ErrCodeInvalid)
ErrAuthUnathorized = errors.New("auth unauthorized", ErrLayer, ErrCodeUnauthorized)
ErrNamespaceLimitReached = errors.New("namespace limit reached", ErrLayer, ErrCodeLimit)
ErrNamespaceCreationIsForbidden = errors.New("namespace creation not permitted for user", ErrLayer, ErrCodeForbidden)
ErrDeviceRemovedCount = errors.New("device removed count", ErrLayer, ErrCodeNotFound)
ErrDeviceRemovedInsert = errors.New("device removed insert", ErrLayer, ErrCodeStore)
ErrDeviceRemovedFull = errors.New("device removed full", ErrLayer, ErrCodePayment)
Expand Down Expand Up @@ -432,6 +433,11 @@ func NewErrNamespaceLimitReached(limit int, err error) error {
return NewErrLimit(ErrNamespaceLimitReached, limit, err)
}

// NewErrNamespaceCreationIsForbidden a error, since user have no permition to add a new namespace
func NewErrNamespaceCreationIsForbidden(limit int, err error) error {
return NewErrLimit(ErrNamespaceCreationIsForbidden, limit, err)
}

func NewErrDeviceRemovedCount(next error) error {
return NewErrInvalid(ErrDeviceRemovedCount, nil, next)
}
Expand Down
8 changes: 5 additions & 3 deletions api/services/namespace.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,11 @@ func (s *service) CreateNamespace(ctx context.Context, req *requests.NamespaceCr
return nil, NewErrUserNotFound(req.UserID, err)
}

// When MaxNamespaces is less than or equal to zero, it means that the user has no limit
// of namespaces.
if user.MaxNamespaces > 0 {
// When MaxNamespaces is less than zero, it means that the user has no limit
// of namespaces. If the value is zero, it means he has no right to create a new namespace
if user.MaxNamespaces == 0 {
return nil, NewErrNamespaceCreationIsForbidden(user.MaxNamespaces, nil)
} else if user.MaxNamespaces > 0 {
info, err := s.store.UserGetInfo(ctx, req.UserID)
switch {
case err != nil:
Expand Down
118 changes: 110 additions & 8 deletions api/services/namespace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,27 @@ func TestCreateNamespace(t *testing.T) {
err: NewErrUserNotFound("000000000000000000000000", store.ErrNoDocuments),
},
},
{
description: "fails when user reachs the zero namespaces",
req: &requests.NamespaceCreate{
UserID: "000000000000000000000000",
Name: "namespace",
TenantID: "00000000-0000-4000-0000-000000000000",
},
requiredMocks: func() {
storeMock.
On("UserGetByID", ctx, "000000000000000000000000", false).
Return(&models.User{
ID: "000000000000000000000000",
MaxNamespaces: 0,
}, 0, nil).
Once()
},
expected: Expected{
ns: nil,
err: NewErrNamespaceCreationIsForbidden(0, nil),
},
},
{
description: "fails when user reachs the max namespaces",
req: &requests.NamespaceCreate{
Expand All @@ -412,7 +433,10 @@ func TestCreateNamespace(t *testing.T) {
requiredMocks: func() {
storeMock.
On("UserGetByID", ctx, "000000000000000000000000", false).
Return(&models.User{ID: "000000000000000000000000", MaxNamespaces: 1}, 0, nil).
Return(&models.User{
ID: "000000000000000000000000",
MaxNamespaces: 1,
}, 0, nil).
Once()
storeMock.
On("UserGetInfo", ctx, "000000000000000000000000").
Expand All @@ -438,9 +462,22 @@ func TestCreateNamespace(t *testing.T) {
TenantID: "00000000-0000-4000-0000-000000000000",
},
requiredMocks: func() {
storeMock.
On("UserGetInfo", ctx, "000000000000000000000000").
Return(
&models.UserInfo{
OwnedNamespaces: []models.Namespace{{}},
AssociatedNamespaces: []models.Namespace{},
},
nil,
).
Once()
storeMock.
On("UserGetByID", ctx, "000000000000000000000000", false).
Return(&models.User{ID: "000000000000000000000000"}, 0, nil).
Return(&models.User{
ID: "000000000000000000000000",
MaxNamespaces: 3,
}, 0, nil).
Once()
storeMock.
On("NamespaceGetByName", ctx, "namespace").
Expand All @@ -460,9 +497,22 @@ func TestCreateNamespace(t *testing.T) {
TenantID: "00000000-0000-4000-0000-000000000000",
},
requiredMocks: func() {
storeMock.
On("UserGetInfo", ctx, "000000000000000000000000").
Return(
&models.UserInfo{
OwnedNamespaces: []models.Namespace{{}},
AssociatedNamespaces: []models.Namespace{},
},
nil,
).
Once()
storeMock.
On("UserGetByID", ctx, "000000000000000000000000", false).
Return(&models.User{ID: "000000000000000000000000"}, 0, nil).
Return(&models.User{
ID: "000000000000000000000000",
MaxNamespaces: 3,
}, 0, nil).
Once()
storeMock.
On("NamespaceGetByName", ctx, "namespace").
Expand All @@ -482,9 +532,22 @@ func TestCreateNamespace(t *testing.T) {
TenantID: "00000000-0000-4000-0000-000000000000",
},
requiredMocks: func() {
storeMock.
On("UserGetInfo", ctx, "000000000000000000000000").
Return(
&models.UserInfo{
OwnedNamespaces: []models.Namespace{{}},
AssociatedNamespaces: []models.Namespace{},
},
nil,
).
Once()
storeMock.
On("UserGetByID", ctx, "000000000000000000000000", false).
Return(&models.User{ID: "000000000000000000000000"}, 0, nil).
Return(&models.User{
ID: "000000000000000000000000",
MaxNamespaces: 3,
}, 0, nil).
Once()
storeMock.
On("NamespaceGetByName", ctx, "namespace").
Expand Down Expand Up @@ -545,9 +608,22 @@ func TestCreateNamespace(t *testing.T) {
TenantID: "00000000-0000-4000-0000-000000000000",
},
requiredMocks: func() {
storeMock.
On("UserGetInfo", ctx, "000000000000000000000000").
Return(
&models.UserInfo{
OwnedNamespaces: []models.Namespace{{}},
AssociatedNamespaces: []models.Namespace{},
},
nil,
).
Once()
storeMock.
On("UserGetByID", ctx, "000000000000000000000000", false).
Return(&models.User{ID: "000000000000000000000000"}, 0, nil).
Return(&models.User{
ID: "000000000000000000000000",
MaxNamespaces: 3,
}, 0, nil).
Once()
storeMock.
On("NamespaceGetByName", ctx, "namespace").
Expand Down Expand Up @@ -645,9 +721,22 @@ func TestCreateNamespace(t *testing.T) {
TenantID: "",
},
requiredMocks: func() {
storeMock.
On("UserGetInfo", ctx, "000000000000000000000000").
Return(
&models.UserInfo{
OwnedNamespaces: []models.Namespace{{}},
AssociatedNamespaces: []models.Namespace{},
},
nil,
).
Once()
storeMock.
On("UserGetByID", ctx, "000000000000000000000000", false).
Return(&models.User{ID: "000000000000000000000000"}, 0, nil).
Return(&models.User{
ID: "000000000000000000000000",
MaxNamespaces: 3,
}, 0, nil).
Once()
storeMock.
On("NamespaceGetByName", ctx, "namespace").
Expand Down Expand Up @@ -749,15 +838,28 @@ func TestCreateNamespace(t *testing.T) {
TenantID: "00000000-0000-4000-0000-000000000000",
},
requiredMocks: func() {
storeMock.
On("UserGetInfo", ctx, "000000000000000000000000").
Return(
&models.UserInfo{
OwnedNamespaces: []models.Namespace{{}},
AssociatedNamespaces: []models.Namespace{},
},
nil,
).
Once()
// envs.IsCommunity = false
storeMock.
On("UserGetByID", ctx, "000000000000000000000000", false).
Return(&models.User{ID: "000000000000000000000000"}, 0, nil).
Return(&models.User{
ID: "000000000000000000000000",
MaxNamespaces: 3,
}, 0, nil).
Once()
storeMock.
On("NamespaceGetByName", ctx, "namespace").
Return(nil, store.ErrNoDocuments).
Once()
// envs.IsCommunity = false
envMock.
On("Get", "SHELLHUB_CLOUD").
Return("true").
Expand Down
1 change: 1 addition & 0 deletions pkg/models/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ type UserAuthResponse struct {
RecoveryEmail string `json:"recovery_email"`
Role string `json:"role"`
MFA bool `json:"mfa"`
MaxNamespaces int `json:"max_namespaces"`
}

// NOTE: This struct has been moved to the cloud repo as it is only used in a cloud context;
Expand Down

0 comments on commit 6fe009b

Please sign in to comment.