Skip to content

Commit

Permalink
add capabilities endpoint (#1359)
Browse files Browse the repository at this point in the history
* add capabilities endpoint
---------

Co-authored-by: Ales Stimec <[email protected]>
  • Loading branch information
SimoneDutto and alesstimec authored Sep 11, 2024
1 parent 5728cfa commit 07eed59
Show file tree
Hide file tree
Showing 6 changed files with 184 additions and 3 deletions.
1 change: 1 addition & 0 deletions internal/rebac_admin/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ func SetupBackend(ctx context.Context, jimm jujuapi.JIMM) (*rebac_handlers.ReBAC
Groups: newGroupService(jimm),
Identities: newidentitiesService(jimm),
Resources: newResourcesService(jimm),
Capabilities: newCapabilitiesService(),
})
if err != nil {
zapctx.Error(ctx, "failed to create rebac admin backend", zap.Error(err))
Expand Down
116 changes: 116 additions & 0 deletions internal/rebac_admin/capabilities.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Copyright 2024 Canonical.

package rebac_admin

import (
"context"

"github.com/canonical/rebac-admin-ui-handlers/v1/interfaces"
"github.com/canonical/rebac-admin-ui-handlers/v1/resources"
)

// capabilitiesService implements the `CapabilitiesService` interface.
type capabilitiesService struct {
}

// For doc/test sake, to hint that the struct needs to implement a specific interface.
var _ interfaces.CapabilitiesService = &capabilitiesService{}

func newCapabilitiesService() *capabilitiesService {
return &capabilitiesService{}
}

var capabilities = []resources.Capability{
{
Endpoint: "/swagger.json",
Methods: []resources.CapabilityMethods{
"GET",
},
},
{
Endpoint: "/capabilities",
Methods: []resources.CapabilityMethods{
"GET",
},
},
{
Endpoint: "/identities",
Methods: []resources.CapabilityMethods{
"GET",
"POST",
},
},
{
Endpoint: "/identities/{id}",
Methods: []resources.CapabilityMethods{
"GET",
},
},
{
Endpoint: "/identities/{id}/groups",
Methods: []resources.CapabilityMethods{
"GET",
"PATCH",
},
},
{
Endpoint: "/identities/{id}/entitlements",
Methods: []resources.CapabilityMethods{
"GET",
"PATCH",
},
},
{
Endpoint: "/groups",
Methods: []resources.CapabilityMethods{
"GET",
"POST",
},
},
{
Endpoint: "/groups/{id}",
Methods: []resources.CapabilityMethods{
"GET",
"PUT",
"DELETE",
},
},
{
Endpoint: "/groups/{id}/identities",
Methods: []resources.CapabilityMethods{
"GET",
"PATCH",
},
},
{
Endpoint: "/groups/{id}/entitlements",
Methods: []resources.CapabilityMethods{
"GET",
"PATCH",
},
},
{
Endpoint: "/entitlements",
Methods: []resources.CapabilityMethods{
"GET",
},
},
{
Endpoint: "/entitlements/raw",
Methods: []resources.CapabilityMethods{
"GET",
},
},
{
Endpoint: "/resources",
Methods: []resources.CapabilityMethods{
"GET",
},
},
}

// ListCapabilities returns a list of capabilities supported by this service.
func (s *capabilitiesService) ListCapabilities(ctx context.Context) ([]resources.Capability, error) {

return capabilities, nil
}
55 changes: 55 additions & 0 deletions internal/rebac_admin/capabilities_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright 2024 Canonical.

package rebac_admin_test

import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"testing"

qt "github.com/frankban/quicktest"

"github.com/canonical/jimm/v3/internal/jimmtest"
"github.com/canonical/jimm/v3/internal/rebac_admin"
)

