Merge pull request #12 from paralus/imporove-coverage

Imporove coverage
This commit is contained in:
Nirav Parikh
2022-06-17 20:11:21 +05:30
committed by GitHub
14 changed files with 978 additions and 49 deletions
+2 -2
View File
@@ -14,7 +14,7 @@ func GetProjectNamespaces(ctx context.Context, db bun.IDB, projectID uuid.UUID)
var panr []models.ProjectAccountNamespaceRole
err := db.NewSelect().Model(&panr).Where("project_id = ?", projectID).Where("trash = ?", false).Scan(ctx)
if err != sql.ErrNoRows {
if err != nil && err != sql.ErrNoRows {
return nil, err
}
for _, nr := range panr {
@@ -23,7 +23,7 @@ func GetProjectNamespaces(ctx context.Context, db bun.IDB, projectID uuid.UUID)
var pgnr []models.ProjectGroupNamespaceRole
err = db.NewSelect().Model(&pgnr).Where("project_id = ?", projectID).Where("trash = ?", false).Scan(ctx)
if err != sql.ErrNoRows {
if err != nil && err != sql.ErrNoRows {
return nil, err
}
for _, nr := range pgnr {
+122
View File
@@ -0,0 +1,122 @@
package service
import (
"context"
"testing"
"github.com/DATA-DOG/go-sqlmock"
"github.com/google/uuid"
userrpcv3 "github.com/paralus/paralus/proto/rpc/user"
)
func TestApiKeyCreate(t *testing.T) {
db, mock := getDB(t)
defer db.Close()
ak := NewApiKeyService(db, getLogger())
uuuid := uuid.NewString()
auuid := uuid.NewString()
req := &userrpcv3.ApiKeyRequest{Username: "user-" + uuuid, Id: uuuid}
// mocks
mock.ExpectQuery(`INSERT INTO "authsrv_apikey"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow(auuid, "apikey-"+auuid))
resp, err := ak.Create(context.Background(), req)
if err != nil {
t.Error("unable to create apikey:", err)
}
if resp.ID != uuid.MustParse(auuid) {
t.Errorf("incorrect id for apikey; expected '%v', got '%v'", uuid.MustParse(auuid), resp.ID)
}
if resp.Name != "apikey-"+auuid {
t.Errorf("incorrect name for apikey; expected '%v', got '%v'", "apikey-"+auuid, resp.Name)
}
}
func TestApiKeyDelete(t *testing.T) {
db, mock := getDB(t)
defer db.Close()
ak := NewApiKeyService(db, getLogger())
uuuid := uuid.NewString()
req := &userrpcv3.ApiKeyRequest{Username: "user-" + uuuid, Id: uuuid}
// mocks
mock.ExpectExec(`UPDATE "authsrv_apikey" AS "apikey" SET trash = TRUE WHERE \(account_id = 'user-` + uuuid + `'\) AND \(key = '` + uuuid + `'\)`).
WillReturnResult(sqlmock.NewResult(1, 1))
_, err := ak.Delete(context.Background(), req)
if err != nil {
t.Error("unable to delete apikey:", err)
}
}
func TestApiKeyGet(t *testing.T) {
db, mock := getDB(t)
defer db.Close()
ak := NewApiKeyService(db, getLogger())
uuuid := uuid.NewString()
req := &userrpcv3.ApiKeyRequest{Username: "user-" + uuuid, Id: uuuid}
// mocks
mock.ExpectQuery(`SELECT "apikey"."id", "apikey"."name", .*FROM "authsrv_apikey" AS "apikey" WHERE \(name = 'user-` + uuuid + `'\) AND \(trash = FALSE\)`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow(uuuid, "user-"+uuuid))
resp, err := ak.Get(context.Background(), req)
if err != nil {
t.Error("unable to get apikey:", err)
}
if resp.ID != uuid.MustParse(uuuid) {
t.Errorf("incorrect id for apikey; expected '%v', got '%v'", uuid.MustParse(uuuid), resp.ID)
}
if resp.Name != "user-"+uuuid {
t.Errorf("incorrect name for apikey; expected '%v', got '%v'", "apikey-"+uuuid, resp.Name)
}
}
func TestApiKeyGetByKey(t *testing.T) {
db, mock := getDB(t)
defer db.Close()
ak := NewApiKeyService(db, getLogger())
uuuid := uuid.NewString()
req := &userrpcv3.ApiKeyRequest{Username: "user-" + uuuid, Id: uuuid}
// mocks
mock.ExpectQuery(`SELECT "apikey"."id", "apikey"."name", .*FROM "authsrv_apikey" AS "apikey" WHERE \(key = '` + uuuid + `'\) AND \(trash = FALSE\)`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow(uuuid, "user-"+uuuid))
resp, err := ak.GetByKey(context.Background(), req)
if err != nil {
t.Error("unable to get apikey:", err)
}
if resp.ID != uuid.MustParse(uuuid) {
t.Errorf("incorrect id for apikey; expected '%v', got '%v'", uuid.MustParse(uuuid), resp.ID)
}
if resp.Name != "user-"+uuuid {
t.Errorf("incorrect name for apikey; expected '%v', got '%v'", "apikey-"+uuuid, resp.Name)
}
}
func TestApiKeyList(t *testing.T) {
db, mock := getDB(t)
defer db.Close()
ak := NewApiKeyService(db, getLogger())
uuuid := uuid.NewString()
req := &userrpcv3.ApiKeyRequest{Username: "user-" + uuuid, Id: uuuid}
// mocks
mock.ExpectQuery(`SELECT "apikey"."id", "apikey"."name", .*FROM "authsrv_apikey" AS "apikey" WHERE \(account_id = 'user-` + uuuid + `'\) AND \(trash = FALSE\)`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow(uuuid, "user-"+uuuid))
resp, err := ak.List(context.Background(), req)
if err != nil {
t.Error("unable to list apikey:", err)
}
if resp.Items[0].Name != "user-"+uuuid {
t.Errorf("incorrect name for apikey; expected '%v', got '%v'", "apikey-"+uuuid, resp.Items[0].Name)
}
}
+1 -1
View File
@@ -11,7 +11,7 @@ import (
)
type AuditLogService struct {
auditQuery *ElasticSearchQuery
auditQuery ElasticSearchQuery
}
func NewAuditLogService(url string, auditPattern string, logPrefix string) (*AuditLogService, error) {
+145
View File
@@ -0,0 +1,145 @@
package service
import (
"bytes"
"encoding/json"
"strings"
"testing"
v1 "github.com/paralus/paralus/proto/rpc/audit"
v3 "github.com/paralus/paralus/proto/types/commonpb/v3"
)
type md struct {
Source []string `json:"_source"`
Aggs struct {
GroupByProject struct {
Aggs struct {
GroupByType struct {
Terms struct {
Field string `json:"field"`
Size int `json:"size"`
} `json:"terms"`
} `json:"group_by_type"`
GroupByUsername struct {
Terms struct {
Field string `json:"field"`
Size int `json:"size"`
} `json:"terms"`
} `json:"group_by_username"`
} `json:"aggs"`
Terms struct {
Field string `json:"field"`
Size int `json:"size"`
} `json:"terms"`
} `json:"group_by_project"`
GroupByType struct {
Terms struct {
Field string `json:"field"`
} `json:"terms"`
} `json:"group_by_type"`
GroupByUsername struct {
Terms struct {
Field string `json:"field"`
} `json:"terms"`
} `json:"group_by_username"`
} `json:"aggs"`
Query struct {
Bool struct {
Filter struct {
Range struct {
JSONTimestamp struct {
Gte string `json:"gte"`
Lt string `json:"lt"`
} `json:"json.timestamp"`
} `json:"range"`
} `json:"filter"`
Must []struct {
Term struct {
JSONCategory string `json:"json.category"`
} `json:"term,omitempty"`
Terms struct {
JSONProject []string `json:"json.project"`
} `json:"terms,omitempty"`
QueryString struct {
Query string `json:"query"`
} `json:"query_string,omitempty"`
} `json:"must"`
} `json:"bool"`
} `json:"query"`
Size int `json:"size"`
Sort struct {
JSONTimestamp struct {
Order string `json:"order"`
} `json:"json.timestamp"`
} `json:"sort"`
}
type mockElasticSearchQuery struct {
msg []bytes.Buffer
}
func (m *mockElasticSearchQuery) Handle(msg bytes.Buffer) (map[string]interface{}, error) {
m.msg = append(m.msg, msg)
return map[string]interface{}{}, nil
}
func TestGetAuditLogByProjectsSimple(t *testing.T) {
esq := &mockElasticSearchQuery{}
al := &AuditLogService{auditQuery: esq}
req := v1.AuditLogSearchRequest{
Filter: &v1.AuditLogQueryFilter{
QueryString: "query-string",
Projects: []string{"project-one", "project-two"},
Timefrom: "now-1h",
Type: "fake-type",
User: "fake-user",
Client: "fake-client",
DashboardData: true,
},
}
_, err := al.GetAuditLogByProjects(&req)
if err != nil {
t.Error("unable to get audit logs")
}
if len(esq.msg) != 1 {
t.Fatalf("incorrect number of searches; expected '%v', got '%v'", 1, len(esq.msg))
}
m := &md{}
err = json.Unmarshal(esq.msg[0].Bytes(), m)
if err != nil {
t.Fatal("unable to unmarshall es request")
}
expected := `{"_source":["json"],"aggs":{"group_by_project":{"aggs":{"group_by_type":{"terms":{"field":"json.type","size":1000}},"group_by_username":{"terms":{"field":"json.actor.account.username","size":1000}}},"terms":{"field":"json.project","size":1000}},"group_by_type":{"terms":{"field":"json.type"}},"group_by_username":{"terms":{"field":"json.actor.account.username"}}},"query":{"bool":{"filter":{"range":{"json.timestamp":{"gte":"now-1h","lt":"now"}}},"must":[{"term":{"json.category":"AUDIT"}},{"term":{"json.type":"fake-type"}},{"term":{"json.actor.account.username":"fake-user"}},{"term":{"json.client.type":"fake-client"}},{"terms":{"json.project":["project-one","project-two"]}},{"query_string":{"query":"query-string"}}]}},"size":0,"sort":{"json.timestamp":{"order":"desc"}}}`
if strings.TrimSpace(esq.msg[0].String()) != expected {
t.Errorf("incorrect es query; expected '%v', got '%v'", expected, strings.TrimSpace(esq.msg[0].String()))
}
}
func TestGetAuditLogByProjectsNoProject(t *testing.T) {
esq := &mockElasticSearchQuery{}
al := &AuditLogService{auditQuery: esq}
req := v1.AuditLogSearchRequest{
Metadata: &v3.Metadata{UrlScope: "url/project"},
Filter: &v1.AuditLogQueryFilter{
QueryString: "query-string",
},
}
_, err := al.GetAuditLog(&req)
if err != nil {
t.Error("unable to get audit logs", err)
}
if len(esq.msg) != 1 {
t.Fatalf("incorrect number of searches; expected '%v', got '%v'", 1, len(esq.msg))
}
m := &md{}
err = json.Unmarshal(esq.msg[0].Bytes(), m)
if err != nil {
t.Fatal("unable to unmarshall es request")
}
expected := `{"_source":["json"],"aggs":{"group_by_type":{"terms":{"field":"json.type"}},"group_by_username":{"terms":{"field":"json.actor.account.username"}}},"query":{"bool":{"must":[{"term":{"json.category":"AUDIT"}},{"terms":{"json.project":["project"]}},{"query_string":{"query":"query-string"}}]}},"size":500,"sort":{"json.timestamp":{"order":"desc"}}}`
if strings.TrimSpace(esq.msg[0].String()) != expected {
t.Errorf("incorrect es query; expected '%v', got '%v'", expected, strings.TrimSpace(esq.msg[0].String()))
}
}
+8 -4
View File
@@ -9,14 +9,18 @@ import (
v6Client "github.com/elastic/go-elasticsearch"
)
type ElasticSearchQuery struct {
type elasticSearchQuery struct {
url string
indexPattern string
logPrefix string
esClient *v6Client.Client
}
func NewElasticSearchQuery(url string, indexPattern string, logPrefix string) (*ElasticSearchQuery, error) {
type ElasticSearchQuery interface {
Handle(bytes.Buffer) (map[string]interface{}, error)
}
func NewElasticSearchQuery(url string, indexPattern string, logPrefix string) (ElasticSearchQuery, error) {
cfg := v6Client.Config{
Addresses: []string{
url,
@@ -33,7 +37,7 @@ func NewElasticSearchQuery(url string, indexPattern string, logPrefix string) (*
// return nil, err
// }
// _log.Infow(logPrefix+":Connected to elastic search ", "cluster", res, "index", indexPattern)
esQuery := &ElasticSearchQuery{
esQuery := &elasticSearchQuery{
url: url,
indexPattern: indexPattern,
logPrefix: logPrefix,
@@ -43,7 +47,7 @@ func NewElasticSearchQuery(url string, indexPattern string, logPrefix string) (*
}
//Handle Fires the search query
func (q *ElasticSearchQuery) Handle(msg bytes.Buffer) (map[string]interface{}, error) {
func (q *elasticSearchQuery) Handle(msg bytes.Buffer) (map[string]interface{}, error) {
_log.Debugw("Searching elastic search: ", "index", q.indexPattern, "url", q.url, "q", q)
res, err := q.esClient.Search(
q.esClient.Search.WithContext(context.Background()),
+79
View File
@@ -0,0 +1,79 @@
package service
import (
"context"
"testing"
"github.com/DATA-DOG/go-sqlmock"
"github.com/google/uuid"
)
func TestGetProjectNamespaces(t *testing.T) {
db, mock := getDB(t)
defer db.Close()
puuid := uuid.New()
mock.ExpectQuery(`SELECT "projectaccountnamespacerole"."id", "projectaccountnamespacerole"."name", "projectaccountnamespacerole"."description", "projectaccountnamespacerole"."created_at", "projectaccountnamespacerole"."modified_at", "projectaccountnamespacerole"."trash", "projectaccountnamespacerole"."organization_id", "projectaccountnamespacerole"."partner_id", "projectaccountnamespacerole"."role_id", "projectaccountnamespacerole"."account_id", "projectaccountnamespacerole"."project_id", "projectaccountnamespacerole"."namespace", "projectaccountnamespacerole"."active" FROM "authsrv_projectaccountnamespacerole" AS "projectaccountnamespacerole" WHERE \(project_id = '` + puuid.String() + `'\) AND \(trash = FALSE\)`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"namespace"}).AddRow("namespace1"))
mock.ExpectQuery(`SELECT "projectgroupnamespacerole"."id", "projectgroupnamespacerole"."name", "projectgroupnamespacerole"."description", "projectgroupnamespacerole"."created_at", "projectgroupnamespacerole"."modified_at", "projectgroupnamespacerole"."trash", "projectgroupnamespacerole"."organization_id", "projectgroupnamespacerole"."partner_id", "projectgroupnamespacerole"."role_id", "projectgroupnamespacerole"."group_id", "projectgroupnamespacerole"."project_id", "projectgroupnamespacerole"."namespace", "projectgroupnamespacerole"."active" FROM "authsrv_projectgroupnamespacerole" AS "projectgroupnamespacerole" WHERE \(project_id = '` + puuid.String() + `'\) AND \(trash = FALSE\)`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"namespace"}).AddRow("namespace2"))
ns := NewNamespaceService(db)
nl, err := ns.GetProjectNamespaces(context.Background(), puuid)
if err != nil {
t.Fatal("unable to get namespaces", err)
}
if len(nl) != 2 {
t.Errorf("incorrect number of namespaces; expected '%v', got '%v'", 2, len(nl))
}
if nl[0] != "namespace1" {
t.Errorf("incorrect namespace name; expected '%v', got '%v'", "namespace1", nl[0])
}
if nl[1] != "namespace2" {
t.Errorf("incorrect namespace name; expected '%v', got '%v'", "namespace2", nl[1])
}
}
func TestGetAccountProjectNamespaces(t *testing.T) {
db, mock := getDB(t)
defer db.Close()
puuid := uuid.New()
uuuid := uuid.New()
mock.ExpectQuery(`SELECT "projectaccountnamespacerole"."id", "projectaccountnamespacerole"."name", "projectaccountnamespacerole"."description", "projectaccountnamespacerole"."created_at", "projectaccountnamespacerole"."modified_at", "projectaccountnamespacerole"."trash", "projectaccountnamespacerole"."organization_id", "projectaccountnamespacerole"."partner_id", "projectaccountnamespacerole"."role_id", "projectaccountnamespacerole"."account_id", "projectaccountnamespacerole"."project_id", "projectaccountnamespacerole"."namespace", "projectaccountnamespacerole"."active" FROM "authsrv_projectaccountnamespacerole" AS "projectaccountnamespacerole" WHERE \(project_id = '` + puuid.String() + `'\) AND \(account_id = '` + uuuid.String() + `'\)`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"namespace"}).AddRow("namespace1"))
ns := NewNamespaceService(db)
nl, err := ns.GetAccountProjectNamespaces(context.Background(), puuid, uuuid)
if err != nil {
t.Fatal("unable to get namespaces", err)
}
if len(nl) != 1 {
t.Errorf("incorrect number of namespaces; expected '%v', got '%v'", 1, len(nl))
}
if nl[0] != "namespace1" {
t.Errorf("incorrect namespace name; expected '%v', got '%v'", "namespace1", nl[0])
}
}
func TestGetGroupProjectNamespaces(t *testing.T) {
db, mock := getDB(t)
defer db.Close()
puuid := uuid.New()
uuuid := uuid.New()
mock.ExpectQuery(`SELECT "projectgroupnamespacerole"."id", "projectgroupnamespacerole"."name", "projectgroupnamespacerole"."description", "projectgroupnamespacerole"."created_at", "projectgroupnamespacerole"."modified_at", "projectgroupnamespacerole"."trash", "projectgroupnamespacerole"."organization_id", "projectgroupnamespacerole"."partner_id", "projectgroupnamespacerole"."role_id", "projectgroupnamespacerole"."group_id", "projectgroupnamespacerole"."project_id", "projectgroupnamespacerole"."namespace", "projectgroupnamespacerole"."active" FROM "authsrv_projectgroupnamespacerole" AS "projectgroupnamespacerole" JOIN authsrv_groupaccount ON projectgroupnamespacerole.group_id=authsrv_groupaccount.group_id WHERE \(project_id = '+` + puuid.String() + `+'\) AND \(authsrv_groupaccount.account_id = '` + uuuid.String() + `'\) AND \(projectgroupnamespacerole.trash = FALSE\) AND \(authsrv_groupaccount.trash = FALSE\)`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"namespace"}).AddRow("namespace1"))
ns := NewNamespaceService(db)
nl, err := ns.GetGroupProjectNamespaces(context.Background(), puuid, uuuid)
if err != nil {
t.Fatal("unable to get namespaces", err)
}
if len(nl) != 1 {
t.Errorf("incorrect number of namespaces; expected '%v', got '%v'", 1, len(nl))
}
if nl[0] != "namespace1" {
t.Errorf("incorrect namespace name; expected '%v', got '%v'", "namespace1", nl[0])
}
}
+168 -23
View File
@@ -7,8 +7,10 @@ import (
"github.com/DATA-DOG/go-sqlmock"
"github.com/google/uuid"
"github.com/paralus/paralus/pkg/common"
v3 "github.com/paralus/paralus/proto/types/commonpb/v3"
systemv3 "github.com/paralus/paralus/proto/types/systempb/v3"
userv3 "github.com/paralus/paralus/proto/types/userpb/v3"
)
func performProjectBasicChecks(t *testing.T, project *systemv3.Project, puuid string) {
@@ -25,10 +27,8 @@ func TestCreateProject(t *testing.T) {
ps := NewProjectService(db, &mazc, getLogger(), true)
puuid := uuid.New().String()
ouuid := uuid.New().String()
mock.ExpectQuery(`SELECT "organization"."id", "organization"."name"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(ouuid))
addFetchExpectation(mock, "organization")
mock.ExpectBegin()
mock.ExpectQuery(`INSERT INTO "authsrv_project"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(puuid))
@@ -68,6 +68,52 @@ func TestCreateProjectDuplicate(t *testing.T) {
}
}
func TestCreateProjectFull(t *testing.T) {
db, mock := getDB(t)
defer db.Close()
mazc := mockAuthzClient{}
ps := NewProjectService(db, &mazc, getLogger(), true)
puuid := uuid.New().String()
addFetchExpectation(mock, "organization")
addFetchEmptyExpecteation(mock, "project") // not existing project
mock.ExpectBegin()
mock.ExpectQuery(`INSERT INTO "authsrv_project"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(puuid))
mock.ExpectQuery(`SELECT "resourcerole"."id", "resourcerole"."name", "resourcerole"."description", "resourcerole"."created_at", "resourcerole"."modified_at", "resourcerole"."trash", "resourcerole"."organization_id", "resourcerole"."partner_id", "resourcerole"."is_global", "resourcerole"."builtin", "resourcerole"."scope" FROM "authsrv_resourcerole" AS "resourcerole" WHERE \(name = 'test-role'\) AND \(trash = FALSE\)`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "name", "scope"}).AddRow(puuid, "resourcerole-"+puuid, "namespace"))
addFetchExpectation(mock, "group")
mock.ExpectQuery(`INSERT INTO "authsrv_projectgroupnamespacerole"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(uuid.NewString()))
mock.ExpectQuery(`SELECT "identities"."id".* FROM "identities" WHERE .*traits ->> 'email' = 'test-user'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "traits"}).AddRow(uuid.NewString(), []byte(`{"email":"test-user", "first_name": "John", "last_name": "Doe", "description": "The OG user."}`)))
mock.ExpectQuery(`SELECT "resourcerole"."id", "resourcerole"."name", "resourcerole"."description", "resourcerole"."created_at", "resourcerole"."modified_at", "resourcerole"."trash", "resourcerole"."organization_id", "resourcerole"."partner_id", "resourcerole"."is_global", "resourcerole"."builtin", "resourcerole"."scope" FROM "authsrv_resourcerole" AS "resourcerole" WHERE \(name = 'test-role'\) AND \(trash = FALSE\)`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "name", "scope"}).AddRow(puuid, "resourcerole-"+puuid, "namespace"))
mock.ExpectQuery(`INSERT INTO "authsrv_projectaccountnamespacerole"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(uuid.NewString()))
mock.ExpectCommit()
te := []string{"test-project", "test-namespace", "test-group", "test-role", "test-user"}
project := &systemv3.Project{
Metadata: &v3.Metadata{Id: puuid, Name: "project-" + puuid, Organization: "orgname"},
Spec: &systemv3.ProjectSpec{
ProjectNamespaceRoles: []*userv3.ProjectNamespaceRole{
{Project: &te[0], Namespace: &te[1], Group: &te[2], Role: te[3]},
},
UserRoles: []*userv3.UserRole{
{User: te[4], Role: te[3]},
},
},
}
project, err := ps.Create(context.Background(), project)
if err != nil {
t.Fatal("could not create project:", err)
}
performProjectBasicChecks(t, project, puuid)
}
func TestProjectDelete(t *testing.T) {
db, mock := getDB(t)
defer db.Close()
@@ -109,8 +155,7 @@ func TestProjectDeleteNonExist(t *testing.T) {
puuid := uuid.New().String()
mock.ExpectQuery(`SELECT "project"."id", "project"."name", .* FROM "authsrv_project" AS "project" WHERE`).
WithArgs().WillReturnError(fmt.Errorf("no data available"))
addFailingFetchExpecteation(mock, "project")
project := &systemv3.Project{
Metadata: &v3.Metadata{Id: puuid, Name: "project-" + puuid},
@@ -128,18 +173,10 @@ func TestProjectGetByName(t *testing.T) {
mazc := mockAuthzClient{}
ps := NewProjectService(db, &mazc, getLogger(), true)
partuuid := uuid.New().String()
ouuid := uuid.New().String()
puuid := uuid.New().String()
mock.ExpectQuery(`SELECT "project"."id", "project"."name"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "organization_id", "partner_id"}).AddRow(puuid, ouuid, partuuid))
mock.ExpectQuery(`SELECT "organization"."id", "organization"."name"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(ouuid))
mock.ExpectQuery(`SELECT "partner"."id", "partner"."name"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(partuuid))
addFetchExpectation(mock, "project")
addFetchExpectation(mock, "organization")
addFetchExpectation(mock, "partner")
mock.ExpectQuery(`SELECT distinct authsrv_resourcerole.name as role, authsrv_project.name as project, authsrv_group.name as group
FROM "authsrv_projectgrouprole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectgrouprole.role_id
@@ -176,10 +213,9 @@ func TestProjectGetById(t *testing.T) {
mazc := mockAuthzClient{}
ps := NewProjectService(db, &mazc, getLogger(), true)
puuid := uuid.New().String()
puuid := uuid.NewString()
mock.ExpectQuery(`SELECT "project"."id", "project"."name", .* FROM "authsrv_project" AS "project" WHERE .*id = '` + puuid + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow(puuid, "project-"+puuid))
addFetchByIdExpectation(mock, "project", puuid)
project := &systemv3.Project{
Metadata: &v3.Metadata{Id: puuid, Name: "project-" + puuid},
@@ -198,10 +234,7 @@ func TestProjectUpdate(t *testing.T) {
mazc := mockAuthzClient{}
ps := NewProjectService(db, &mazc, getLogger(), true)
puuid := uuid.New().String()
mock.ExpectQuery(`SELECT "project"."id", "project"."name", .* FROM "authsrv_project" AS "project" WHERE`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow(puuid, "project-"+puuid))
puuid := addFetchExpectation(mock, "project")
mock.ExpectBegin()
mock.ExpectExec(`UPDATE "authsrv_projectgrouprole" AS "projectgrouprole" SET trash = TRUE WHERE`).WillReturnResult(sqlmock.NewResult(1, 1))
mock.ExpectQuery(`UPDATE "authsrv_projectgroupnamespacerole" AS "projectgroupnamespacerole" SET trash = TRUE WHERE ."project_id" = '` + puuid + `'. AND .trash = false. RETURNING *`).
@@ -237,3 +270,115 @@ func TestProjectUpdate(t *testing.T) {
t.Fatal("could not update project:", err)
}
}
func TestProjectList(t *testing.T) {
db, mock := getDB(t)
defer db.Close()
mazc := mockAuthzClient{}
ps := NewProjectService(db, &mazc, getLogger(), true)
uid := uuid.NewString()
ouuid := addFetchExpectation(mock, "organization")
puuid := addFetchExpectation(mock, "partner")
mock.ExpectQuery(`SELECT "project"."id", "project"."name", .* FROM "authsrv_project" AS "project" WHERE \(partner_id = '` + puuid + `'\) AND \(organization_id = '` + ouuid + `'\) AND \(trash = false\)`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow(uid, "project-"+uid))
mock.ExpectQuery(`SELECT distinct authsrv_resourcerole.name as role, authsrv_project.name as project, authsrv_group.name as group FROM "authsrv_projectgrouprole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectgrouprole.role_id JOIN authsrv_group ON authsrv_group.id=authsrv_projectgrouprole.group_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectgrouprole.project_id WHERE \(authsrv_projectgrouprole.project_id = '` + uid + `'\)`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project", "group"}).AddRow("test-role1", "test-project1", "test-group1"))
mock.ExpectQuery(`SELECT distinct authsrv_resourcerole.name as role, authsrv_project.name as project, authsrv_group.name as group, namespace FROM "authsrv_projectgroupnamespacerole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectgroupnamespacerole.role_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectgroupnamespacerole.project_id JOIN authsrv_group ON authsrv_group.id=authsrv_projectgroupnamespacerole.group_id WHERE \(authsrv_projectgroupnamespacerole.project_id = '` + uid + `'\)`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project", "group", "namespace"}).AddRow("test-role2", "test-project2", "test-group2", "test-namespace2"))
mock.ExpectQuery(`SELECT distinct authsrv_resourcerole.name as role, identities.traits ->> 'email' as user FROM "authsrv_projectaccountresourcerole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectaccountresourcerole.role_id JOIN identities ON identities.id=authsrv_projectaccountresourcerole.account_id WHERE \(authsrv_projectaccountresourcerole.project_id = '` + uid + `'\)`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "user"}).AddRow("test-role3", "test-user3"))
mock.ExpectQuery(`SELECT distinct authsrv_resourcerole.name as role, identities.traits ->> 'email' as user, namespace FROM "authsrv_projectaccountnamespacerole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectaccountnamespacerole.role_id JOIN identities ON identities.id=authsrv_projectaccountnamespacerole.account_id WHERE \(authsrv_projectaccountnamespacerole.project_id = '` + uid + `'\) AND \(authsrv_projectaccountnamespacerole.trash = FALSE\)`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "user", "namespace"}).AddRow("test-role4", "test-user4", "test-namespace4"))
project := &systemv3.Project{
Metadata: &v3.Metadata{
Organization: ouuid,
},
}
pl, err := ps.List(context.Background(), project)
if err != nil {
t.Fatal("could not get project:", err)
}
if pl.Metadata.Count != 1 {
t.Errorf("incorrect number of projects returned; expected '%v', got '%v'", 1, pl.Metadata.Count)
}
if pl.Items[0].Metadata.Name != "project-"+uid {
t.Errorf("incorrect project name; expected '%v', got '%v'", "project-"+uid, pl.Items[0].Metadata.Name)
}
if *pl.Items[0].Spec.ProjectNamespaceRoles[0].Project != "test-project1" {
t.Errorf("incorrect projectnamespacerole; expected '%v', got '%v'", "test-project1", *pl.Items[0].Spec.ProjectNamespaceRoles[0].Project)
}
if *pl.Items[0].Spec.ProjectNamespaceRoles[1].Namespace != "test-namespace2" {
t.Errorf("incorrect projectnamespacerole; expected '%v', got '%v'", "test-namespace2", *pl.Items[0].Spec.ProjectNamespaceRoles[1].Namespace)
}
if pl.Items[0].Spec.UserRoles[0].User != "test-user3" {
t.Errorf("incorrect userrole; expected '%v', got '%v'", "test-user3", pl.Items[0].Spec.UserRoles[0].User)
}
if pl.Items[0].Spec.UserRoles[1].Namespace != "test-namespace4" {
t.Errorf("incorrect userrole; expected '%v', got '%v'", "test-namespace4", pl.Items[0].Spec.UserRoles[1].Namespace)
}
}
func TestProjectListNonDev(t *testing.T) {
db, mock := getDB(t)
defer db.Close()
mazc := mockAuthzClient{}
ps := NewProjectService(db, &mazc, getLogger(), false)
uid := uuid.NewString()
ouuid := addFetchExpectation(mock, "organization")
puuid := addFetchExpectation(mock, "partner")
uuuid := addUserFetchExpectation(mock)
mock.ExpectQuery(`SELECT distinct account_id, project_id FROM "sentry_account_permission" AS "sap" WHERE \(sap.partner_id = '` + puuid + `'\) AND \(sap.organization_id = '` + ouuid + `'\) AND \(sap.account_id = '` + uuuid + `'\) AND \(sap.permission_name IN \('project.read', 'ops_star.all'\)\)`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"account_id", "project_id"}).AddRow(uuuid, uid))
mock.ExpectQuery(`SELECT "project"."id", "project"."name", .* FROM "authsrv_project" AS "project" WHERE \(project.partner_id = '` + puuid + `'\) AND \(project.organization_id = '` + ouuid + `'\) AND \(project.trash = FALSE\) AND \(project.id IN \('` + uid + `'\)\)`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow(uid, "project-"+uid))
mock.ExpectQuery(`SELECT distinct authsrv_resourcerole.name as role, authsrv_project.name as project, authsrv_group.name as group FROM "authsrv_projectgrouprole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectgrouprole.role_id JOIN authsrv_group ON authsrv_group.id=authsrv_projectgrouprole.group_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectgrouprole.project_id WHERE \(authsrv_projectgrouprole.project_id = '` + uid + `'\)`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project", "group"}).AddRow("test-role1", "test-project1", "test-group1"))
mock.ExpectQuery(`SELECT distinct authsrv_resourcerole.name as role, authsrv_project.name as project, authsrv_group.name as group, namespace FROM "authsrv_projectgroupnamespacerole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectgroupnamespacerole.role_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectgroupnamespacerole.project_id JOIN authsrv_group ON authsrv_group.id=authsrv_projectgroupnamespacerole.group_id WHERE \(authsrv_projectgroupnamespacerole.project_id = '` + uid + `'\)`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project", "group", "namespace"}).AddRow("test-role2", "test-project2", "test-group2", "test-namespace2"))
mock.ExpectQuery(`SELECT distinct authsrv_resourcerole.name as role, identities.traits ->> 'email' as user FROM "authsrv_projectaccountresourcerole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectaccountresourcerole.role_id JOIN identities ON identities.id=authsrv_projectaccountresourcerole.account_id WHERE \(authsrv_projectaccountresourcerole.project_id = '` + uid + `'\)`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "user"}).AddRow("test-role3", "test-user3"))
mock.ExpectQuery(`SELECT distinct authsrv_resourcerole.name as role, identities.traits ->> 'email' as user, namespace FROM "authsrv_projectaccountnamespacerole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectaccountnamespacerole.role_id JOIN identities ON identities.id=authsrv_projectaccountnamespacerole.account_id WHERE \(authsrv_projectaccountnamespacerole.project_id = '` + uid + `'\) AND \(authsrv_projectaccountnamespacerole.trash = FALSE\)`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "user", "namespace"}).AddRow("test-role4", "test-user4", "test-namespace4"))
project := &systemv3.Project{
Metadata: &v3.Metadata{
Organization: ouuid,
},
}
sd := v3.SessionData{Username: "user-" + uuuid}
ctx := context.WithValue(context.Background(), common.SessionDataKey, &sd)
pl, err := ps.List(ctx, project)
if err != nil {
t.Fatal("could not get project:", err)
}
if pl.Metadata.Count != 1 {
t.Errorf("incorrect number of projects returned; expected '%v', got '%v'", 1, pl.Metadata.Count)
}
if pl.Items[0].Metadata.Name != "project-"+uid {
t.Errorf("incorrect project name; expected '%v', got '%v'", "project-"+uid, pl.Items[0].Metadata.Name)
}
if *pl.Items[0].Spec.ProjectNamespaceRoles[0].Project != "test-project1" {
t.Errorf("incorrect projectnamespacerole; expected '%v', got '%v'", "test-project1", *pl.Items[0].Spec.ProjectNamespaceRoles[0].Project)
}
if *pl.Items[0].Spec.ProjectNamespaceRoles[1].Namespace != "test-namespace2" {
t.Errorf("incorrect projectnamespacerole; expected '%v', got '%v'", "test-namespace2", *pl.Items[0].Spec.ProjectNamespaceRoles[1].Namespace)
}
if pl.Items[0].Spec.UserRoles[0].User != "test-user3" {
t.Errorf("incorrect userrole; expected '%v', got '%v'", "test-user3", pl.Items[0].Spec.UserRoles[0].User)
}
if pl.Items[0].Spec.UserRoles[1].Namespace != "test-namespace4" {
t.Errorf("incorrect userrole; expected '%v', got '%v'", "test-namespace4", pl.Items[0].Spec.UserRoles[1].Namespace)
}
}
+1 -1
View File
@@ -9,7 +9,7 @@ import (
)
type RelayAuditService struct {
relayQuery *ElasticSearchQuery
relayQuery ElasticSearchQuery
}
func NewRelayAuditService(url string, auditPattern string, logPrefix string) (*RelayAuditService, error) {
+149
View File
@@ -0,0 +1,149 @@
package service
import (
"encoding/json"
"strings"
"testing"
v1 "github.com/paralus/paralus/proto/rpc/audit"
v3 "github.com/paralus/paralus/proto/types/commonpb/v3"
)
type rmd struct {
Source []string `json:"_source"`
Aggs struct {
GroupByCluster struct {
Aggs struct {
GroupByNamespace struct {
Terms struct {
Field string `json:"field"`
Size int `json:"size"`
} `json:"terms"`
} `json:"group_by_namespace"`
GroupByUsername struct {
Terms struct {
Field string `json:"field"`
Size int `json:"size"`
} `json:"terms"`
} `json:"group_by_username"`
} `json:"aggs"`
Terms struct {
Field string `json:"field"`
Size int `json:"size"`
} `json:"terms"`
} `json:"group_by_cluster"`
GroupByKind struct {
Terms struct {
Field string `json:"field"`
} `json:"terms"`
} `json:"group_by_kind"`
GroupByMethod struct {
Terms struct {
Field string `json:"field"`
} `json:"terms"`
} `json:"group_by_method"`
GroupByNamespace struct {
Terms struct {
Field string `json:"field"`
} `json:"terms"`
} `json:"group_by_namespace"`
GroupByUsername struct {
Terms struct {
Field string `json:"field"`
} `json:"terms"`
} `json:"group_by_username"`
} `json:"aggs"`
Query struct {
Bool struct {
Filter struct {
Range struct {
JSONTs struct {
Gte string `json:"gte"`
Lt string `json:"lt"`
} `json:"json.ts"`
} `json:"range"`
} `json:"filter"`
Must []struct {
Term struct {
JSONUn string `json:"json.un"`
} `json:"term,omitempty"`
Terms struct {
JSONProject []string `json:"json.project"`
} `json:"terms,omitempty"`
QueryString struct {
Query string `json:"query"`
} `json:"query_string,omitempty"`
} `json:"must"`
} `json:"bool"`
} `json:"query"`
Size int `json:"size"`
Sort struct {
JSONTs struct {
Order string `json:"order"`
} `json:"json.ts"`
} `json:"sort"`
}
func TestGetRelayAuditLogByProjectsSimple(t *testing.T) {
esq := &mockElasticSearchQuery{}
al := &RelayAuditService{relayQuery: esq}
req := v1.RelayAuditSearchRequest{
Filter: &v1.RelayAuditQueryFilter{
QueryString: "query-string",
Projects: []string{"project-one", "project-two"},
Timefrom: "now-1h",
Type: "test-type",
User: "test-user",
Client: "test-client",
Cluster: "test-cluster",
Namespace: "test-namespace",
Kind: "test-kind",
Method: "test-method",
DashboardData: true,
},
}
_, err := al.GetRelayAuditByProjects(&req)
if err != nil {
t.Error("unable to get audit logs")
}
if len(esq.msg) != 1 {
t.Fatalf("incorrect number of searches; expected '%v', got '%v'", 1, len(esq.msg))
}
m := &rmd{}
err = json.Unmarshal(esq.msg[0].Bytes(), m)
if err != nil {
t.Fatal("unable to unmarshall es request")
}
expected := `{"_source":["json"],"aggs":{"group_by_cluster":{"aggs":{"group_by_namespace":{"terms":{"field":"json.ns","size":1000}},"group_by_username":{"terms":{"field":"json.un","size":1000}}},"terms":{"field":"json.cn","size":1000}},"group_by_kind":{"terms":{"field":"json.k"}},"group_by_method":{"terms":{"field":"json.m"}},"group_by_namespace":{"terms":{"field":"json.ns"}},"group_by_username":{"terms":{"field":"json.un"}}},"query":{"bool":{"filter":{"range":{"json.ts":{"gte":"now-1h","lt":"now"}}},"must":[{"term":{"json.un":"test-user"}},{"term":{"json.cn":"test-cluster"}},{"term":{"json.ns":"test-namespace"}},{"term":{"json.k":"test-kind"}},{"term":{"json.m":"test-method"}},{"terms":{"json.project":["project-one","project-two"]}},{"query_string":{"query":"query-string"}}]}},"size":0,"sort":{"json.ts":{"order":"desc"}}}`
if strings.TrimSpace(esq.msg[0].String()) != expected {
t.Errorf("incorrect es query; expected '%v', got '%v'", expected, strings.TrimSpace(esq.msg[0].String()))
}
}
func TestGetRelayAuditLogByProjectsNoProject(t *testing.T) {
esq := &mockElasticSearchQuery{}
al := &RelayAuditService{relayQuery: esq}
req := v1.RelayAuditSearchRequest{
Metadata: &v3.Metadata{UrlScope: "url/project"},
Filter: &v1.RelayAuditQueryFilter{
QueryString: "query-string",
},
}
_, err := al.GetRelayAudit(&req)
if err != nil {
t.Error("unable to get audit logs", err)
}
if len(esq.msg) != 1 {
t.Fatalf("incorrect number of searches; expected '%v', got '%v'", 1, len(esq.msg))
}
m := &rmd{}
err = json.Unmarshal(esq.msg[0].Bytes(), m)
if err != nil {
t.Fatal("unable to unmarshall es request")
}
expected := `{"_source":["json"],"aggs":{"group_by_cluster":{"terms":{"field":"json.cn"}},"group_by_kind":{"terms":{"field":"json.k"}},"group_by_method":{"terms":{"field":"json.m"}},"group_by_namespace":{"terms":{"field":"json.ns"}},"group_by_username":{"terms":{"field":"json.un"}}},"query":{"bool":{"must":[{"terms":{"json.project":["project"]}},{"query_string":{"query":"query-string"}}]}},"size":500,"sort":{"json.ts":{"order":"desc"}}}`
if strings.TrimSpace(esq.msg[0].String()) != expected {
t.Errorf("incorrect es query; expected '%v', got '%v'", expected, strings.TrimSpace(esq.msg[0].String()))
}
}
-15
View File
@@ -49,21 +49,6 @@ func (s *rolepermissionService) toV3Rolepermission(rolepermission *rolev3.RolePe
return rolepermission
}
func (s *rolepermissionService) getPartnerOrganization(ctx context.Context, rolepermission *rolev3.RolePermission) (uuid.UUID, uuid.UUID, error) {
partner := rolepermission.GetMetadata().GetPartner()
org := rolepermission.GetMetadata().GetOrganization()
partnerId, err := dao.GetPartnerId(ctx, s.db, partner)
if err != nil {
return uuid.Nil, uuid.Nil, err
}
organizationId, err := dao.GetOrganizationId(ctx, s.db, org)
if err != nil {
return partnerId, uuid.Nil, err
}
return partnerId, organizationId, nil
}
func (s *rolepermissionService) GetByName(ctx context.Context, rolepermission *rolev3.RolePermission) (*rolev3.RolePermission, error) {
name := rolepermission.GetMetadata().GetName()
entity, err := dao.GetByName(ctx, s.db, name, &models.ResourcePermission{})
+61
View File
@@ -8,6 +8,7 @@ import (
"github.com/google/uuid"
"github.com/paralus/paralus/pkg/query"
commonv3 "github.com/paralus/paralus/proto/types/commonpb/v3"
v3 "github.com/paralus/paralus/proto/types/commonpb/v3"
rolev3 "github.com/paralus/paralus/proto/types/rolepb/v3"
)
@@ -55,3 +56,63 @@ func TestRolePermissionList(t *testing.T) {
t.Errorf("incorrect role ids returned when listing")
}
}
func TestRolePermissionListWithSelectors(t *testing.T) {
db, mock := getDB(t)
defer db.Close()
rs := NewRolepermissionService(db)
ruuid1 := uuid.New().String()
ruuid2 := uuid.New().String()
puuid := uuid.New().String()
ouuid := uuid.New().String()
mock.ExpectQuery(`SELECT authsrv_resourcepermission.name as name, authsrv_resourcepermission.scope as scope FROM "authsrv_resourcepermission" WHERE \(scope = 'NAMESPACE'\) AND \(authsrv_resourcepermission.trash = FALSE\)`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).
AddRow(ruuid1, "role-"+ruuid1))
mock.ExpectQuery(`SELECT authsrv_resourcepermission.name as name, authsrv_resourcepermission.scope as scope FROM "authsrv_resourcepermission" WHERE \(scope = 'PROJECT'\) AND \(authsrv_resourcepermission.trash = FALSE\)`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).
AddRow(ruuid2, "role-"+ruuid2))
req := &commonv3.QueryOptions{
Partner: "partner-" + puuid,
Organization: "org-" + ouuid,
Selector: "namespace",
}
rolelist, err := rs.List(context.Background(), query.WithOptions(req))
if err != nil {
t.Fatal("could not list rolepermissions:", err)
}
if rolelist.Metadata.Count != 2 {
t.Errorf("incorrect number of rolepermissions returned, expected 2; got %v", rolelist.Metadata.Count)
}
if rolelist.Items[0].Metadata.Name != "role-"+ruuid1 || rolelist.Items[1].Metadata.Name != "role-"+ruuid2 {
t.Errorf("incorrect role ids returned when listing")
}
}
func TestRolePermissionGetByName(t *testing.T) {
db, mock := getDB(t)
defer db.Close()
rs := NewRolepermissionService(db)
ruuid := uuid.New().String()
mock.ExpectQuery(`SELECT "resourcepermission"."id", "resourcepermission"."name".* FROM "authsrv_resourcepermission" AS "resourcepermission"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).
AddRow(ruuid, "role-"+ruuid))
req := &rolev3.RolePermission{
Metadata: &v3.Metadata{},
}
role, err := rs.GetByName(context.Background(), req)
if err != nil {
t.Fatal("could not list rolepermissions:", err)
}
if role.Metadata.Name != "role-"+ruuid {
t.Errorf("incorrect role name; expected '%v', got '%v'", "role-"+ruuid, role.Metadata.Name)
}
}
+31 -2
View File
@@ -12,7 +12,7 @@ import (
func getLogger() *zap.Logger {
ao := audit.AuditOptions{
LogPath: "stdout",
LogPath: "/dev/stdout",
MaxSizeMB: 1,
MaxBackups: 10, // Should we let sidecar do rotation?
MaxAgeDays: 10, // Make these configurable via env
@@ -65,6 +65,16 @@ func idnamea(uid string, resource string) *string {
return &name
}
func addFetchEmptyExpecteation(mock sqlmock.Sqlmock, resource string) {
mock.ExpectQuery(`SELECT "` + resource + `"."id" FROM "authsrv_` + resource + `" AS "` + resource + `"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}))
}
func addFailingFetchExpecteation(mock sqlmock.Sqlmock, resource string) {
mock.ExpectQuery(`SELECT "` + resource + `"."id" FROM "authsrv_` + resource + `" AS "` + resource + `"`).
WithArgs().WillReturnError(fmt.Errorf("no data available"))
}
func addFetchIdExpectation(mock sqlmock.Sqlmock, resource string) string {
uid := uuid.New().String()
mock.ExpectQuery(`SELECT "` + resource + `"."id" FROM "authsrv_` + resource + `" AS "` + resource + `"`).
@@ -72,10 +82,22 @@ func addFetchIdExpectation(mock sqlmock.Sqlmock, resource string) string {
return uid
}
func addFetchByIdExpectation(mock sqlmock.Sqlmock, resource, uid string) {
mock.ExpectQuery(`SELECT "` + resource + `"."id".* FROM "authsrv_` + resource + `" AS "` + resource + `" WHERE .id = '` + uid + `'.`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow(uid, resource+"-"+uid))
}
func addFetchIdByNameExpectation(mock sqlmock.Sqlmock, resource, name string) string {
uid := uuid.NewString()
mock.ExpectQuery(`SELECT "` + resource + `"."id" FROM "authsrv_` + resource + `" AS "` + resource + `" WHERE .name = '` + name + `'.`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(uid))
return uid
}
func addFetchExpectation(mock sqlmock.Sqlmock, resource string) string {
uid := uuid.New().String()
mock.ExpectQuery(`SELECT "` + resource + `"."id".* FROM "authsrv_` + resource + `" AS "` + resource + `"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow(uid, "role-name"))
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow(uid, resource+"-name"))
return uid
}
@@ -152,6 +174,13 @@ func addUserFullFetchExpectation(mock sqlmock.Sqlmock) string {
return uid
}
func addUserFullFetchExpectationWithIdpGroups(mock sqlmock.Sqlmock) string {
uid := uuid.New().String()
mock.ExpectQuery(`SELECT "identities"."id", "identities"."schema_id", "identities"."traits", "identities"."created_at", "identities"."updated_at", "identities"."state", "identities"."state_changed_at", "identities"."nid", "identity_credential"."id" AS "identity_credential__id", "identity_credential"."identity_id" AS "identity_credential__identity_id", "identity_credential"."identity_credential_type_id" AS "identity_credential__identity_credential_type_id", "identity_credential__identity_credential_type"."id" AS "identity_credential__identity_credential_type__id", "identity_credential__identity_credential_type"."name" AS "identity_credential__identity_credential_type__name" FROM "identities" LEFT JOIN "identity_credentials" AS "identity_credential" ON ."identity_credential"."identity_id" = "identities"."id". LEFT JOIN "identity_credential_types" AS "identity_credential__identity_credential_type" ON ."identity_credential__identity_credential_type"."id" = "identity_credential"."identity_credential_type_id". WHERE .traits ->> 'email' = 'user-` + uid + `'.`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "traits", "identity_credential__identity_credential_type__name"}).AddRow(uid, []byte(`{"email":"user-`+uid+`", "first_name": "John", "last_name": "Doe", "description": "The OG user.", "idp_group": "BigShot"}`), "password"))
return uid
}
func addUsersGroupFetchExpectation(mock sqlmock.Sqlmock, user string) string {
uid := uuid.New().String()
mock.ExpectQuery(`SELECT "group"."id".* FROM "authsrv_group" AS "group" JOIN authsrv_groupaccount ON authsrv_groupaccount.group_id="group".id WHERE .authsrv_groupaccount.account_id = '` + user + `'`).
+5
View File
@@ -125,6 +125,9 @@ func (s *userService) createUserRoleRelations(ctx context.Context, db bun.IDB, u
continue
}
role := pnr.GetRole()
if role == "" {
return &userv3.User{}, nil, fmt.Errorf("cannot use empty role")
}
entity, err := dao.GetByName(ctx, db, role, &models.Role{})
if err != nil {
return &userv3.User{}, nil, fmt.Errorf("unable to find role '%v'", role)
@@ -445,6 +448,7 @@ func (s *userService) Create(ctx context.Context, user *userv3.User) (*userv3.Us
if err != nil {
tx.Rollback()
_log.Warn("unable to commit changes", err)
return &userv3.User{}, err
}
rl, err := s.ap.GetRecoveryLink(ctx, id)
@@ -727,6 +731,7 @@ func (s *userService) Update(ctx context.Context, user *userv3.User) (*userv3.Us
if err != nil {
tx.Rollback()
_log.Warn("unable to commit changes", err)
return &userv3.User{}, fmt.Errorf("unable to update user '%v'", name)
}
CreateUserAuditEvent(ctx, s.al, s.db, AuditActionUpdate, user.GetMetadata().GetName(), usr.ID, rolesBefore, rolesAfter, groupsBefore, groupsAfter)
+206 -1
View File
@@ -3,12 +3,14 @@ package service
import (
"context"
"fmt"
"strings"
"testing"
"github.com/DATA-DOG/go-sqlmock"
"github.com/google/uuid"
"github.com/paralus/paralus/pkg/common"
"github.com/paralus/paralus/pkg/query"
userrpcv3 "github.com/paralus/paralus/proto/rpc/user"
commonv3 "github.com/paralus/paralus/proto/types/commonpb/v3"
v3 "github.com/paralus/paralus/proto/types/commonpb/v3"
userv3 "github.com/paralus/paralus/proto/types/userpb/v3"
@@ -127,7 +129,7 @@ func TestCreateUserWithRole(t *testing.T) {
role.Project = &pruuid
}
if tc.namespace {
var ns string = "ns"
var ns = "ns"
role.Namespace = &ns
}
mock.ExpectQuery(fmt.Sprintf(`INSERT INTO "%v"`, tc.dbname)).
@@ -239,6 +241,92 @@ func TestUpdateUserWithGroup(t *testing.T) {
performBasicAuthProviderChecks(t, *ap, 0, 1, 0, 0)
}
func TestUpdateUserWithIdpGroupPassed(t *testing.T) {
// Having idp groups passed down should not affect, it should come from db
db, mock := getDB(t)
defer db.Close()
ap := &mockAuthProvider{}
mazc := mockAuthzClient{}
us := NewUserService(ap, db, &mazc, nil, common.CliConfigDownloadData{}, getLogger(), true)
// performing update
uuuid := addUserFullFetchExpectation(mock)
puuid, ouuid := addParterOrgFetchExpectation(mock)
mock.ExpectBegin()
_ = addUserRoleMappingsUpdateExpectation(mock, uuuid)
addUserGroupMappingsUpdateExpectation(mock, uuuid)
ruuid := addResourceRoleFetchExpectation(mock, "project")
pruuid := addFetchExpectation(mock, "project")
mock.ExpectQuery(`INSERT INTO "authsrv_projectaccountresourcerole"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(uuid.New().String()))
// addFetchExpectation(mock, "group")
// mock.ExpectQuery(`INSERT INTO "authsrv_groupaccount"`).
// WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(uuid.New().String()))
mock.ExpectCommit()
var ns = "ns"
user := &userv3.User{
Metadata: &v3.Metadata{Partner: "partner-" + puuid, Organization: "org-" + ouuid, Name: "user-" + uuuid},
Spec: &userv3.UserSpec{
IdpGroups: []string{"group"},
ProjectNamespaceRoles: []*userv3.ProjectNamespaceRole{{Project: idnamea(pruuid, "project"), Namespace: &ns, Role: idname(ruuid, "role")}},
},
}
user, err := us.Update(context.Background(), user)
if err != nil {
t.Fatal("could not create user:", err)
}
performUserBasicChecks(t, user, uuuid)
if user.GetMetadata().GetName() != "user-"+uuuid {
t.Errorf("expected name 'user-%v'; got '%v'", uuuid, user.GetMetadata().GetName())
}
performBasicAuthProviderChecks(t, *ap, 0, 1, 0, 0)
}
func TestUpdateUserWithIdpGroupFetched(t *testing.T) {
// Having idp groups passed down should not affect, it should come from db
db, mock := getDB(t)
defer db.Close()
ap := &mockAuthProvider{}
mazc := mockAuthzClient{}
us := NewUserService(ap, db, &mazc, nil, common.CliConfigDownloadData{}, getLogger(), true)
// performing update
uuuid := addUserFullFetchExpectationWithIdpGroups(mock)
puuid, ouuid := addParterOrgFetchExpectation(mock)
mock.ExpectBegin()
_ = addUserRoleMappingsUpdateExpectation(mock, uuuid)
addUserGroupMappingsUpdateExpectation(mock, uuuid)
ruuid := addResourceRoleFetchExpectation(mock, "project")
pruuid := addFetchExpectation(mock, "project")
mock.ExpectQuery(`INSERT INTO "authsrv_projectaccountresourcerole"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(uuid.New().String()))
addFetchExpectation(mock, "group")
mock.ExpectQuery(`INSERT INTO "authsrv_groupaccount"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(uuid.New().String()))
mock.ExpectCommit()
var ns = "ns"
user := &userv3.User{
Metadata: &v3.Metadata{Partner: "partner-" + puuid, Organization: "org-" + ouuid, Name: "user-" + uuuid},
Spec: &userv3.UserSpec{
IdpGroups: []string{"group"},
ProjectNamespaceRoles: []*userv3.ProjectNamespaceRole{{Project: idnamea(pruuid, "project"), Namespace: &ns, Role: idname(ruuid, "role")}},
},
}
user, err := us.Update(context.Background(), user)
if err != nil {
t.Fatal("could not create user:", err)
}
performUserBasicChecks(t, user, uuuid)
if user.GetMetadata().GetName() != "user-"+uuuid {
t.Errorf("expected name 'user-%v'; got '%v'", uuuid, user.GetMetadata().GetName())
}
performBasicAuthProviderChecks(t, *ap, 0, 1, 0, 0)
}
func TestUpdateUserInvalid(t *testing.T) {
db, mock := getDB(t)
defer db.Close()
@@ -468,6 +556,9 @@ func TestUserList(t *testing.T) {
{"simple list", "", 50, 20, "", "", "", "", []string{}, ""},
{"simple list with type", "", 50, 20, "", "", "", "", []string{}, "password"},
{"sorted list", "", 50, 20, "email", "asc", "", "", []string{}, ""},
{"sorted list with ALL projects", "", 50, 20, "email", "asc", "", "", []string{"ALL"}, ""},
{"sorted list with single project", "", 50, 20, "email", "asc", "", "", []string{"project1"}, ""},
{"sorted list with projects", "", 50, 20, "email", "asc", "", "", []string{"project1", "project2"}, ""},
{"sorted list without dir", "", 50, 20, "email", "", "", "", []string{}, ""},
{"sorted list with q", "filter-query", 50, 20, "email", "asc", "", "", []string{}, ""},
{"sorted list with role", "", 50, 20, "email", "asc", "role-name", "", []string{}, ""},
@@ -505,6 +596,12 @@ func TestUserList(t *testing.T) {
if tc.group != "" {
addFetchExpectation(mock, "group")
}
for _, p := range tc.projects {
if p == "ALL" {
continue
}
addFetchIdByNameExpectation(mock, "project", p)
}
if tc.role != "" || tc.group != "" || len(tc.projects) != 0 {
addSentryLookupExpectation(mock, []string{uuuid1, uuuid2}, puuid, ouuid)
mock.ExpectQuery(`SELECT "identities"."id", .*WHERE .identities.id IN .'` + uuuid1 + `', '` + uuuid2 + `'.. ` + q + order + `LIMIT ` + fmt.Sprint(tc.limit) + ` OFFSET ` + fmt.Sprint(tc.offset)).
@@ -544,6 +641,7 @@ func TestUserList(t *testing.T) {
Role: tc.role,
Group: tc.group,
Type: tc.utype,
Project: strings.Join(tc.projects, ","),
}
userlist, err := us.List(context.Background(), query.WithOptions(qo))
@@ -633,3 +731,110 @@ func TestUserDeleteSelf(t *testing.T) {
t.Fatal("user able to delete their own account")
}
}
func TestUserForgotPassword(t *testing.T) {
db, mock := getDB(t)
defer db.Close()
ap := &mockAuthProvider{}
mazc := mockAuthzClient{}
us := NewUserService(ap, db, &mazc, nil, common.CliConfigDownloadData{}, getLogger(), true)
uuuid := addUserFetchExpectation(mock)
fpreq := &userrpcv3.ForgotPasswordRequest{Username: "user-" + uuuid}
fpresp, err := us.ForgotPassword(context.Background(), fpreq)
if err != nil {
t.Fatal("could not fetch password recovery link:", err)
}
if !strings.HasPrefix(fpresp.RecoveryLink, "https://recoverme.testing/") {
t.Error("invalid recovery url generated")
}
}
func TestUserRetrieveCliConfigGet(t *testing.T) {
db, mock := getDB(t)
defer db.Close()
ap := &mockAuthProvider{}
mazc := mockAuthzClient{}
ks := NewApiKeyService(db, getLogger())
us := NewUserService(ap, db, &mazc, ks, common.CliConfigDownloadData{}, getLogger(), true)
uuuid := uuid.NewString()
auuid := uuid.NewString()
mock.ExpectQuery(`SELECT sap.* FROM "sentry_account_permission" AS "sap" JOIN authsrv_project as proj ON \(proj.id = sap.project_id\) AND \(proj.default = TRUE\) WHERE \(account_id = '` + uuuid + `'\) LIMIT 1`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"account_id"}).AddRow(uuuid))
_ = addFetchExpectation(mock, "project")
_ = addFetchExpectation(mock, "organization")
_ = addFetchExpectation(mock, "partner")
mock.ExpectQuery(`SELECT "apikey"."id", "apikey"."name",.* FROM "authsrv_apikey" AS "apikey" WHERE \(name = 'user-` + uuuid + `'\) AND \(trash = FALSE\)`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"key", "secret"}).AddRow(auuid, "apikey-"+auuid))
req := &userrpcv3.ApiKeyRequest{Username: "user-" + uuuid, Id: uuuid}
resp, err := us.RetrieveCliConfig(context.Background(), req)
if err != nil {
t.Fatal("could not fetch cli config:", err)
}
if resp.ApiKey != auuid {
t.Error("incorrect apikey generated")
}
if resp.ApiSecret != "apikey-"+auuid {
t.Error("incorrect apisecret generated")
}
if resp.Project != "project-name" {
t.Error("invalid project name")
}
if resp.Organization != "organization-name" {
t.Error("invalid organization name")
}
if resp.Partner != "partner-name" {
t.Error("invalid partner name")
}
}
func TestUserRetrieveCliConfigCreate(t *testing.T) {
db, mock := getDB(t)
defer db.Close()
ap := &mockAuthProvider{}
mazc := mockAuthzClient{}
ks := NewApiKeyService(db, getLogger())
us := NewUserService(ap, db, &mazc, ks, common.CliConfigDownloadData{}, getLogger(), true)
uuuid := uuid.NewString()
auuid := uuid.NewString()
mock.ExpectQuery(`SELECT sap.* FROM "sentry_account_permission" AS "sap" JOIN authsrv_project as proj ON \(proj.id = sap.project_id\) AND \(proj.default = TRUE\) WHERE \(account_id = '` + uuuid + `'\) LIMIT 1`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"account_id"}).AddRow(uuuid))
_ = addFetchExpectation(mock, "project")
_ = addFetchExpectation(mock, "organization")
_ = addFetchExpectation(mock, "partner")
mock.ExpectQuery(`SELECT "apikey"."id", "apikey"."name",.* FROM "authsrv_apikey" AS "apikey" WHERE \(name = 'user-` + uuuid + `'\) AND \(trash = FALSE\)`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "name"}))
mock.ExpectQuery(`INSERT INTO "authsrv_apikey"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(auuid))
req := &userrpcv3.ApiKeyRequest{Username: "user-" + uuuid, Id: uuuid}
resp, err := us.RetrieveCliConfig(context.Background(), req)
if err != nil {
t.Fatal("could not fetch cli config:", err)
}
if len(resp.ApiKey) == 0 {
t.Error("no apikey generated")
}
if len(resp.ApiSecret) == 0 {
t.Error("no apisecret generated")
}
if resp.Project != "project-name" {
t.Error("invalid project name")
}
if resp.Organization != "organization-name" {
t.Error("invalid organization name")
}
if resp.Partner != "partner-name" {
t.Error("invalid partner name")
}
}