From 9cf37ba403dc27864265471213e0943e829d5c06 Mon Sep 17 00:00:00 2001 From: Ishan Arya Date: Wed, 12 Jun 2024 17:55:41 +0530 Subject: [PATCH 1/2] feat[WIP]: add list users to servicedata service --- core/servicedata/errors.go | 7 +-- core/servicedata/service.go | 53 +++++++++++++++++++ core/servicedata/servicedata.go | 11 ++++ .../store/postgres/servicedata_repository.go | 16 ++++++ 4 files changed, 84 insertions(+), 3 deletions(-) diff --git a/core/servicedata/errors.go b/core/servicedata/errors.go index bdd077d71..eaf83bec9 100644 --- a/core/servicedata/errors.go +++ b/core/servicedata/errors.go @@ -3,7 +3,8 @@ package servicedata import "errors" var ( - ErrInvalidDetail = errors.New("invalid service data detail") - ErrConflict = errors.New("key already exist") - ErrNotExist = errors.New("service data not exist") + ErrInvalidDetail = errors.New("invalid service data detail") + ErrConflict = errors.New("key already exist") + ErrNotExist = errors.New("service data not exist") + ErrProjectMissing = errors.New("project details missing") ) diff --git a/core/servicedata/service.go b/core/servicedata/service.go index 2d661b196..4b3051702 100644 --- a/core/servicedata/service.go +++ b/core/servicedata/service.go @@ -2,6 +2,7 @@ package servicedata import ( "context" + "fmt" "slices" "github.com/goto/shield/core/action" @@ -11,6 +12,8 @@ import ( "github.com/goto/shield/core/resource" "github.com/goto/shield/core/user" "github.com/goto/shield/internal/schema" + "github.com/goto/shield/pkg/errors" + "github.com/goto/shield/pkg/uuid" ) const ( @@ -234,3 +237,53 @@ func (s Service) Get(ctx context.Context, filter Filter) ([]ServiceData, error) return resultSD, nil } + +func (s Service) ListUsers(ctx context.Context, filter ListUsersFilter) ([]user.User, error) { + // fetch current user + currentUser, err := s.userService.FetchCurrentUser(ctx) + if err != nil { + return []user.User{}, err + } + + if len(filter.ServiceData) > 0 && filter.Project == "" { + return []user.User{}, ErrProjectMissing + } + + servicedataKeyResourceIds := []string{} + + if len(filter.ServiceData) > 0 { + projectSlug := filter.Project + if uuid.IsValid(filter.Project) { + prj, err := s.projectService.Get(ctx, filter.Project) + if err != nil { + return []user.User{}, err + } + projectSlug = prj.Slug + } + for k, _ := range filter.ServiceData { + key, err := s.repository.GetKeyByURN(ctx, fmt.Sprintf("%s:servicedata_key:%s", projectSlug, k)) + if err != nil { + return []user.User{}, err + } + + permission, err := s.relationService.CheckPermission(ctx, currentUser, namespace.Namespace{ID: schema.ServiceDataKeyNamespace}, key.ResourceID, action.Action{ID: editActionID}) + if err != nil { + return []user.User{}, err + } + if !permission { + return []user.User{}, errors.ErrForbidden + } + + servicedataKeyResourceIds = append(servicedataKeyResourceIds, key.ResourceID) + } + } else if filter.WithServiceData { + servicedataKeyResourceIds, err = s.relationService.LookupResources(ctx, keyNamespace, viewActionID, userNamespace, currentUser.ID) + if err != nil { + return []user.User{}, err + } + } + + s.repository. + + return []user.User{}, nil +} diff --git a/core/servicedata/servicedata.go b/core/servicedata/servicedata.go index 5f68bafec..9fd98f603 100644 --- a/core/servicedata/servicedata.go +++ b/core/servicedata/servicedata.go @@ -3,6 +3,8 @@ package servicedata import ( "context" "fmt" + + "github.com/goto/shield/core/user" ) type Repository interface { @@ -11,6 +13,7 @@ type Repository interface { Upsert(ctx context.Context, servicedata ServiceData) (ServiceData, error) GetKeyByURN(ctx context.Context, URN string) (Key, error) Get(ctx context.Context, filter Filter) ([]ServiceData, error) + ListUsers(ctx context.Context, filter ListUsersFilter, servicedataKeyResourceIds []string) ([]user.User, error) } type Transactor interface { @@ -45,6 +48,14 @@ type Filter struct { Project string } +type ListUsersFilter struct { + ServiceData map[string]string + Project string + Limit int32 + Page int32 + WithServiceData bool +} + func (key Key) CreateURN() string { return fmt.Sprintf("%s:servicedata_key:%s", key.ProjectSlug, key.Key) } diff --git a/internal/store/postgres/servicedata_repository.go b/internal/store/postgres/servicedata_repository.go index 9502cf395..c6bf404e0 100644 --- a/internal/store/postgres/servicedata_repository.go +++ b/internal/store/postgres/servicedata_repository.go @@ -8,6 +8,7 @@ import ( "github.com/doug-martin/goqu/v9" "github.com/goto/shield/core/servicedata" + "github.com/goto/shield/core/user" "github.com/goto/shield/pkg/db" newrelic "github.com/newrelic/go-agent/v3/newrelic" "go.nhat.io/otelsql" @@ -244,6 +245,21 @@ func (r ServiceDataRepository) Get(ctx context.Context, filter servicedata.Filte return transformedServiceData, nil } +func (r ServiceDataRepository) ListUsers(ctx context.Context, filter servicedata.ListUsersFilter, servicedataKeyResourceIds []string) ([]user.User, error) { + var defaultLimit int32 = 50 + var defaultPage int32 = 1 + if filter.Limit < 1 { + filter.Limit = defaultLimit + } + if filter.Page < 1 { + filter.Page = defaultPage + } + + _ = (filter.Page - 1) * filter.Limit + + return []user.User{}, nil +} + func (r ServiceDataRepository) WithTransaction(ctx context.Context) context.Context { return r.dbc.WithTransaction(ctx, sql.TxOptions{}) } From 4243bac65baa98f5b6f150a2a79c1bbc2326562b Mon Sep 17 00:00:00 2001 From: Ishan Arya Date: Thu, 13 Jun 2024 15:23:30 +0530 Subject: [PATCH 2/2] feat --- core/servicedata/service.go | 12 ++++-- .../store/postgres/servicedata_repository.go | 41 ++++++++++++++++++- 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/core/servicedata/service.go b/core/servicedata/service.go index 4b3051702..1f9653927 100644 --- a/core/servicedata/service.go +++ b/core/servicedata/service.go @@ -273,8 +273,6 @@ func (s Service) ListUsers(ctx context.Context, filter ListUsersFilter) ([]user. if !permission { return []user.User{}, errors.ErrForbidden } - - servicedataKeyResourceIds = append(servicedataKeyResourceIds, key.ResourceID) } } else if filter.WithServiceData { servicedataKeyResourceIds, err = s.relationService.LookupResources(ctx, keyNamespace, viewActionID, userNamespace, currentUser.ID) @@ -282,8 +280,16 @@ func (s Service) ListUsers(ctx context.Context, filter ListUsersFilter) ([]user. return []user.User{}, err } } + fmt.Println(servicedataKeyResourceIds) - s.repository. + if len(filter.ServiceData) > 0 { + serviceData := [][]string{} + for k, v := range filter.ServiceData { + serviceData = append(serviceData, []string{k, v}) + } + + s.repository.ListUsersHavingData(ctx, filter, serviceData) + } return []user.User{}, nil } diff --git a/internal/store/postgres/servicedata_repository.go b/internal/store/postgres/servicedata_repository.go index c6bf404e0..3a4337775 100644 --- a/internal/store/postgres/servicedata_repository.go +++ b/internal/store/postgres/servicedata_repository.go @@ -9,6 +9,7 @@ import ( "github.com/doug-martin/goqu/v9" "github.com/goto/shield/core/servicedata" "github.com/goto/shield/core/user" + "github.com/goto/shield/internal/schema" "github.com/goto/shield/pkg/db" newrelic "github.com/newrelic/go-agent/v3/newrelic" "go.nhat.io/otelsql" @@ -245,7 +246,7 @@ func (r ServiceDataRepository) Get(ctx context.Context, filter servicedata.Filte return transformedServiceData, nil } -func (r ServiceDataRepository) ListUsers(ctx context.Context, filter servicedata.ListUsersFilter, servicedataKeyResourceIds []string) ([]user.User, error) { +func (r ServiceDataRepository) ListUsersHavingData(ctx context.Context, filter servicedata.ListUsersFilter, serviceData [][]string) ([]user.User, error) { var defaultLimit int32 = 50 var defaultPage int32 = 1 if filter.Limit < 1 { @@ -257,6 +258,44 @@ func (r ServiceDataRepository) ListUsers(ctx context.Context, filter servicedata _ = (filter.Page - 1) * filter.Limit + subquery := dialect.Select("sd.entity_id").From(goqu.T(TABLE_SERVICE_DATA).As("sd")). + Join(goqu.T(TABLE_SERVICE_DATA_KEYS).As("sk"), goqu.On( + goqu.I("sk.id").Eq(goqu.I("sd.key_id")))). + Where(goqu.Ex{"sk.project_id": filter.Project}, + goqu.Ex{"sd.namespace_id": schema.UserPrincipal}, + goqu.L( + "(sk.key, sd.value)", + ).In(serviceData)). + GroupBy(goqu.I("sd.entity_id")) + + // Main query + query, params, err := dialect.Select( + goqu.I("u.id"), + goqu.I("u.name"), + goqu.I("u.email"), + goqu.I("u.created_at"), + goqu.I("u.updated_at"), + ).From(goqu.T(TABLE_USERS).As("u")).Join(subquery.As("sd"), goqu.On( + goqu.Cast(goqu.C("id"), "TEXT").Eq(goqu.I("sd.entity_id")))).ToSQL() + if err != nil { + return []user.User{}, nil + } + + var users []User + if err = r.dbc.WithTimeout(ctx, func(ctx context.Context) error { + return r.dbc.SelectContext(ctx, &users, query, params...) + }); err != nil { + err = checkPostgresError(err) + switch { + case errors.Is(err, sql.ErrNoRows): + return []user.User{}, nil + case errors.Is(err, errInvalidTexRepresentation): + return []user.User{}, servicedata.ErrInvalidDetail + default: + return []user.User{}, err + } + } + return []user.User{}, nil }