// test capabilities are reachable
func TestCapabilities(t *testing.T) {
c := qt.New(t)
jimm := jimmtest.JIMM{}
ctx := context.Background()
handlers, err := rebac_admin.SetupBackend(ctx, &jimm)
c.Assert(err, qt.IsNil)
testServer := httptest.NewServer(handlers.Handler(""))
defer testServer.Close()

// test not found endpoint
url := fmt.Sprintf("%s/v1%s", testServer.URL, "/not-found")
req, err := http.NewRequest("GET", url, nil)
c.Assert(err, qt.IsNil)
resp, err := http.DefaultClient.Do(req)
c.Assert(err, qt.IsNil)
defer resp.Body.Close()
c.Assert(resp.StatusCode, qt.Equals, 404)

// test endpoints in capabilities are found
for _, cap := range rebac_admin.Capabilities {
for _, m := range cap.Methods {
c.Run(fmt.Sprintf("%s %s", m, cap.Endpoint), func(c *qt.C) {
url := fmt.Sprintf("%s/v1%s", testServer.URL, cap.Endpoint)
req, err := http.NewRequest(string(m), url, nil)
c.Assert(err, qt.IsNil)
resp, err := http.DefaultClient.Do(req)
c.Assert(err, qt.IsNil)
defer resp.Body.Close()
// 404 is for not found endpoints and 501 is for "not implemented" endpoints in the rebac-admin-ui-handlers library
isNotFound := resp.StatusCode == 404 || resp.StatusCode == 501
c.Assert(isNotFound, qt.IsFalse)
})

}
}

}
1 change: 1 addition & 0 deletions internal/rebac_admin/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ var (
NewGroupService = newGroupService
NewidentitiesService = newidentitiesService
NewResourcesService = newResourcesService
Capabilities = capabilities
)

type GroupsService = groupsService
11 changes: 9 additions & 2 deletions internal/rebac_admin/identities.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"go.uber.org/zap"

"github.com/canonical/jimm/v3/internal/common/pagination"
"github.com/canonical/jimm/v3/internal/errors"
"github.com/canonical/jimm/v3/internal/jujuapi"
"github.com/canonical/jimm/v3/internal/openfga"
ofganames "github.com/canonical/jimm/v3/internal/openfga/names"
Expand Down Expand Up @@ -74,7 +75,10 @@ func (s *identitiesService) CreateIdentity(ctx context.Context, identity *resour
func (s *identitiesService) GetIdentity(ctx context.Context, identityId string) (*resources.Identity, error) {
user, err := s.jimm.FetchIdentity(ctx, identityId)
if err != nil {
return nil, v1.NewNotFoundError(fmt.Sprintf("User with id %s not found", identityId))
if errors.ErrorCode(err) == errors.CodeNotFound {
return nil, v1.NewNotFoundError(fmt.Sprintf("User with id %s not found", identityId))
}
return nil, err
}
identity := utils.FromUserToIdentity(*user)
return &identity, nil
Expand Down Expand Up @@ -190,7 +194,10 @@ func (s *identitiesService) GetIdentityEntitlements(ctx context.Context, identit
}
objUser, err := s.jimm.FetchIdentity(ctx, identityId)
if err != nil {
return nil, v1.NewNotFoundError(fmt.Sprintf("User with id %s not found", identityId))
if errors.ErrorCode(err) == errors.CodeNotFound {
return nil, v1.NewNotFoundError(fmt.Sprintf("User with id %s not found", identityId))
}
return nil, err
}

filter := utils.CreateTokenPaginationFilter(params.Size, params.NextToken, params.NextPageToken)
Expand Down
3 changes: 2 additions & 1 deletion internal/rebac_admin/identities_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/canonical/jimm/v3/internal/common/pagination"
"github.com/canonical/jimm/v3/internal/common/utils"
"github.com/canonical/jimm/v3/internal/dbmodel"
jimmm_errors "github.com/canonical/jimm/v3/internal/errors"
"github.com/canonical/jimm/v3/internal/jimmtest"
"github.com/canonical/jimm/v3/internal/jimmtest/mocks"
"github.com/canonical/jimm/v3/internal/openfga"
Expand All @@ -29,7 +30,7 @@ func TestGetIdentity(t *testing.T) {
if username == "[email protected]" {
return openfga.NewUser(&dbmodel.Identity{Name: "[email protected]"}, nil), nil
}
return nil, dbmodel.IdentityCreationError
return nil, jimmm_errors.E(jimmm_errors.CodeNotFound)
},
}
user := openfga.User{}
Expand Down

0 comments on commit 07eed59

Please sign in to comment.