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 committed Oct 3, 2024
1 parent e9507a8 commit 4307c7c
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 28 deletions.
32 changes: 16 additions & 16 deletions api/routes/api-key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -321,8 +321,8 @@ func TestListAPIKey(t *testing.T) {
CreatedBy: "507f1f77bcf86cd799439011",
TenantID: "00000000-0000-4000-0000-000000000000",
Role: "admin",
CreatedAt: time.Date(2023, 01, 01, 12, 00, 00, 00, time.UTC),
UpdatedAt: time.Date(2023, 01, 01, 12, 00, 00, 00, time.UTC),
CreatedAt: time.Date(2023, 0o1, 0o1, 12, 0o0, 0o0, 0o0, time.UTC),
UpdatedAt: time.Date(2023, 0o1, 0o1, 12, 0o0, 0o0, 0o0, time.UTC),
ExpiresIn: 0,
},
},
Expand All @@ -338,8 +338,8 @@ func TestListAPIKey(t *testing.T) {
CreatedBy: "507f1f77bcf86cd799439011",
TenantID: "00000000-0000-4000-0000-000000000000",
Role: "admin",
CreatedAt: time.Date(2023, 01, 01, 12, 00, 00, 00, time.UTC),
UpdatedAt: time.Date(2023, 01, 01, 12, 00, 00, 00, time.UTC),
CreatedAt: time.Date(2023, 0o1, 0o1, 12, 0o0, 0o0, 0o0, time.UTC),
UpdatedAt: time.Date(2023, 0o1, 0o1, 12, 0o0, 0o0, 0o0, time.UTC),
ExpiresIn: 0,
},
},
Expand Down Expand Up @@ -379,8 +379,8 @@ func TestListAPIKey(t *testing.T) {
CreatedBy: "507f1f77bcf86cd799439011",
TenantID: "00000000-0000-4000-0000-000000000000",
Role: "admin",
CreatedAt: time.Date(2023, 01, 01, 12, 00, 00, 00, time.UTC),
UpdatedAt: time.Date(2023, 01, 01, 12, 00, 00, 00, time.UTC),
CreatedAt: time.Date(2023, 0o1, 0o1, 12, 0o0, 0o0, 0o0, time.UTC),
UpdatedAt: time.Date(2023, 0o1, 0o1, 12, 0o0, 0o0, 0o0, time.UTC),
ExpiresIn: 0,
},
},
Expand All @@ -396,8 +396,8 @@ func TestListAPIKey(t *testing.T) {
CreatedBy: "507f1f77bcf86cd799439011",
TenantID: "00000000-0000-4000-0000-000000000000",
Role: "admin",
CreatedAt: time.Date(2023, 01, 01, 12, 00, 00, 00, time.UTC),
UpdatedAt: time.Date(2023, 01, 01, 12, 00, 00, 00, time.UTC),
CreatedAt: time.Date(2023, 0o1, 0o1, 12, 0o0, 0o0, 0o0, time.UTC),
UpdatedAt: time.Date(2023, 0o1, 0o1, 12, 0o0, 0o0, 0o0, time.UTC),
ExpiresIn: 0,
},
},
Expand Down Expand Up @@ -435,8 +435,8 @@ func TestListAPIKey(t *testing.T) {
CreatedBy: "507f1f77bcf86cd799439011",
TenantID: "00000000-0000-4000-0000-000000000000",
Role: "admin",
CreatedAt: time.Date(2023, 01, 01, 12, 00, 00, 00, time.UTC),
UpdatedAt: time.Date(2023, 01, 01, 12, 00, 00, 00, time.UTC),
CreatedAt: time.Date(2023, 0o1, 0o1, 12, 0o0, 0o0, 0o0, time.UTC),
UpdatedAt: time.Date(2023, 0o1, 0o1, 12, 0o0, 0o0, 0o0, time.UTC),
ExpiresIn: 0,
},
},
Expand All @@ -452,8 +452,8 @@ func TestListAPIKey(t *testing.T) {
CreatedBy: "507f1f77bcf86cd799439011",
TenantID: "00000000-0000-4000-0000-000000000000",
Role: "admin",
CreatedAt: time.Date(2023, 01, 01, 12, 00, 00, 00, time.UTC),
UpdatedAt: time.Date(2023, 01, 01, 12, 00, 00, 00, time.UTC),
CreatedAt: time.Date(2023, 0o1, 0o1, 12, 0o0, 0o0, 0o0, time.UTC),
UpdatedAt: time.Date(2023, 0o1, 0o1, 12, 0o0, 0o0, 0o0, time.UTC),
ExpiresIn: 0,
},
},
Expand Down Expand Up @@ -492,8 +492,8 @@ func TestListAPIKey(t *testing.T) {
CreatedBy: "507f1f77bcf86cd799439011",
TenantID: "00000000-0000-4000-0000-000000000000",
Role: "admin",
CreatedAt: time.Date(2023, 01, 01, 12, 00, 00, 00, time.UTC),
UpdatedAt: time.Date(2023, 01, 01, 12, 00, 00, 00, time.UTC),
CreatedAt: time.Date(2023, 0o1, 0o1, 12, 0o0, 0o0, 0o0, time.UTC),
UpdatedAt: time.Date(2023, 0o1, 0o1, 12, 0o0, 0o0, 0o0, time.UTC),
ExpiresIn: 0,
},
},
Expand All @@ -509,8 +509,8 @@ func TestListAPIKey(t *testing.T) {
CreatedBy: "507f1f77bcf86cd799439011",
TenantID: "00000000-0000-4000-0000-000000000000",
Role: "admin",
CreatedAt: time.Date(2023, 01, 01, 12, 00, 00, 00, time.UTC),
UpdatedAt: time.Date(2023, 01, 01, 12, 00, 00, 00, time.UTC),
CreatedAt: time.Date(2023, 0o1, 0o1, 12, 0o0, 0o0, 0o0, time.UTC),
UpdatedAt: time.Date(2023, 0o1, 0o1, 12, 0o0, 0o0, 0o0, time.UTC),
ExpiresIn: 0,
},
},
Expand Down
1 change: 1 addition & 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
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
120 changes: 111 additions & 9 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 Expand Up @@ -1469,7 +1571,7 @@ func TestAddNamespaceMember(t *testing.T) {
ID: "000000000000000000000001",
Role: authorizer.RoleAdministrator,
Status: models.MemberStatusPending,
ExpiresAt: time.Date(2023, 01, 01, 12, 00, 00, 00, time.UTC),
ExpiresAt: time.Date(2023, 0o1, 0o1, 12, 0o0, 0o0, 0o0, time.UTC),
},
},
}, nil).
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 4307c7c

Please sign in to comment.