Skip to content

Commit

Permalink
feat: add urm client to tenant package (#19198)
Browse files Browse the repository at this point in the history
  • Loading branch information
AlirieGray authored Aug 5, 2020
1 parent 79f6196 commit e82f2d9
Show file tree
Hide file tree
Showing 7 changed files with 262 additions and 13 deletions.
127 changes: 127 additions & 0 deletions tenant/http_client_urm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package tenant

import (
"context"
"path"

"github.com/influxdata/influxdb/v2"
"github.com/influxdata/influxdb/v2/pkg/httpc"
)

type UserResourceMappingClient struct {
Client *httpc.Client
}

// CreateUserResourceMapping will create a user resource mapping
func (s *UserResourceMappingClient) CreateUserResourceMapping(ctx context.Context, m *influxdb.UserResourceMapping) error {
if err := m.Validate(); err != nil {
return err
}

urlPath := resourceIDPath(m.ResourceType, m.ResourceID, string(m.UserType)+"s")
return s.Client.
PostJSON(influxdb.User{ID: m.UserID}, urlPath).
DecodeJSON(m).
Do(ctx)
}

// FindUserResourceMappings returns the user resource mappings
func (s *UserResourceMappingClient) FindUserResourceMappings(ctx context.Context, f influxdb.UserResourceMappingFilter, opt ...influxdb.FindOptions) ([]*influxdb.UserResourceMapping, int, error) {
var results resourceUsersResponse
err := s.Client.
Get(resourceIDPath(f.ResourceType, f.ResourceID, string(f.UserType)+"s")).
DecodeJSON(&results).
Do(ctx)
if err != nil {
return nil, 0, err
}

urs := make([]*influxdb.UserResourceMapping, len(results.Users))
for k, item := range results.Users {
urs[k] = &influxdb.UserResourceMapping{
ResourceID: f.ResourceID,
ResourceType: f.ResourceType,
UserID: item.User.ID,
UserType: item.Role,
}
}
return urs, len(urs), nil
}

// DeleteUserResourceMapping will delete user resource mapping based in criteria.
func (s *UserResourceMappingClient) DeleteUserResourceMapping(ctx context.Context, resourceID influxdb.ID, userID influxdb.ID) error {
urlPath := resourceIDUserPath(influxdb.OrgsResourceType, resourceID, influxdb.Member, userID)
return s.Client.
Delete(urlPath).
Do(ctx)
}

// SpecificURMSvc returns a urm service with specific resource and user types.
// this will help us stay compatible with the existing service contract but also allow for urm deletes to go through the correct
// api
func (s *UserResourceMappingClient) SpecificURMSvc(rt influxdb.ResourceType, ut influxdb.UserType) *SpecificURMSvc {
return &SpecificURMSvc{
Client: s.Client,
rt: rt,
ut: ut,
}
}

// SpecificURMSvc is a URM client that speaks to a specific resource with a specified user type
type SpecificURMSvc struct {
Client *httpc.Client
rt influxdb.ResourceType
ut influxdb.UserType
}

// FindUserResourceMappings returns the user resource mappings
func (s *SpecificURMSvc) FindUserResourceMappings(ctx context.Context, f influxdb.UserResourceMappingFilter, opt ...influxdb.FindOptions) ([]*influxdb.UserResourceMapping, int, error) {
var results resourceUsersResponse
err := s.Client.
Get(resourceIDPath(s.rt, f.ResourceID, string(s.ut)+"s")).
DecodeJSON(&results).
Do(ctx)
if err != nil {
return nil, 0, err
}

urs := make([]*influxdb.UserResourceMapping, len(results.Users))
for k, item := range results.Users {
urs[k] = &influxdb.UserResourceMapping{
ResourceID: f.ResourceID,
ResourceType: f.ResourceType,
UserID: item.User.ID,
UserType: item.Role,
}
}
return urs, len(urs), nil
}

// CreateUserResourceMapping will create a user resource mapping
func (s *SpecificURMSvc) CreateUserResourceMapping(ctx context.Context, m *influxdb.UserResourceMapping) error {
if err := m.Validate(); err != nil {
return err
}

urlPath := resourceIDPath(s.rt, m.ResourceID, string(s.ut)+"s")
return s.Client.
PostJSON(influxdb.User{ID: m.UserID}, urlPath).
DecodeJSON(m).
Do(ctx)
}

// DeleteUserResourceMapping will delete user resource mapping based in criteria.
func (s *SpecificURMSvc) DeleteUserResourceMapping(ctx context.Context, resourceID influxdb.ID, userID influxdb.ID) error {
urlPath := resourceIDUserPath(s.rt, resourceID, s.ut, userID)
return s.Client.
Delete(urlPath).
Do(ctx)
}

func resourceIDPath(resourceType influxdb.ResourceType, resourceID influxdb.ID, p string) string {
return path.Join("/api/v2/", string(resourceType), resourceID.String(), p)
}

func resourceIDUserPath(resourceType influxdb.ResourceType, resourceID influxdb.ID, userType influxdb.UserType, userID influxdb.ID) string {
return path.Join("/api/v2/", string(resourceType), resourceID.String(), string(userType)+"s", userID.String())
}
6 changes: 3 additions & 3 deletions tenant/http_client_user.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ type UserClientService struct {

// FindMe returns user information about the owner of the token
func (s *UserClientService) FindMe(ctx context.Context, id influxdb.ID) (*influxdb.User, error) {
var res userResponse
var res UserResponse
err := s.Client.
Get(prefixMe).
DecodeJSON(&res).
Expand All @@ -31,7 +31,7 @@ func (s *UserClientService) FindMe(ctx context.Context, id influxdb.ID) (*influx

// FindUserByID returns a single user by ID.
func (s *UserClientService) FindUserByID(ctx context.Context, id influxdb.ID) (*influxdb.User, error) {
var res userResponse
var res UserResponse
err := s.Client.
Get(prefixUsers, id.String()).
DecodeJSON(&res).
Expand Down Expand Up @@ -105,7 +105,7 @@ func (s *UserClientService) CreateUser(ctx context.Context, u *influxdb.User) er
// UpdateUser updates a single user with changeset.
// Returns the new user state after update.
func (s *UserClientService) UpdateUser(ctx context.Context, id influxdb.ID, upd influxdb.UserUpdate) (*influxdb.User, error) {
var res userResponse
var res UserResponse
err := s.Client.
PatchJSON(upd, prefixUsers, id.String()).
DecodeJSON(&res).
Expand Down
4 changes: 2 additions & 2 deletions tenant/http_handler_urm.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,13 +231,13 @@ func (h *urmHandler) decodeDeleteRequest(ctx context.Context, r *http.Request) (

type resourceUserResponse struct {
Role influxdb.UserType `json:"role"`
*userResponse
*UserResponse
}

func newResourceUserResponse(u *influxdb.User, userType influxdb.UserType) *resourceUserResponse {
return &resourceUserResponse{
Role: userType,
userResponse: newUserResponse(u),
UserResponse: newUserResponse(u),
}
}

Expand Down
122 changes: 122 additions & 0 deletions tenant/http_handler_urm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ import (
"github.com/go-chi/chi"
"github.com/google/go-cmp/cmp"
"github.com/influxdata/influxdb/v2"
ihttp "github.com/influxdata/influxdb/v2/http"
"github.com/influxdata/influxdb/v2/mock"
"github.com/influxdata/influxdb/v2/tenant"
itesting "github.com/influxdata/influxdb/v2/testing"
"go.uber.org/zap/zaptest"
)

Expand Down Expand Up @@ -369,3 +371,123 @@ func TestUserResourceMappingService_PostMembersHandler(t *testing.T) {
}
}
}

func TestUserResourceMappingService_Client(t *testing.T) {
type fields struct {
userService influxdb.UserService
userResourceMappingService influxdb.UserResourceMappingService
}
type args struct {
resourceID string
userType influxdb.UserType
user influxdb.User
}
tests := []struct {
name string
fields fields
args args
}{
{
name: "post members",
fields: fields{
userService: &mock.UserService{
FindUserByIDFn: func(ctx context.Context, id influxdb.ID) (*influxdb.User, error) {
return &influxdb.User{ID: id, Name: fmt.Sprintf("user%s", id), Status: influxdb.Active}, nil
},
},
userResourceMappingService: &mock.UserResourceMappingService{
CreateMappingFn: func(ctx context.Context, m *influxdb.UserResourceMapping) error {
return nil
},
FindMappingsFn: func(ctx context.Context, f influxdb.UserResourceMappingFilter) ([]*influxdb.UserResourceMapping, int, error) {
return []*influxdb.UserResourceMapping{&influxdb.UserResourceMapping{}}, 1, nil
},
},
},
args: args{
resourceID: "0000000000000099",
user: influxdb.User{
ID: 1,
Name: "user0000000000000001",
Status: influxdb.Active,
},
userType: influxdb.Member,
},
},

{
name: "post owners",
fields: fields{
userService: &mock.UserService{
FindUserByIDFn: func(ctx context.Context, id influxdb.ID) (*influxdb.User, error) {
return &influxdb.User{ID: id, Name: fmt.Sprintf("user%s", id), Status: influxdb.Active}, nil
},
},
userResourceMappingService: &mock.UserResourceMappingService{
CreateMappingFn: func(ctx context.Context, m *influxdb.UserResourceMapping) error {
return nil
},
FindMappingsFn: func(ctx context.Context, f influxdb.UserResourceMappingFilter) ([]*influxdb.UserResourceMapping, int, error) {
return []*influxdb.UserResourceMapping{&influxdb.UserResourceMapping{}}, 1, nil
},
},
},
args: args{
resourceID: "0000000000000099",
user: influxdb.User{
ID: 2,
Name: "user0000000000000002",
Status: influxdb.Active,
},
userType: influxdb.Owner,
},
},
}

for _, tt := range tests {
resourceTypes := []influxdb.ResourceType{
influxdb.BucketsResourceType,
influxdb.DashboardsResourceType,
influxdb.OrgsResourceType,
influxdb.SourcesResourceType,
influxdb.TasksResourceType,
influxdb.TelegrafsResourceType,
influxdb.UsersResourceType,
}

for _, resourceType := range resourceTypes {
t.Run(tt.name+"_"+string(resourceType), func(t *testing.T) {
// create server
h := tenant.NewURMHandler(zaptest.NewLogger(t), resourceType, "id", tt.fields.userService, tt.fields.userResourceMappingService)
router := chi.NewRouter()
router.Mount(fmt.Sprintf("/api/v2/%s/{id}/members", resourceType), h)
router.Mount(fmt.Sprintf("/api/v2/%s/{id}/owners", resourceType), h)
s := httptest.NewServer(router)
defer s.Close()
ctx := context.Background()

resourceID := itesting.MustIDBase16(tt.args.resourceID)
urm := &influxdb.UserResourceMapping{ResourceType: resourceType, ResourceID: resourceID, UserType: tt.args.userType, UserID: tt.args.user.ID}

httpClient, err := ihttp.NewHTTPClient(s.URL, "", false)
if err != nil {
t.Fatal(err)
}
c := tenant.UserResourceMappingClient{Client: httpClient}
err = c.CreateUserResourceMapping(ctx, urm)

if err != nil {
t.Fatal(err)
}

_, n, err := c.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{ResourceID: resourceID, ResourceType: resourceType, UserType: tt.args.userType})
if err != nil {
t.Fatal(err)
}
if n != 1 {
t.Fatalf("expected 1 urm to be created, got: %d", n)
}
})
}
}
}
2 changes: 1 addition & 1 deletion tenant/http_server_onboarding.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ func (h *OnboardHandler) handleOnboardRequest(w http.ResponseWriter, r *http.Req
}

type onboardingResponse struct {
User *userResponse `json:"user"`
User *UserResponse `json:"user"`
Bucket *bucketResponse `json:"bucket"`
Organization orgResponse `json:"org"`
Auth *authResponse `json:"auth"`
Expand Down
12 changes: 6 additions & 6 deletions tenant/http_server_user.go
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ func decodeDeleteUserRequest(ctx context.Context, r *http.Request) (*deleteUserR

type usersResponse struct {
Links map[string]string `json:"links"`
Users []*userResponse `json:"users"`
Users []*UserResponse `json:"users"`
}

func (us usersResponse) ToInfluxdb() []*influxdb.User {
Expand All @@ -367,22 +367,22 @@ func newUsersResponse(users []*influxdb.User) *usersResponse {
Links: map[string]string{
"self": "/api/v2/users",
},
Users: []*userResponse{},
Users: []*UserResponse{},
}
for _, user := range users {
res.Users = append(res.Users, newUserResponse(user))
}
return &res
}

// userResponse is the response of user
type userResponse struct {
// UserResponse is the response of user
type UserResponse struct {
Links map[string]string `json:"links"`
influxdb.User
}

func newUserResponse(u *influxdb.User) *userResponse {
return &userResponse{
func newUserResponse(u *influxdb.User) *UserResponse {
return &UserResponse{
Links: map[string]string{
"self": fmt.Sprintf("/api/v2/users/%s", u.ID),
},
Expand Down
2 changes: 1 addition & 1 deletion tenant/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func (ts *Service) NewOrgHTTPHandler(log *zap.Logger, secretSvc influxdb.SecretS
}

func (ts *Service) NewBucketHTTPHandler(log *zap.Logger, labelSvc influxdb.LabelService) *BucketHandler {
urmHandler := NewURMHandler(log.With(zap.String("handler", "urm")), influxdb.OrgsResourceType, "id", ts.UserService, NewAuthedURMService(ts.OrganizationService, ts.UserResourceMappingService))
urmHandler := NewURMHandler(log.With(zap.String("handler", "urm")), influxdb.BucketsResourceType, "id", ts.UserService, NewAuthedURMService(ts.OrganizationService, ts.UserResourceMappingService))
labelHandler := label.NewHTTPEmbeddedHandler(log.With(zap.String("handler", "label")), influxdb.BucketsResourceType, labelSvc)
return NewHTTPBucketHandler(log.With(zap.String("handler", "bucket")), NewAuthedBucketService(ts.BucketService), labelSvc, urmHandler, labelHandler)
}
Expand Down

0 comments on commit e82f2d9

Please sign in to comment.