mirror of
https://github.com/kubevela/kubevela.git
synced 2026-08-27 16:17:34 +00:00
Feat: add RBAC support (#3493)
* Feat: add the rbac data model Signed-off-by: barnettZQG <barnett.zqg@gmail.com> * Feat: add some api about the project Signed-off-by: barnettZQG <barnett.zqg@gmail.com> * Feat: add CRUD about the project and the project user Signed-off-by: barnettZQG <barnett.zqg@gmail.com> * Feat: add CRUD about the role and perm check filter function Signed-off-by: barnettZQG <barnett.zqg@gmail.com> * Feat: update swagger config Signed-off-by: barnettZQG <barnett.zqg@gmail.com> * Feat: add default roles and perm policies Signed-off-by: barnettZQG <barnett.zqg@gmail.com> * Feat: add perm check filter for all webservice Signed-off-by: barnettZQG <barnett.zqg@gmail.com> * Feat: change the method that find project name Signed-off-by: barnettZQG <barnett.zqg@gmail.com> * Feat: query applications and envs by user perm Signed-off-by: barnettZQG <barnett.zqg@gmail.com> * Feat: support get login user info Signed-off-by: barnettZQG <barnett.zqg@gmail.com> * Fix: change default permissions Signed-off-by: barnettZQG <barnett.zqg@gmail.com> * Feat: change PermPolicy to Permission Signed-off-by: barnettZQG <barnett.zqg@gmail.com> * Feat: add some unit test and fix the e2e test error Signed-off-by: barnettZQG <barnett.zqg@gmail.com> * Fix: change some comment word Signed-off-by: barnettZQG <barnett.zqg@gmail.com> * Fix: e2e api path error Signed-off-by: barnettZQG <barnett.zqg@gmail.com>
This commit is contained in:
@@ -112,9 +112,22 @@ type FuzzyQueryOption struct {
|
||||
Query string
|
||||
}
|
||||
|
||||
// InQueryOption defines the include search filter option
|
||||
type InQueryOption struct {
|
||||
Key string
|
||||
Values []string
|
||||
}
|
||||
|
||||
// IsNotExistQueryOption means the value is empty
|
||||
type IsNotExistQueryOption struct {
|
||||
Key string
|
||||
}
|
||||
|
||||
// FilterOptions filter query returned items
|
||||
type FilterOptions struct {
|
||||
Queries []FuzzyQueryOption
|
||||
Queries []FuzzyQueryOption
|
||||
In []InQueryOption
|
||||
IsNotExist []IsNotExistQueryOption
|
||||
}
|
||||
|
||||
// ListOptions list api options
|
||||
|
||||
@@ -120,23 +120,23 @@ func (m *kubeapi) Add(ctx context.Context, entity datastore.Entity) error {
|
||||
}
|
||||
|
||||
// BatchAdd batch add entity, this operation has some atomicity.
|
||||
func (m *kubeapi) BatchAdd(ctx context.Context, entitys []datastore.Entity) error {
|
||||
donotRollback := make(map[string]int)
|
||||
for i, saveEntity := range entitys {
|
||||
func (m *kubeapi) BatchAdd(ctx context.Context, entities []datastore.Entity) error {
|
||||
notRollback := make(map[string]int)
|
||||
for i, saveEntity := range entities {
|
||||
if err := m.Add(ctx, saveEntity); err != nil {
|
||||
if errors.Is(err, datastore.ErrRecordExist) {
|
||||
donotRollback[saveEntity.PrimaryKey()] = 1
|
||||
notRollback[saveEntity.PrimaryKey()] = 1
|
||||
}
|
||||
for _, deleteEntity := range entitys[:i] {
|
||||
if _, exit := donotRollback[deleteEntity.PrimaryKey()]; !exit {
|
||||
for _, deleteEntity := range entities[:i] {
|
||||
if _, exit := notRollback[deleteEntity.PrimaryKey()]; !exit {
|
||||
if err := m.Delete(ctx, deleteEntity); err != nil {
|
||||
if !errors.Is(err, datastore.ErrRecordNotExist) {
|
||||
log.Logger.Errorf("rollback delete component failure %w", err)
|
||||
log.Logger.Errorf("rollback delete entity failure %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return datastore.NewDBError(fmt.Errorf("save components occur error, %w", err))
|
||||
return datastore.NewDBError(fmt.Errorf("save entities occur error, %w", err))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -347,6 +347,24 @@ func (m *kubeapi) List(ctx context.Context, entity datastore.Entity, op *datasto
|
||||
}
|
||||
selector = selector.Add(*rq)
|
||||
}
|
||||
if op != nil {
|
||||
for _, inFilter := range op.In {
|
||||
rq, err := labels.NewRequirement(inFilter.Key, selection.In, inFilter.Values)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("new list requirement failure %s", err.Error())
|
||||
return nil, datastore.ErrIndexInvalid
|
||||
}
|
||||
selector = selector.Add(*rq)
|
||||
}
|
||||
for _, notFilter := range op.IsNotExist {
|
||||
rq, err := labels.NewRequirement(notFilter.Key, selection.DoesNotExist, []string{})
|
||||
if err != nil {
|
||||
log.Logger.Errorf("new list requirement failure %s", err.Error())
|
||||
return nil, datastore.ErrIndexInvalid
|
||||
}
|
||||
selector = selector.Add(*rq)
|
||||
}
|
||||
}
|
||||
options := &client.ListOptions{
|
||||
LabelSelector: selector,
|
||||
Namespace: m.namespace,
|
||||
@@ -385,7 +403,6 @@ func (m *kubeapi) List(ctx context.Context, entity datastore.Entity, op *datasto
|
||||
items = items[:limit]
|
||||
}
|
||||
var list []datastore.Entity
|
||||
log.Logger.Debugf("query %s result count %d", selector, len(items))
|
||||
for _, item := range items {
|
||||
ent, err := datastore.NewEntity(entity)
|
||||
if err != nil {
|
||||
@@ -416,6 +433,24 @@ func (m *kubeapi) Count(ctx context.Context, entity datastore.Entity, filterOpti
|
||||
}
|
||||
selector = selector.Add(*rq)
|
||||
}
|
||||
if filterOptions != nil {
|
||||
for _, inFilter := range filterOptions.In {
|
||||
rq, err := labels.NewRequirement(inFilter.Key, selection.In, inFilter.Values)
|
||||
if err != nil {
|
||||
return 0, datastore.ErrIndexInvalid
|
||||
}
|
||||
selector = selector.Add(*rq)
|
||||
}
|
||||
for _, notFilter := range filterOptions.IsNotExist {
|
||||
rq, err := labels.NewRequirement(notFilter.Key, selection.DoesNotExist, []string{})
|
||||
if err != nil {
|
||||
log.Logger.Errorf("new list requirement failure %s", err.Error())
|
||||
return 0, datastore.ErrIndexInvalid
|
||||
}
|
||||
selector = selector.Add(*rq)
|
||||
}
|
||||
}
|
||||
|
||||
options := &client.ListOptions{
|
||||
LabelSelector: selector,
|
||||
Namespace: m.namespace,
|
||||
|
||||
@@ -96,7 +96,7 @@ var _ = Describe("Test kubeapi datastore driver", func() {
|
||||
var datas = []datastore.Entity{
|
||||
&model.Application{Name: "kubevela-app-2", Description: "this is demo 2"},
|
||||
&model.Application{Name: "kubevela-app-3", Description: "this is demo 3"},
|
||||
&model.Application{Name: "kubevela-app-4", Description: "this is demo 4"},
|
||||
&model.Application{Name: "kubevela-app-4", Project: "testProject", Description: "this is demo 4"},
|
||||
}
|
||||
err := kubeStore.BatchAdd(context.TODO(), datas)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
@@ -106,7 +106,7 @@ var _ = Describe("Test kubeapi datastore driver", func() {
|
||||
&model.Application{Name: "kubevela-app-2", Description: "this is demo 2"},
|
||||
}
|
||||
err = kubeStore.BatchAdd(context.TODO(), datas2)
|
||||
equal := cmp.Diff(strings.Contains(err.Error(), "save components occur error"), true)
|
||||
equal := cmp.Diff(strings.Contains(err.Error(), "save entities occur error"), true)
|
||||
Expect(equal).To(BeEmpty())
|
||||
})
|
||||
|
||||
@@ -157,6 +157,26 @@ var _ = Describe("Test kubeapi datastore driver", func() {
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
diff = cmp.Diff(len(list), 4)
|
||||
Expect(diff).Should(BeEmpty())
|
||||
|
||||
list, err = kubeStore.List(context.TODO(), &app, &datastore.ListOptions{FilterOptions: datastore.FilterOptions{In: []datastore.InQueryOption{
|
||||
{
|
||||
Key: "name",
|
||||
Values: []string{"kubevela-app-3", "kubevela-app-2"},
|
||||
},
|
||||
}}})
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
diff = cmp.Diff(len(list), 2)
|
||||
Expect(diff).Should(BeEmpty())
|
||||
|
||||
list, err = kubeStore.List(context.TODO(), &app, &datastore.ListOptions{FilterOptions: datastore.FilterOptions{IsNotExist: []datastore.IsNotExistQueryOption{
|
||||
{
|
||||
Key: "project",
|
||||
},
|
||||
}}})
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
diff = cmp.Diff(len(list), 3)
|
||||
Expect(diff).Should(BeEmpty())
|
||||
|
||||
})
|
||||
|
||||
It("Test list clusters with sort and fuzzy query", func() {
|
||||
@@ -221,6 +241,23 @@ var _ = Describe("Test kubeapi datastore driver", func() {
|
||||
})
|
||||
Expect(err).Should(Succeed())
|
||||
Expect(count).Should(Equal(int64(2)))
|
||||
|
||||
count, err = kubeStore.Count(context.TODO(), &app, &datastore.FilterOptions{In: []datastore.InQueryOption{
|
||||
{
|
||||
Key: "name",
|
||||
Values: []string{"kubevela-app-3", "kubevela-app-2"},
|
||||
},
|
||||
}})
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
Expect(count).Should(Equal(int64(2)))
|
||||
|
||||
count, err = kubeStore.Count(context.TODO(), &app, &datastore.FilterOptions{IsNotExist: []datastore.IsNotExistQueryOption{
|
||||
{
|
||||
Key: "project",
|
||||
},
|
||||
}})
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
Expect(count).Should(Equal(int64(3)))
|
||||
})
|
||||
|
||||
It("Test isExist function", func() {
|
||||
|
||||
@@ -84,23 +84,23 @@ func (m *mongodb) Add(ctx context.Context, entity datastore.Entity) error {
|
||||
}
|
||||
|
||||
// BatchAdd batch add entity, this operation has some atomicity.
|
||||
func (m *mongodb) BatchAdd(ctx context.Context, entitys []datastore.Entity) error {
|
||||
donotRollback := make(map[string]int)
|
||||
for i, saveEntity := range entitys {
|
||||
func (m *mongodb) BatchAdd(ctx context.Context, entities []datastore.Entity) error {
|
||||
notRollback := make(map[string]int)
|
||||
for i, saveEntity := range entities {
|
||||
if err := m.Add(ctx, saveEntity); err != nil {
|
||||
if errors.Is(err, datastore.ErrRecordExist) {
|
||||
donotRollback[saveEntity.PrimaryKey()] = 1
|
||||
notRollback[saveEntity.PrimaryKey()] = 1
|
||||
}
|
||||
for _, deleteEntity := range entitys[:i] {
|
||||
if _, exit := donotRollback[deleteEntity.PrimaryKey()]; !exit {
|
||||
for _, deleteEntity := range entities[:i] {
|
||||
if _, exit := notRollback[deleteEntity.PrimaryKey()]; !exit {
|
||||
if err := m.Delete(ctx, deleteEntity); err != nil {
|
||||
if !errors.Is(err, datastore.ErrRecordNotExist) {
|
||||
log.Logger.Errorf("rollback delete component failure %w", err)
|
||||
log.Logger.Errorf("rollback delete entity failure %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return datastore.NewDBError(fmt.Errorf("save components occur error, %w", err))
|
||||
return datastore.NewDBError(fmt.Errorf("save entities occur error, %w", err))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -192,10 +192,14 @@ func (m *mongodb) Delete(ctx context.Context, entity datastore.Entity) error {
|
||||
}
|
||||
|
||||
func _applyFilterOptions(filter bson.D, filterOptions datastore.FilterOptions) bson.D {
|
||||
if len(filterOptions.Queries) > 0 {
|
||||
for _, queryOp := range filterOptions.Queries {
|
||||
filter = append(filter, bson.E{Key: strings.ToLower(queryOp.Key), Value: bsonx.Regex(".*"+queryOp.Query+".*", "s")})
|
||||
}
|
||||
for _, queryOp := range filterOptions.Queries {
|
||||
filter = append(filter, bson.E{Key: strings.ToLower(queryOp.Key), Value: bsonx.Regex(".*"+queryOp.Query+".*", "s")})
|
||||
}
|
||||
for _, queryOp := range filterOptions.In {
|
||||
filter = append(filter, bson.E{Key: strings.ToLower(queryOp.Key), Value: bson.D{bson.E{Key: "$in", Value: queryOp.Values}}})
|
||||
}
|
||||
for _, queryOp := range filterOptions.IsNotExist {
|
||||
filter = append(filter, bson.E{Key: strings.ToLower(queryOp.Key), Value: bson.D{bson.E{Key: "$eq", Value: ""}}})
|
||||
}
|
||||
return filter
|
||||
}
|
||||
@@ -216,7 +220,7 @@ func (m *mongodb) List(ctx context.Context, entity datastore.Entity, op *datasto
|
||||
})
|
||||
}
|
||||
}
|
||||
if op != nil && len(op.Queries) > 0 {
|
||||
if op != nil {
|
||||
filter = _applyFilterOptions(filter, op.FilterOptions)
|
||||
}
|
||||
var findOptions options.FindOptions
|
||||
@@ -276,7 +280,7 @@ func (m *mongodb) Count(ctx context.Context, entity datastore.Entity, filterOpti
|
||||
})
|
||||
}
|
||||
}
|
||||
if filterOptions != nil && len(filterOptions.Queries) > 0 {
|
||||
if filterOptions != nil {
|
||||
filter = _applyFilterOptions(filter, *filterOptions)
|
||||
}
|
||||
count, err := collection.CountDocuments(ctx, filter)
|
||||
|
||||
@@ -70,7 +70,7 @@ var _ = Describe("Test mongodb datastore driver", func() {
|
||||
var datas = []datastore.Entity{
|
||||
&model.Application{Name: "kubevela-app-2", Description: "this is demo 2"},
|
||||
&model.Application{Name: "kubevela-app-3", Description: "this is demo 3"},
|
||||
&model.Application{Name: "kubevela-app-4", Description: "this is demo 4"},
|
||||
&model.Application{Name: "kubevela-app-4", Project: "test-project", Description: "this is demo 4"},
|
||||
&model.Workflow{Name: "kubevela-app-workflow", AppPrimaryKey: "kubevela-app-2", Description: "this is workflow"},
|
||||
&model.ApplicationTrigger{Name: "kubevela-app-trigger", AppPrimaryKey: "kubevela-app-2", Token: "token-test", Description: "this is demo 4"},
|
||||
}
|
||||
@@ -82,7 +82,7 @@ var _ = Describe("Test mongodb datastore driver", func() {
|
||||
&model.Application{Name: "kubevela-app-2", Description: "this is demo 2"},
|
||||
}
|
||||
err = mongodbDriver.BatchAdd(context.TODO(), datas2)
|
||||
equal := cmp.Diff(strings.Contains(err.Error(), "save components occur error"), true)
|
||||
equal := cmp.Diff(strings.Contains(err.Error(), "save entities occur error"), true)
|
||||
Expect(equal).To(BeEmpty())
|
||||
})
|
||||
|
||||
@@ -133,6 +133,25 @@ var _ = Describe("Test mongodb datastore driver", func() {
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
diff = cmp.Diff(len(list), 1)
|
||||
Expect(diff).Should(BeEmpty())
|
||||
|
||||
list, err = mongodbDriver.List(context.TODO(), &app, &datastore.ListOptions{FilterOptions: datastore.FilterOptions{In: []datastore.InQueryOption{
|
||||
{
|
||||
Key: "name",
|
||||
Values: []string{"kubevela-app-3", "kubevela-app-2"},
|
||||
},
|
||||
}}})
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
diff = cmp.Diff(len(list), 2)
|
||||
Expect(diff).Should(BeEmpty())
|
||||
|
||||
list, err = mongodbDriver.List(context.TODO(), &app, &datastore.ListOptions{FilterOptions: datastore.FilterOptions{IsNotExist: []datastore.IsNotExistQueryOption{
|
||||
{
|
||||
Key: "project",
|
||||
},
|
||||
}}})
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
diff = cmp.Diff(len(list), 3)
|
||||
Expect(diff).Should(BeEmpty())
|
||||
})
|
||||
|
||||
It("Test list clusters with sort and fuzzy query", func() {
|
||||
@@ -196,6 +215,23 @@ var _ = Describe("Test mongodb datastore driver", func() {
|
||||
})
|
||||
Expect(err).Should(Succeed())
|
||||
Expect(count).Should(Equal(int64(2)))
|
||||
|
||||
count, err = mongodbDriver.Count(context.TODO(), &app, &datastore.FilterOptions{In: []datastore.InQueryOption{
|
||||
{
|
||||
Key: "name",
|
||||
Values: []string{"kubevela-app-3", "kubevela-app-2"},
|
||||
},
|
||||
}})
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
Expect(count).Should(Equal(int64(2)))
|
||||
|
||||
count, err = mongodbDriver.Count(context.TODO(), &app, &datastore.FilterOptions{IsNotExist: []datastore.IsNotExistQueryOption{
|
||||
{
|
||||
Key: "project",
|
||||
},
|
||||
}})
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
Expect(count).Should(Equal(int64(3)))
|
||||
})
|
||||
|
||||
It("Test isExist function", func() {
|
||||
|
||||
@@ -25,6 +25,7 @@ type Project struct {
|
||||
BaseModel
|
||||
Name string `json:"name"`
|
||||
Alias string `json:"alias"`
|
||||
Owner string `json:"owner"`
|
||||
Description string `json:"description,omitempty"`
|
||||
}
|
||||
|
||||
@@ -49,5 +50,8 @@ func (p *Project) Index() map[string]string {
|
||||
if p.Name != "" {
|
||||
index["name"] = p.Name
|
||||
}
|
||||
if p.Owner != "" {
|
||||
index["owner"] = p.Owner
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ type Target struct {
|
||||
BaseModel
|
||||
Name string `json:"name"`
|
||||
Alias string `json:"alias,omitempty"`
|
||||
Project string `json:"project"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Cluster *ClusterTarget `json:"cluster,omitempty"`
|
||||
Variable map[string]interface{} `json:"variable,omitempty"`
|
||||
@@ -52,6 +53,9 @@ func (d *Target) Index() map[string]string {
|
||||
if d.Name != "" {
|
||||
index["name"] = d.Name
|
||||
}
|
||||
if d.Project != "" {
|
||||
index["project"] = d.Project
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
|
||||
+149
-3
@@ -27,8 +27,14 @@ import (
|
||||
func init() {
|
||||
RegisterModel(&User{})
|
||||
RegisterModel(&ProjectUser{})
|
||||
RegisterModel(&Role{})
|
||||
RegisterModel(&Permission{})
|
||||
RegisterModel(&PermissionTemplate{})
|
||||
}
|
||||
|
||||
// DefaultAdminUserName default admin user name
|
||||
const DefaultAdminUserName = "admin"
|
||||
|
||||
// User is the model of user
|
||||
type User struct {
|
||||
BaseModel
|
||||
@@ -38,6 +44,8 @@ type User struct {
|
||||
Password string `json:"password,omitempty"`
|
||||
Disabled bool `json:"disabled"`
|
||||
LastLoginTime time.Time `json:"lastLoginTime,omitempty"`
|
||||
// UserRoles binding the platform level roles
|
||||
UserRoles []string `json:"userRoles"`
|
||||
}
|
||||
|
||||
// TableName return custom table name
|
||||
@@ -70,9 +78,10 @@ func (u *User) Index() map[string]string {
|
||||
// ProjectUser is the model of user in project
|
||||
type ProjectUser struct {
|
||||
BaseModel
|
||||
Username string `json:"username"`
|
||||
ProjectName string `json:"projectName"`
|
||||
UserRoles []string `json:"userRoles"`
|
||||
Username string `json:"username"`
|
||||
ProjectName string `json:"projectName"`
|
||||
// UserRoles binding the project level roles
|
||||
UserRoles []string `json:"userRoles"`
|
||||
}
|
||||
|
||||
// TableName return custom table name
|
||||
@@ -114,3 +123,140 @@ type CustomClaims struct {
|
||||
GrantType string `json:"grantType"`
|
||||
jwt.StandardClaims
|
||||
}
|
||||
|
||||
// Role is a model for a new RBAC mode.
|
||||
type Role struct {
|
||||
BaseModel
|
||||
Name string `json:"name"`
|
||||
Alias string `json:"alias"`
|
||||
Project string `json:"project,omitempty"`
|
||||
Permissions []string `json:"permissions"`
|
||||
}
|
||||
|
||||
// Permission is a model for a new RBAC mode.
|
||||
type Permission struct {
|
||||
BaseModel
|
||||
Name string `json:"name"`
|
||||
Alias string `json:"alias"`
|
||||
Project string `json:"project,omitempty"`
|
||||
Resources []string `json:"resources"`
|
||||
Actions []string `json:"actions"`
|
||||
// Effect option values: Allow,Deny
|
||||
Effect string `json:"effect"`
|
||||
Principal *Principal `json:"principal,omitempty"`
|
||||
Condition *Condition `json:"condition,omitempty"`
|
||||
}
|
||||
|
||||
// Principal is a model for a new RBAC mode.
|
||||
type Principal struct {
|
||||
// Type options: User or Role
|
||||
Type string `json:"type"`
|
||||
Names []string `json:"names"`
|
||||
}
|
||||
|
||||
// Condition is a model for a new RBAC mode.
|
||||
type Condition struct {
|
||||
}
|
||||
|
||||
// TableName return custom table name
|
||||
func (r *Role) TableName() string {
|
||||
return tableNamePrefix + "role"
|
||||
}
|
||||
|
||||
// ShortTableName return custom table name
|
||||
func (r *Role) ShortTableName() string {
|
||||
return "role"
|
||||
}
|
||||
|
||||
// PrimaryKey return custom primary key
|
||||
func (r *Role) PrimaryKey() string {
|
||||
if r.Project == "" {
|
||||
return r.Name
|
||||
}
|
||||
return fmt.Sprintf("%s-%s", r.Project, r.Name)
|
||||
}
|
||||
|
||||
// Index return custom index
|
||||
func (r *Role) Index() map[string]string {
|
||||
index := make(map[string]string)
|
||||
if r.Name != "" {
|
||||
index["name"] = r.Name
|
||||
}
|
||||
if r.Project != "" {
|
||||
index["project"] = r.Project
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
// TableName return custom table name
|
||||
func (p *Permission) TableName() string {
|
||||
return tableNamePrefix + "perm"
|
||||
}
|
||||
|
||||
// ShortTableName return custom table name
|
||||
func (p *Permission) ShortTableName() string {
|
||||
return "perm"
|
||||
}
|
||||
|
||||
// PrimaryKey return custom primary key
|
||||
func (p *Permission) PrimaryKey() string {
|
||||
if p.Project == "" {
|
||||
return p.Name
|
||||
}
|
||||
return fmt.Sprintf("%s-%s", p.Project, p.Name)
|
||||
}
|
||||
|
||||
// Index return custom index
|
||||
func (p *Permission) Index() map[string]string {
|
||||
index := make(map[string]string)
|
||||
if p.Name != "" {
|
||||
index["name"] = p.Name
|
||||
}
|
||||
if p.Project != "" {
|
||||
index["project"] = p.Project
|
||||
}
|
||||
if p.Principal != nil && p.Principal.Type != "" {
|
||||
index["principal.type"] = p.Principal.Type
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
// PermissionTemplate is a model for a new RBAC mode.
|
||||
type PermissionTemplate struct {
|
||||
BaseModel
|
||||
Name string `json:"name"`
|
||||
Alias string `json:"alias"`
|
||||
// Scope options: project or platform
|
||||
Scope string `json:"scope"`
|
||||
Resources []string `json:"resources"`
|
||||
Actions []string `json:"actions"`
|
||||
Effect string `json:"effect"`
|
||||
Condition *Condition `json:"condition,omitempty"`
|
||||
}
|
||||
|
||||
// TableName return custom table name
|
||||
func (p *PermissionTemplate) TableName() string {
|
||||
return tableNamePrefix + "perm_temp"
|
||||
}
|
||||
|
||||
// ShortTableName return custom table name
|
||||
func (p *PermissionTemplate) ShortTableName() string {
|
||||
return "perm_temp"
|
||||
}
|
||||
|
||||
// PrimaryKey return custom primary key
|
||||
func (p *PermissionTemplate) PrimaryKey() string {
|
||||
return p.Name
|
||||
}
|
||||
|
||||
// Index return custom index
|
||||
func (p *PermissionTemplate) Index() map[string]string {
|
||||
index := make(map[string]string)
|
||||
if p.Name != "" {
|
||||
index["name"] = p.Name
|
||||
}
|
||||
if p.Scope != "" {
|
||||
index["scope"] = p.Scope
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
@@ -289,10 +289,10 @@ type ClusterBase struct {
|
||||
|
||||
// ListApplicationOptions list application query options
|
||||
type ListApplicationOptions struct {
|
||||
Project string `json:"project"`
|
||||
Env string `json:"env"`
|
||||
TargetName string `json:"targetName"`
|
||||
Query string `json:"query"`
|
||||
Projects []string `json:"projects"`
|
||||
Env string `json:"env"`
|
||||
TargetName string `json:"targetName"`
|
||||
Query string `json:"query"`
|
||||
}
|
||||
|
||||
// ListApplicationResponse list applications by query params
|
||||
@@ -678,6 +678,7 @@ type ProjectBase struct {
|
||||
Description string `json:"description"`
|
||||
CreateTime time.Time `json:"createTime"`
|
||||
UpdateTime time.Time `json:"updateTime"`
|
||||
Owner NameAlias `json:"owner,omitempty"`
|
||||
}
|
||||
|
||||
// CreateProjectRequest create project request body
|
||||
@@ -685,6 +686,14 @@ type CreateProjectRequest struct {
|
||||
Name string `json:"name" validate:"checkname"`
|
||||
Alias string `json:"alias" validate:"checkalias" optional:"true"`
|
||||
Description string `json:"description" optional:"true"`
|
||||
Owner string `json:"owner" optional:"true"`
|
||||
}
|
||||
|
||||
// UpdateProjectRequest update a project request body
|
||||
type UpdateProjectRequest struct {
|
||||
Alias string `json:"alias" validate:"checkalias" optional:"true"`
|
||||
Description string `json:"description" optional:"true"`
|
||||
Owner string `json:"owner" optional:"true"`
|
||||
}
|
||||
|
||||
// Env models the data of env in API
|
||||
@@ -989,6 +998,7 @@ type ApplicationTrait struct {
|
||||
type CreateTargetRequest struct {
|
||||
Name string `json:"name" validate:"checkname"`
|
||||
Alias string `json:"alias,omitempty" validate:"checkalias" optional:"true"`
|
||||
Project string `json:"project" validate:"checkname"`
|
||||
Description string `json:"description,omitempty" optional:"true"`
|
||||
Cluster *ClusterTarget `json:"cluster,omitempty"`
|
||||
Variable map[string]interface{} `json:"variable,omitempty"`
|
||||
@@ -1029,6 +1039,7 @@ type TargetBase struct {
|
||||
CreateTime time.Time `json:"createTime"`
|
||||
UpdateTime time.Time `json:"updateTime"`
|
||||
AppNum int64 `json:"appNum,omitempty"`
|
||||
Project NameAlias `json:"project"`
|
||||
}
|
||||
|
||||
// ApplicationRevisionBase application revision base spec
|
||||
@@ -1118,29 +1129,39 @@ type DexConfigResponse struct {
|
||||
// DetailUserResponse is the response of user detail
|
||||
type DetailUserResponse struct {
|
||||
UserBase
|
||||
Projects []ProjectUserBase `json:"projects"`
|
||||
Projects []*ProjectBase `json:"projects"`
|
||||
Roles []NameAlias `json:"roles"`
|
||||
}
|
||||
|
||||
// ProjectUserBase project user base
|
||||
type ProjectUserBase struct {
|
||||
Name string `json:"name"`
|
||||
Alias string `json:"alias"`
|
||||
UserRoles []string `json:"userRoles"`
|
||||
UserName string `json:"name"`
|
||||
UserRoles []string `json:"userRoles"`
|
||||
CreateTime time.Time `json:"createTime"`
|
||||
UpdateTime time.Time `json:"updateTime"`
|
||||
}
|
||||
|
||||
// ListProjectUsersResponse the response body that list users belong to a project
|
||||
type ListProjectUsersResponse struct {
|
||||
Users []*ProjectUserBase `json:"users"`
|
||||
Total int64 `json:"total"`
|
||||
}
|
||||
|
||||
// CreateUserRequest create user request
|
||||
type CreateUserRequest struct {
|
||||
Name string `json:"name" validate:"checkname"`
|
||||
Alias string `json:"alias,omitempty" validate:"checkalias" optional:"true"`
|
||||
Email string `json:"email" validate:"checkemail"`
|
||||
Password string `json:"password" validate:"checkpassword"`
|
||||
Name string `json:"name" validate:"checkname"`
|
||||
Alias string `json:"alias,omitempty" validate:"checkalias" optional:"true"`
|
||||
Email string `json:"email" validate:"checkemail"`
|
||||
Password string `json:"password" validate:"checkpassword"`
|
||||
Roles []string `json:"roles"`
|
||||
}
|
||||
|
||||
// UpdateUserRequest update user request
|
||||
type UpdateUserRequest struct {
|
||||
Alias string `json:"alias,omitempty" optional:"true"`
|
||||
Password string `json:"password,omitempty" validate:"checkpassword" optional:"true"`
|
||||
Email string `json:"email,omitempty" validate:"checkemail" optional:"true"`
|
||||
Alias string `json:"alias,omitempty" optional:"true"`
|
||||
Password string `json:"password,omitempty" validate:"checkpassword" optional:"true"`
|
||||
Email string `json:"email,omitempty" validate:"checkemail" optional:"true"`
|
||||
Roles *[]string `json:"roles"`
|
||||
}
|
||||
|
||||
// ListUserResponse list user response
|
||||
@@ -1170,3 +1191,80 @@ type ListUserOptions struct {
|
||||
type GetLoginTypeResponse struct {
|
||||
LoginType string `json:"loginType"`
|
||||
}
|
||||
|
||||
// AddProjectUserRequest the request body that add user to project
|
||||
type AddProjectUserRequest struct {
|
||||
UserName string `json:"userName" validate:"checkname"`
|
||||
UserRoles []string `json:"userRoles"`
|
||||
}
|
||||
|
||||
// UpdateProjectUserRequest the request body that update user role in a project
|
||||
type UpdateProjectUserRequest struct {
|
||||
UserRoles []string `json:"userRoles"`
|
||||
}
|
||||
|
||||
// CreateRoleRequest the request body that create a role
|
||||
type CreateRoleRequest struct {
|
||||
Name string `json:"name" validate:"checkname"`
|
||||
Alias string `json:"alias" validate:"checkalias"`
|
||||
Permissions []string `json:"permissions"`
|
||||
}
|
||||
|
||||
// UpdateRoleRequest the request body that update a role
|
||||
type UpdateRoleRequest struct {
|
||||
Alias string `json:"alias" validate:"checkalias"`
|
||||
Permissions []string `json:"permissions"`
|
||||
}
|
||||
|
||||
// RoleBase the base struct of role
|
||||
type RoleBase struct {
|
||||
CreateTime time.Time `json:"createTime"`
|
||||
UpdateTime time.Time `json:"updateTime"`
|
||||
Name string `json:"name"`
|
||||
Alias string `json:"alias,omitempty"`
|
||||
Permissions []NameAlias `json:"permissions"`
|
||||
}
|
||||
|
||||
// ListRolesResponse the response body of list roles
|
||||
type ListRolesResponse struct {
|
||||
Total int64 `json:"total"`
|
||||
Roles []*RoleBase `json:"roles"`
|
||||
}
|
||||
|
||||
// PermissionTemplateBase the perm policy template base struct
|
||||
type PermissionTemplateBase struct {
|
||||
Name string `json:"name"`
|
||||
Alias string `json:"alias"`
|
||||
Resources []string `json:"resources"`
|
||||
Actions []string `json:"actions"`
|
||||
Effect string `json:"effect"`
|
||||
CreateTime time.Time `json:"createTime"`
|
||||
UpdateTime time.Time `json:"updateTime"`
|
||||
}
|
||||
|
||||
// PermissionBase the perm policy base struct
|
||||
type PermissionBase struct {
|
||||
Name string `json:"name"`
|
||||
Alias string `json:"alias"`
|
||||
Resources []string `json:"resources"`
|
||||
Actions []string `json:"actions"`
|
||||
Effect string `json:"effect"`
|
||||
CreateTime time.Time `json:"createTime"`
|
||||
UpdateTime time.Time `json:"updateTime"`
|
||||
}
|
||||
|
||||
// UpdatePermissionRequest the request body that update permission policy
|
||||
type UpdatePermissionRequest struct {
|
||||
Alias string `json:"alias" validate:"checkalias"`
|
||||
Resources []string `json:"resources"`
|
||||
Actions []string `json:"actions"`
|
||||
Effect string `json:"effect" validate:"oneof=Allow Deny"`
|
||||
}
|
||||
|
||||
// LoginUserInfoResponse the response body of login user info
|
||||
type LoginUserInfoResponse struct {
|
||||
UserBase
|
||||
Projects []*ProjectBase `json:"projects"`
|
||||
PlatformPermissions []PermissionBase `json:"platformPermissions"`
|
||||
ProjectPermissions map[string][]PermissionBase `json:"projectPermissions"`
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ package rest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -27,22 +26,16 @@ import (
|
||||
restfulspec "github.com/emicklei/go-restful-openapi/v2"
|
||||
"github.com/emicklei/go-restful/v3"
|
||||
"github.com/go-openapi/spec"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
k8stypes "k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/tools/leaderelection"
|
||||
"k8s.io/client-go/tools/leaderelection/resourcelock"
|
||||
"k8s.io/klog/v2"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/clients"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/datastore"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/datastore/kubeapi"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/datastore/mongodb"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/log"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/model"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/usecase"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/webservice"
|
||||
@@ -78,13 +71,15 @@ type leaderConfig struct {
|
||||
// APIServer interface for call api server
|
||||
type APIServer interface {
|
||||
Run(context.Context) error
|
||||
RegisterServices() restfulspec.Config
|
||||
RegisterServices(ctx context.Context, initDatabase bool) restfulspec.Config
|
||||
}
|
||||
|
||||
type restServer struct {
|
||||
webContainer *restful.Container
|
||||
cfg Config
|
||||
dataStore datastore.DataStore
|
||||
// usecases, we register part of the usecase instances
|
||||
usecases map[string]interface{}
|
||||
}
|
||||
|
||||
// New create restserver with config data
|
||||
@@ -114,11 +109,7 @@ func New(cfg Config) (a APIServer, err error) {
|
||||
}
|
||||
|
||||
func (s *restServer) Run(ctx context.Context) error {
|
||||
s.RegisterServices()
|
||||
|
||||
if err := s.InitAdmin(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
s.RegisterServices(ctx, true)
|
||||
|
||||
l, err := s.setupLeaderElection()
|
||||
if err != nil {
|
||||
@@ -149,7 +140,7 @@ func (s *restServer) setupLeaderElection() (*leaderelection.LeaderElectionConfig
|
||||
RetryPeriod: time.Second * 2,
|
||||
Callbacks: leaderelection.LeaderCallbacks{
|
||||
OnStartedLeading: func(ctx context.Context) {
|
||||
go velasync.Start(ctx, s.dataStore, restCfg)
|
||||
go velasync.Start(ctx, s.dataStore, restCfg, s.usecases)
|
||||
s.runWorkflowRecordSync(ctx, s.cfg.LeaderConfig.Duration)
|
||||
},
|
||||
OnStoppedLeading: func() {
|
||||
@@ -169,10 +160,9 @@ func (s *restServer) setupLeaderElection() (*leaderelection.LeaderElectionConfig
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s restServer) runWorkflowRecordSync(ctx context.Context, duration time.Duration) {
|
||||
func (s *restServer) runWorkflowRecordSync(ctx context.Context, duration time.Duration) {
|
||||
klog.Infof("start to syncing workflow record")
|
||||
w := usecase.NewWorkflowUsecase(s.dataStore, usecase.NewEnvUsecase(s.dataStore))
|
||||
|
||||
w := s.usecases["workflow"].(usecase.WorkflowUsecase)
|
||||
t := time.NewTicker(duration)
|
||||
defer t.Stop()
|
||||
|
||||
@@ -189,8 +179,9 @@ func (s restServer) runWorkflowRecordSync(ctx context.Context, duration time.Dur
|
||||
}
|
||||
|
||||
// RegisterServices register web service
|
||||
func (s *restServer) RegisterServices() restfulspec.Config {
|
||||
webservice.Init(s.dataStore, s.cfg.AddonCacheTime)
|
||||
func (s *restServer) RegisterServices(ctx context.Context, initDatabase bool) restfulspec.Config {
|
||||
s.usecases = webservice.Init(ctx, s.dataStore, s.cfg.AddonCacheTime, initDatabase)
|
||||
|
||||
/* ************************************************************** */
|
||||
/* ************* Open API Route Group ***************** */
|
||||
/* ************************************************************** */
|
||||
@@ -223,57 +214,6 @@ func (s *restServer) RegisterServices() restfulspec.Config {
|
||||
return config
|
||||
}
|
||||
|
||||
// RegisterServices register web service
|
||||
func (s *restServer) InitAdmin(ctx context.Context) error {
|
||||
admin := "admin"
|
||||
if err := s.dataStore.Get(ctx, &model.User{
|
||||
Name: admin,
|
||||
}); err != nil {
|
||||
if errors.Is(err, datastore.ErrRecordNotExist) {
|
||||
pwd := utils2.RandomString(8)
|
||||
encrypted, err := usecase.GeneratePasswordHash(pwd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.dataStore.Add(ctx, &model.User{
|
||||
Name: admin,
|
||||
Password: encrypted,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
kubecli, err := clients.GetKubeClient()
|
||||
if err != nil {
|
||||
log.Logger.Fatalf("failed to get kube client: %s", err.Error())
|
||||
return err
|
||||
}
|
||||
secret := &corev1.Secret{}
|
||||
if err := kubecli.Get(ctx, k8stypes.NamespacedName{
|
||||
Name: admin,
|
||||
Namespace: types.DefaultKubeVelaNS,
|
||||
}, secret); err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
if err := kubecli.Create(ctx, &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: admin,
|
||||
Namespace: types.DefaultKubeVelaNS,
|
||||
},
|
||||
StringData: map[string]string{
|
||||
admin: pwd,
|
||||
},
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *restServer) requestLog(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
|
||||
start := time.Now()
|
||||
c := utils.NewResponseCapture(resp.ResponseWriter)
|
||||
|
||||
@@ -48,7 +48,7 @@ import (
|
||||
apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/sync"
|
||||
syncconvert "github.com/oam-dev/kubevela/pkg/apiserver/sync/convert"
|
||||
"github.com/oam-dev/kubevela/pkg/appfile/dryrun"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/discoverymapper"
|
||||
@@ -130,7 +130,7 @@ func NewApplicationUsecase(ds datastore.DataStore,
|
||||
) ApplicationUsecase {
|
||||
kubecli, err := clients.GetKubeClient()
|
||||
if err != nil {
|
||||
log.Logger.Fatalf("get kubeclient failure %s", err.Error())
|
||||
log.Logger.Fatalf("get kube client failure %s", err.Error())
|
||||
}
|
||||
return &applicationUsecaseImpl{
|
||||
ds: ds,
|
||||
@@ -147,9 +147,6 @@ func NewApplicationUsecase(ds datastore.DataStore,
|
||||
|
||||
func listApp(ctx context.Context, ds datastore.DataStore, listOptions apisv1.ListApplicationOptions) ([]*model.Application, error) {
|
||||
var app = model.Application{}
|
||||
if listOptions.Project != "" {
|
||||
app.Project = listOptions.Project
|
||||
}
|
||||
var err error
|
||||
var envBinding []*apisv1.EnvBindingBase
|
||||
if listOptions.Env != "" || listOptions.TargetName != "" {
|
||||
@@ -159,8 +156,14 @@ func listApp(ctx context.Context, ds datastore.DataStore, listOptions apisv1.Lis
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
entities, err := ds.List(ctx, &app, &datastore.ListOptions{})
|
||||
var filterOptions datastore.FilterOptions
|
||||
if len(listOptions.Projects) > 0 {
|
||||
filterOptions.In = append(filterOptions.In, datastore.InQueryOption{
|
||||
Key: "project",
|
||||
Values: listOptions.Projects,
|
||||
})
|
||||
}
|
||||
entities, err := ds.List(ctx, &app, &datastore.ListOptions{FilterOptions: filterOptions})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -202,13 +205,36 @@ func listApp(ctx context.Context, ds datastore.DataStore, listOptions apisv1.Lis
|
||||
|
||||
// ListApplications list applications
|
||||
func (c *applicationUsecaseImpl) ListApplications(ctx context.Context, listOptions apisv1.ListApplicationOptions) ([]*apisv1.ApplicationBase, error) {
|
||||
userName, ok := ctx.Value(&apisv1.CtxKeyUser).(string)
|
||||
if !ok {
|
||||
return nil, bcode.ErrUnauthorized
|
||||
}
|
||||
projects, err := c.projectUsecase.ListUserProjects(ctx, userName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var availableProjectNames []string
|
||||
for _, project := range projects {
|
||||
availableProjectNames = append(availableProjectNames, project.Name)
|
||||
}
|
||||
if len(availableProjectNames) == 0 {
|
||||
return []*apisv1.ApplicationBase{}, nil
|
||||
}
|
||||
if len(listOptions.Projects) > 0 {
|
||||
if !utils2.SliceIncludeSlice(availableProjectNames, listOptions.Projects) {
|
||||
return []*apisv1.ApplicationBase{}, nil
|
||||
}
|
||||
}
|
||||
if len(listOptions.Projects) == 0 {
|
||||
listOptions.Projects = availableProjectNames
|
||||
}
|
||||
apps, err := listApp(ctx, c.ds, listOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var list []*apisv1.ApplicationBase
|
||||
for _, app := range apps {
|
||||
appBase := c.convertAppModelToBase(ctx, app)
|
||||
appBase := c.convertAppModelToBase(app, projects)
|
||||
list = append(list, appBase)
|
||||
}
|
||||
sort.Slice(list, func(i, j int) bool {
|
||||
@@ -233,8 +259,16 @@ func (c *applicationUsecaseImpl) GetApplication(ctx context.Context, appName str
|
||||
|
||||
// DetailApplication detail application info
|
||||
func (c *applicationUsecaseImpl) DetailApplication(ctx context.Context, app *model.Application) (*apisv1.DetailApplicationResponse, error) {
|
||||
base := c.convertAppModelToBase(ctx, app)
|
||||
policys, err := c.queryApplicationPolicies(ctx, app)
|
||||
var project *apisv1.ProjectBase
|
||||
if app.Project != "" {
|
||||
var err error
|
||||
project, err = c.projectUsecase.DetailProject(ctx, app.Project)
|
||||
if err != nil {
|
||||
return nil, bcode.ErrProjectIsNotExist
|
||||
}
|
||||
}
|
||||
base := c.convertAppModelToBase(app, []*apisv1.ProjectBase{project})
|
||||
policies, err := c.queryApplicationPolicies(ctx, app)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -248,7 +282,7 @@ func (c *applicationUsecaseImpl) DetailApplication(ctx context.Context, app *mod
|
||||
}
|
||||
var policyNames []string
|
||||
var envBindingNames []string
|
||||
for _, p := range policys {
|
||||
for _, p := range policies {
|
||||
policyNames = append(policyNames, p.Name)
|
||||
}
|
||||
for _, e := range envBindings {
|
||||
@@ -349,9 +383,9 @@ func (c *applicationUsecaseImpl) CreateApplication(ctx context.Context, req apis
|
||||
return nil, bcode.ErrApplicationExist
|
||||
}
|
||||
// check project
|
||||
project, err := c.projectUsecase.GetProject(ctx, req.Project)
|
||||
project, err := c.projectUsecase.DetailProject(ctx, req.Project)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, bcode.ErrProjectIsNotExist
|
||||
}
|
||||
application.Project = project.Name
|
||||
|
||||
@@ -385,7 +419,7 @@ func (c *applicationUsecaseImpl) CreateApplication(ctx context.Context, req apis
|
||||
return nil, err
|
||||
}
|
||||
// render app base info.
|
||||
base := c.convertAppModelToBase(ctx, &application)
|
||||
base := c.convertAppModelToBase(&application, []*apisv1.ProjectBase{project})
|
||||
return base, nil
|
||||
}
|
||||
|
||||
@@ -506,6 +540,14 @@ func (c *applicationUsecaseImpl) saveApplicationEnvBinding(ctx context.Context,
|
||||
}
|
||||
|
||||
func (c *applicationUsecaseImpl) UpdateApplication(ctx context.Context, app *model.Application, req apisv1.UpdateApplicationRequest) (*apisv1.ApplicationBase, error) {
|
||||
var project *apisv1.ProjectBase
|
||||
if app.Project != "" {
|
||||
var err error
|
||||
project, err = c.projectUsecase.DetailProject(ctx, app.Project)
|
||||
if err != nil {
|
||||
return nil, bcode.ErrProjectIsNotExist
|
||||
}
|
||||
}
|
||||
app.Alias = req.Alias
|
||||
app.Description = req.Description
|
||||
app.Labels = req.Labels
|
||||
@@ -513,7 +555,7 @@ func (c *applicationUsecaseImpl) UpdateApplication(ctx context.Context, app *mod
|
||||
if err := c.ds.Put(ctx, app); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.convertAppModelToBase(ctx, app), nil
|
||||
return c.convertAppModelToBase(app, []*apisv1.ProjectBase{project}), nil
|
||||
}
|
||||
|
||||
// ListRecords list application record
|
||||
@@ -617,11 +659,11 @@ func (c *applicationUsecaseImpl) queryApplicationPolicies(ctx context.Context, a
|
||||
var policy = model.ApplicationPolicy{
|
||||
AppPrimaryKey: app.PrimaryKey(),
|
||||
}
|
||||
policys, err := c.ds.List(ctx, &policy, &datastore.ListOptions{})
|
||||
policies, err := c.ds.List(ctx, &policy, &datastore.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, policy := range policys {
|
||||
for _, policy := range policies {
|
||||
pm := policy.(*model.ApplicationPolicy)
|
||||
list = append(list, pm)
|
||||
}
|
||||
@@ -893,7 +935,7 @@ func (c *applicationUsecaseImpl) renderOAMApplication(ctx context.Context, appMo
|
||||
return app, nil
|
||||
}
|
||||
|
||||
func (c *applicationUsecaseImpl) convertAppModelToBase(ctx context.Context, app *model.Application) *apisv1.ApplicationBase {
|
||||
func (c *applicationUsecaseImpl) convertAppModelToBase(app *model.Application, projects []*apisv1.ProjectBase) *apisv1.ApplicationBase {
|
||||
appBase := &apisv1.ApplicationBase{
|
||||
Name: app.Name,
|
||||
Alias: app.Alias,
|
||||
@@ -902,17 +944,15 @@ func (c *applicationUsecaseImpl) convertAppModelToBase(ctx context.Context, app
|
||||
Description: app.Description,
|
||||
Icon: app.Icon,
|
||||
Labels: app.Labels,
|
||||
Project: &apisv1.ProjectBase{Name: app.Project},
|
||||
}
|
||||
if app.IsSynced() {
|
||||
appBase.ReadOnly = true
|
||||
}
|
||||
|
||||
project, err := c.projectUsecase.GetProject(ctx, app.Project)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("query project info failure %s", err.Error())
|
||||
}
|
||||
if project != nil {
|
||||
appBase.Project = convertProjectModel2Base(project)
|
||||
for _, project := range projects {
|
||||
if project.Name == app.Project {
|
||||
appBase.Project = project
|
||||
}
|
||||
}
|
||||
return appBase
|
||||
}
|
||||
@@ -1610,7 +1650,7 @@ func (c *applicationUsecaseImpl) resetApp(ctx context.Context, targetApp *v1beta
|
||||
for _, comp := range targetComps {
|
||||
// add or update new app's components from old app
|
||||
if utils.StringsContain(readyToAdd, comp.Name) || utils.StringsContain(readyToUpdate, comp.Name) {
|
||||
compModel, err := sync.ConvertFromCRComponent(appPrimaryKey, comp)
|
||||
compModel, err := syncconvert.FromCRComponent(appPrimaryKey, comp)
|
||||
if err != nil {
|
||||
return &apisv1.AppResetResponse{}, bcode.ErrInvalidProperties
|
||||
}
|
||||
|
||||
@@ -42,11 +42,13 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/apply"
|
||||
)
|
||||
|
||||
var _ = Describe("Test application usecase function", func() {
|
||||
var (
|
||||
rbacUsecase *rbacUsecaseImpl
|
||||
appUsecase *applicationUsecaseImpl
|
||||
workflowUsecase *workflowUsecaseImpl
|
||||
envUsecase *envUsecaseImpl
|
||||
@@ -54,6 +56,7 @@ var _ = Describe("Test application usecase function", func() {
|
||||
targetUsecase *targetUsecaseImpl
|
||||
definitionUsecase *definitionUsecaseImpl
|
||||
projectUsecase *projectUsecaseImpl
|
||||
userUsecase *userUsecaseImpl
|
||||
testProject = "app-project"
|
||||
testApp = "test-app"
|
||||
defaultTarget = "default"
|
||||
@@ -66,12 +69,14 @@ var _ = Describe("Test application usecase function", func() {
|
||||
ds, err := NewDatastore(datastore.Config{Type: "kubeapi", Database: "app-test-kubevela"})
|
||||
Expect(ds).ToNot(BeNil())
|
||||
Expect(err).Should(BeNil())
|
||||
envUsecase = &envUsecaseImpl{ds: ds, kubeClient: k8sClient}
|
||||
rbacUsecase = &rbacUsecaseImpl{ds: ds}
|
||||
userUsecase = &userUsecaseImpl{ds: ds, k8sClient: k8sClient}
|
||||
projectUsecase = &projectUsecaseImpl{ds: ds, k8sClient: k8sClient, rbacUsecase: rbacUsecase}
|
||||
envUsecase = &envUsecaseImpl{ds: ds, kubeClient: k8sClient, projectUsecase: projectUsecase}
|
||||
workflowUsecase = &workflowUsecaseImpl{ds: ds, envUsecase: envUsecase}
|
||||
definitionUsecase = &definitionUsecaseImpl{kubeClient: k8sClient, caches: utils.NewMemoryCacheStore(context.Background())}
|
||||
envBindingUsecase = &envBindingUsecaseImpl{ds: ds, envUsecase: envUsecase, workflowUsecase: workflowUsecase, kubeClient: k8sClient, definitionUsecase: definitionUsecase}
|
||||
targetUsecase = &targetUsecaseImpl{ds: ds, k8sClient: k8sClient}
|
||||
projectUsecase = &projectUsecaseImpl{ds: ds, k8sClient: k8sClient}
|
||||
appUsecase = &applicationUsecaseImpl{
|
||||
ds: ds,
|
||||
workflowUsecase: workflowUsecase,
|
||||
@@ -87,11 +92,19 @@ var _ = Describe("Test application usecase function", func() {
|
||||
|
||||
It("Test CreateApplication function", func() {
|
||||
|
||||
By("test sample create")
|
||||
_, err := targetUsecase.CreateTarget(context.TODO(), v1.CreateTargetRequest{Name: defaultTarget, Cluster: &v1.ClusterTarget{ClusterName: "local", Namespace: namespace1}})
|
||||
By("init default admin user")
|
||||
var ns = corev1.Namespace{}
|
||||
ns.Name = types.DefaultKubeVelaNS
|
||||
err := k8sClient.Create(context.TODO(), &ns)
|
||||
Expect(err).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
err = userUsecase.Init(context.TODO())
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
_, err = projectUsecase.CreateProject(context.TODO(), v1.CreateProjectRequest{Name: testProject})
|
||||
By("prepare test project")
|
||||
_, err = projectUsecase.CreateProject(context.TODO(), v1.CreateProjectRequest{Name: testProject, Owner: model.DefaultAdminUserName})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
_, err = targetUsecase.CreateTarget(context.TODO(), v1.CreateTargetRequest{Name: defaultTarget, Project: testProject, Cluster: &v1.ClusterTarget{ClusterName: "local", Namespace: namespace1}})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
_, err = envUsecase.CreateEnv(context.TODO(), v1.CreateEnvRequest{Name: "app-dev", Namespace: envnsdev, Targets: []string{defaultTarget}, Project: "app-dev"})
|
||||
@@ -114,6 +127,7 @@ var _ = Describe("Test application usecase function", func() {
|
||||
Properties: "{\"image\":\"nginx\"}",
|
||||
},
|
||||
}
|
||||
By("test create application")
|
||||
base, err := appUsecase.CreateApplication(context.TODO(), req)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(base.Description, req.Description)).Should(BeEmpty())
|
||||
@@ -124,13 +138,13 @@ var _ = Describe("Test application usecase function", func() {
|
||||
})
|
||||
|
||||
It("Test ListApplications function", func() {
|
||||
_, err := appUsecase.ListApplications(context.TODO(), v1.ListApplicationOptions{})
|
||||
_, err := appUsecase.ListApplications(context.WithValue(context.TODO(), &v1.CtxKeyUser, model.DefaultAdminUserName), v1.ListApplicationOptions{})
|
||||
Expect(err).Should(BeNil())
|
||||
})
|
||||
|
||||
It("Test ListApplications and filter by targetName function", func() {
|
||||
list, err := appUsecase.ListApplications(context.TODO(), v1.ListApplicationOptions{
|
||||
Project: testProject,
|
||||
list, err := appUsecase.ListApplications(context.WithValue(context.TODO(), &v1.CtxKeyUser, model.DefaultAdminUserName), v1.ListApplicationOptions{
|
||||
Projects: []string{testProject},
|
||||
TargetName: defaultTarget})
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(cmp.Diff(len(list), 1)).Should(BeEmpty())
|
||||
@@ -481,7 +495,7 @@ var _ = Describe("Test application usecase function", func() {
|
||||
Expect(cmp.Diff(compareResponse.IsDiff, false)).Should(BeEmpty())
|
||||
|
||||
By("compare when app's env add target, should return true")
|
||||
_, err = targetUsecase.CreateTarget(context.TODO(), v1.CreateTargetRequest{Name: "dev-target1", Cluster: &v1.ClusterTarget{ClusterName: "local", Namespace: "dev-target1"}})
|
||||
_, err = targetUsecase.CreateTarget(context.TODO(), v1.CreateTargetRequest{Name: "dev-target1", Project: appModel.Project, Cluster: &v1.ClusterTarget{ClusterName: "local", Namespace: "dev-target1"}})
|
||||
Expect(err).Should(BeNil())
|
||||
_, err = envUsecase.UpdateEnv(context.TODO(), "app-dev",
|
||||
v1.UpdateEnvRequest{
|
||||
@@ -594,8 +608,9 @@ var _ = Describe("Test application component usecase function", func() {
|
||||
ds, err := NewDatastore(datastore.Config{Type: "kubeapi", Database: "app-test-kubevela"})
|
||||
Expect(ds).ToNot(BeNil())
|
||||
Expect(err).Should(BeNil())
|
||||
projectUsecase = &projectUsecaseImpl{ds: ds, k8sClient: k8sClient}
|
||||
envUsecase = &envUsecaseImpl{ds: ds, kubeClient: k8sClient}
|
||||
rbacUsecase := &rbacUsecaseImpl{ds: ds}
|
||||
projectUsecase = &projectUsecaseImpl{ds: ds, k8sClient: k8sClient, rbacUsecase: rbacUsecase}
|
||||
envUsecase = &envUsecaseImpl{ds: ds, kubeClient: k8sClient, projectUsecase: projectUsecase}
|
||||
workflowUsecase := &workflowUsecaseImpl{ds: ds, envUsecase: envUsecase}
|
||||
envBindingUsecase := &envBindingUsecaseImpl{ds: ds, envUsecase: envUsecase, workflowUsecase: workflowUsecase, kubeClient: k8sClient}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ import (
|
||||
"github.com/form3tech-oss/jwt-go"
|
||||
"golang.org/x/oauth2"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/yaml"
|
||||
@@ -172,6 +173,9 @@ func (a *authenticationUsecaseImpl) Login(ctx context.Context, loginReq apisv1.L
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if userBase.Disabled {
|
||||
return nil, bcode.ErrUserAlreadyDisabled
|
||||
}
|
||||
accessToken, err := a.generateJWTToken(userBase.Name, GrantTypeAccess, time.Hour)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -255,6 +259,9 @@ func (a *authenticationUsecaseImpl) GetDexConfig(ctx context.Context) (*apisv1.D
|
||||
Name: secretDexConfig,
|
||||
Namespace: velatypes.DefaultKubeVelaNS,
|
||||
}, secret); err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
return nil, bcode.ErrDexConfigNotFound
|
||||
}
|
||||
log.Logger.Errorf("failed to get dex config: %s", err.Error())
|
||||
return nil, err
|
||||
}
|
||||
@@ -287,8 +294,12 @@ func (a *authenticationUsecaseImpl) GetLoginType(ctx context.Context) (*apisv1.G
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
loginType := sysInfo.LoginType
|
||||
if loginType == "" {
|
||||
loginType = model.LoginTypeLocal
|
||||
}
|
||||
return &apisv1.GetLoginTypeResponse{
|
||||
LoginType: sysInfo.LoginType,
|
||||
LoginType: loginType,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -337,7 +348,7 @@ func (l *localHandlerImpl) login(ctx context.Context) (*apisv1.UserBase, error)
|
||||
if err := compareHashWithPassword(user.Password, l.password); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := l.userUsecase.updateUserLoginTime(ctx, user); err != nil {
|
||||
if err := l.userUsecase.UpdateUserLoginTime(ctx, user); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &apisv1.UserBase{
|
||||
|
||||
@@ -179,11 +179,6 @@ var _ = Describe("Test namespace usecase functions", func() {
|
||||
Expect(cmp.Diff(len(uiSchema), 12)).Should(BeEmpty())
|
||||
Expect(cmp.Diff(uiSchema[7].JSONKey, "livenessProbe")).Should(BeEmpty())
|
||||
Expect(cmp.Diff(len(uiSchema[7].SubParameters), 8)).Should(BeEmpty())
|
||||
|
||||
outdata, err := yaml.Marshal(uiSchema)
|
||||
Expect(err).Should(Succeed())
|
||||
err = ioutil.WriteFile("./testdata/ui-schema.yaml", outdata, 0755)
|
||||
Expect(err).Should(Succeed())
|
||||
})
|
||||
|
||||
It("Test sortDefaultUISchema", testSortDefaultUISchema)
|
||||
|
||||
@@ -39,23 +39,25 @@ import (
|
||||
type EnvUsecase interface {
|
||||
GetEnv(ctx context.Context, envName string) (*model.Env, error)
|
||||
ListEnvs(ctx context.Context, page, pageSize int, listOption apisv1.ListEnvOptions) (*apisv1.ListEnvResponse, error)
|
||||
ListEnvCount(ctx context.Context, listOption apisv1.ListEnvOptions) (int64, error)
|
||||
DeleteEnv(ctx context.Context, envName string) error
|
||||
CreateEnv(ctx context.Context, req apisv1.CreateEnvRequest) (*apisv1.Env, error)
|
||||
UpdateEnv(ctx context.Context, envName string, req apisv1.UpdateEnvRequest) (*apisv1.Env, error)
|
||||
}
|
||||
|
||||
type envUsecaseImpl struct {
|
||||
ds datastore.DataStore
|
||||
kubeClient client.Client
|
||||
ds datastore.DataStore
|
||||
projectUsecase ProjectUsecase
|
||||
kubeClient client.Client
|
||||
}
|
||||
|
||||
// NewEnvUsecase new env usecase
|
||||
func NewEnvUsecase(ds datastore.DataStore) EnvUsecase {
|
||||
func NewEnvUsecase(ds datastore.DataStore, projectUsecase ProjectUsecase) EnvUsecase {
|
||||
kubecli, err := clients.GetKubeClient()
|
||||
if err != nil {
|
||||
log.Logger.Fatalf("get kubeclient failure %s", err.Error())
|
||||
}
|
||||
return &envUsecaseImpl{kubeClient: kubecli, ds: ds}
|
||||
return &envUsecaseImpl{kubeClient: kubecli, ds: ds, projectUsecase: projectUsecase}
|
||||
}
|
||||
|
||||
// GetEnv get env
|
||||
@@ -96,40 +98,75 @@ func (p *envUsecaseImpl) DeleteEnv(ctx context.Context, envName string) error {
|
||||
|
||||
// ListEnvs list envs
|
||||
func (p *envUsecaseImpl) ListEnvs(ctx context.Context, page, pageSize int, listOption apisv1.ListEnvOptions) (*apisv1.ListEnvResponse, error) {
|
||||
entities, err := listEnvs(ctx, p.ds, listOption.Project, &datastore.ListOptions{Page: page, PageSize: pageSize, SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}}})
|
||||
userName, ok := ctx.Value(&apisv1.CtxKeyUser).(string)
|
||||
if !ok {
|
||||
return nil, bcode.ErrUnauthorized
|
||||
}
|
||||
projects, err := p.projectUsecase.ListUserProjects(ctx, userName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var availableProjectNames []string
|
||||
var projectNameAlias = make(map[string]string)
|
||||
for _, project := range projects {
|
||||
availableProjectNames = append(availableProjectNames, project.Name)
|
||||
projectNameAlias[project.Name] = project.Alias
|
||||
}
|
||||
if len(availableProjectNames) == 0 {
|
||||
return &apisv1.ListEnvResponse{Envs: []*apisv1.Env{}, Total: 0}, nil
|
||||
}
|
||||
if listOption.Project != "" {
|
||||
if !util.StringsContain(availableProjectNames, listOption.Project) {
|
||||
return &apisv1.ListEnvResponse{Envs: []*apisv1.Env{}, Total: 0}, nil
|
||||
}
|
||||
}
|
||||
projectNames := []string{listOption.Project}
|
||||
if listOption.Project == "" {
|
||||
projectNames = availableProjectNames
|
||||
}
|
||||
filter := datastore.FilterOptions{
|
||||
In: []datastore.InQueryOption{
|
||||
{
|
||||
Key: "project",
|
||||
Values: projectNames,
|
||||
},
|
||||
},
|
||||
}
|
||||
entities, err := listEnvs(ctx, p.ds, &datastore.ListOptions{
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}},
|
||||
FilterOptions: filter,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
Targets, err := listTarget(ctx, p.ds, nil)
|
||||
targets, err := listTarget(ctx, p.ds, listOption.Project, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var envs []*apisv1.Env
|
||||
for _, ee := range entities {
|
||||
envs = append(envs, convertEnvModel2Base(ee, Targets))
|
||||
envs = append(envs, convertEnvModel2Base(ee, targets))
|
||||
}
|
||||
|
||||
projectResp, err := listProjects(ctx, p.ds, 0, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
for i := range envs {
|
||||
envs[i].Project.Alias = projectNameAlias[envs[i].Project.Name]
|
||||
}
|
||||
for _, e := range envs {
|
||||
for _, pj := range projectResp.Projects {
|
||||
if e.Project.Name == pj.Name {
|
||||
e.Project.Alias = pj.Alias
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
total, err := p.ds.Count(ctx, &model.Env{Project: listOption.Project}, nil)
|
||||
|
||||
total, err := p.ds.Count(ctx, &model.Env{Project: listOption.Project}, &filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &apisv1.ListEnvResponse{Envs: envs, Total: total}, nil
|
||||
}
|
||||
|
||||
func (p *envUsecaseImpl) ListEnvCount(ctx context.Context, listOption apisv1.ListEnvOptions) (int64, error) {
|
||||
return p.ds.Count(ctx, &model.Env{Project: listOption.Project}, nil)
|
||||
}
|
||||
|
||||
func checkEqual(old, new []string) bool {
|
||||
if old == nil && new == nil {
|
||||
return true
|
||||
@@ -186,7 +223,7 @@ func (p *envUsecaseImpl) UpdateEnv(ctx context.Context, name string, req apisv1.
|
||||
env.Targets = req.Targets
|
||||
}
|
||||
|
||||
targets, err := listTarget(ctx, p.ds, nil)
|
||||
targets, err := listTarget(ctx, p.ds, "", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -232,7 +269,7 @@ func (p *envUsecaseImpl) CreateEnv(ctx context.Context, req apisv1.CreateEnvRequ
|
||||
return nil, bcode.ErrEnvTargetConflict
|
||||
}
|
||||
|
||||
targets, err := listTarget(ctx, p.ds, nil)
|
||||
targets, err := listTarget(ctx, p.ds, "", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -79,11 +79,8 @@ func getEnv(ctx context.Context, ds datastore.DataStore, envName string) (*model
|
||||
return env, nil
|
||||
}
|
||||
|
||||
func listEnvs(ctx context.Context, ds datastore.DataStore, project string, listOption *datastore.ListOptions) ([]*model.Env, error) {
|
||||
func listEnvs(ctx context.Context, ds datastore.DataStore, listOption *datastore.ListOptions) ([]*model.Env, error) {
|
||||
var env = model.Env{}
|
||||
if project != "" {
|
||||
env.Project = project
|
||||
}
|
||||
entities, err := ds.List(ctx, &env, listOption)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -43,7 +43,9 @@ var _ = Describe("Test env usecase functions", func() {
|
||||
ds, err = NewDatastore(datastore.Config{Type: "kubeapi", Database: "env-test-kubevela"})
|
||||
Expect(ds).ToNot(BeNil())
|
||||
Expect(err).Should(BeNil())
|
||||
envUsecase = &envUsecaseImpl{kubeClient: k8sClient, ds: ds}
|
||||
rbacUsecase := &rbacUsecaseImpl{ds: ds}
|
||||
projectUsecase := &projectUsecaseImpl{ds: ds, k8sClient: k8sClient, rbacUsecase: rbacUsecase}
|
||||
envUsecase = &envUsecaseImpl{kubeClient: k8sClient, ds: ds, projectUsecase: projectUsecase}
|
||||
})
|
||||
It("Test Create/Get/Delete Env function", func() {
|
||||
// create target
|
||||
@@ -111,7 +113,7 @@ var _ = Describe("Test env usecase functions", func() {
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
By("Test ListEnvs function")
|
||||
_, err = envUsecase.ListEnvs(context.TODO(), 1, 1, apisv1.ListEnvOptions{})
|
||||
_, err = envUsecase.ListEnvs(context.WithValue(context.TODO(), &apisv1.CtxKeyUser, "admin"), 1, 1, apisv1.ListEnvOptions{})
|
||||
Expect(err).Should(BeNil())
|
||||
})
|
||||
|
||||
|
||||
@@ -95,15 +95,26 @@ func pickEnv(envs []*model.Env, name string) (*model.Env, error) {
|
||||
func listFullEnvBinding(ctx context.Context, ds datastore.DataStore, option envListOption) ([]*apisv1.EnvBindingBase, error) {
|
||||
envBindings, err := listEnvBindings(ctx, ds, option)
|
||||
if err != nil {
|
||||
|
||||
return nil, bcode.ErrEnvBindingsNotExist
|
||||
}
|
||||
targets, err := listTarget(ctx, ds, nil)
|
||||
targets, err := listTarget(ctx, ds, "", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// TODO: list by project
|
||||
envs, err := listEnvs(ctx, ds, "", nil)
|
||||
var listOption *datastore.ListOptions
|
||||
if option.projectName != "" {
|
||||
listOption = &datastore.ListOptions{
|
||||
FilterOptions: datastore.FilterOptions{
|
||||
In: []datastore.InQueryOption{
|
||||
{
|
||||
Key: "project",
|
||||
Values: []string{option.projectName},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
envs, err := listEnvs(ctx, ds, listOption)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -120,7 +131,7 @@ func listFullEnvBinding(ctx context.Context, ds datastore.DataStore, option envL
|
||||
}
|
||||
|
||||
func (e *envBindingUsecaseImpl) GetEnvBindings(ctx context.Context, app *model.Application) ([]*apisv1.EnvBindingBase, error) {
|
||||
full, err := listFullEnvBinding(ctx, e.ds, envListOption{appPrimaryKey: app.PrimaryKey()})
|
||||
full, err := listFullEnvBinding(ctx, e.ds, envListOption{appPrimaryKey: app.PrimaryKey(), projectName: app.Project})
|
||||
if err != nil {
|
||||
log.Logger.Errorf("list envbinding for app %s err: %v\n", app.Name, err)
|
||||
return nil, err
|
||||
@@ -306,7 +317,7 @@ func (e *envBindingUsecaseImpl) deleteEnvWorkflow(ctx context.Context, app *mode
|
||||
}
|
||||
|
||||
func (e *envBindingUsecaseImpl) DetailEnvBinding(ctx context.Context, app *model.Application, envBinding *model.EnvBinding) (*apisv1.DetailEnvBindingResponse, error) {
|
||||
targets, err := listTarget(ctx, e.ds, nil)
|
||||
targets, err := listTarget(ctx, e.ds, "", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
type envListOption struct {
|
||||
appPrimaryKey string
|
||||
envName string
|
||||
projectName string
|
||||
}
|
||||
|
||||
func listEnvBindings(ctx context.Context, ds datastore.DataStore, listOption envListOption) ([]*model.EnvBinding, error) {
|
||||
|
||||
@@ -48,7 +48,9 @@ var _ = Describe("Test envBindingUsecase functions", func() {
|
||||
testApp = &model.Application{
|
||||
Name: "test-app-env",
|
||||
}
|
||||
envUsecase = &envUsecaseImpl{ds: ds, kubeClient: k8sClient}
|
||||
rbacUsecase := &rbacUsecaseImpl{ds: ds}
|
||||
projectUsecase := &projectUsecaseImpl{ds: ds, k8sClient: k8sClient, rbacUsecase: rbacUsecase}
|
||||
envUsecase = &envUsecaseImpl{ds: ds, kubeClient: k8sClient, projectUsecase: projectUsecase}
|
||||
workflowUsecase = &workflowUsecaseImpl{ds: ds, kubeClient: k8sClient, envUsecase: envUsecase}
|
||||
definitionUsecase = &definitionUsecaseImpl{kubeClient: k8sClient, caches: utils.NewMemoryCacheStore(context.TODO())}
|
||||
envBindingUsecase = &envBindingUsecaseImpl{ds: ds, workflowUsecase: workflowUsecase, definitionUsecase: definitionUsecase, kubeClient: k8sClient, envUsecase: envUsecase}
|
||||
|
||||
@@ -19,6 +19,7 @@ package usecase
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
@@ -34,44 +35,80 @@ import (
|
||||
// ProjectUsecase project manage usecase.
|
||||
type ProjectUsecase interface {
|
||||
GetProject(ctx context.Context, projectName string) (*model.Project, error)
|
||||
DetailProject(ctx context.Context, projectName string) (*apisv1.ProjectBase, error)
|
||||
ListProjects(ctx context.Context, page, pageSize int) (*apisv1.ListProjectResponse, error)
|
||||
ListUserProjects(ctx context.Context, userName string) ([]*apisv1.ProjectBase, error)
|
||||
CreateProject(ctx context.Context, req apisv1.CreateProjectRequest) (*apisv1.ProjectBase, error)
|
||||
DeleteProject(ctx context.Context, projectName string) error
|
||||
UpdateProject(ctx context.Context, projectName string, req apisv1.UpdateProjectRequest) (*apisv1.ProjectBase, error)
|
||||
ListProjectUser(ctx context.Context, projectName string, page, pageSize int) (*apisv1.ListProjectUsersResponse, error)
|
||||
AddProjectUser(ctx context.Context, projectName string, req apisv1.AddProjectUserRequest) (*apisv1.ProjectUserBase, error)
|
||||
DeleteProjectUser(ctx context.Context, projectName string, userName string) error
|
||||
UpdateProjectUser(ctx context.Context, projectName string, userName string, req apisv1.UpdateProjectUserRequest) (*apisv1.ProjectUserBase, error)
|
||||
Init(ctx context.Context) error
|
||||
}
|
||||
|
||||
type projectUsecaseImpl struct {
|
||||
ds datastore.DataStore
|
||||
k8sClient client.Client
|
||||
ds datastore.DataStore
|
||||
k8sClient client.Client
|
||||
rbacUsecase RBACUsecase
|
||||
}
|
||||
|
||||
// NewProjectUsecase new project usecase
|
||||
func NewProjectUsecase(ds datastore.DataStore) ProjectUsecase {
|
||||
func NewProjectUsecase(ds datastore.DataStore, rbacUsecase RBACUsecase) ProjectUsecase {
|
||||
k8sClient, err := clients.GetKubeClient()
|
||||
if err != nil {
|
||||
log.Logger.Fatalf("get k8sClient failure: %s", err.Error())
|
||||
}
|
||||
p := &projectUsecaseImpl{ds: ds, k8sClient: k8sClient}
|
||||
p.initDefaultProjectEnvTarget(model.DefaultInitNamespace)
|
||||
p := &projectUsecaseImpl{ds: ds, k8sClient: k8sClient, rbacUsecase: rbacUsecase}
|
||||
return p
|
||||
}
|
||||
|
||||
// Init init default data
|
||||
func (p *projectUsecaseImpl) Init(ctx context.Context) error {
|
||||
return p.InitDefaultProjectEnvTarget(ctx, model.DefaultInitNamespace)
|
||||
}
|
||||
|
||||
// initDefaultProjectEnvTarget will initialize a default project with a default env that contain a default target
|
||||
// the default env and default target both using the `default` namespace in control plane cluster
|
||||
func (p *projectUsecaseImpl) initDefaultProjectEnvTarget(defaultNamespace string) {
|
||||
|
||||
ctx := context.Background()
|
||||
projResp, err := listProjects(ctx, p.ds, 0, 0)
|
||||
func (p *projectUsecaseImpl) InitDefaultProjectEnvTarget(ctx context.Context, defaultNamespace string) error {
|
||||
var project = model.Project{}
|
||||
entities, err := p.ds.List(ctx, &project, &datastore.ListOptions{FilterOptions: datastore.FilterOptions{
|
||||
IsNotExist: []datastore.IsNotExistQueryOption{
|
||||
{
|
||||
Key: "owner",
|
||||
},
|
||||
},
|
||||
}})
|
||||
if err != nil {
|
||||
log.Logger.Errorf("initialize project failed %v", err)
|
||||
return
|
||||
return fmt.Errorf("initialize project failed %w", err)
|
||||
}
|
||||
if len(projResp.Projects) > 0 {
|
||||
return
|
||||
if len(entities) > 0 {
|
||||
for _, project := range entities {
|
||||
pro := project.(*model.Project)
|
||||
var init = pro.Owner == ""
|
||||
pro.Owner = model.DefaultAdminUserName
|
||||
if err := p.ds.Put(ctx, pro); err != nil {
|
||||
return err
|
||||
}
|
||||
// owner is empty, it is old data
|
||||
if init {
|
||||
if err := p.rbacUsecase.InitDefaultRoleAndUsersForProject(ctx, pro); err != nil {
|
||||
return fmt.Errorf("init default role and users for project %s failure %w", pro.Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
count, _ := p.ds.Count(ctx, &project, nil)
|
||||
if count > 0 {
|
||||
return nil
|
||||
}
|
||||
log.Logger.Info("no default project found, adding a default project with default env and target")
|
||||
|
||||
if err := createTargetNamespace(ctx, p.k8sClient, multicluster.ClusterLocalName, defaultNamespace, model.DefaultInitName); err != nil {
|
||||
log.Logger.Errorf("initialize default target namespace failed %v", err)
|
||||
return
|
||||
return fmt.Errorf("initialize default target namespace failed %w", err)
|
||||
}
|
||||
// initialize default target first
|
||||
err = createTarget(ctx, p.ds, &model.Target{
|
||||
@@ -85,8 +122,7 @@ func (p *projectUsecaseImpl) initDefaultProjectEnvTarget(defaultNamespace string
|
||||
})
|
||||
// for idempotence, ignore default target already exist error
|
||||
if err != nil && errors.Is(err, bcode.ErrTargetExist) {
|
||||
log.Logger.Errorf("initialize default target failed %v", err)
|
||||
return
|
||||
return fmt.Errorf("initialize default target failed %w", err)
|
||||
}
|
||||
|
||||
// initialize default target first
|
||||
@@ -100,20 +136,20 @@ func (p *projectUsecaseImpl) initDefaultProjectEnvTarget(defaultNamespace string
|
||||
})
|
||||
// for idempotence, ignore default env already exist error
|
||||
if err != nil && errors.Is(err, bcode.ErrEnvAlreadyExists) {
|
||||
log.Logger.Errorf("initialize default environment failed %v", err)
|
||||
return
|
||||
|
||||
return fmt.Errorf("initialize default environment failed %w", err)
|
||||
}
|
||||
|
||||
_, err = p.CreateProject(ctx, apisv1.CreateProjectRequest{
|
||||
Name: model.DefaultInitName,
|
||||
Alias: "Default",
|
||||
Description: model.DefaultProjectDescription,
|
||||
Owner: model.DefaultAdminUserName,
|
||||
})
|
||||
if err != nil {
|
||||
log.Logger.Errorf("initialize project failed %v", err)
|
||||
return
|
||||
return fmt.Errorf("initialize project failed %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetProject get project
|
||||
@@ -128,24 +164,73 @@ func (p *projectUsecaseImpl) GetProject(ctx context.Context, projectName string)
|
||||
return project, nil
|
||||
}
|
||||
|
||||
func (p *projectUsecaseImpl) DetailProject(ctx context.Context, projectName string) (*apisv1.ProjectBase, error) {
|
||||
project, err := p.GetProject(ctx, projectName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var user = &model.User{Name: project.Owner}
|
||||
if project.Owner != "" {
|
||||
if err := p.ds.Get(ctx, user); err != nil {
|
||||
log.Logger.Warnf("get project owner %s info failure %s", project.Owner, err.Error())
|
||||
}
|
||||
}
|
||||
return ConvertProjectModel2Base(project, user), nil
|
||||
}
|
||||
|
||||
func listProjects(ctx context.Context, ds datastore.DataStore, page, pageSize int) (*apisv1.ListProjectResponse, error) {
|
||||
var project = model.Project{}
|
||||
entitys, err := ds.List(ctx, &project, &datastore.ListOptions{Page: page, PageSize: pageSize, SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}}})
|
||||
entities, err := ds.List(ctx, &project, &datastore.ListOptions{Page: page, PageSize: pageSize, SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}}})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var projects []*apisv1.ProjectBase
|
||||
for _, entity := range entitys {
|
||||
for _, entity := range entities {
|
||||
project := entity.(*model.Project)
|
||||
projects = append(projects, convertProjectModel2Base(project))
|
||||
var user = &model.User{Name: project.Owner}
|
||||
if project.Owner != "" {
|
||||
if err := ds.Get(ctx, user); err != nil {
|
||||
log.Logger.Warnf("get project owner %s info failure %s", project.Owner, err.Error())
|
||||
}
|
||||
}
|
||||
projects = append(projects, ConvertProjectModel2Base(project, user))
|
||||
}
|
||||
total, err := ds.Count(ctx, &model.Project{}, nil)
|
||||
total, err := ds.Count(ctx, &project, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &apisv1.ListProjectResponse{Projects: projects, Total: total}, nil
|
||||
}
|
||||
|
||||
func (p *projectUsecaseImpl) ListUserProjects(ctx context.Context, userName string) ([]*apisv1.ProjectBase, error) {
|
||||
var projectUser = model.ProjectUser{
|
||||
Username: userName,
|
||||
}
|
||||
entities, err := p.ds.List(ctx, &projectUser, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var projectNames []string
|
||||
for _, entity := range entities {
|
||||
projectNames = append(projectNames, entity.(*model.ProjectUser).ProjectName)
|
||||
}
|
||||
if len(projectNames) == 0 {
|
||||
return []*apisv1.ProjectBase{}, nil
|
||||
}
|
||||
projectEntities, err := p.ds.List(ctx, &model.Project{}, &datastore.ListOptions{FilterOptions: datastore.FilterOptions{In: []datastore.InQueryOption{{
|
||||
Key: "name",
|
||||
Values: projectNames,
|
||||
}}}})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var projectBases []*apisv1.ProjectBase
|
||||
for _, entity := range projectEntities {
|
||||
projectBases = append(projectBases, ConvertProjectModel2Base(entity.(*model.Project), nil))
|
||||
}
|
||||
return projectBases, nil
|
||||
}
|
||||
|
||||
// ListProjects list projects
|
||||
func (p *projectUsecaseImpl) ListProjects(ctx context.Context, page, pageSize int) (*apisv1.ListProjectResponse, error) {
|
||||
return listProjects(ctx, p.ds, page, pageSize)
|
||||
@@ -153,10 +238,58 @@ func (p *projectUsecaseImpl) ListProjects(ctx context.Context, page, pageSize in
|
||||
|
||||
// DeleteProject delete a project
|
||||
func (p *projectUsecaseImpl) DeleteProject(ctx context.Context, name string) error {
|
||||
_, err := p.GetProject(ctx, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO(@wonderflow): it's not supported for delete a project now, just used in test
|
||||
// we should prevent delete a project that contain any application/env inside.
|
||||
count, err := p.ds.Count(ctx, &model.Application{Project: name}, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if count > 0 {
|
||||
return bcode.ErrProjectDenyDeleteByApplication
|
||||
}
|
||||
|
||||
count, err = p.ds.Count(ctx, &model.Target{Project: name}, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if count > 0 {
|
||||
return bcode.ErrProjectDenyDeleteByTarget
|
||||
}
|
||||
|
||||
count, err = p.ds.Count(ctx, &model.Env{Project: name}, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if count > 0 {
|
||||
return bcode.ErrProjectDenyDeleteByEnvironment
|
||||
}
|
||||
|
||||
users, _ := p.ListProjectUser(ctx, name, 0, 0)
|
||||
for _, user := range users.Users {
|
||||
err := p.DeleteProjectUser(ctx, name, user.UserName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
roles, _ := p.rbacUsecase.ListRole(ctx, name, 0, 0)
|
||||
for _, role := range roles.Roles {
|
||||
err := p.rbacUsecase.DeleteRole(ctx, name, role.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
permissions, _ := p.rbacUsecase.ListPermissions(ctx, name)
|
||||
for _, perm := range permissions {
|
||||
err := p.rbacUsecase.DeletePermission(ctx, name, perm.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return p.ds.Delete(ctx, &model.Project{Name: name})
|
||||
}
|
||||
|
||||
@@ -171,32 +304,191 @@ func (p *projectUsecaseImpl) CreateProject(ctx context.Context, req apisv1.Creat
|
||||
if exist {
|
||||
return nil, bcode.ErrProjectIsExist
|
||||
}
|
||||
owner := req.Owner
|
||||
if owner == "" {
|
||||
loginUserName, ok := ctx.Value(&apisv1.CtxKeyUser).(string)
|
||||
if ok {
|
||||
owner = loginUserName
|
||||
}
|
||||
}
|
||||
var user = &model.User{Name: owner}
|
||||
if owner != "" {
|
||||
if err := p.ds.Get(ctx, user); err != nil {
|
||||
return nil, bcode.ErrProjectOwnerIsNotExist
|
||||
}
|
||||
}
|
||||
|
||||
newProject := &model.Project{
|
||||
Name: req.Name,
|
||||
Description: req.Description,
|
||||
Alias: req.Alias,
|
||||
Owner: owner,
|
||||
}
|
||||
|
||||
if err := p.ds.Add(ctx, newProject); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &apisv1.ProjectBase{
|
||||
Name: newProject.Name,
|
||||
Alias: newProject.Alias,
|
||||
Description: newProject.Description,
|
||||
CreateTime: newProject.CreateTime,
|
||||
UpdateTime: newProject.UpdateTime,
|
||||
}, nil
|
||||
if err := p.rbacUsecase.InitDefaultRoleAndUsersForProject(ctx, newProject); err != nil {
|
||||
log.Logger.Errorf("init default role and users for project failure %s", err.Error())
|
||||
}
|
||||
|
||||
return ConvertProjectModel2Base(newProject, user), nil
|
||||
}
|
||||
|
||||
func convertProjectModel2Base(project *model.Project) *apisv1.ProjectBase {
|
||||
return &apisv1.ProjectBase{
|
||||
// UpdateProject update project
|
||||
func (p *projectUsecaseImpl) UpdateProject(ctx context.Context, projectName string, req apisv1.UpdateProjectRequest) (*apisv1.ProjectBase, error) {
|
||||
project, err := p.GetProject(ctx, projectName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
project.Alias = req.Alias
|
||||
project.Description = req.Description
|
||||
var user = &model.User{Name: req.Owner}
|
||||
if req.Owner != "" {
|
||||
if err := p.ds.Get(ctx, user); err != nil {
|
||||
if errors.Is(err, datastore.ErrRecordNotExist) {
|
||||
return nil, bcode.ErrProjectOwnerIsNotExist
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
project.Owner = req.Owner
|
||||
}
|
||||
err = p.ds.Put(ctx, project)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ConvertProjectModel2Base(project, user), nil
|
||||
}
|
||||
|
||||
func (p *projectUsecaseImpl) ListProjectUser(ctx context.Context, projectName string, page, pageSize int) (*apisv1.ListProjectUsersResponse, error) {
|
||||
var projectUser = model.ProjectUser{
|
||||
ProjectName: projectName,
|
||||
}
|
||||
entities, err := p.ds.List(ctx, &projectUser, &datastore.ListOptions{Page: page, PageSize: pageSize, SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}}})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var res apisv1.ListProjectUsersResponse
|
||||
for _, entity := range entities {
|
||||
res.Users = append(res.Users, ConvertProjectUserModel2Base(entity.(*model.ProjectUser)))
|
||||
}
|
||||
count, err := p.ds.Count(ctx, &projectUser, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res.Total = count
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func (p *projectUsecaseImpl) AddProjectUser(ctx context.Context, projectName string, req apisv1.AddProjectUserRequest) (*apisv1.ProjectUserBase, error) {
|
||||
project, err := p.GetProject(ctx, projectName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// check user roles
|
||||
for _, role := range req.UserRoles {
|
||||
var projectUser = model.Role{
|
||||
Name: role,
|
||||
Project: projectName,
|
||||
}
|
||||
if err := p.ds.Get(ctx, &projectUser); err != nil {
|
||||
return nil, bcode.ErrProjectRoleCheckFailure
|
||||
}
|
||||
if projectUser.Project != "" && projectUser.Project != projectName {
|
||||
return nil, bcode.ErrProjectRoleCheckFailure
|
||||
}
|
||||
}
|
||||
var projectUser = model.ProjectUser{
|
||||
Username: req.UserName,
|
||||
ProjectName: project.Name,
|
||||
UserRoles: req.UserRoles,
|
||||
}
|
||||
if err := p.ds.Add(ctx, &projectUser); err != nil {
|
||||
if errors.Is(err, datastore.ErrRecordExist) {
|
||||
return nil, bcode.ErrProjectUserExist
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return ConvertProjectUserModel2Base(&projectUser), nil
|
||||
}
|
||||
|
||||
func (p *projectUsecaseImpl) DeleteProjectUser(ctx context.Context, projectName string, userName string) error {
|
||||
project, err := p.GetProject(ctx, projectName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var projectUser = model.ProjectUser{
|
||||
Username: userName,
|
||||
ProjectName: project.Name,
|
||||
}
|
||||
if err := p.ds.Delete(ctx, &projectUser); err != nil {
|
||||
if errors.Is(err, datastore.ErrRecordExist) {
|
||||
return bcode.ErrProjectUserExist
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *projectUsecaseImpl) UpdateProjectUser(ctx context.Context, projectName string, userName string, req apisv1.UpdateProjectUserRequest) (*apisv1.ProjectUserBase, error) {
|
||||
project, err := p.GetProject(ctx, projectName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// check user roles
|
||||
for _, role := range req.UserRoles {
|
||||
var projectUser = model.Role{
|
||||
Name: role,
|
||||
Project: projectName,
|
||||
}
|
||||
if err := p.ds.Get(ctx, &projectUser); err != nil {
|
||||
return nil, bcode.ErrProjectRoleCheckFailure
|
||||
}
|
||||
if projectUser.Project != "" && projectUser.Project != projectName {
|
||||
return nil, bcode.ErrProjectRoleCheckFailure
|
||||
}
|
||||
}
|
||||
var projectUser = model.ProjectUser{
|
||||
Username: userName,
|
||||
ProjectName: project.Name,
|
||||
}
|
||||
if err := p.ds.Get(ctx, &projectUser); err != nil {
|
||||
if errors.Is(err, datastore.ErrRecordExist) {
|
||||
return nil, bcode.ErrProjectUserExist
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
projectUser.UserRoles = req.UserRoles
|
||||
if err := p.ds.Put(ctx, &projectUser); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ConvertProjectUserModel2Base(&projectUser), nil
|
||||
}
|
||||
|
||||
// ConvertProjectModel2Base convert project model to base struct
|
||||
func ConvertProjectModel2Base(project *model.Project, owner *model.User) *apisv1.ProjectBase {
|
||||
base := &apisv1.ProjectBase{
|
||||
Name: project.Name,
|
||||
Description: project.Description,
|
||||
Alias: project.Alias,
|
||||
CreateTime: project.CreateTime,
|
||||
UpdateTime: project.UpdateTime,
|
||||
Owner: apisv1.NameAlias{Name: project.Owner},
|
||||
}
|
||||
if owner != nil && owner.Name == project.Owner {
|
||||
base.Owner = apisv1.NameAlias{Name: owner.Name, Alias: owner.Alias}
|
||||
}
|
||||
return base
|
||||
}
|
||||
|
||||
// ConvertProjectUserModel2Base convert project user model to base struct
|
||||
func ConvertProjectUserModel2Base(user *model.ProjectUser) *apisv1.ProjectUserBase {
|
||||
base := &apisv1.ProjectUserBase{
|
||||
UserName: user.Username,
|
||||
UserRoles: user.UserRoles,
|
||||
CreateTime: user.CreateTime,
|
||||
UpdateTime: user.UpdateTime,
|
||||
}
|
||||
return base
|
||||
}
|
||||
|
||||
@@ -26,9 +26,11 @@ import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
velatypes "github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/datastore"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/model"
|
||||
apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode"
|
||||
"github.com/oam-dev/kubevela/pkg/multicluster"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/oam/util"
|
||||
@@ -38,6 +40,7 @@ var _ = Describe("Test project usecase functions", func() {
|
||||
var (
|
||||
projectUsecase *projectUsecaseImpl
|
||||
envImpl *envUsecaseImpl
|
||||
userUsecase *userUsecaseImpl
|
||||
targetImpl *targetUsecaseImpl
|
||||
defaultNamespace = "project-default-ns1-test"
|
||||
)
|
||||
@@ -45,11 +48,13 @@ var _ = Describe("Test project usecase functions", func() {
|
||||
ds, err := NewDatastore(datastore.Config{Type: "kubeapi", Database: "target-test-kubevela"})
|
||||
Expect(ds).ToNot(BeNil())
|
||||
Expect(err).Should(BeNil())
|
||||
userUsecase = &userUsecaseImpl{ds: ds, k8sClient: k8sClient}
|
||||
var ns = corev1.Namespace{}
|
||||
ns.Name = defaultNamespace
|
||||
err = k8sClient.Create(context.TODO(), &ns)
|
||||
Expect(err).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
projectUsecase = &projectUsecaseImpl{k8sClient: k8sClient, ds: ds}
|
||||
|
||||
projectUsecase = &projectUsecaseImpl{k8sClient: k8sClient, ds: ds, rbacUsecase: &rbacUsecaseImpl{ds: ds}}
|
||||
pp, err := projectUsecase.ListProjects(context.TODO(), 0, 0)
|
||||
Expect(err).Should(BeNil())
|
||||
// reset all projects
|
||||
@@ -57,24 +62,35 @@ var _ = Describe("Test project usecase functions", func() {
|
||||
_ = projectUsecase.DeleteProject(context.TODO(), p.Name)
|
||||
}
|
||||
|
||||
envImpl = &envUsecaseImpl{kubeClient: k8sClient, ds: ds}
|
||||
envs, err := envImpl.ListEnvs(context.TODO(), 0, 0, apisv1.ListEnvOptions{})
|
||||
envImpl = &envUsecaseImpl{kubeClient: k8sClient, ds: ds, projectUsecase: projectUsecase}
|
||||
ctx := context.WithValue(context.TODO(), &apisv1.CtxKeyUser, "admin")
|
||||
envs, err := envImpl.ListEnvs(ctx, 0, 0, apisv1.ListEnvOptions{})
|
||||
Expect(err).Should(BeNil())
|
||||
// reset all projects
|
||||
for _, e := range envs.Envs {
|
||||
_ = envImpl.DeleteEnv(context.TODO(), e.Name)
|
||||
}
|
||||
targetImpl = &targetUsecaseImpl{k8sClient: k8sClient, ds: ds}
|
||||
targs, err := targetImpl.ListTargets(context.TODO(), 0, 0)
|
||||
targets, err := targetImpl.ListTargets(context.TODO(), 0, 0, "")
|
||||
Expect(err).Should(BeNil())
|
||||
// reset all projects
|
||||
for _, e := range targs.Targets {
|
||||
_ = envImpl.DeleteEnv(context.TODO(), e.Name)
|
||||
for _, t := range targets.Targets {
|
||||
_ = targetImpl.DeleteTarget(context.TODO(), t.Name)
|
||||
}
|
||||
})
|
||||
It("Test project initialize function", func() {
|
||||
|
||||
projectUsecase.initDefaultProjectEnvTarget(defaultNamespace)
|
||||
// init admin user
|
||||
var ns = corev1.Namespace{}
|
||||
ns.Name = velatypes.DefaultKubeVelaNS
|
||||
err := k8sClient.Create(context.TODO(), &ns)
|
||||
Expect(err).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{}))
|
||||
err = userUsecase.Init(context.TODO())
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
// init default project
|
||||
err = projectUsecase.InitDefaultProjectEnvTarget(context.WithValue(context.TODO(), &apisv1.CtxKeyUser, model.DefaultAdminUserName), defaultNamespace)
|
||||
Expect(err).Should(BeNil())
|
||||
By("test env created")
|
||||
var namespace corev1.Namespace
|
||||
Eventually(func() error {
|
||||
@@ -134,4 +150,97 @@ var _ = Describe("Test project usecase functions", func() {
|
||||
projectUsecase.DeleteProject(context.TODO(), "test-project")
|
||||
})
|
||||
|
||||
It("Test Update project function", func() {
|
||||
req := apisv1.CreateProjectRequest{
|
||||
Name: "test-project",
|
||||
Description: "this is a project description",
|
||||
}
|
||||
_, err := projectUsecase.CreateProject(context.TODO(), req)
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
base, err := projectUsecase.UpdateProject(context.TODO(), "test-project", apisv1.UpdateProjectRequest{
|
||||
Alias: "Change alias",
|
||||
Description: "Change description",
|
||||
Owner: "admin",
|
||||
})
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(base.Alias).Should(BeEquivalentTo("Change alias"))
|
||||
Expect(base.Description).Should(BeEquivalentTo("Change description"))
|
||||
Expect(base.Owner.Alias).Should(BeEquivalentTo("Administrator"))
|
||||
|
||||
_, err = projectUsecase.UpdateProject(context.TODO(), "test-project", apisv1.UpdateProjectRequest{
|
||||
Alias: "Change alias",
|
||||
Description: "Change description",
|
||||
Owner: "admin-error",
|
||||
})
|
||||
Expect(err).Should(BeEquivalentTo(bcode.ErrProjectOwnerIsNotExist))
|
||||
err = projectUsecase.DeleteProject(context.TODO(), "test-project")
|
||||
Expect(err).Should(BeNil())
|
||||
})
|
||||
|
||||
It("Test Create project user function", func() {
|
||||
req := apisv1.CreateProjectRequest{
|
||||
Name: "test-project",
|
||||
Description: "this is a project description",
|
||||
}
|
||||
_, err := projectUsecase.CreateProject(context.TODO(), req)
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
_, err = projectUsecase.AddProjectUser(context.TODO(), "test-project", apisv1.AddProjectUserRequest{
|
||||
UserName: "admin",
|
||||
UserRoles: []string{"project-admin"},
|
||||
})
|
||||
Expect(err).Should(BeNil())
|
||||
})
|
||||
|
||||
It("Test Update project user function", func() {
|
||||
req := apisv1.CreateProjectRequest{
|
||||
Name: "test-project",
|
||||
Description: "this is a project description",
|
||||
}
|
||||
_, err := projectUsecase.CreateProject(context.TODO(), req)
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
_, err = projectUsecase.AddProjectUser(context.TODO(), "test-project", apisv1.AddProjectUserRequest{
|
||||
UserName: "admin",
|
||||
UserRoles: []string{"project-admin"},
|
||||
})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
_, err = projectUsecase.UpdateProjectUser(context.TODO(), "test-project", "admin", apisv1.UpdateProjectUserRequest{
|
||||
UserRoles: []string{"project-admin", "app-developer"},
|
||||
})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
_, err = projectUsecase.UpdateProjectUser(context.TODO(), "test-project", "admin", apisv1.UpdateProjectUserRequest{
|
||||
UserRoles: []string{"project-admin", "app-developer", "xxx"},
|
||||
})
|
||||
Expect(err).Should(BeEquivalentTo(bcode.ErrProjectRoleCheckFailure))
|
||||
})
|
||||
|
||||
It("Test delete project user and delete project function", func() {
|
||||
req := apisv1.CreateProjectRequest{
|
||||
Name: "test-project",
|
||||
Description: "this is a project description",
|
||||
}
|
||||
_, err := projectUsecase.CreateProject(context.TODO(), req)
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
_, err = projectUsecase.AddProjectUser(context.TODO(), "test-project", apisv1.AddProjectUserRequest{
|
||||
UserName: "admin",
|
||||
UserRoles: []string{"project-admin"},
|
||||
})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
err = projectUsecase.DeleteProjectUser(context.TODO(), "test-project", "admin")
|
||||
Expect(err).Should(BeNil())
|
||||
err = projectUsecase.DeleteProject(context.TODO(), "test-project")
|
||||
Expect(err).Should(BeNil())
|
||||
perms, err := projectUsecase.rbacUsecase.ListPermissions(context.TODO(), "test-project")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(len(perms)).Should(BeEquivalentTo(0))
|
||||
roles, err := projectUsecase.rbacUsecase.ListRole(context.TODO(), "test-project", 0, 0)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(roles.Total).Should(BeEquivalentTo(0))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -0,0 +1,913 @@
|
||||
/*
|
||||
Copyright 2022 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package usecase
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/emicklei/go-restful/v3"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/datastore"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/log"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/model"
|
||||
apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode"
|
||||
"github.com/oam-dev/kubevela/pkg/utils"
|
||||
)
|
||||
|
||||
// resourceActions all register resources and actions
|
||||
var resourceActions map[string][]string
|
||||
var lock sync.Mutex
|
||||
var reg = regexp.MustCompile(`(?U)\{.*\}`)
|
||||
|
||||
var defaultProjectPermissionTemplate = []*model.PermissionTemplate{
|
||||
{
|
||||
Name: "project-read",
|
||||
Alias: "Project Read",
|
||||
Resources: []string{"project:{projectName}"},
|
||||
Actions: []string{"detail"},
|
||||
Effect: "Allow",
|
||||
Scope: "project",
|
||||
},
|
||||
{
|
||||
Name: "app-management",
|
||||
Alias: "App Management",
|
||||
Resources: []string{"project:{projectName}/application:*/*", "definition:*"},
|
||||
Actions: []string{"*"},
|
||||
Effect: "Allow",
|
||||
Scope: "project",
|
||||
},
|
||||
{
|
||||
Name: "env-management",
|
||||
Alias: "Environment Management",
|
||||
Resources: []string{"project:{projectName}/environment:*"},
|
||||
Actions: []string{"*"},
|
||||
Effect: "Allow",
|
||||
Scope: "project",
|
||||
},
|
||||
{
|
||||
Name: "role-management",
|
||||
Alias: "Role Management",
|
||||
Resources: []string{"project:{projectName}/role:*", "project:{projectName}/projectUser:*", "project:{projectName}/permission:*"},
|
||||
Actions: []string{"*"},
|
||||
Effect: "Allow",
|
||||
Scope: "project",
|
||||
},
|
||||
}
|
||||
|
||||
var defaultPlatformPermission = []*model.PermissionTemplate{
|
||||
{
|
||||
Name: "cluster-manage",
|
||||
Alias: "Cluster Management",
|
||||
Resources: []string{"cluster:*/*"},
|
||||
Actions: []string{"*"},
|
||||
Effect: "Allow",
|
||||
Scope: "platform",
|
||||
},
|
||||
{
|
||||
Name: "project-management",
|
||||
Alias: "Project Management",
|
||||
Resources: []string{"project:*"},
|
||||
Actions: []string{"*"},
|
||||
Effect: "Allow",
|
||||
Scope: "platform",
|
||||
},
|
||||
{
|
||||
Name: "addon-management",
|
||||
Alias: "Addon Management",
|
||||
Resources: []string{"addon:*", "addonRegistry:*"},
|
||||
Actions: []string{"*"},
|
||||
Effect: "Allow",
|
||||
Scope: "platform",
|
||||
},
|
||||
{
|
||||
Name: "target-manage",
|
||||
Alias: "Target Management",
|
||||
Resources: []string{"target:*", "cluster:*/namespace:*"},
|
||||
Actions: []string{"*"},
|
||||
Effect: "Allow",
|
||||
Scope: "platform",
|
||||
},
|
||||
{
|
||||
Name: "user-manage",
|
||||
Alias: "User Management",
|
||||
Resources: []string{"user:*"},
|
||||
Actions: []string{"*"},
|
||||
Effect: "Allow",
|
||||
Scope: "platform",
|
||||
},
|
||||
{
|
||||
Name: "role-manage",
|
||||
Alias: "Platform Role Management",
|
||||
Resources: []string{"role:*", "permission:*"},
|
||||
Actions: []string{"*"},
|
||||
Effect: "Allow",
|
||||
Scope: "platform",
|
||||
},
|
||||
{
|
||||
Name: "admin",
|
||||
Alias: "Admin",
|
||||
Resources: []string{"*"},
|
||||
Actions: []string{"*"},
|
||||
Effect: "Allow",
|
||||
Scope: "platform",
|
||||
},
|
||||
}
|
||||
|
||||
// ResourceMaps all resources definition for RBAC
|
||||
var ResourceMaps = map[string]resourceMetadata{
|
||||
"project": {
|
||||
subResources: map[string]resourceMetadata{
|
||||
"application": {
|
||||
pathName: "appName",
|
||||
subResources: map[string]resourceMetadata{
|
||||
"component": {
|
||||
subResources: map[string]resourceMetadata{
|
||||
"trait": {
|
||||
pathName: "traitType",
|
||||
},
|
||||
},
|
||||
pathName: "compName",
|
||||
},
|
||||
"workflow": {
|
||||
subResources: map[string]resourceMetadata{
|
||||
"record": {
|
||||
pathName: "record",
|
||||
},
|
||||
},
|
||||
pathName: "workflowName",
|
||||
},
|
||||
"policy": {
|
||||
pathName: "policyName",
|
||||
},
|
||||
"revision": {
|
||||
pathName: "revision",
|
||||
},
|
||||
"envBinding": {
|
||||
pathName: "envName",
|
||||
},
|
||||
"trigger": {},
|
||||
},
|
||||
},
|
||||
"environment": {
|
||||
pathName: "envName",
|
||||
},
|
||||
"workflow": {
|
||||
pathName: "workflowName",
|
||||
},
|
||||
"role": {
|
||||
pathName: "roleName",
|
||||
},
|
||||
"permission": {},
|
||||
"projectUser": {
|
||||
pathName: "userName",
|
||||
},
|
||||
"applicationTemplate": {},
|
||||
},
|
||||
pathName: "projectName",
|
||||
},
|
||||
"cluster": {
|
||||
pathName: "clusterName",
|
||||
subResources: map[string]resourceMetadata{
|
||||
"namespace": {},
|
||||
},
|
||||
},
|
||||
"addon": {
|
||||
pathName: "addonName",
|
||||
},
|
||||
"addonRegistry": {
|
||||
pathName: "addonRegName",
|
||||
},
|
||||
"target": {
|
||||
pathName: "targetName",
|
||||
},
|
||||
"user": {
|
||||
pathName: "userName",
|
||||
},
|
||||
"role": {},
|
||||
"permission": {},
|
||||
"systemSetting": {},
|
||||
"definition": {},
|
||||
}
|
||||
|
||||
var existResourcePaths = convert(ResourceMaps)
|
||||
|
||||
type resourceMetadata struct {
|
||||
subResources map[string]resourceMetadata
|
||||
pathName string
|
||||
}
|
||||
|
||||
func checkResourcePath(resource string) (string, error) {
|
||||
if sub, exist := ResourceMaps[resource]; exist {
|
||||
if sub.pathName != "" {
|
||||
return fmt.Sprintf("%s:{%s}", resource, sub.pathName), nil
|
||||
}
|
||||
return fmt.Sprintf("%s:*", resource), nil
|
||||
}
|
||||
path := ""
|
||||
exist := 0
|
||||
lastResourceName := resource[strings.LastIndex(resource, "/")+1:]
|
||||
for key, erp := range existResourcePaths {
|
||||
allMatchIndex := strings.Index(key, fmt.Sprintf("/%s/", resource))
|
||||
index := strings.Index(erp, fmt.Sprintf("/%s:", lastResourceName))
|
||||
if index > -1 && allMatchIndex > -1 {
|
||||
pre := erp[:index+len(lastResourceName)+2]
|
||||
next := strings.Replace(erp, pre, "", 1)
|
||||
nameIndex := strings.Index(next, "/")
|
||||
if nameIndex > -1 {
|
||||
pre += next[:nameIndex]
|
||||
}
|
||||
if pre != path {
|
||||
exist++
|
||||
}
|
||||
path = pre
|
||||
}
|
||||
}
|
||||
path = strings.TrimSuffix(path, "/")
|
||||
path = strings.TrimPrefix(path, "/")
|
||||
if exist == 1 {
|
||||
return path, nil
|
||||
}
|
||||
if exist > 1 {
|
||||
return path, fmt.Errorf("the resource name %s is not unique", resource)
|
||||
}
|
||||
return path, fmt.Errorf("there is no resource %s", resource)
|
||||
}
|
||||
|
||||
func convert(sources map[string]resourceMetadata) map[string]string {
|
||||
list := make(map[string]string)
|
||||
for k, v := range sources {
|
||||
if len(v.subResources) > 0 {
|
||||
for sub, subWithPathName := range convert(v.subResources) {
|
||||
if subWithPathName != "" {
|
||||
withPathname := fmt.Sprintf("/%s:*%s", k, subWithPathName)
|
||||
if v.pathName != "" {
|
||||
withPathname = fmt.Sprintf("/%s:{%s}%s", k, v.pathName, subWithPathName)
|
||||
}
|
||||
list[fmt.Sprintf("/%s%s", k, sub)] = withPathname
|
||||
}
|
||||
}
|
||||
}
|
||||
withPathname := fmt.Sprintf("/%s:*/", k)
|
||||
if v.pathName != "" {
|
||||
withPathname = fmt.Sprintf("/%s:{%s}/", k, v.pathName)
|
||||
}
|
||||
list[fmt.Sprintf("/%s/", k)] = withPathname
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
// registerResourceAction register resource actions
|
||||
func registerResourceAction(resource string, actions ...string) {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
if resourceActions == nil {
|
||||
resourceActions = make(map[string][]string)
|
||||
}
|
||||
path, err := checkResourcePath(resource)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("resource %s is not exist", resource))
|
||||
}
|
||||
resource = path
|
||||
if _, exist := resourceActions[resource]; exist {
|
||||
for _, action := range actions {
|
||||
if !utils.StringsContain(resourceActions[resource], action) {
|
||||
resourceActions[resource] = append(resourceActions[resource], action)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
resourceActions[resource] = actions
|
||||
}
|
||||
}
|
||||
|
||||
type rbacUsecaseImpl struct {
|
||||
ds datastore.DataStore
|
||||
}
|
||||
|
||||
// RBACUsecase implement RBAC-related business logic.
|
||||
type RBACUsecase interface {
|
||||
CheckPerm(resource string, actions ...string) func(req *restful.Request, res *restful.Response, chain *restful.FilterChain)
|
||||
GetUserPermissions(ctx context.Context, user *model.User, projectName string, withPlatform bool) ([]*model.Permission, error)
|
||||
CreateRole(ctx context.Context, projectName string, req apisv1.CreateRoleRequest) (*apisv1.RoleBase, error)
|
||||
DeleteRole(ctx context.Context, projectName, roleName string) error
|
||||
UpdateRole(ctx context.Context, projectName, roleName string, req apisv1.UpdateRoleRequest) (*apisv1.RoleBase, error)
|
||||
ListRole(ctx context.Context, projectName string, page, pageSize int) (*apisv1.ListRolesResponse, error)
|
||||
ListPermissionTemplate(ctx context.Context, projectName string) ([]apisv1.PermissionTemplateBase, error)
|
||||
ListPermissions(ctx context.Context, projectName string) ([]apisv1.PermissionBase, error)
|
||||
DeletePermission(ctx context.Context, projectName, permName string) error
|
||||
InitDefaultRoleAndUsersForProject(ctx context.Context, project *model.Project) error
|
||||
Init(ctx context.Context) error
|
||||
}
|
||||
|
||||
// NewRBACUsecase is the usecase service of RBAC
|
||||
func NewRBACUsecase(ds datastore.DataStore) RBACUsecase {
|
||||
rbacUsecase := &rbacUsecaseImpl{
|
||||
ds: ds,
|
||||
}
|
||||
return rbacUsecase
|
||||
}
|
||||
|
||||
func (p *rbacUsecaseImpl) Init(ctx context.Context) error {
|
||||
count, _ := p.ds.Count(ctx, &model.Permission{}, &datastore.FilterOptions{
|
||||
IsNotExist: []datastore.IsNotExistQueryOption{
|
||||
{
|
||||
Key: "project",
|
||||
},
|
||||
},
|
||||
})
|
||||
if count > 0 {
|
||||
return nil
|
||||
}
|
||||
var batchData []datastore.Entity
|
||||
for _, policy := range defaultPlatformPermission {
|
||||
batchData = append(batchData, &model.Permission{
|
||||
Name: policy.Name,
|
||||
Alias: policy.Alias,
|
||||
Resources: policy.Resources,
|
||||
Actions: policy.Actions,
|
||||
Effect: policy.Effect,
|
||||
})
|
||||
}
|
||||
batchData = append(batchData, &model.Role{
|
||||
Name: "admin",
|
||||
Alias: "Admin",
|
||||
Permissions: []string{"admin"},
|
||||
})
|
||||
if err := p.ds.BatchAdd(context.Background(), batchData); err != nil {
|
||||
return fmt.Errorf("init the platform perm policies failure %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetUserPermissions get user permission policies, if projectName is empty, will only get the platform permission policies
|
||||
func (p *rbacUsecaseImpl) GetUserPermissions(ctx context.Context, user *model.User, projectName string, withPlatform bool) ([]*model.Permission, error) {
|
||||
var permissionNames []string
|
||||
var perms []*model.Permission
|
||||
if withPlatform && len(user.UserRoles) > 0 {
|
||||
entities, err := p.ds.List(ctx, &model.Role{}, &datastore.ListOptions{FilterOptions: datastore.FilterOptions{
|
||||
In: []datastore.InQueryOption{
|
||||
{
|
||||
Key: "name",
|
||||
Values: user.UserRoles,
|
||||
},
|
||||
},
|
||||
IsNotExist: []datastore.IsNotExistQueryOption{
|
||||
{
|
||||
Key: "project",
|
||||
},
|
||||
},
|
||||
}})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, entity := range entities {
|
||||
permissionNames = append(permissionNames, entity.(*model.Role).Permissions...)
|
||||
}
|
||||
perms, err = p.listPermPolices(ctx, "", permissionNames)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if projectName != "" {
|
||||
var projectUser = model.ProjectUser{
|
||||
ProjectName: projectName,
|
||||
Username: user.Name,
|
||||
}
|
||||
var roles []string
|
||||
if err := p.ds.Get(ctx, &projectUser); err == nil {
|
||||
roles = append(roles, projectUser.UserRoles...)
|
||||
}
|
||||
if len(roles) > 0 {
|
||||
entities, err := p.ds.List(ctx, &model.Role{Project: projectName}, &datastore.ListOptions{FilterOptions: datastore.FilterOptions{In: []datastore.InQueryOption{
|
||||
{
|
||||
Key: "name",
|
||||
Values: roles,
|
||||
},
|
||||
}}})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, entity := range entities {
|
||||
permissionNames = append(permissionNames, entity.(*model.Role).Permissions...)
|
||||
}
|
||||
projectPerms, err := p.listPermPolices(ctx, projectName, permissionNames)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
perms = append(perms, projectPerms...)
|
||||
}
|
||||
}
|
||||
return perms, nil
|
||||
}
|
||||
|
||||
func (p *rbacUsecaseImpl) UpdatePermission(ctx context.Context, projetName string, permissionName string, req *apisv1.UpdatePermissionRequest) (*apisv1.PermissionBase, error) {
|
||||
perm := &model.Permission{
|
||||
Project: projetName,
|
||||
Name: permissionName,
|
||||
}
|
||||
err := p.ds.Get(ctx, perm)
|
||||
if err != nil {
|
||||
if errors.Is(err, datastore.ErrRecordNotExist) {
|
||||
return nil, bcode.ErrPermissionNotExist
|
||||
}
|
||||
}
|
||||
//TODO: check req validate
|
||||
perm.Actions = req.Actions
|
||||
perm.Alias = req.Alias
|
||||
perm.Resources = req.Resources
|
||||
perm.Effect = req.Effect
|
||||
if err := p.ds.Put(ctx, perm); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &apisv1.PermissionBase{
|
||||
Name: perm.Name,
|
||||
Alias: perm.Alias,
|
||||
Resources: perm.Resources,
|
||||
Actions: perm.Actions,
|
||||
Effect: perm.Effect,
|
||||
CreateTime: perm.CreateTime,
|
||||
UpdateTime: perm.UpdateTime,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *rbacUsecaseImpl) listPermPolices(ctx context.Context, projectName string, permissionNames []string) ([]*model.Permission, error) {
|
||||
if len(permissionNames) == 0 {
|
||||
return []*model.Permission{}, nil
|
||||
}
|
||||
filter := datastore.FilterOptions{In: []datastore.InQueryOption{
|
||||
{
|
||||
Key: "name",
|
||||
Values: permissionNames,
|
||||
},
|
||||
}}
|
||||
if projectName == "" {
|
||||
filter.IsNotExist = append(filter.IsNotExist, datastore.IsNotExistQueryOption{
|
||||
Key: "project",
|
||||
})
|
||||
}
|
||||
permEntities, err := p.ds.List(ctx, &model.Permission{Project: projectName}, &datastore.ListOptions{FilterOptions: filter})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var perms []*model.Permission
|
||||
for _, entity := range permEntities {
|
||||
perms = append(perms, entity.(*model.Permission))
|
||||
}
|
||||
return perms, nil
|
||||
}
|
||||
|
||||
func (p *rbacUsecaseImpl) CheckPerm(resource string, actions ...string) func(req *restful.Request, res *restful.Response, chain *restful.FilterChain) {
|
||||
registerResourceAction(resource, actions...)
|
||||
f := func(req *restful.Request, res *restful.Response, chain *restful.FilterChain) {
|
||||
// get login user info
|
||||
userName, ok := req.Request.Context().Value(&apisv1.CtxKeyUser).(string)
|
||||
if !ok {
|
||||
bcode.ReturnError(req, res, bcode.ErrUnauthorized)
|
||||
return
|
||||
}
|
||||
user := &model.User{Name: userName}
|
||||
if err := p.ds.Get(req.Request.Context(), user); err != nil {
|
||||
bcode.ReturnError(req, res, bcode.ErrUnauthorized)
|
||||
return
|
||||
}
|
||||
path, err := checkResourcePath(resource)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("check resource path failure %s", err.Error())
|
||||
bcode.ReturnError(req, res, bcode.ErrForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
// multiple method for get the project name.
|
||||
getProjectName := func() string {
|
||||
if value := req.PathParameter("projectName"); value != "" {
|
||||
return value
|
||||
}
|
||||
if value := req.QueryParameter("project"); value != "" {
|
||||
return value
|
||||
}
|
||||
if value := req.QueryParameter("projectName"); value != "" {
|
||||
return value
|
||||
}
|
||||
if appName := req.PathParameter(ResourceMaps["project"].subResources["application"].pathName); appName != "" {
|
||||
app := &model.Application{Name: appName}
|
||||
if err := p.ds.Get(req.Request.Context(), app); err == nil {
|
||||
return app.Project
|
||||
}
|
||||
}
|
||||
if envName := req.PathParameter(ResourceMaps["project"].subResources["environment"].pathName); envName != "" {
|
||||
env := &model.Env{Name: envName}
|
||||
if err := p.ds.Get(req.Request.Context(), env); err == nil {
|
||||
return env.Project
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
ra := &RequestResourceAction{}
|
||||
ra.SetResourceWithName(path, func(name string) string {
|
||||
if name == ResourceMaps["project"].pathName {
|
||||
return getProjectName()
|
||||
}
|
||||
return req.PathParameter(name)
|
||||
})
|
||||
|
||||
// get user's perm list.
|
||||
projectName := getProjectName()
|
||||
permissions, err := p.GetUserPermissions(req.Request.Context(), user, projectName, true)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("get user's perm policies failure %s, user is %s", err.Error(), user.Name)
|
||||
bcode.ReturnError(req, res, bcode.ErrForbidden)
|
||||
return
|
||||
}
|
||||
if !ra.Match(permissions) {
|
||||
bcode.ReturnError(req, res, bcode.ErrForbidden)
|
||||
return
|
||||
}
|
||||
chain.ProcessFilter(req, res)
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func (p *rbacUsecaseImpl) CreateRole(ctx context.Context, projectName string, req apisv1.CreateRoleRequest) (*apisv1.RoleBase, error) {
|
||||
if projectName != "" {
|
||||
var project = model.Project{
|
||||
Name: projectName,
|
||||
}
|
||||
if err := p.ds.Get(ctx, &project); err != nil {
|
||||
return nil, bcode.ErrProjectIsNotExist
|
||||
}
|
||||
}
|
||||
if len(req.Permissions) == 0 {
|
||||
return nil, bcode.ErrRolePermissionCheckFailure
|
||||
}
|
||||
policies, err := p.listPermPolices(ctx, projectName, req.Permissions)
|
||||
if err != nil || len(policies) != len(req.Permissions) {
|
||||
return nil, bcode.ErrRolePermissionCheckFailure
|
||||
}
|
||||
var role = model.Role{
|
||||
Name: req.Name,
|
||||
Alias: req.Alias,
|
||||
Project: projectName,
|
||||
Permissions: req.Permissions,
|
||||
}
|
||||
if err := p.ds.Add(ctx, &role); err != nil {
|
||||
if errors.Is(err, datastore.ErrRecordExist) {
|
||||
return nil, bcode.ErrRoleIsExist
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return ConvertRole2Model(&role, policies), nil
|
||||
}
|
||||
|
||||
func (p *rbacUsecaseImpl) DeleteRole(ctx context.Context, projectName, roleName string) error {
|
||||
var role = model.Role{
|
||||
Name: roleName,
|
||||
Project: projectName,
|
||||
}
|
||||
if err := p.ds.Delete(ctx, &role); err != nil {
|
||||
if errors.Is(err, datastore.ErrRecordNotExist) {
|
||||
return bcode.ErrRoleIsNotExist
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *rbacUsecaseImpl) DeletePermission(ctx context.Context, projectName, permName string) error {
|
||||
var perm = model.Permission{
|
||||
Name: permName,
|
||||
Project: projectName,
|
||||
}
|
||||
if err := p.ds.Delete(ctx, &perm); err != nil {
|
||||
if errors.Is(err, datastore.ErrRecordNotExist) {
|
||||
return bcode.ErrRoleIsNotExist
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *rbacUsecaseImpl) UpdateRole(ctx context.Context, projectName, roleName string, req apisv1.UpdateRoleRequest) (*apisv1.RoleBase, error) {
|
||||
if projectName != "" {
|
||||
var project = model.Project{
|
||||
Name: projectName,
|
||||
}
|
||||
if err := p.ds.Get(ctx, &project); err != nil {
|
||||
return nil, bcode.ErrProjectIsNotExist
|
||||
}
|
||||
}
|
||||
if len(req.Permissions) == 0 {
|
||||
return nil, bcode.ErrRolePermissionCheckFailure
|
||||
}
|
||||
policies, err := p.listPermPolices(ctx, projectName, req.Permissions)
|
||||
if err != nil || len(policies) != len(req.Permissions) {
|
||||
return nil, bcode.ErrRolePermissionCheckFailure
|
||||
}
|
||||
var role = model.Role{
|
||||
Name: roleName,
|
||||
Project: projectName,
|
||||
}
|
||||
if err := p.ds.Get(ctx, &role); err != nil {
|
||||
if errors.Is(err, datastore.ErrRecordNotExist) {
|
||||
return nil, bcode.ErrRoleIsNotExist
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
role.Alias = req.Alias
|
||||
role.Permissions = req.Permissions
|
||||
if err := p.ds.Put(ctx, &role); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ConvertRole2Model(&role, policies), nil
|
||||
}
|
||||
|
||||
func (p *rbacUsecaseImpl) ListRole(ctx context.Context, projectName string, page, pageSize int) (*apisv1.ListRolesResponse, error) {
|
||||
var role = model.Role{
|
||||
Project: projectName,
|
||||
}
|
||||
var filter datastore.FilterOptions
|
||||
if projectName == "" {
|
||||
filter.IsNotExist = append(filter.IsNotExist, datastore.IsNotExistQueryOption{
|
||||
Key: "project",
|
||||
})
|
||||
}
|
||||
entities, err := p.ds.List(ctx, &role, &datastore.ListOptions{FilterOptions: filter, Page: page, PageSize: pageSize, SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}}})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var policySet = make(map[string]string)
|
||||
for _, entity := range entities {
|
||||
for _, p := range entity.(*model.Role).Permissions {
|
||||
policySet[p] = p
|
||||
}
|
||||
}
|
||||
|
||||
policies, err := p.listPermPolices(ctx, projectName, utils.MapKey2Array(policySet))
|
||||
if err != nil {
|
||||
log.Logger.Errorf("list perm policies failure %s", err.Error())
|
||||
}
|
||||
var policyMap = make(map[string]*model.Permission)
|
||||
for i, policy := range policies {
|
||||
policyMap[policy.Name] = policies[i]
|
||||
}
|
||||
var res apisv1.ListRolesResponse
|
||||
for _, entity := range entities {
|
||||
role := entity.(*model.Role)
|
||||
var rolePolicies []*model.Permission
|
||||
for _, perm := range role.Permissions {
|
||||
rolePolicies = append(rolePolicies, policyMap[perm])
|
||||
}
|
||||
res.Roles = append(res.Roles, ConvertRole2Model(entity.(*model.Role), rolePolicies))
|
||||
}
|
||||
count, err := p.ds.Count(ctx, &role, &filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res.Total = count
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
// ListPermissionTemplate TODO:
|
||||
func (p *rbacUsecaseImpl) ListPermissionTemplate(ctx context.Context, projectName string) ([]apisv1.PermissionTemplateBase, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (p *rbacUsecaseImpl) ListPermissions(ctx context.Context, projectName string) ([]apisv1.PermissionBase, error) {
|
||||
var filter datastore.FilterOptions
|
||||
if projectName == "" {
|
||||
filter.IsNotExist = append(filter.IsNotExist, datastore.IsNotExistQueryOption{
|
||||
Key: "project",
|
||||
})
|
||||
}
|
||||
permEntities, err := p.ds.List(ctx, &model.Permission{Project: projectName}, &datastore.ListOptions{FilterOptions: filter})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var perms []apisv1.PermissionBase
|
||||
for _, entity := range permEntities {
|
||||
perm := entity.(*model.Permission)
|
||||
perms = append(perms, apisv1.PermissionBase{
|
||||
Name: perm.Name,
|
||||
Alias: perm.Alias,
|
||||
Resources: perm.Resources,
|
||||
Actions: perm.Actions,
|
||||
Effect: perm.Effect,
|
||||
CreateTime: perm.CreateTime,
|
||||
UpdateTime: perm.UpdateTime,
|
||||
})
|
||||
}
|
||||
return perms, nil
|
||||
}
|
||||
|
||||
func (p *rbacUsecaseImpl) InitDefaultRoleAndUsersForProject(ctx context.Context, project *model.Project) error {
|
||||
var batchData []datastore.Entity
|
||||
for _, permissionTemp := range defaultProjectPermissionTemplate {
|
||||
var rra = RequestResourceAction{}
|
||||
var formatedResource []string
|
||||
for _, resource := range permissionTemp.Resources {
|
||||
rra.SetResourceWithName(resource, func(name string) string {
|
||||
if name == ResourceMaps["project"].pathName {
|
||||
return project.Name
|
||||
}
|
||||
return ""
|
||||
})
|
||||
formatedResource = append(formatedResource, rra.GetResource().String())
|
||||
}
|
||||
batchData = append(batchData, &model.Permission{
|
||||
Name: permissionTemp.Name,
|
||||
Alias: permissionTemp.Alias,
|
||||
Project: project.Name,
|
||||
Resources: formatedResource,
|
||||
Actions: permissionTemp.Actions,
|
||||
Effect: permissionTemp.Effect,
|
||||
})
|
||||
}
|
||||
batchData = append(batchData, &model.Role{
|
||||
Name: "app-developer",
|
||||
Alias: "App Developer",
|
||||
Permissions: []string{"project-read", "app-management", "env-management"},
|
||||
Project: project.Name,
|
||||
}, &model.Role{
|
||||
Name: "project-admin",
|
||||
Alias: "Project Admin",
|
||||
Permissions: []string{"project-read", "app-management", "env-management", "role-management"},
|
||||
Project: project.Name,
|
||||
})
|
||||
if project.Owner != "" {
|
||||
var projectUser = &model.ProjectUser{
|
||||
ProjectName: project.Name,
|
||||
UserRoles: []string{"project-admin"},
|
||||
Username: project.Owner,
|
||||
}
|
||||
batchData = append(batchData, projectUser)
|
||||
}
|
||||
return p.ds.BatchAdd(ctx, batchData)
|
||||
}
|
||||
|
||||
// ConvertRole2Model convert role model to role base struct
|
||||
func ConvertRole2Model(role *model.Role, policies []*model.Permission) *apisv1.RoleBase {
|
||||
return &apisv1.RoleBase{
|
||||
CreateTime: role.CreateTime,
|
||||
UpdateTime: role.UpdateTime,
|
||||
Name: role.Name,
|
||||
Alias: role.Alias,
|
||||
Permissions: func() (list []apisv1.NameAlias) {
|
||||
for _, policy := range policies {
|
||||
if policy != nil {
|
||||
list = append(list, apisv1.NameAlias{Name: policy.Name, Alias: policy.Alias})
|
||||
}
|
||||
}
|
||||
return
|
||||
}(),
|
||||
}
|
||||
}
|
||||
|
||||
// ResourceName it is similar to ARNs
|
||||
// <type>:<value>/<type>:<value>
|
||||
type ResourceName struct {
|
||||
Type string
|
||||
Value string
|
||||
Next *ResourceName
|
||||
}
|
||||
|
||||
// ParseResourceName parse string to ResourceName
|
||||
func ParseResourceName(resource string) *ResourceName {
|
||||
RNs := strings.Split(resource, "/")
|
||||
var resourceName = ResourceName{}
|
||||
var current = &resourceName
|
||||
for _, rn := range RNs {
|
||||
rnData := strings.Split(rn, ":")
|
||||
if len(rnData) == 2 {
|
||||
current.Type = rnData[0]
|
||||
current.Value = rnData[1]
|
||||
}
|
||||
if len(rnData) == 1 {
|
||||
current.Type = rnData[0]
|
||||
current.Value = "*"
|
||||
}
|
||||
var next = &ResourceName{}
|
||||
current.Next = next
|
||||
current = next
|
||||
}
|
||||
return &resourceName
|
||||
}
|
||||
|
||||
// Match the resource types same with target and resource value include
|
||||
// target resource means request resources
|
||||
func (r *ResourceName) Match(target *ResourceName) bool {
|
||||
current := r
|
||||
currentTarget := target
|
||||
for current != nil && current.Type != "" {
|
||||
if current.Type == "*" {
|
||||
return true
|
||||
}
|
||||
if currentTarget == nil || currentTarget.Type == "" {
|
||||
return false
|
||||
}
|
||||
if current.Type != currentTarget.Type {
|
||||
return false
|
||||
}
|
||||
if current.Value != currentTarget.Value && current.Value != "*" {
|
||||
return false
|
||||
}
|
||||
current = current.Next
|
||||
currentTarget = currentTarget.Next
|
||||
}
|
||||
if currentTarget != nil && currentTarget.Type != "" {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (r *ResourceName) String() string {
|
||||
strBuilder := &strings.Builder{}
|
||||
current := r
|
||||
for current != nil && current.Type != "" {
|
||||
strBuilder.WriteString(fmt.Sprintf("%s:%s/", current.Type, current.Value))
|
||||
current = current.Next
|
||||
}
|
||||
return strings.TrimSuffix(strBuilder.String(), "/")
|
||||
}
|
||||
|
||||
// RequestResourceAction resource permission boundary
|
||||
type RequestResourceAction struct {
|
||||
resource *ResourceName
|
||||
actions []string
|
||||
}
|
||||
|
||||
// SetResourceWithName format resource and assign a value from path parameter
|
||||
func (r *RequestResourceAction) SetResourceWithName(resource string, pathParameter func(name string) string) {
|
||||
resultKey := reg.FindAllString(resource, -1)
|
||||
for _, sourcekey := range resultKey {
|
||||
key := sourcekey[1 : len(sourcekey)-1]
|
||||
value := pathParameter(key)
|
||||
if value == "" {
|
||||
value = "*"
|
||||
}
|
||||
resource = strings.Replace(resource, sourcekey, value, 1)
|
||||
}
|
||||
r.resource = ParseResourceName(resource)
|
||||
}
|
||||
|
||||
// GetResource return the resource after be formated
|
||||
func (r *RequestResourceAction) GetResource() *ResourceName {
|
||||
return r.resource
|
||||
}
|
||||
|
||||
// SetActions set request actions
|
||||
func (r *RequestResourceAction) SetActions(actions []string) {
|
||||
r.actions = actions
|
||||
}
|
||||
|
||||
func (r *RequestResourceAction) match(policy *model.Permission) bool {
|
||||
// match actions, the policy actions will include the actions of request
|
||||
if !utils.SliceIncludeSlice(policy.Actions, r.actions) && !utils.StringsContain(policy.Actions, "*") {
|
||||
return false
|
||||
}
|
||||
// match resources
|
||||
for _, resource := range policy.Resources {
|
||||
resourceName := ParseResourceName(resource)
|
||||
if resourceName.Match(r.resource) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Match determines whether the request resources and actions matches the user permission set.
|
||||
func (r *RequestResourceAction) Match(policies []*model.Permission) bool {
|
||||
for _, policy := range policies {
|
||||
if strings.EqualFold(policy.Effect, "deny") {
|
||||
if r.match(policy) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, policy := range policies {
|
||||
if strings.EqualFold(policy.Effect, "allow") || policy.Effect == "" {
|
||||
if r.match(policy) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,272 @@
|
||||
/*
|
||||
Copyright 2022 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package usecase
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/emicklei/go-restful/v3"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/datastore"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/model"
|
||||
apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode"
|
||||
)
|
||||
|
||||
var _ = Describe("Test rbac service", func() {
|
||||
var ds datastore.DataStore
|
||||
BeforeEach(func() {
|
||||
var err error
|
||||
ds, err = NewDatastore(datastore.Config{Type: "kubeapi", Database: "rbac-test-kubevela"})
|
||||
Expect(ds).ToNot(BeNil())
|
||||
Expect(err).Should(BeNil())
|
||||
})
|
||||
It("Test check resource", func() {
|
||||
path, err := checkResourcePath("project")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(path).Should(BeEquivalentTo("project:{projectName}"))
|
||||
|
||||
path, err = checkResourcePath("application")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(path).Should(BeEquivalentTo("project:{projectName}/application:{appName}"))
|
||||
|
||||
_, err = checkResourcePath("applications")
|
||||
Expect(err).ShouldNot(BeNil())
|
||||
|
||||
_, err = checkResourcePath("project/component")
|
||||
Expect(err).ShouldNot(BeNil())
|
||||
|
||||
_, err = checkResourcePath("workflow")
|
||||
Expect(err).ShouldNot(BeNil())
|
||||
|
||||
path, err = checkResourcePath("project/application/workflow")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(path).Should(BeEquivalentTo("project:{projectName}/application:{appName}/workflow:{workflowName}"))
|
||||
|
||||
path, err = checkResourcePath("project/workflow")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(path).Should(BeEquivalentTo("project:{projectName}/workflow:{workflowName}"))
|
||||
|
||||
path, err = checkResourcePath("component")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(path).Should(BeEquivalentTo("project:{projectName}/application:{appName}/component:{compName}"))
|
||||
|
||||
path, err = checkResourcePath("role")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(path).Should(BeEquivalentTo("role:*"))
|
||||
|
||||
})
|
||||
|
||||
It("Test resource action", func() {
|
||||
ra := &RequestResourceAction{}
|
||||
ra.SetResourceWithName("project:{projectName}/workflow:{empty}", testPathParameter)
|
||||
Expect(ra.GetResource()).ShouldNot(BeNil())
|
||||
Expect(ra.GetResource().Value).Should(BeEquivalentTo("projectName"))
|
||||
Expect(ra.GetResource().Next).ShouldNot(BeNil())
|
||||
Expect(ra.GetResource().Next.Value).Should(BeEquivalentTo("*"))
|
||||
Expect(ra.GetResource().String()).Should(BeEquivalentTo("project:projectName/workflow:*"))
|
||||
})
|
||||
|
||||
It("Test init and list platform permissions", func() {
|
||||
rbacUsecase := rbacUsecaseImpl{ds: ds}
|
||||
err := rbacUsecase.Init(context.TODO())
|
||||
Expect(err).Should(BeNil())
|
||||
policies, err := rbacUsecase.ListPermissions(context.TODO(), "")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(len(policies)).Should(BeEquivalentTo(int64(7)))
|
||||
})
|
||||
|
||||
It("Test checkPerm by admin user", func() {
|
||||
|
||||
err := ds.Add(context.TODO(), &model.User{Name: "admin", UserRoles: []string{"admin"}})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
rbac := rbacUsecaseImpl{ds: ds}
|
||||
req := &http.Request{}
|
||||
req = req.WithContext(context.WithValue(req.Context(), &apisv1.CtxKeyUser, "admin"))
|
||||
res := &restful.Response{}
|
||||
pass := false
|
||||
filter := &restful.FilterChain{
|
||||
Target: restful.RouteFunction(func(req *restful.Request, res *restful.Response) {
|
||||
pass = true
|
||||
}),
|
||||
}
|
||||
rbac.CheckPerm("cluster", "create")(restful.NewRequest(req), res, filter)
|
||||
Expect(pass).Should(BeTrue())
|
||||
pass = false
|
||||
rbac.CheckPerm("role", "list")(restful.NewRequest(req), res, filter)
|
||||
Expect(pass).Should(BeTrue())
|
||||
})
|
||||
|
||||
It("Test checkPerm by dev user", func() {
|
||||
var projectName = "test-app-project"
|
||||
|
||||
err := ds.Add(context.TODO(), &model.User{Name: "dev"})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
err = ds.Add(context.TODO(), &model.Project{Name: projectName})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
err = ds.Add(context.TODO(), &model.ProjectUser{Username: "dev", ProjectName: projectName, UserRoles: []string{"application-admin"}})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
err = ds.Add(context.TODO(), &model.Role{Project: projectName, Name: "application-admin", Permissions: []string{"application-manage"}})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
err = ds.Add(context.TODO(), &model.Permission{Project: projectName, Name: "application-manage", Resources: []string{"project:test-app-project/application:*"}, Actions: []string{"*"}})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
rbac := rbacUsecaseImpl{ds: ds}
|
||||
header := http.Header{}
|
||||
header.Set("Accept", "application/json")
|
||||
header.Set("Content-Type", "application/json")
|
||||
req := &http.Request{
|
||||
Header: header,
|
||||
}
|
||||
req = req.WithContext(context.WithValue(req.Context(), &apisv1.CtxKeyUser, "dev"))
|
||||
req.Form = url.Values{}
|
||||
req.Form.Set("project", projectName)
|
||||
|
||||
record := httptest.NewRecorder()
|
||||
res := restful.NewResponse(record)
|
||||
res.SetRequestAccepts("application/json")
|
||||
pass := false
|
||||
filter := &restful.FilterChain{
|
||||
Target: restful.RouteFunction(func(req *restful.Request, res *restful.Response) {
|
||||
pass = true
|
||||
}),
|
||||
}
|
||||
rbac.CheckPerm("cluster", "create")(restful.NewRequest(req), res, filter)
|
||||
Expect(pass).Should(BeFalse())
|
||||
Expect(res.StatusCode()).Should(Equal(int(bcode.ErrForbidden.HTTPCode)))
|
||||
|
||||
rbac.CheckPerm("component", "list")(restful.NewRequest(req), res, filter)
|
||||
Expect(res.StatusCode()).Should(Equal(int(bcode.ErrForbidden.HTTPCode)))
|
||||
|
||||
// add list application permission to role
|
||||
// err = ds.Add(context.TODO(), &model.Permission{Project: projectName, Name: "application-list", Resources: []string{"project:*/application:*"}, Actions: []string{"list"}})
|
||||
// Expect(err).Should(BeNil())
|
||||
// _, err = rbac.UpdateRole(context.TODO(), projectName, "application-admin", apisv1.UpdateRoleRequest{
|
||||
// Permissions: []string{"application-list", "application-manage"},
|
||||
// })
|
||||
// Expect(err).Should(BeNil())
|
||||
|
||||
// req.Form.Del("project")
|
||||
// pass = false
|
||||
// rbac.CheckPerm("application", "list")(restful.NewRequest(req), res, filter)
|
||||
// Expect(pass).Should(BeTrue())
|
||||
})
|
||||
|
||||
It("Test initDefaultRoleAndUsersForProject", func() {
|
||||
rbacUsecase := rbacUsecaseImpl{ds: ds}
|
||||
err := ds.Add(context.TODO(), &model.User{Name: "test-user"})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
err = ds.Add(context.TODO(), &model.Project{Name: "init-test", Owner: "test-user"})
|
||||
Expect(err).Should(BeNil())
|
||||
err = rbacUsecase.InitDefaultRoleAndUsersForProject(context.TODO(), &model.Project{Name: "init-test"})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
roles, err := rbacUsecase.ListRole(context.TODO(), "init-test", 0, 0)
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(roles.Total).Should(BeEquivalentTo(int64(2)))
|
||||
|
||||
policies, err := rbacUsecase.ListPermissions(context.TODO(), "init-test")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(len(policies)).Should(BeEquivalentTo(int64(4)))
|
||||
})
|
||||
|
||||
It("Test UpdatePermission", func() {
|
||||
rbacUsecase := rbacUsecaseImpl{ds: ds}
|
||||
base, err := rbacUsecase.UpdatePermission(context.TODO(), "test-app-project", "application-manage", &apisv1.UpdatePermissionRequest{
|
||||
Resources: []string{"project:{projectName}/application:*/*"},
|
||||
Actions: []string{"*"},
|
||||
Alias: "App Management Update",
|
||||
})
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(base.Alias).Should(BeEquivalentTo("App Management Update"))
|
||||
})
|
||||
})
|
||||
|
||||
func testPathParameter(name string) string {
|
||||
if name == "empty" {
|
||||
return ""
|
||||
}
|
||||
return name
|
||||
}
|
||||
func TestRequestResourceAction(t *testing.T) {
|
||||
ra := &RequestResourceAction{}
|
||||
ra.SetResourceWithName("project:{projectName}/workflow:{empty}", testPathParameter)
|
||||
assert.NotEqual(t, ra.GetResource(), nil)
|
||||
assert.Equal(t, ra.GetResource().Value, "projectName")
|
||||
assert.NotEqual(t, ra.GetResource().Next, nil)
|
||||
assert.Equal(t, ra.GetResource().Next.Value, "*")
|
||||
|
||||
ra2 := &RequestResourceAction{}
|
||||
ra2.SetResourceWithName("project:{empty}/application:{empty}", testPathParameter)
|
||||
assert.Equal(t, ra2.GetResource().String(), "project:*/application:*")
|
||||
}
|
||||
|
||||
func TestRequestResourceActionMatch(t *testing.T) {
|
||||
ra := &RequestResourceAction{}
|
||||
ra.SetResourceWithName("project:{projectName}/workflow:{empty}", testPathParameter)
|
||||
ra.SetActions([]string{"create"})
|
||||
assert.Equal(t, ra.Match([]*model.Permission{{Resources: []string{"project:*/workflow:*"}, Actions: []string{"*"}}}), true)
|
||||
assert.Equal(t, ra.Match([]*model.Permission{{Resources: []string{"project:ddd/workflow:*"}, Actions: []string{"create"}}}), false)
|
||||
assert.Equal(t, ra.Match([]*model.Permission{{Resources: []string{"project:projectName/workflow:*"}, Actions: []string{"create"}}}), true)
|
||||
assert.Equal(t, ra.Match([]*model.Permission{{Resources: []string{"project:projectName/workflow:*"}, Actions: []string{"create"}, Effect: "Deny"}}), false)
|
||||
|
||||
ra2 := &RequestResourceAction{}
|
||||
ra2.SetResourceWithName("project:{projectName}/application:{app1}/component:{empty}", testPathParameter)
|
||||
ra2.SetActions([]string{"delete"})
|
||||
assert.Equal(t, ra2.Match([]*model.Permission{{Resources: []string{"project:*/application:app1/component:*"}, Actions: []string{"*"}}}), true)
|
||||
assert.Equal(t, ra2.Match([]*model.Permission{{Resources: []string{"project:*/application:app1/component:*"}, Actions: []string{"list", "delete"}}}), true)
|
||||
assert.Equal(t, ra2.Match([]*model.Permission{{Resources: []string{"project:*", "project:*/application:app1/component:*"}, Actions: []string{"list", "delete"}}}), true)
|
||||
assert.Equal(t, ra2.Match([]*model.Permission{{Resources: []string{"project:*/application:app1/component:*"}, Actions: []string{"list", "detail"}}}), false)
|
||||
assert.Equal(t, ra2.Match([]*model.Permission{{Resources: []string{"*"}, Actions: []string{"*"}}}), true)
|
||||
assert.Equal(t, ra2.Match([]*model.Permission{{Resources: []string{"*"}, Actions: []string{"*"}}, {Actions: []string{"*"}, Resources: []string{"project:*/application:app1/component:*"}, Effect: "Deny"}}), false)
|
||||
assert.Equal(t, ra2.Match([]*model.Permission{{Resources: []string{"project:projectName/application:*/*"}, Actions: []string{"*"}}}), true)
|
||||
|
||||
ra3 := &RequestResourceAction{}
|
||||
ra3.SetResourceWithName("project:test-123", testPathParameter)
|
||||
ra3.SetActions([]string{"detail"})
|
||||
assert.Equal(t, ra3.Match([]*model.Permission{{Resources: []string{"*"}, Actions: []string{"*"}, Effect: "Allow"}}), true)
|
||||
|
||||
ra4 := &RequestResourceAction{}
|
||||
ra4.SetResourceWithName("role:*", testPathParameter)
|
||||
ra4.SetActions([]string{"list"})
|
||||
assert.Equal(t, ra4.Match([]*model.Permission{{Resources: []string{"*"}, Actions: []string{"*"}, Effect: "Allow"}}), true)
|
||||
|
||||
ra5 := &RequestResourceAction{}
|
||||
ra5.SetResourceWithName("project:*/application:*", testPathParameter)
|
||||
ra5.SetActions([]string{"list"})
|
||||
assert.Equal(t, ra5.Match([]*model.Permission{{Resources: []string{"project:*/application:*"}, Actions: []string{"list"}, Effect: "Allow"}}), true)
|
||||
|
||||
}
|
||||
|
||||
func TestRegisterResourceAction(t *testing.T) {
|
||||
registerResourceAction("role", "list")
|
||||
registerResourceAction("project/role", "list")
|
||||
t.Log(resourceActions)
|
||||
}
|
||||
@@ -30,7 +30,6 @@ import (
|
||||
// SystemInfoUsecase is usecase for systemInfoCollection
|
||||
type SystemInfoUsecase interface {
|
||||
GetSystemInfo(ctx context.Context) (*v1.SystemInfoResponse, error)
|
||||
DeleteSystemInfo(ctx context.Context) error
|
||||
UpdateSystemInfo(ctx context.Context, sysInfo v1.SystemInfoRequest) (*v1.SystemInfoResponse, error)
|
||||
}
|
||||
|
||||
@@ -52,6 +51,9 @@ func (u systemInfoUsecaseImpl) GetSystemInfo(ctx context.Context) (*v1.SystemInf
|
||||
}
|
||||
if len(entities) != 0 {
|
||||
info := entities[0].(*model.SystemInfo)
|
||||
if info.LoginType == "" {
|
||||
info.LoginType = model.LoginTypeLocal
|
||||
}
|
||||
return &v1.SystemInfoResponse{SystemInfo: *info, SystemVersion: v1.SystemVersion{VelaVersion: version.VelaVersion, GitVersion: version.GitRevision}}, nil
|
||||
}
|
||||
installID := rand.String(16)
|
||||
@@ -83,15 +85,3 @@ func (u systemInfoUsecaseImpl) UpdateSystemInfo(ctx context.Context, sysInfo v1.
|
||||
}
|
||||
return &v1.SystemInfoResponse{SystemInfo: modifiedInfo, SystemVersion: v1.SystemVersion{VelaVersion: version.VelaVersion, GitVersion: version.GitRevision}}, nil
|
||||
}
|
||||
|
||||
func (u systemInfoUsecaseImpl) DeleteSystemInfo(ctx context.Context) error {
|
||||
info, err := u.GetSystemInfo(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = u.ds.Delete(ctx, info)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package usecase
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
@@ -38,7 +39,9 @@ type TargetUsecase interface {
|
||||
DeleteTarget(ctx context.Context, TargetName string) error
|
||||
CreateTarget(ctx context.Context, req apisv1.CreateTargetRequest) (*apisv1.DetailTargetResponse, error)
|
||||
UpdateTarget(ctx context.Context, Target *model.Target, req apisv1.UpdateTargetRequest) (*apisv1.DetailTargetResponse, error)
|
||||
ListTargets(ctx context.Context, page, pageSize int) (*apisv1.ListTargetResponse, error)
|
||||
ListTargets(ctx context.Context, page, pageSize int, projectName string) (*apisv1.ListTargetResponse, error)
|
||||
ListTargetCount(ctx context.Context, projectName string) (int64, error)
|
||||
Init(ctx context.Context) error
|
||||
}
|
||||
|
||||
type targetUsecaseImpl struct {
|
||||
@@ -57,20 +60,42 @@ func NewTargetUsecase(ds datastore.DataStore) TargetUsecase {
|
||||
ds: ds,
|
||||
}
|
||||
}
|
||||
|
||||
func (dt *targetUsecaseImpl) ListTargets(ctx context.Context, page, pageSize int) (*apisv1.ListTargetResponse, error) {
|
||||
|
||||
Targets, err := listTarget(ctx, dt.ds, &datastore.ListOptions{Page: page, PageSize: pageSize, SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}}})
|
||||
func (dt *targetUsecaseImpl) Init(ctx context.Context) error {
|
||||
targets, err := dt.ds.List(ctx, &model.Target{}, &datastore.ListOptions{FilterOptions: datastore.FilterOptions{
|
||||
IsNotExist: []datastore.IsNotExistQueryOption{
|
||||
{
|
||||
Key: "project",
|
||||
},
|
||||
},
|
||||
}})
|
||||
if err != nil {
|
||||
return fmt.Errorf("list target failure %w", err)
|
||||
}
|
||||
for _, target := range targets {
|
||||
t := target.(*model.Target)
|
||||
t.Project = model.DefaultInitName
|
||||
if err := dt.ds.Put(ctx, t); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (dt *targetUsecaseImpl) ListTargets(ctx context.Context, page, pageSize int, projectName string) (*apisv1.ListTargetResponse, error) {
|
||||
targets, err := listTarget(ctx, dt.ds, projectName, &datastore.ListOptions{
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
SortBy: []datastore.SortOption{{Key: "createTime", Order: datastore.SortOrderDescending}},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp := &apisv1.ListTargetResponse{
|
||||
Targets: []apisv1.TargetBase{},
|
||||
}
|
||||
for _, raw := range Targets {
|
||||
for _, raw := range targets {
|
||||
resp.Targets = append(resp.Targets, *(dt.convertFromTargetModel(ctx, raw)))
|
||||
}
|
||||
count, err := dt.ds.Count(ctx, &model.Target{}, nil)
|
||||
count, err := dt.ds.Count(ctx, &model.Target{Project: projectName}, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -79,9 +104,13 @@ func (dt *targetUsecaseImpl) ListTargets(ctx context.Context, page, pageSize int
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (dt *targetUsecaseImpl) ListTargetCount(ctx context.Context, projectName string) (int64, error) {
|
||||
return dt.ds.Count(ctx, &model.Target{Project: projectName}, nil)
|
||||
}
|
||||
|
||||
// DeleteTarget delete application Target
|
||||
func (dt *targetUsecaseImpl) DeleteTarget(ctx context.Context, targetName string) error {
|
||||
Target := &model.Target{
|
||||
target := &model.Target{
|
||||
Name: targetName,
|
||||
}
|
||||
ddt, err := dt.GetTarget(ctx, targetName)
|
||||
@@ -94,7 +123,7 @@ func (dt *targetUsecaseImpl) DeleteTarget(ctx context.Context, targetName string
|
||||
if err = deleteTargetNamespace(ctx, dt.k8sClient, ddt.Cluster.ClusterName, ddt.Cluster.Namespace, targetName); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = dt.ds.Delete(ctx, Target); err != nil {
|
||||
if err = dt.ds.Delete(ctx, target); err != nil {
|
||||
if errors.Is(err, datastore.ErrRecordNotExist) {
|
||||
return bcode.ErrTargetNotExist
|
||||
}
|
||||
@@ -106,26 +135,32 @@ func (dt *targetUsecaseImpl) DeleteTarget(ctx context.Context, targetName string
|
||||
// CreateTarget will create a delivery target binding with a cluster and namespace, by default, it will use local cluster and namespace align with targetName
|
||||
// TODO(@wonderflow): we should support empty target in the future which only delivery cloud resources
|
||||
func (dt *targetUsecaseImpl) CreateTarget(ctx context.Context, req apisv1.CreateTargetRequest) (*apisv1.DetailTargetResponse, error) {
|
||||
Target := convertCreateReqToTargetModel(req)
|
||||
var project = model.Project{
|
||||
Name: req.Project,
|
||||
}
|
||||
if err := dt.ds.Get(ctx, &project); err != nil {
|
||||
return nil, bcode.ErrProjectIsNotExist
|
||||
}
|
||||
target := convertCreateReqToTargetModel(req)
|
||||
if req.Cluster == nil {
|
||||
req.Cluster = &apisv1.ClusterTarget{ClusterName: multicluster.ClusterLocalName, Namespace: req.Name}
|
||||
}
|
||||
if err := createTargetNamespace(ctx, dt.k8sClient, req.Cluster.ClusterName, req.Cluster.Namespace, req.Name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err := createTarget(ctx, dt.ds, &Target)
|
||||
err := createTarget(ctx, dt.ds, &target)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dt.DetailTarget(ctx, &Target)
|
||||
return dt.DetailTarget(ctx, &target)
|
||||
}
|
||||
|
||||
func (dt *targetUsecaseImpl) UpdateTarget(ctx context.Context, target *model.Target, req apisv1.UpdateTargetRequest) (*apisv1.DetailTargetResponse, error) {
|
||||
TargetModel := convertUpdateReqToTargetModel(target, req)
|
||||
if err := dt.ds.Put(ctx, TargetModel); err != nil {
|
||||
targetModel := convertUpdateReqToTargetModel(target, req)
|
||||
if err := dt.ds.Put(ctx, targetModel); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dt.DetailTarget(ctx, TargetModel)
|
||||
return dt.DetailTarget(ctx, targetModel)
|
||||
}
|
||||
|
||||
// DetailTarget detail Target
|
||||
@@ -154,14 +189,15 @@ func convertUpdateReqToTargetModel(target *model.Target, req apisv1.UpdateTarget
|
||||
}
|
||||
|
||||
func convertCreateReqToTargetModel(req apisv1.CreateTargetRequest) model.Target {
|
||||
Target := model.Target{
|
||||
target := model.Target{
|
||||
Name: req.Name,
|
||||
Alias: req.Alias,
|
||||
Description: req.Description,
|
||||
Cluster: (*model.ClusterTarget)(req.Cluster),
|
||||
Variable: req.Variable,
|
||||
Project: req.Project,
|
||||
}
|
||||
return Target
|
||||
return target
|
||||
}
|
||||
|
||||
func (dt *targetUsecaseImpl) convertFromTargetModel(ctx context.Context, target *model.Target) *apisv1.TargetBase {
|
||||
@@ -177,7 +213,15 @@ func (dt *targetUsecaseImpl) convertFromTargetModel(ctx context.Context, target
|
||||
UpdateTime: target.UpdateTime,
|
||||
AppNum: appNum,
|
||||
}
|
||||
|
||||
if target.Project != "" {
|
||||
var project = model.Project{
|
||||
Name: target.Project,
|
||||
}
|
||||
if err := dt.ds.Get(ctx, &project); err != nil {
|
||||
log.Logger.Errorf("get project failure %s", err.Error())
|
||||
}
|
||||
targetBase.Project = apisv1.NameAlias{Name: project.Name, Alias: project.Alias}
|
||||
}
|
||||
if targetBase.Cluster != nil && targetBase.Cluster.ClusterName != "" {
|
||||
cluster, err := _getClusterFromDataStore(ctx, dt.ds, target.Cluster.ClusterName)
|
||||
if err != nil {
|
||||
@@ -187,6 +231,5 @@ func (dt *targetUsecaseImpl) convertFromTargetModel(ctx context.Context, target
|
||||
targetBase.ClusterAlias = cluster.Alias
|
||||
}
|
||||
}
|
||||
|
||||
return targetBase
|
||||
}
|
||||
|
||||
@@ -85,13 +85,15 @@ func createTarget(ctx context.Context, ds datastore.DataStore, tg *model.Target)
|
||||
return nil
|
||||
}
|
||||
|
||||
func listTarget(ctx context.Context, ds datastore.DataStore, dsOption *datastore.ListOptions) ([]*model.Target, error) {
|
||||
func listTarget(ctx context.Context, ds datastore.DataStore, project string, dsOption *datastore.ListOptions) ([]*model.Target, error) {
|
||||
if dsOption == nil {
|
||||
dsOption = &datastore.ListOptions{}
|
||||
}
|
||||
|
||||
Target := model.Target{}
|
||||
Targets, err := ds.List(ctx, &Target, dsOption)
|
||||
target := model.Target{}
|
||||
if project != "" {
|
||||
target.Project = project
|
||||
}
|
||||
Targets, err := ds.List(ctx, &target, dsOption)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("list target err %v", err)
|
||||
return nil, err
|
||||
|
||||
@@ -38,7 +38,8 @@ var _ = Describe("Test target usecase functions", func() {
|
||||
ds, err := NewDatastore(datastore.Config{Type: "kubeapi", Database: "target-test-kubevela"})
|
||||
Expect(ds).ToNot(BeNil())
|
||||
Expect(err).Should(BeNil())
|
||||
projectUsecase = &projectUsecaseImpl{ds: ds, k8sClient: k8sClient}
|
||||
rbacUsecase := &rbacUsecaseImpl{ds: ds}
|
||||
projectUsecase = &projectUsecaseImpl{ds: ds, k8sClient: k8sClient, rbacUsecase: rbacUsecase}
|
||||
targetUsecase = &targetUsecaseImpl{ds: ds, k8sClient: k8sClient}
|
||||
})
|
||||
It("Test CreateTarget function", func() {
|
||||
@@ -49,6 +50,7 @@ var _ = Describe("Test target usecase functions", func() {
|
||||
Name: "test--target",
|
||||
Alias: "test-alias",
|
||||
Description: "this is a Target",
|
||||
Project: testProject,
|
||||
Cluster: &apisv1.ClusterTarget{ClusterName: "cluster-dev", Namespace: "dev"},
|
||||
Variable: map[string]interface{}{"terraform-provider": "provider", "region": "us-1"},
|
||||
}
|
||||
@@ -65,7 +67,7 @@ var _ = Describe("Test target usecase functions", func() {
|
||||
Expect(cmp.Diff(Target.Name, "test--target")).Should(BeEmpty())
|
||||
|
||||
By("Test ListTargets function")
|
||||
resp, err := targetUsecase.ListTargets(context.TODO(), 1, 1)
|
||||
resp, err := targetUsecase.ListTargets(context.TODO(), 1, 1, "")
|
||||
Expect(err).Should(BeNil())
|
||||
Expect(resp.Targets[0].ClusterAlias).Should(Equal("dev-alias"))
|
||||
|
||||
|
||||
@@ -18,17 +18,24 @@ package usecase
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"helm.sh/helm/v3/pkg/time"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
k8stypes "k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/types"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/clients"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/datastore"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/log"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/model"
|
||||
apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode"
|
||||
utils2 "github.com/oam-dev/kubevela/pkg/utils"
|
||||
)
|
||||
|
||||
// UserUsecase User manage api
|
||||
@@ -41,18 +48,21 @@ type UserUsecase interface {
|
||||
ListUsers(ctx context.Context, page, pageSize int, listOptions apisv1.ListUserOptions) (*apisv1.ListUserResponse, error)
|
||||
DisableUser(ctx context.Context, user *model.User) error
|
||||
EnableUser(ctx context.Context, user *model.User) error
|
||||
updateUserLoginTime(ctx context.Context, user *model.User) error
|
||||
DetailLoginUserInfo(ctx context.Context) (*apisv1.LoginUserInfoResponse, error)
|
||||
UpdateUserLoginTime(ctx context.Context, user *model.User) error
|
||||
Init(ctx context.Context) error
|
||||
}
|
||||
|
||||
type userUsecaseImpl struct {
|
||||
ds datastore.DataStore
|
||||
k8sClient client.Client
|
||||
projectUsecase ProjectUsecase
|
||||
rbacUsecase RBACUsecase
|
||||
sysUsecase SystemInfoUsecase
|
||||
}
|
||||
|
||||
// NewUserUsecase new User usecase
|
||||
func NewUserUsecase(ds datastore.DataStore, projectUsecase ProjectUsecase, sysUsecase SystemInfoUsecase) UserUsecase {
|
||||
func NewUserUsecase(ds datastore.DataStore, projectUsecase ProjectUsecase, sysUsecase SystemInfoUsecase, rbacUsecase RBACUsecase) UserUsecase {
|
||||
k8sClient, err := clients.GetKubeClient()
|
||||
if err != nil {
|
||||
log.Logger.Fatalf("get k8sClient failure: %s", err.Error())
|
||||
@@ -62,9 +72,60 @@ func NewUserUsecase(ds datastore.DataStore, projectUsecase ProjectUsecase, sysUs
|
||||
ds: ds,
|
||||
projectUsecase: projectUsecase,
|
||||
sysUsecase: sysUsecase,
|
||||
rbacUsecase: rbacUsecase,
|
||||
}
|
||||
}
|
||||
|
||||
func (u *userUsecaseImpl) Init(ctx context.Context) error {
|
||||
admin := model.DefaultAdminUserName
|
||||
if err := u.ds.Get(ctx, &model.User{
|
||||
Name: admin,
|
||||
}); err != nil {
|
||||
if errors.Is(err, datastore.ErrRecordNotExist) {
|
||||
pwd := utils2.RandomString(8)
|
||||
encrypted, err := GeneratePasswordHash(pwd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := u.ds.Add(ctx, &model.User{
|
||||
Name: admin,
|
||||
Alias: "Administrator",
|
||||
Password: encrypted,
|
||||
UserRoles: []string{"admin"},
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
// print default password of admin user in log
|
||||
log.Logger.Infof("init admin user, password is %s", pwd)
|
||||
secret := &corev1.Secret{}
|
||||
if err := u.k8sClient.Get(ctx, k8stypes.NamespacedName{
|
||||
Name: admin,
|
||||
Namespace: types.DefaultKubeVelaNS,
|
||||
}, secret); err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
if err := u.k8sClient.Create(ctx, &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: admin,
|
||||
Namespace: types.DefaultKubeVelaNS,
|
||||
},
|
||||
StringData: map[string]string{
|
||||
admin: pwd,
|
||||
},
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
log.Logger.Info("admin user is exist")
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetUser get user
|
||||
func (u *userUsecaseImpl) GetUser(ctx context.Context, username string) (*model.User, error) {
|
||||
user := &model.User{
|
||||
@@ -78,7 +139,11 @@ func (u *userUsecaseImpl) GetUser(ctx context.Context, username string) (*model.
|
||||
|
||||
// DetailUser return user detail
|
||||
func (u *userUsecaseImpl) DetailUser(ctx context.Context, user *model.User) (*apisv1.DetailUserResponse, error) {
|
||||
detailUser := convertUserModel(user)
|
||||
roles, err := u.rbacUsecase.ListRole(ctx, "", 0, 0)
|
||||
if err != nil {
|
||||
log.Logger.Warnf("list platform roles failure %s", err.Error())
|
||||
}
|
||||
detailUser := convertUserModel(user, roles)
|
||||
pUser := &model.ProjectUser{
|
||||
Username: user.Name,
|
||||
}
|
||||
@@ -91,16 +156,12 @@ func (u *userUsecaseImpl) DetailUser(ctx context.Context, user *model.User) (*ap
|
||||
for _, v := range projectUsers {
|
||||
pu, ok := v.(*model.ProjectUser)
|
||||
if ok {
|
||||
project, err := u.projectUsecase.GetProject(ctx, pu.ProjectName)
|
||||
project, err := u.projectUsecase.DetailProject(ctx, pu.ProjectName)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("failed to delete project(%s) info: %s", pu.ProjectName, err.Error())
|
||||
continue
|
||||
}
|
||||
detailUser.Projects = append(detailUser.Projects, apisv1.ProjectUserBase{
|
||||
Name: pu.ProjectName,
|
||||
Alias: project.Alias,
|
||||
UserRoles: pu.UserRoles,
|
||||
})
|
||||
detailUser.Projects = append(detailUser.Projects, project)
|
||||
}
|
||||
}
|
||||
return detailUser, nil
|
||||
@@ -142,12 +203,14 @@ func (u *userUsecaseImpl) CreateUser(ctx context.Context, req apisv1.CreateUserR
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// TODO: validate the roles, they must be platform roles
|
||||
user := &model.User{
|
||||
Name: req.Name,
|
||||
Alias: req.Alias,
|
||||
Email: req.Email,
|
||||
Password: hash,
|
||||
Disabled: false,
|
||||
Name: req.Name,
|
||||
Alias: req.Alias,
|
||||
Email: req.Email,
|
||||
UserRoles: req.Roles,
|
||||
Password: hash,
|
||||
Disabled: false,
|
||||
}
|
||||
if err := u.ds.Add(ctx, user); err != nil {
|
||||
return nil, err
|
||||
@@ -180,6 +243,10 @@ func (u *userUsecaseImpl) UpdateUser(ctx context.Context, user *model.User, req
|
||||
}
|
||||
user.Email = req.Email
|
||||
}
|
||||
// TODO: validate the roles, they must be platform roles
|
||||
if req.Roles != nil {
|
||||
user.UserRoles = *req.Roles
|
||||
}
|
||||
if err := u.ds.Put(ctx, user); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -211,10 +278,14 @@ func (u *userUsecaseImpl) ListUsers(ctx context.Context, page, pageSize int, lis
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
roles, err := u.rbacUsecase.ListRole(ctx, "", 0, 0)
|
||||
if err != nil {
|
||||
log.Logger.Warnf("list platform roles failure %s", err.Error())
|
||||
}
|
||||
for _, v := range users {
|
||||
user, ok := v.(*model.User)
|
||||
if ok {
|
||||
userList = append(userList, convertUserModel(user))
|
||||
userList = append(userList, convertUserModel(user, roles))
|
||||
}
|
||||
}
|
||||
count, err := u.ds.Count(ctx, user, &fo)
|
||||
@@ -246,16 +317,90 @@ func (u *userUsecaseImpl) EnableUser(ctx context.Context, user *model.User) erro
|
||||
return u.ds.Put(ctx, user)
|
||||
}
|
||||
|
||||
// updateUserLoginTime update user login time
|
||||
func (u *userUsecaseImpl) updateUserLoginTime(ctx context.Context, user *model.User) error {
|
||||
// UpdateUserLoginTime update user login time
|
||||
func (u *userUsecaseImpl) UpdateUserLoginTime(ctx context.Context, user *model.User) error {
|
||||
user.LastLoginTime = time.Now().Time
|
||||
return u.ds.Put(ctx, user)
|
||||
}
|
||||
|
||||
func convertUserModel(user *model.User) *apisv1.DetailUserResponse {
|
||||
// DetailLoginUserInfo get projects and permission policies of login user
|
||||
func (u *userUsecaseImpl) DetailLoginUserInfo(ctx context.Context) (*apisv1.LoginUserInfoResponse, error) {
|
||||
userName, ok := ctx.Value(&apisv1.CtxKeyUser).(string)
|
||||
if !ok {
|
||||
return nil, bcode.ErrUnauthorized
|
||||
}
|
||||
user, err := u.GetUser(ctx, userName)
|
||||
if !ok {
|
||||
log.Logger.Errorf("get login user model failure %s", err.Error())
|
||||
return nil, bcode.ErrUnauthorized
|
||||
}
|
||||
projects, err := u.projectUsecase.ListUserProjects(ctx, userName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var projectPermissions = make(map[string][]apisv1.PermissionBase)
|
||||
for _, project := range projects {
|
||||
perms, err := u.rbacUsecase.GetUserPermissions(ctx, user, project.Name, false)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("list user %s perm policies from project %s failure %s", user.Name, project.Name, err.Error())
|
||||
continue
|
||||
}
|
||||
projectPermissions[project.Name] = func() (list []apisv1.PermissionBase) {
|
||||
for _, perm := range perms {
|
||||
list = append(list, apisv1.PermissionBase{
|
||||
Name: perm.Name,
|
||||
Alias: perm.Alias,
|
||||
Resources: perm.Resources,
|
||||
Actions: perm.Actions,
|
||||
Effect: perm.Effect,
|
||||
CreateTime: perm.CreateTime,
|
||||
UpdateTime: perm.UpdateTime,
|
||||
})
|
||||
}
|
||||
return
|
||||
}()
|
||||
}
|
||||
perms, err := u.rbacUsecase.GetUserPermissions(ctx, user, "", true)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("list user %s platform perm policies failure %s", user.Name, err.Error())
|
||||
}
|
||||
var platformPermissions []apisv1.PermissionBase
|
||||
for _, perm := range perms {
|
||||
platformPermissions = append(platformPermissions, apisv1.PermissionBase{
|
||||
Name: perm.Name,
|
||||
Alias: perm.Alias,
|
||||
Resources: perm.Resources,
|
||||
Actions: perm.Actions,
|
||||
Effect: perm.Effect,
|
||||
CreateTime: perm.CreateTime,
|
||||
UpdateTime: perm.UpdateTime,
|
||||
})
|
||||
}
|
||||
return &apisv1.LoginUserInfoResponse{
|
||||
UserBase: *convertUserBase(user),
|
||||
Projects: projects,
|
||||
ProjectPermissions: projectPermissions,
|
||||
PlatformPermissions: platformPermissions,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func convertUserModel(user *model.User, roles *apisv1.ListRolesResponse) *apisv1.DetailUserResponse {
|
||||
|
||||
var nameAlias = make(map[string]string)
|
||||
if roles != nil {
|
||||
for _, role := range roles.Roles {
|
||||
nameAlias[role.Name] = role.Alias
|
||||
}
|
||||
}
|
||||
return &apisv1.DetailUserResponse{
|
||||
UserBase: *convertUserBase(user),
|
||||
Projects: make([]apisv1.ProjectUserBase, 0),
|
||||
Roles: func() (list []apisv1.NameAlias) {
|
||||
for _, r := range user.UserRoles {
|
||||
list = append(list, apisv1.NameAlias{Name: r, Alias: nameAlias[r]})
|
||||
}
|
||||
return
|
||||
}(),
|
||||
Projects: make([]*apisv1.ProjectBase, 0),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,9 +46,10 @@ var _ = Describe("Test authentication usecase functions", func() {
|
||||
ds, err = NewDatastore(datastore.Config{Type: "kubeapi", Database: db})
|
||||
Expect(ds).ToNot(BeNil())
|
||||
Expect(err).Should(BeNil())
|
||||
projectUsecase := &projectUsecaseImpl{k8sClient: k8sClient, ds: ds}
|
||||
rbacUsecase := &rbacUsecaseImpl{ds: ds}
|
||||
projectUsecase := &projectUsecaseImpl{k8sClient: k8sClient, ds: ds, rbacUsecase: rbacUsecase}
|
||||
sysUsecase := &systemInfoUsecaseImpl{ds: ds}
|
||||
userUsecase = &userUsecaseImpl{ds: ds, projectUsecase: projectUsecase, sysUsecase: sysUsecase}
|
||||
userUsecase = &userUsecaseImpl{ds: ds, projectUsecase: projectUsecase, sysUsecase: sysUsecase, rbacUsecase: rbacUsecase}
|
||||
})
|
||||
AfterEach(func() {
|
||||
err := k8sClient.Delete(context.Background(), &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: db}})
|
||||
@@ -111,18 +112,7 @@ var _ = Describe("Test authentication usecase functions", func() {
|
||||
Expect(user.Name).Should(Equal("name"))
|
||||
Expect(user.Alias).Should(Equal("alias"))
|
||||
Expect(user.Email).Should(Equal("email@example.com"))
|
||||
Expect(user.Projects).Should(Equal([]apisv1.ProjectUserBase{
|
||||
{
|
||||
Name: "project-1",
|
||||
Alias: "project-alias-1",
|
||||
UserRoles: []string{"user-role-1"},
|
||||
},
|
||||
{
|
||||
Name: "project-0",
|
||||
Alias: "project-alias-0",
|
||||
UserRoles: []string{"user-role-0"},
|
||||
},
|
||||
}))
|
||||
Expect(len(user.Projects)).Should(Equal(2))
|
||||
})
|
||||
|
||||
It("Test list users", func() {
|
||||
|
||||
@@ -49,12 +49,13 @@ var _ = Describe("Test application usecase function", func() {
|
||||
ds, err := NewDatastore(datastore.Config{Type: "kubeapi", Database: "app-test-kubevela"})
|
||||
Expect(ds).ToNot(BeNil())
|
||||
Expect(err).Should(BeNil())
|
||||
envUsecase = &envUsecaseImpl{ds: ds, kubeClient: k8sClient}
|
||||
workflowUsecase = &workflowUsecaseImpl{ds: ds, envUsecase: envUsecase}
|
||||
definitionUsecase = &definitionUsecaseImpl{kubeClient: k8sClient}
|
||||
envBindingUsecase = &envBindingUsecaseImpl{ds: ds, envUsecase: envUsecase, workflowUsecase: workflowUsecase, kubeClient: k8sClient, definitionUsecase: definitionUsecase}
|
||||
targetUsecase = &targetUsecaseImpl{ds: ds, k8sClient: k8sClient}
|
||||
projectUsecase = &projectUsecaseImpl{ds: ds, k8sClient: k8sClient}
|
||||
rbacUsecase := &rbacUsecaseImpl{ds: ds}
|
||||
projectUsecase = &projectUsecaseImpl{ds: ds, k8sClient: k8sClient, rbacUsecase: rbacUsecase}
|
||||
envUsecase = &envUsecaseImpl{ds: ds, kubeClient: k8sClient, projectUsecase: projectUsecase}
|
||||
appUsecase = &applicationUsecaseImpl{
|
||||
ds: ds,
|
||||
workflowUsecase: workflowUsecase,
|
||||
@@ -73,10 +74,10 @@ var _ = Describe("Test application usecase function", func() {
|
||||
})
|
||||
|
||||
It("Test HandleApplicationWebhook function", func() {
|
||||
_, err := targetUsecase.CreateTarget(context.TODO(), apisv1.CreateTargetRequest{Name: "dev-target-webhook"})
|
||||
_, err := projectUsecase.CreateProject(context.TODO(), apisv1.CreateProjectRequest{Name: "project-webhook"})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
_, err = projectUsecase.CreateProject(context.TODO(), apisv1.CreateProjectRequest{Name: "project-webhook"})
|
||||
_, err = targetUsecase.CreateTarget(context.TODO(), apisv1.CreateTargetRequest{Name: "dev-target-webhook", Project: "project-webhook"})
|
||||
Expect(err).Should(BeNil())
|
||||
|
||||
_, err = envUsecase.CreateEnv(context.TODO(), apisv1.CreateEnvRequest{Name: "webhook-dev", Namespace: "webhook-dev", Targets: []string{"dev-target-webhook"}, Project: "project-webhook"})
|
||||
|
||||
@@ -57,8 +57,9 @@ var _ = Describe("Test workflow usecase functions", func() {
|
||||
ds, err = NewDatastore(datastore.Config{Type: "kubeapi", Database: "workflow-test-" + strconv.FormatInt(time.Now().UnixNano(), 10)})
|
||||
Expect(ds).ToNot(BeNil())
|
||||
Expect(err).Should(BeNil())
|
||||
projectUsecase = &projectUsecaseImpl{ds: ds}
|
||||
envUsecase = &envUsecaseImpl{ds: ds, kubeClient: k8sClient}
|
||||
rbacUsecase := &rbacUsecaseImpl{ds: ds}
|
||||
projectUsecase = &projectUsecaseImpl{ds: ds, rbacUsecase: rbacUsecase}
|
||||
envUsecase = &envUsecaseImpl{ds: ds, kubeClient: k8sClient, projectUsecase: projectUsecase}
|
||||
workflowUsecase = &workflowUsecaseImpl{
|
||||
ds: ds,
|
||||
kubeClient: k8sClient,
|
||||
|
||||
@@ -27,3 +27,21 @@ var ErrProjectNamespaceFail = NewBcode(400, 30003, "project bind namespace failu
|
||||
|
||||
// ErrProjectNamespaceIsExist the namespace belongs to the other project
|
||||
var ErrProjectNamespaceIsExist = NewBcode(400, 30004, "the namespace belongs to the other project")
|
||||
|
||||
// ErrProjectDenyDeleteByApplication the project can't be deleted as there are applications inside
|
||||
var ErrProjectDenyDeleteByApplication = NewBcode(400, 30005, "the project can't be deleted as there are applications inside")
|
||||
|
||||
// ErrProjectDenyDeleteByEnvironment the project can't be deleted because there are environments inside
|
||||
var ErrProjectDenyDeleteByEnvironment = NewBcode(400, 30006, "the project can't be deleted before you clean up all the environments inside")
|
||||
|
||||
// ErrProjectDenyDeleteByTarget the project can't be deleted as there are targets inside
|
||||
var ErrProjectDenyDeleteByTarget = NewBcode(400, 30007, "the project can't be deleted before you clean up all these targets inside")
|
||||
|
||||
// ErrProjectRoleCheckFailure means the specified role does't belong to this project or not exist
|
||||
var ErrProjectRoleCheckFailure = NewBcode(400, 30008, "the specified role does't belong to this project or not exist")
|
||||
|
||||
// ErrProjectUserExist means the user is already exist in this project
|
||||
var ErrProjectUserExist = NewBcode(400, 30009, "the user is already exist in this project")
|
||||
|
||||
// ErrProjectOwnerIsNotExist means the project owner name is invalid
|
||||
var ErrProjectOwnerIsNotExist = NewBcode(400, 30010, "the project owner name is invalid")
|
||||
|
||||
@@ -27,4 +27,6 @@ var (
|
||||
ErrUserCannotModified = NewBcode(400, 14004, "the user cannot be modified in dex login mode")
|
||||
// ErrUserInvalidPassword is the error of user invalid password
|
||||
ErrUserInvalidPassword = NewBcode(400, 14005, "the password is invalid")
|
||||
// ErrDexConfigNotFound means the dex config is not configured
|
||||
ErrDexConfigNotFound = NewBcode(200, 14006, "the dex config is not found")
|
||||
)
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
Copyright 2022 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package bcode
|
||||
|
||||
var (
|
||||
// ErrRolePermissionCheckFailure means the perm policy is invalid where create or update role
|
||||
ErrRolePermissionCheckFailure = NewBcode(400, 15001, "the permissions are invalid")
|
||||
// ErrRoleIsExist means the role is exist
|
||||
ErrRoleIsExist = NewBcode(400, 15002, "the role name is exist")
|
||||
// ErrRoleIsNotExist means the role is not exist
|
||||
ErrRoleIsNotExist = NewBcode(400, 15003, "the role is not exist")
|
||||
// ErrPermissionNotExist means the permission is not exist
|
||||
ErrPermissionNotExist = NewBcode(404, 15004, "the permission is not exist")
|
||||
)
|
||||
@@ -35,6 +35,12 @@ import (
|
||||
// ErrServer an unexpected mistake.
|
||||
var ErrServer = NewBcode(500, 500, "The service has lapsed.")
|
||||
|
||||
// ErrForbidden check user perms failure
|
||||
var ErrForbidden = NewBcode(403, 403, "403 Forbidden")
|
||||
|
||||
// ErrUnauthorized check user auth failure
|
||||
var ErrUnauthorized = NewBcode(401, 401, "401 Unauthorized")
|
||||
|
||||
// Bcode business error code
|
||||
type Bcode struct {
|
||||
HTTPCode int32 `json:"-"`
|
||||
|
||||
@@ -29,21 +29,24 @@ import (
|
||||
)
|
||||
|
||||
// NewAddonWebService returns addon web service
|
||||
func NewAddonWebService(u usecase.AddonHandler) WebService {
|
||||
func NewAddonWebService(u usecase.AddonHandler, rbacUsecase usecase.RBACUsecase) WebService {
|
||||
return &addonWebService{
|
||||
handler: u,
|
||||
handler: u,
|
||||
rbacUsecase: rbacUsecase,
|
||||
}
|
||||
}
|
||||
|
||||
// NewEnabledAddonWebService returns enabled addon web service
|
||||
func NewEnabledAddonWebService(u usecase.AddonHandler) WebService {
|
||||
func NewEnabledAddonWebService(u usecase.AddonHandler, rbacUsecase usecase.RBACUsecase) WebService {
|
||||
return &enabledAddonWebService{
|
||||
addonUsecase: u,
|
||||
rbacUsecase: rbacUsecase,
|
||||
}
|
||||
}
|
||||
|
||||
type addonWebService struct {
|
||||
handler usecase.AddonHandler
|
||||
rbacUsecase usecase.RBACUsecase
|
||||
handler usecase.AddonHandler
|
||||
}
|
||||
|
||||
func (s *addonWebService) GetWebService() *restful.WebService {
|
||||
@@ -59,6 +62,7 @@ func (s *addonWebService) GetWebService() *restful.WebService {
|
||||
ws.Route(ws.GET("/").To(s.listAddons).
|
||||
Doc("list all addons").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(s.rbacUsecase.CheckPerm("addon", "list")).
|
||||
Param(ws.QueryParameter("registry", "filter addons from given registry").DataType("string")).
|
||||
Param(ws.QueryParameter("query", "Fuzzy search based on name and description.").DataType("string")).
|
||||
Returns(200, "OK", apis.ListAddonResponse{}).
|
||||
@@ -66,53 +70,58 @@ func (s *addonWebService) GetWebService() *restful.WebService {
|
||||
Writes(apis.ListAddonResponse{}))
|
||||
|
||||
// GET
|
||||
ws.Route(ws.GET("/{name}").To(s.detailAddon).
|
||||
ws.Route(ws.GET("/{addonName}").To(s.detailAddon).
|
||||
Doc("show details of an addon").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(s.rbacUsecase.CheckPerm("addon", "detail")).
|
||||
Returns(200, "OK", apis.DetailAddonResponse{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Param(ws.PathParameter("name", "addon name to query detail").DataType("string").Required(true)).
|
||||
Param(ws.PathParameter("addonName", "addon name to query detail").DataType("string").Required(true)).
|
||||
Param(ws.QueryParameter("registry", "filter addons from given registry").DataType("string")).
|
||||
Writes(apis.DetailAddonResponse{}))
|
||||
|
||||
// GET status
|
||||
ws.Route(ws.GET("/{name}/status").To(s.statusAddon).
|
||||
ws.Route(ws.GET("/{addonName}/status").To(s.statusAddon).
|
||||
Doc("show status of an addon").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(s.rbacUsecase.CheckPerm("addon", "detail")).
|
||||
Returns(200, "OK", apis.AddonStatusResponse{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Param(ws.PathParameter("name", "addon name to query status").DataType("string").Required(true)).
|
||||
Param(ws.PathParameter("addonName", "addon name to query status").DataType("string").Required(true)).
|
||||
Writes(apis.AddonStatusResponse{}))
|
||||
|
||||
// enable addon
|
||||
ws.Route(ws.POST("/{name}/enable").To(s.enableAddon).
|
||||
ws.Route(ws.POST("/{addonName}/enable").To(s.enableAddon).
|
||||
Doc("enable an addon").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Reads(apis.EnableAddonRequest{}).
|
||||
Filter(s.rbacUsecase.CheckPerm("addon", "enable")).
|
||||
Returns(200, "OK", apis.AddonStatusResponse{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Param(ws.PathParameter("name", "addon name to enable").DataType("string").Required(true)).
|
||||
Param(ws.PathParameter("addonName", "addon name to enable").DataType("string").Required(true)).
|
||||
Writes(apis.AddonStatusResponse{}))
|
||||
|
||||
// disable addon
|
||||
ws.Route(ws.POST("/{name}/disable").To(s.disableAddon).
|
||||
ws.Route(ws.POST("/{addonName}/disable").To(s.disableAddon).
|
||||
Doc("disable an addon").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Returns(200, "OK", apis.AddonStatusResponse{}).
|
||||
Filter(s.rbacUsecase.CheckPerm("addon", "disable")).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Param(ws.PathParameter("name", "addon name to enable").DataType("string").Required(true)).
|
||||
Param(ws.PathParameter("addonName", "addon name to enable").DataType("string").Required(true)).
|
||||
Param(ws.QueryParameter("force", "force disable an addon").DataType("boolean").Required(false)).
|
||||
Writes(apis.AddonStatusResponse{}))
|
||||
|
||||
// update addon
|
||||
ws.Route(ws.PUT("/{name}/update").To(s.updateAddon).
|
||||
ws.Route(ws.PUT("/{addonName}/update").To(s.updateAddon).
|
||||
Doc("update an addon").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Reads(apis.EnableAddonRequest{}).
|
||||
Returns(200, "OK", apis.AddonStatusResponse{}).
|
||||
Filter(s.rbacUsecase.CheckPerm("addon", "update")).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Param(ws.PathParameter("name", "addon name to update").DataType("string").Required(true)).
|
||||
Param(ws.PathParameter("addonName", "addon name to update").DataType("string").Required(true)).
|
||||
Writes(apis.AddonStatusResponse{}))
|
||||
|
||||
ws.Filter(authCheckFilter)
|
||||
@@ -144,7 +153,7 @@ func (s *addonWebService) listAddons(req *restful.Request, res *restful.Response
|
||||
}
|
||||
|
||||
func (s *addonWebService) detailAddon(req *restful.Request, res *restful.Response) {
|
||||
name := req.PathParameter("name")
|
||||
name := req.PathParameter("addonName")
|
||||
addon, err := s.handler.GetAddon(req.Request.Context(), name, req.QueryParameter("registry"))
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
@@ -178,8 +187,7 @@ func (s *addonWebService) enableAddon(req *restful.Request, res *restful.Respons
|
||||
createReq.Args[types.ClustersArg] = createReq.Clusters
|
||||
}
|
||||
|
||||
name := req.PathParameter("name")
|
||||
|
||||
name := req.PathParameter("addonName")
|
||||
err = s.handler.EnableAddon(req.Request.Context(), name, createReq)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
@@ -190,7 +198,7 @@ func (s *addonWebService) enableAddon(req *restful.Request, res *restful.Respons
|
||||
}
|
||||
|
||||
func (s *addonWebService) disableAddon(req *restful.Request, res *restful.Response) {
|
||||
name := req.PathParameter("name")
|
||||
name := req.PathParameter("addonName")
|
||||
forceParam := req.QueryParameter("force")
|
||||
force, _ := strconv.ParseBool(forceParam)
|
||||
err := s.handler.DisableAddon(req.Request.Context(), name, force)
|
||||
@@ -202,7 +210,7 @@ func (s *addonWebService) disableAddon(req *restful.Request, res *restful.Respon
|
||||
}
|
||||
|
||||
func (s *addonWebService) statusAddon(req *restful.Request, res *restful.Response) {
|
||||
name := req.PathParameter("name")
|
||||
name := req.PathParameter("addonName")
|
||||
status, err := s.handler.StatusAddon(req.Request.Context(), name)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
@@ -235,7 +243,7 @@ func (s *addonWebService) updateAddon(req *restful.Request, res *restful.Respons
|
||||
createReq.Args[types.ClustersArg] = createReq.Clusters
|
||||
}
|
||||
|
||||
name := req.PathParameter("name")
|
||||
name := req.PathParameter("addonName")
|
||||
err = s.handler.UpdateAddon(req.Request.Context(), name, createReq)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
@@ -247,6 +255,7 @@ func (s *addonWebService) updateAddon(req *restful.Request, res *restful.Respons
|
||||
|
||||
type enabledAddonWebService struct {
|
||||
addonUsecase usecase.AddonHandler
|
||||
rbacUsecase usecase.RBACUsecase
|
||||
}
|
||||
|
||||
func (s *enabledAddonWebService) GetWebService() *restful.WebService {
|
||||
@@ -262,12 +271,14 @@ func (s *enabledAddonWebService) GetWebService() *restful.WebService {
|
||||
ws.Route(ws.GET("/").To(s.list).
|
||||
Doc("list all addons").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(s.rbacUsecase.CheckPerm("addon", "list")).
|
||||
Param(ws.QueryParameter("registry", "filter addons from given registry").DataType("string")).
|
||||
Param(ws.QueryParameter("query", "Fuzzy search based on name and description.").DataType("string")).
|
||||
Returns(200, "OK", apis.ListAddonResponse{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.ListAddonResponse{}))
|
||||
|
||||
ws.Filter(authCheckFilter)
|
||||
return ws
|
||||
}
|
||||
|
||||
|
||||
@@ -26,14 +26,16 @@ import (
|
||||
)
|
||||
|
||||
// NewAddonRegistryWebService returns addon registry web service
|
||||
func NewAddonRegistryWebService(u usecase.AddonHandler) WebService {
|
||||
func NewAddonRegistryWebService(u usecase.AddonHandler, rbacUsecase usecase.RBACUsecase) WebService {
|
||||
return &addonRegistryWebService{
|
||||
addonUsecase: u,
|
||||
rbacUsecase: rbacUsecase,
|
||||
}
|
||||
}
|
||||
|
||||
type addonRegistryWebService struct {
|
||||
addonUsecase usecase.AddonHandler
|
||||
rbacUsecase usecase.RBACUsecase
|
||||
}
|
||||
|
||||
func (s *addonRegistryWebService) GetWebService() *restful.WebService {
|
||||
@@ -50,6 +52,7 @@ func (s *addonRegistryWebService) GetWebService() *restful.WebService {
|
||||
Doc("create an addon registry").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Reads(apis.CreateAddonRegistryRequest{}).
|
||||
Filter(s.rbacUsecase.CheckPerm("addonRegistry", "create")).
|
||||
Returns(200, "OK", apis.AddonRegistry{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.AddonRegistry{}))
|
||||
@@ -57,24 +60,27 @@ func (s *addonRegistryWebService) GetWebService() *restful.WebService {
|
||||
ws.Route(ws.GET("/").To(s.listAddonRegistry).
|
||||
Doc("list all addon registry").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(s.rbacUsecase.CheckPerm("addonRegistry", "list")).
|
||||
Returns(200, "OK", apis.ListAddonRegistryResponse{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.ListAddonRegistryResponse{}))
|
||||
|
||||
// Delete
|
||||
ws.Route(ws.DELETE("/{name}").To(s.deleteAddonRegistry).
|
||||
ws.Route(ws.DELETE("/{addonRegName}").To(s.deleteAddonRegistry).
|
||||
Doc("delete an addon registry").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Param(ws.PathParameter("name", "identifier of the addon registry").DataType("string")).
|
||||
Param(ws.PathParameter("addonRegName", "identifier of the addon registry").DataType("string")).
|
||||
Returns(200, "OK", apis.AddonRegistry{}).
|
||||
Filter(s.rbacUsecase.CheckPerm("addonRegistry", "delete")).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.AddonRegistry{}))
|
||||
|
||||
ws.Route(ws.PUT("/{name}").To(s.updateAddonRegistry).
|
||||
ws.Route(ws.PUT("/{addonRegName}").To(s.updateAddonRegistry).
|
||||
Doc("update an addon registry").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Reads(apis.UpdateAddonRegistryRequest{}).
|
||||
Param(ws.PathParameter("name", "identifier of the addon registry").DataType("string")).
|
||||
Filter(s.rbacUsecase.CheckPerm("addonRegistry", "update")).
|
||||
Param(ws.PathParameter("addonRegName", "identifier of the addon registry").DataType("string")).
|
||||
Returns(200, "OK", apis.AddonRegistry{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.AddonRegistry{}))
|
||||
@@ -110,7 +116,7 @@ func (s *addonRegistryWebService) createAddonRegistry(req *restful.Request, res
|
||||
}
|
||||
|
||||
func (s *addonRegistryWebService) deleteAddonRegistry(req *restful.Request, res *restful.Response) {
|
||||
r, err := s.addonUsecase.GetAddonRegistry(req.Request.Context(), req.PathParameter("name"))
|
||||
r, err := s.addonUsecase.GetAddonRegistry(req.Request.Context(), req.PathParameter("addonRegName"))
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
@@ -151,7 +157,7 @@ func (s *addonRegistryWebService) updateAddonRegistry(req *restful.Request, res
|
||||
return
|
||||
}
|
||||
// Call the usecase layer code
|
||||
meta, err := s.addonUsecase.UpdateAddonRegistry(req.Request.Context(), req.PathParameter("name"), updateReq)
|
||||
meta, err := s.addonUsecase.UpdateAddonRegistry(req.Request.Context(), req.PathParameter("addonRegName"), updateReq)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
|
||||
@@ -33,17 +33,19 @@ import (
|
||||
|
||||
type applicationWebService struct {
|
||||
workflowWebService
|
||||
rbacUsecase usecase.RBACUsecase
|
||||
applicationUsecase usecase.ApplicationUsecase
|
||||
envBindingUsecase usecase.EnvBindingUsecase
|
||||
}
|
||||
|
||||
// NewApplicationWebService new application manage webservice
|
||||
func NewApplicationWebService(applicationUsecase usecase.ApplicationUsecase, envBindingUsecase usecase.EnvBindingUsecase, workflowUsecase usecase.WorkflowUsecase) WebService {
|
||||
func NewApplicationWebService(applicationUsecase usecase.ApplicationUsecase, envBindingUsecase usecase.EnvBindingUsecase, workflowUsecase usecase.WorkflowUsecase, rbacUsecase usecase.RBACUsecase) WebService {
|
||||
return &applicationWebService{
|
||||
workflowWebService: workflowWebService{
|
||||
workflowUsecase: workflowUsecase,
|
||||
applicationUsecase: applicationUsecase,
|
||||
},
|
||||
rbacUsecase: rbacUsecase,
|
||||
applicationUsecase: applicationUsecase,
|
||||
envBindingUsecase: envBindingUsecase,
|
||||
}
|
||||
@@ -62,8 +64,11 @@ func (c *applicationWebService) GetWebService() *restful.WebService {
|
||||
Doc("list all applications").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Param(ws.QueryParameter("query", "Fuzzy search based on name or description").DataType("string")).
|
||||
Param(ws.QueryParameter("namespace", "The namespace of the managed cluster").DataType("string")).
|
||||
Param(ws.QueryParameter("project", "search base on project name").DataType("string")).
|
||||
Param(ws.QueryParameter("env", "search base on env name").DataType("string")).
|
||||
Param(ws.QueryParameter("targetName", "Name of the application delivery target").DataType("string")).
|
||||
// This api will filter the app by user's permissions
|
||||
// Filter(c.rbacUsecase.CheckPerm("application", "list")).
|
||||
Returns(200, "OK", apis.ListApplicationResponse{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.ListApplicationResponse{}))
|
||||
@@ -72,132 +77,146 @@ func (c *applicationWebService) GetWebService() *restful.WebService {
|
||||
Doc("create one application ").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Reads(apis.CreateApplicationRequest{}).
|
||||
Filter(c.rbacUsecase.CheckPerm("application", "create")).
|
||||
Returns(200, "OK", apis.ApplicationBase{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.ApplicationBase{}))
|
||||
|
||||
ws.Route(ws.DELETE("/{name}").To(c.deleteApplication).
|
||||
ws.Route(ws.DELETE("/{appName}").To(c.deleteApplication).
|
||||
Doc("delete one application").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.rbacUsecase.CheckPerm("application", "delete")).
|
||||
Filter(c.appCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application ").DataType("string")).
|
||||
Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")).
|
||||
Returns(200, "OK", apis.EmptyResponse{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.EmptyResponse{}))
|
||||
|
||||
ws.Route(ws.GET("/{name}").To(c.detailApplication).
|
||||
ws.Route(ws.GET("/{appName}").To(c.detailApplication).
|
||||
Doc("detail one application ").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.rbacUsecase.CheckPerm("application", "detail")).
|
||||
Filter(c.appCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application ").DataType("string")).
|
||||
Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")).
|
||||
Returns(200, "OK", apis.DetailApplicationResponse{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.DetailApplicationResponse{}))
|
||||
|
||||
ws.Route(ws.PUT("/{name}").To(c.updateApplication).
|
||||
ws.Route(ws.PUT("/{appName}").To(c.updateApplication).
|
||||
Doc("update one application ").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.rbacUsecase.CheckPerm("application", "update")).
|
||||
Filter(c.appCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application ").DataType("string")).
|
||||
Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")).
|
||||
Reads(apis.UpdateApplicationRequest{}).
|
||||
Returns(200, "OK", apis.ApplicationBase{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.ApplicationBase{}))
|
||||
|
||||
ws.Route(ws.GET("/{name}/statistics").To(c.applicationStatistics).
|
||||
ws.Route(ws.GET("/{appName}/statistics").To(c.applicationStatistics).
|
||||
Doc("detail one application ").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.rbacUsecase.CheckPerm("application", "detail")).
|
||||
Filter(c.appCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application ").DataType("string")).
|
||||
Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")).
|
||||
Returns(200, "OK", apis.ApplicationStatisticsResponse{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.ApplicationStatisticsResponse{}))
|
||||
|
||||
ws.Route(ws.POST("/{name}/triggers").To(c.createApplicationTrigger).
|
||||
ws.Route(ws.POST("/{appName}/triggers").To(c.createApplicationTrigger).
|
||||
Doc("create one application trigger").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.rbacUsecase.CheckPerm("trigger", "create")).
|
||||
Filter(c.appCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application ").DataType("string")).
|
||||
Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")).
|
||||
Reads(apis.CreateApplicationTriggerRequest{}).
|
||||
Returns(200, "OK", apis.ApplicationTriggerBase{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.ApplicationTriggerBase{}))
|
||||
|
||||
ws.Route(ws.DELETE("/{name}/triggers/{token}").To(c.deleteApplicationTrigger).
|
||||
ws.Route(ws.DELETE("/{appName}/triggers/{token}").To(c.deleteApplicationTrigger).
|
||||
Doc("delete one application trigger").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.rbacUsecase.CheckPerm("trigger", "delete")).
|
||||
Filter(c.appCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application ").DataType("string")).
|
||||
Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")).
|
||||
Param(ws.PathParameter("token", "identifier of the trigger").DataType("string")).
|
||||
Returns(200, "OK", apis.EmptyResponse{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes([]*apis.EmptyResponse{}))
|
||||
|
||||
ws.Route(ws.GET("/{name}/triggers").To(c.listApplicationTriggers).
|
||||
ws.Route(ws.GET("/{appName}/triggers").To(c.listApplicationTriggers).
|
||||
Doc("list application triggers").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.rbacUsecase.CheckPerm("trigger", "list")).
|
||||
Filter(c.appCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application ").DataType("string")).
|
||||
Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")).
|
||||
Returns(200, "OK", apis.ListApplicationTriggerResponse{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes([]*apis.ApplicationTriggerBase{}))
|
||||
|
||||
ws.Route(ws.POST("/{name}/template").To(c.publishApplicationTemplate).
|
||||
ws.Route(ws.POST("/{appName}/template").To(c.publishApplicationTemplate).
|
||||
Doc("create one application template").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.rbacUsecase.CheckPerm("applicationTemplate", "create")).
|
||||
Filter(c.appCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application ").DataType("string")).
|
||||
Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")).
|
||||
Reads(apis.CreateApplicationTemplateRequest{}).
|
||||
Returns(200, "OK", apis.ApplicationTemplateBase{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.ApplicationTemplateBase{}))
|
||||
|
||||
ws.Route(ws.POST("/{name}/deploy").To(c.deployApplication).
|
||||
ws.Route(ws.POST("/{appName}/deploy").To(c.deployApplication).
|
||||
Doc("deploy or upgrade the application").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.rbacUsecase.CheckPerm("application", "deploy")).
|
||||
Filter(c.appCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application ").DataType("string")).
|
||||
Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")).
|
||||
Reads(apis.ApplicationDeployRequest{}).
|
||||
Returns(200, "OK", apis.ApplicationDeployResponse{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.ApplicationDeployResponse{}))
|
||||
|
||||
ws.Route(ws.GET("/{name}/components").To(c.listApplicationComponents).
|
||||
ws.Route(ws.GET("/{appName}/components").To(c.listApplicationComponents).
|
||||
Doc("gets the list of application components").
|
||||
Filter(c.rbacUsecase.CheckPerm("component", "list")).
|
||||
Filter(c.appCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application ").DataType("string")).
|
||||
Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")).
|
||||
Param(ws.QueryParameter("envName", "list components that deployed in define env").DataType("string")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Returns(200, "OK", apis.ComponentListResponse{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.ComponentListResponse{}))
|
||||
|
||||
ws.Route(ws.POST("/{name}/components").To(c.createComponent).
|
||||
ws.Route(ws.POST("/{appName}/components").To(c.createComponent).
|
||||
Doc("create component for application ").
|
||||
Filter(c.rbacUsecase.CheckPerm("component", "create")).
|
||||
Filter(c.appCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application ").DataType("string")).
|
||||
Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Reads(apis.CreateComponentRequest{}).
|
||||
Returns(200, "OK", apis.ComponentBase{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.ComponentBase{}))
|
||||
|
||||
ws.Route(ws.GET("/{name}/components/{compName}").To(c.detailComponent).
|
||||
ws.Route(ws.GET("/{appName}/components/{compName}").To(c.detailComponent).
|
||||
Doc("detail component for application ").
|
||||
Filter(c.rbacUsecase.CheckPerm("component", "detail")).
|
||||
Filter(c.appCheckFilter).
|
||||
Filter(c.componentCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application ").DataType("string")).
|
||||
Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")).
|
||||
Param(ws.PathParameter("compName", "identifier of the component").DataType("string")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Returns(200, "OK", apis.DetailComponentResponse{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.DetailComponentResponse{}))
|
||||
|
||||
ws.Route(ws.PUT("/{name}/components/{compName}").To(c.updateComponent).
|
||||
ws.Route(ws.PUT("/{appName}/components/{compName}").To(c.updateComponent).
|
||||
Doc("update component config").
|
||||
Filter(c.rbacUsecase.CheckPerm("component", "update")).
|
||||
Filter(c.appCheckFilter).
|
||||
Filter(c.componentCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application").DataType("string")).
|
||||
Param(ws.PathParameter("appName", "identifier of the application").DataType("string")).
|
||||
Param(ws.PathParameter("compName", "identifier of the component").DataType("string")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Reads(apis.UpdateApplicationComponentRequest{}).
|
||||
@@ -205,11 +224,12 @@ func (c *applicationWebService) GetWebService() *restful.WebService {
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.ComponentBase{}))
|
||||
|
||||
ws.Route(ws.DELETE("/{name}/components/{compName}").To(c.deleteComponent).
|
||||
ws.Route(ws.DELETE("/{appName}/components/{compName}").To(c.deleteComponent).
|
||||
Doc("delete a component").
|
||||
Filter(c.rbacUsecase.CheckPerm("component", "delete")).
|
||||
Filter(c.appCheckFilter).
|
||||
Filter(c.componentCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application").DataType("string")).
|
||||
Param(ws.PathParameter("appName", "identifier of the application").DataType("string")).
|
||||
Param(ws.PathParameter("compName", "identifier of the component").DataType("string")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Returns(200, "OK", apis.EmptyResponse{}).
|
||||
@@ -217,49 +237,54 @@ func (c *applicationWebService) GetWebService() *restful.WebService {
|
||||
Returns(404, "Not Found", bcode.Bcode{}).
|
||||
Writes(apis.EmptyResponse{}))
|
||||
|
||||
ws.Route(ws.GET("/{name}/policies").To(c.listApplicationPolicies).
|
||||
ws.Route(ws.GET("/{appName}/policies").To(c.listApplicationPolicies).
|
||||
Doc("list policy for application").
|
||||
Filter(c.rbacUsecase.CheckPerm("policy", "list")).
|
||||
Filter(c.appCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application ").DataType("string")).
|
||||
Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Returns(200, "OK", apis.ListApplicationPolicy{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.ListApplicationPolicy{}))
|
||||
|
||||
ws.Route(ws.POST("/{name}/policies").To(c.createApplicationPolicy).
|
||||
ws.Route(ws.POST("/{appName}/policies").To(c.createApplicationPolicy).
|
||||
Doc("create policy for application").
|
||||
Filter(c.rbacUsecase.CheckPerm("policy", "create")).
|
||||
Filter(c.appCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application").DataType("string")).
|
||||
Param(ws.PathParameter("appName", "identifier of the application").DataType("string")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Reads(apis.CreatePolicyRequest{}).
|
||||
Returns(200, "OK", apis.PolicyBase{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.PolicyBase{}))
|
||||
|
||||
ws.Route(ws.GET("/{name}/policies/{policyName}").To(c.detailApplicationPolicy).
|
||||
ws.Route(ws.GET("/{appName}/policies/{policyName}").To(c.detailApplicationPolicy).
|
||||
Doc("detail policy for application").
|
||||
Filter(c.rbacUsecase.CheckPerm("policy", "detail")).
|
||||
Filter(c.appCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application").DataType("string")).
|
||||
Param(ws.PathParameter("appName", "identifier of the application").DataType("string")).
|
||||
Param(ws.PathParameter("policyName", "identifier of the application policy").DataType("string")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Returns(200, "OK", apis.DetailPolicyResponse{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.DetailPolicyResponse{}))
|
||||
|
||||
ws.Route(ws.DELETE("/{name}/policies/{policyName}").To(c.deleteApplicationPolicy).
|
||||
ws.Route(ws.DELETE("/{appName}/policies/{policyName}").To(c.deleteApplicationPolicy).
|
||||
Doc("detail policy for application").
|
||||
Filter(c.rbacUsecase.CheckPerm("policy", "delete")).
|
||||
Filter(c.appCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application").DataType("string")).
|
||||
Param(ws.PathParameter("appName", "identifier of the application").DataType("string")).
|
||||
Param(ws.PathParameter("policyName", "identifier of the application policy").DataType("string")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Returns(200, "OK", apis.EmptyResponse{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.EmptyResponse{}))
|
||||
|
||||
ws.Route(ws.PUT("/{name}/policies/{policyName}").To(c.updateApplicationPolicy).
|
||||
ws.Route(ws.PUT("/{appName}/policies/{policyName}").To(c.updateApplicationPolicy).
|
||||
Doc("update policy for application").
|
||||
Filter(c.rbacUsecase.CheckPerm("policy", "update")).
|
||||
Filter(c.appCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application").DataType("string")).
|
||||
Param(ws.PathParameter("appName", "identifier of the application").DataType("string")).
|
||||
Param(ws.PathParameter("policyName", "identifier of the application policy").DataType("string")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Reads(apis.UpdatePolicyRequest{}).
|
||||
@@ -267,11 +292,12 @@ func (c *applicationWebService) GetWebService() *restful.WebService {
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.DetailPolicyResponse{}))
|
||||
|
||||
ws.Route(ws.POST("/{name}/components/{compName}/traits").To(c.addApplicationTrait).
|
||||
ws.Route(ws.POST("/{appName}/components/{compName}/traits").To(c.addApplicationTrait).
|
||||
Doc("add trait for a component").
|
||||
Filter(c.rbacUsecase.CheckPerm("trait", "create")).
|
||||
Filter(c.appCheckFilter).
|
||||
Filter(c.componentCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application").DataType("string")).
|
||||
Param(ws.PathParameter("appName", "identifier of the application").DataType("string")).
|
||||
Param(ws.PathParameter("compName", "identifier of the component").DataType("string")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Reads(apis.CreateApplicationTraitRequest{}).
|
||||
@@ -279,11 +305,12 @@ func (c *applicationWebService) GetWebService() *restful.WebService {
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.ApplicationTrait{}))
|
||||
|
||||
ws.Route(ws.PUT("/{name}/components/{compName}/traits/{traitType}").To(c.updateApplicationTrait).
|
||||
ws.Route(ws.PUT("/{appName}/components/{compName}/traits/{traitType}").To(c.updateApplicationTrait).
|
||||
Doc("update trait from a component").
|
||||
Filter(c.rbacUsecase.CheckPerm("trait", "update")).
|
||||
Filter(c.appCheckFilter).
|
||||
Filter(c.componentCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application").DataType("string")).
|
||||
Param(ws.PathParameter("appName", "identifier of the application").DataType("string")).
|
||||
Param(ws.PathParameter("compName", "identifier of the component").DataType("string")).
|
||||
Param(ws.PathParameter("traitType", "identifier of the type of trait").DataType("string")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
@@ -292,11 +319,12 @@ func (c *applicationWebService) GetWebService() *restful.WebService {
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.ApplicationTrait{}))
|
||||
|
||||
ws.Route(ws.DELETE("/{name}/components/{compName}/traits/{traitType}").To(c.deleteApplicationTrait).
|
||||
ws.Route(ws.DELETE("/{appName}/components/{compName}/traits/{traitType}").To(c.deleteApplicationTrait).
|
||||
Doc("delete trait from a component").
|
||||
Filter(c.rbacUsecase.CheckPerm("trait", "delete")).
|
||||
Filter(c.appCheckFilter).
|
||||
Filter(c.componentCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application").DataType("string")).
|
||||
Param(ws.PathParameter("appName", "identifier of the application").DataType("string")).
|
||||
Param(ws.PathParameter("compName", "identifier of the component").DataType("string")).
|
||||
Param(ws.PathParameter("traitType", "identifier of the type of trait").DataType("string")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
@@ -304,10 +332,11 @@ func (c *applicationWebService) GetWebService() *restful.WebService {
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.EmptyResponse{}))
|
||||
|
||||
ws.Route(ws.GET("/{name}/revisions").To(c.listApplicationRevisions).
|
||||
ws.Route(ws.GET("/{appName}/revisions").To(c.listApplicationRevisions).
|
||||
Doc("list revisions for application").
|
||||
Filter(c.rbacUsecase.CheckPerm("revision", "list")).
|
||||
Filter(c.appCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application ").DataType("string")).
|
||||
Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")).
|
||||
Param(ws.QueryParameter("envName", "query identifier of the env").DataType("string")).
|
||||
Param(ws.QueryParameter("status", "query identifier of the status").DataType("string")).
|
||||
Param(ws.QueryParameter("page", "query the page number").DataType("integer")).
|
||||
@@ -317,135 +346,148 @@ func (c *applicationWebService) GetWebService() *restful.WebService {
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.ListRevisionsResponse{}))
|
||||
|
||||
ws.Route(ws.GET("/{name}/revisions/{revision}").To(c.detailApplicationRevision).
|
||||
ws.Route(ws.GET("/{appName}/revisions/{revision}").To(c.detailApplicationRevision).
|
||||
Doc("detail revision for application").
|
||||
Filter(c.rbacUsecase.CheckPerm("revision", "detail")).
|
||||
Filter(c.appCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application").DataType("string")).
|
||||
Param(ws.PathParameter("appName", "identifier of the application").DataType("string")).
|
||||
Param(ws.PathParameter("revision", "identifier of the application revision").DataType("string")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Returns(200, "OK", apis.DetailRevisionResponse{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.DetailRevisionResponse{}))
|
||||
|
||||
ws.Route(ws.GET("/{name}/envs").To(c.listApplicationEnvs).
|
||||
ws.Route(ws.GET("/{appName}/envs").To(c.listApplicationEnvs).
|
||||
Doc("list policy for application").
|
||||
Filter(c.rbacUsecase.CheckPerm("envBinding", "list")).
|
||||
Filter(c.appCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application ").DataType("string")).
|
||||
Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Returns(200, "OK", apis.ListApplicationEnvBinding{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.ListApplicationEnvBinding{}))
|
||||
|
||||
ws.Route(ws.POST("/{name}/envs").To(c.createApplicationEnv).
|
||||
ws.Route(ws.POST("/{appName}/envs").To(c.createApplicationEnv).
|
||||
Doc("creating an application environment ").
|
||||
Filter(c.rbacUsecase.CheckPerm("envBinding", "create")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.appCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application ").DataType("string")).
|
||||
Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")).
|
||||
Reads(apis.CreateApplicationEnvbindingRequest{}).
|
||||
Returns(200, "OK", apis.EnvBinding{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.EmptyResponse{}))
|
||||
|
||||
ws.Route(ws.PUT("/{name}/envs/{envName}").To(c.updateApplicationEnv).
|
||||
ws.Route(ws.PUT("/{appName}/envs/{envName}").To(c.updateApplicationEnv).
|
||||
Doc("set application differences in the specified environment").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.rbacUsecase.CheckPerm("envBinding", "update")).
|
||||
Filter(c.appCheckFilter).
|
||||
Filter(c.envCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application ").DataType("string")).
|
||||
Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")).
|
||||
Param(ws.PathParameter("envName", "identifier of the envBinding ").DataType("string")).
|
||||
Reads(apis.PutApplicationEnvBindingRequest{}).
|
||||
Returns(200, "OK", apis.EnvBinding{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.EnvBinding{}))
|
||||
|
||||
ws.Route(ws.DELETE("/{name}/envs/{envName}").To(c.deleteApplicationEnv).
|
||||
ws.Route(ws.DELETE("/{appName}/envs/{envName}").To(c.deleteApplicationEnv).
|
||||
Doc("delete an application environment ").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.rbacUsecase.CheckPerm("envBinding", "delete")).
|
||||
Filter(c.appCheckFilter).
|
||||
Filter(c.envCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application ").DataType("string")).
|
||||
Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")).
|
||||
Param(ws.PathParameter("envName", "identifier of the envBinding ").DataType("string")).
|
||||
Returns(200, "OK", apis.EmptyResponse{}).
|
||||
Returns(404, "Not Found", bcode.Bcode{}).
|
||||
Writes(apis.EmptyResponse{}))
|
||||
|
||||
ws.Route(ws.GET("/{name}/envs/{envName}/status").To(c.getApplicationStatus).
|
||||
ws.Route(ws.GET("/{appName}/envs/{envName}/status").To(c.getApplicationStatus).
|
||||
Doc("get application status").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.rbacUsecase.CheckPerm("envBinding", "detail")).
|
||||
Filter(c.appCheckFilter).
|
||||
Filter(c.envCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application ").DataType("string")).
|
||||
Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")).
|
||||
Param(ws.PathParameter("envName", "identifier of the application envbinding").DataType("string")).
|
||||
Returns(200, "OK", apis.ApplicationStatusResponse{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.ApplicationStatusResponse{}))
|
||||
|
||||
ws.Route(ws.POST("/{name}/envs/{envName}/recycle").To(c.recycleApplicationEnv).
|
||||
ws.Route(ws.POST("/{appName}/envs/{envName}/recycle").To(c.recycleApplicationEnv).
|
||||
Doc("get application status").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.rbacUsecase.CheckPerm("envBinding", "recycle")).
|
||||
Filter(c.appCheckFilter).
|
||||
Filter(c.envCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application ").DataType("string").Required(true)).
|
||||
Param(ws.PathParameter("appName", "identifier of the application ").DataType("string").Required(true)).
|
||||
Param(ws.PathParameter("envName", "identifier of the application envbinding").DataType("string").Required(true)).
|
||||
Returns(200, "OK", apis.EmptyResponse{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.EmptyResponse{}))
|
||||
|
||||
ws.Route(ws.GET("/{name}/workflows").To(c.listApplicationWorkflows).
|
||||
ws.Route(ws.GET("/{appName}/workflows").To(c.listApplicationWorkflows).
|
||||
Doc("list application workflow").
|
||||
Filter(c.rbacUsecase.CheckPerm("application/workflow", "list")).
|
||||
Filter(c.appCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application.").DataType("string").Required(true)).
|
||||
Param(ws.PathParameter("appName", "identifier of the application.").DataType("string").Required(true)).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Returns(200, "OK", apis.ListWorkflowResponse{}).
|
||||
Writes(apis.ListWorkflowResponse{}).Do(returns200, returns500))
|
||||
|
||||
ws.Route(ws.POST("/{name}/workflows").To(c.createOrUpdateApplicationWorkflow).
|
||||
ws.Route(ws.POST("/{appName}/workflows").To(c.createOrUpdateApplicationWorkflow).
|
||||
Doc("create application workflow").
|
||||
Filter(c.rbacUsecase.CheckPerm("application/workflow", "create")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Reads(apis.CreateWorkflowRequest{}).
|
||||
Filter(c.appCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application.").DataType("string").Required(true)).
|
||||
Param(ws.PathParameter("appName", "identifier of the application.").DataType("string").Required(true)).
|
||||
Returns(200, "create success", apis.DetailWorkflowResponse{}).
|
||||
Returns(400, "create failure", bcode.Bcode{}).
|
||||
Writes(apis.DetailWorkflowResponse{}).Do(returns200, returns500))
|
||||
|
||||
ws.Route(ws.GET("/{name}/workflows/{workflowName}").To(c.detailWorkflow).
|
||||
ws.Route(ws.GET("/{appName}/workflows/{workflowName}").To(c.detailWorkflow).
|
||||
Doc("detail application workflow").
|
||||
Filter(c.rbacUsecase.CheckPerm("application/workflow", "detail")).
|
||||
Filter(c.appCheckFilter).
|
||||
Filter(c.workflowCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application.").DataType("string").Required(true)).
|
||||
Param(ws.PathParameter("appName", "identifier of the application.").DataType("string").Required(true)).
|
||||
Param(ws.PathParameter("workflowName", "identifier of the workfloc.").DataType("string")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.workflowCheckFilter).
|
||||
Returns(200, "create success", apis.DetailWorkflowResponse{}).
|
||||
Writes(apis.DetailWorkflowResponse{}).Do(returns200, returns500))
|
||||
|
||||
ws.Route(ws.PUT("/{name}/workflows/{workflowName}").To(c.updateWorkflow).
|
||||
ws.Route(ws.PUT("/{appName}/workflows/{workflowName}").To(c.updateWorkflow).
|
||||
Doc("update application workflow config").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.rbacUsecase.CheckPerm("application/workflow", "update")).
|
||||
Filter(c.appCheckFilter).
|
||||
Filter(c.workflowCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application.").DataType("string").Required(true)).
|
||||
Param(ws.PathParameter("appName", "identifier of the application.").DataType("string").Required(true)).
|
||||
Param(ws.PathParameter("workflowName", "identifier of the workflow").DataType("string")).
|
||||
Reads(apis.UpdateWorkflowRequest{}).
|
||||
Returns(200, "OK", apis.DetailWorkflowResponse{}).
|
||||
Writes(apis.DetailWorkflowResponse{}).Do(returns200, returns500))
|
||||
|
||||
ws.Route(ws.DELETE("/{name}/workflows/{workflowName}").To(c.deleteWorkflow).
|
||||
ws.Route(ws.DELETE("/{appName}/workflows/{workflowName}").To(c.deleteWorkflow).
|
||||
Doc("deletet workflow").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.rbacUsecase.CheckPerm("application/workflow", "delete")).
|
||||
Filter(c.appCheckFilter).
|
||||
Filter(c.workflowCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application.").DataType("string").Required(true)).
|
||||
Param(ws.PathParameter("appName", "identifier of the application.").DataType("string").Required(true)).
|
||||
Param(ws.PathParameter("workflowName", "identifier of the workflow").DataType("string")).
|
||||
Returns(200, "OK", apis.EmptyResponse{}).
|
||||
Writes(apis.EmptyResponse{}).Do(returns200, returns500))
|
||||
|
||||
ws.Route(ws.GET("/{name}/workflows/{workflowName}/records").To(c.listWorkflowRecords).
|
||||
ws.Route(ws.GET("/{appName}/workflows/{workflowName}/records").To(c.listWorkflowRecords).
|
||||
Doc("query application workflow execution record").
|
||||
Param(ws.PathParameter("name", "identifier of the application.").DataType("string").Required(true)).
|
||||
Param(ws.PathParameter("appName", "identifier of the application.").DataType("string").Required(true)).
|
||||
Param(ws.PathParameter("workflowName", "identifier of the workflow").DataType("string")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.rbacUsecase.CheckPerm("application/workflow/record", "list")).
|
||||
Filter(c.appCheckFilter).
|
||||
Filter(c.workflowCheckFilter).
|
||||
Param(ws.QueryParameter("page", "query the page number").DataType("integer")).
|
||||
@@ -453,9 +495,10 @@ func (c *applicationWebService) GetWebService() *restful.WebService {
|
||||
Returns(200, "OK", apis.ListWorkflowRecordsResponse{}).
|
||||
Writes(apis.ListWorkflowRecordsResponse{}).Do(returns200, returns500))
|
||||
|
||||
ws.Route(ws.GET("/{name}/workflows/{workflowName}/records/{record}").To(c.detailWorkflowRecord).
|
||||
ws.Route(ws.GET("/{appName}/workflows/{workflowName}/records/{record}").To(c.detailWorkflowRecord).
|
||||
Doc("query application workflow execution record detail").
|
||||
Param(ws.PathParameter("name", "identifier of the application.").DataType("string").Required(true)).
|
||||
Filter(c.rbacUsecase.CheckPerm("application/workflow/record", "detail")).
|
||||
Param(ws.PathParameter("appName", "identifier of the application.").DataType("string").Required(true)).
|
||||
Param(ws.PathParameter("workflowName", "identifier of the workflow").DataType("string")).
|
||||
Param(ws.PathParameter("record", "identifier of the workflow record").DataType("string")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
@@ -464,9 +507,10 @@ func (c *applicationWebService) GetWebService() *restful.WebService {
|
||||
Returns(200, "OK", apis.DetailWorkflowRecordResponse{}).
|
||||
Writes(apis.DetailWorkflowRecordResponse{}).Do(returns200, returns500))
|
||||
|
||||
ws.Route(ws.GET("/{name}/workflows/{workflowName}/records/{record}/resume").To(c.resumeWorkflowRecord).
|
||||
ws.Route(ws.GET("/{appName}/workflows/{workflowName}/records/{record}/resume").To(c.resumeWorkflowRecord).
|
||||
Doc("resume suspend workflow record").
|
||||
Param(ws.PathParameter("name", "identifier of the application.").DataType("string").Required(true)).
|
||||
Filter(c.rbacUsecase.CheckPerm("application/workflow/record", "resume")).
|
||||
Param(ws.PathParameter("appName", "identifier of the application.").DataType("string").Required(true)).
|
||||
Param(ws.PathParameter("workflowName", "identifier of the workflow").DataType("string")).
|
||||
Param(ws.PathParameter("record", "identifier of the workflow record").DataType("string")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
@@ -476,9 +520,10 @@ func (c *applicationWebService) GetWebService() *restful.WebService {
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.DetailWorkflowRecordResponse{}))
|
||||
|
||||
ws.Route(ws.GET("/{name}/workflows/{workflowName}/records/{record}/terminate").To(c.terminateWorkflowRecord).
|
||||
ws.Route(ws.GET("/{appName}/workflows/{workflowName}/records/{record}/terminate").To(c.terminateWorkflowRecord).
|
||||
Doc("terminate suspend workflow record").
|
||||
Param(ws.PathParameter("name", "identifier of the application.").DataType("string").Required(true)).
|
||||
Filter(c.rbacUsecase.CheckPerm("application/workflow/record", "terminate")).
|
||||
Param(ws.PathParameter("appName", "identifier of the application.").DataType("string").Required(true)).
|
||||
Param(ws.PathParameter("workflowName", "identifier of the workflow").DataType("string")).
|
||||
Param(ws.PathParameter("record", "identifier of the workflow record").DataType("string")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
@@ -488,9 +533,10 @@ func (c *applicationWebService) GetWebService() *restful.WebService {
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.DetailWorkflowRecordResponse{}))
|
||||
|
||||
ws.Route(ws.GET("/{name}/workflows/{workflowName}/records/{record}/rollback").To(c.rollbackWorkflowRecord).
|
||||
ws.Route(ws.GET("/{appName}/workflows/{workflowName}/records/{record}/rollback").To(c.rollbackWorkflowRecord).
|
||||
Doc("rollback suspend application record").
|
||||
Param(ws.PathParameter("name", "identifier of the application.").DataType("string").Required(true)).
|
||||
Filter(c.rbacUsecase.CheckPerm("application/workflow/record", "rollback")).
|
||||
Param(ws.PathParameter("appName", "identifier of the application.").DataType("string").Required(true)).
|
||||
Param(ws.PathParameter("workflowName", "identifier of the workflow").DataType("string")).
|
||||
Param(ws.PathParameter("record", "identifier of the workflow record").DataType("string")).
|
||||
Param(ws.QueryParameter("rollbackVersion", "identifier of the rollback revision").DataType("string")).
|
||||
@@ -501,38 +547,42 @@ func (c *applicationWebService) GetWebService() *restful.WebService {
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.DetailWorkflowRecordResponse{}))
|
||||
|
||||
ws.Route(ws.GET("/{name}/records").To(c.listApplicationRecords).
|
||||
ws.Route(ws.GET("/{appName}/records").To(c.listApplicationRecords).
|
||||
Doc("list application records").
|
||||
Param(ws.PathParameter("name", "identifier of the application.").DataType("string").Required(true)).
|
||||
Filter(c.rbacUsecase.CheckPerm("application/workflow/record", "list")).
|
||||
Param(ws.PathParameter("appName", "identifier of the application.").DataType("string").Required(true)).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.appCheckFilter).
|
||||
Returns(200, "OK", nil).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.ListWorkflowRecordsResponse{}))
|
||||
|
||||
ws.Route(ws.POST("/{name}/compare").To(c.compareAppWithLatestRevision).
|
||||
ws.Route(ws.POST("/{appName}/compare").To(c.compareAppWithLatestRevision).
|
||||
Doc("compare application with env latest revision").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.rbacUsecase.CheckPerm("application", "compare")).
|
||||
Filter(c.appCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application ").DataType("string")).
|
||||
Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")).
|
||||
Returns(200, "OK", apis.ApplicationBase{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.AppCompareResponse{}))
|
||||
|
||||
ws.Route(ws.POST("/{name}/reset").To(c.resetAppToLatestRevision).
|
||||
ws.Route(ws.POST("/{appName}/reset").To(c.resetAppToLatestRevision).
|
||||
Doc("reset application to latest revision").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.rbacUsecase.CheckPerm("application", "reset")).
|
||||
Filter(c.appCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application ").DataType("string")).
|
||||
Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")).
|
||||
Returns(200, "OK", apis.AppResetResponse{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.AppResetResponse{}))
|
||||
|
||||
ws.Route(ws.POST("/{name}/dry-run").To(c.dryRunAppOrRevision).
|
||||
ws.Route(ws.POST("/{appName}/dry-run").To(c.dryRunAppOrRevision).
|
||||
Doc("dry-run application to latest revision").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.rbacUsecase.CheckPerm("application", "detail")).
|
||||
Filter(c.appCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the application ").DataType("string")).
|
||||
Param(ws.PathParameter("appName", "identifier of the application ").DataType("string")).
|
||||
Returns(200, "OK", apis.AppDryRunResponse{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.AppDryRunResponse{}))
|
||||
@@ -568,8 +618,12 @@ func (c *applicationWebService) createApplication(req *restful.Request, res *res
|
||||
}
|
||||
|
||||
func (c *applicationWebService) listApplications(req *restful.Request, res *restful.Response) {
|
||||
var projetNames []string
|
||||
if req.QueryParameter("project") != "" {
|
||||
projetNames = append(projetNames, req.QueryParameter("project"))
|
||||
}
|
||||
apps, err := c.applicationUsecase.ListApplications(req.Request.Context(), apis.ListApplicationOptions{
|
||||
Project: req.QueryParameter("project"),
|
||||
Projects: projetNames,
|
||||
Env: req.QueryParameter("env"),
|
||||
TargetName: req.QueryParameter("targetName"),
|
||||
Query: req.QueryParameter("query"),
|
||||
@@ -962,13 +1016,13 @@ func (c *applicationWebService) getApplicationStatus(req *restful.Request, res *
|
||||
}
|
||||
|
||||
func (c *applicationWebService) listApplicationRevisions(req *restful.Request, res *restful.Response) {
|
||||
app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application)
|
||||
page, pageSize, err := utils.ExtractPagingParams(req, minPageSize, maxPageSize)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
|
||||
revisions, err := c.applicationUsecase.ListRevisions(req.Request.Context(), req.PathParameter("name"), req.QueryParameter("envName"), req.QueryParameter("status"), page, pageSize)
|
||||
revisions, err := c.applicationUsecase.ListRevisions(req.Request.Context(), app.Name, req.QueryParameter("envName"), req.QueryParameter("status"), page, pageSize)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
@@ -980,7 +1034,8 @@ func (c *applicationWebService) listApplicationRevisions(req *restful.Request, r
|
||||
}
|
||||
|
||||
func (c *applicationWebService) detailApplicationRevision(req *restful.Request, res *restful.Response) {
|
||||
detail, err := c.applicationUsecase.DetailRevision(req.Request.Context(), req.PathParameter("name"), req.PathParameter("revision"))
|
||||
app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application)
|
||||
detail, err := c.applicationUsecase.DetailRevision(req.Request.Context(), app.Name, req.PathParameter("revision"))
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
@@ -1064,7 +1119,7 @@ func (c *applicationWebService) deleteApplicationEnv(req *restful.Request, res *
|
||||
}
|
||||
|
||||
func (c *applicationWebService) appCheckFilter(req *restful.Request, res *restful.Response, chain *restful.FilterChain) {
|
||||
app, err := c.applicationUsecase.GetApplication(req.Request.Context(), req.PathParameter("name"))
|
||||
app, err := c.applicationUsecase.GetApplication(req.Request.Context(), req.PathParameter("appName"))
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
@@ -1123,7 +1178,8 @@ func (c *applicationWebService) recycleApplicationEnv(req *restful.Request, res
|
||||
}
|
||||
|
||||
func (c *applicationWebService) listApplicationRecords(req *restful.Request, res *restful.Response) {
|
||||
records, err := c.applicationUsecase.ListRecords(req.Request.Context(), req.PathParameter("name"))
|
||||
app := req.Request.Context().Value(&apis.CtxKeyApplication).(*model.Application)
|
||||
records, err := c.applicationUsecase.ListRecords(req.Request.Context(), app.Name)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
|
||||
@@ -30,12 +30,14 @@ import (
|
||||
|
||||
type authenticationWebService struct {
|
||||
authenticationUsecase usecase.AuthenticationUsecase
|
||||
userUsecase usecase.UserUsecase
|
||||
}
|
||||
|
||||
// NewAuthenticationWebService is the webservice of authentication
|
||||
func NewAuthenticationWebService(authenticationUsecase usecase.AuthenticationUsecase) WebService {
|
||||
func NewAuthenticationWebService(authenticationUsecase usecase.AuthenticationUsecase, userUsecase usecase.UserUsecase) WebService {
|
||||
return &authenticationWebService{
|
||||
authenticationUsecase: authenticationUsecase,
|
||||
userUsecase: userUsecase,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,26 +58,34 @@ func (c *authenticationWebService) GetWebService() *restful.WebService {
|
||||
Returns(400, "", bcode.Bcode{}).
|
||||
Writes(apis.LoginResponse{}))
|
||||
|
||||
ws.Route(ws.GET("/dexConfig").To(c.getDexConfig).
|
||||
ws.Route(ws.GET("/dex_config").To(c.getDexConfig).
|
||||
Doc("get Dex config").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Returns(200, "", apis.DexConfigResponse{}).
|
||||
Returns(400, "", bcode.Bcode{}).
|
||||
Writes(apis.DexConfigResponse{}))
|
||||
|
||||
ws.Route(ws.GET("/refreshToken").To(c.refreshToken).
|
||||
ws.Route(ws.GET("/refresh_token").To(c.refreshToken).
|
||||
Doc("refresh token").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Returns(200, "", apis.RefreshTokenResponse{}).
|
||||
Returns(400, "", bcode.Bcode{}).
|
||||
Writes(apis.RefreshTokenResponse{}))
|
||||
|
||||
ws.Route(ws.GET("/loginType").To(c.getLoginType).
|
||||
ws.Route(ws.GET("/login_type").To(c.getLoginType).
|
||||
Doc("get login type").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Returns(200, "", apis.GetLoginTypeResponse{}).
|
||||
Returns(400, "", bcode.Bcode{}).
|
||||
Writes(apis.GetLoginTypeResponse{}))
|
||||
|
||||
ws.Route(ws.GET("/user_info").To(c.getLoginUserInfo).
|
||||
Doc("get login user detail info").
|
||||
Filter(authCheckFilter).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Returns(200, "", apis.LoginUserInfoResponse{}).
|
||||
Returns(400, "", bcode.Bcode{}).
|
||||
Writes(apis.LoginUserInfoResponse{}))
|
||||
return ws
|
||||
}
|
||||
|
||||
@@ -157,3 +167,15 @@ func (c *authenticationWebService) getLoginType(req *restful.Request, res *restf
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (c *authenticationWebService) getLoginUserInfo(req *restful.Request, res *restful.Response) {
|
||||
info, err := c.userUsecase.DetailLoginUserInfo(req.Request.Context())
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
if err := res.WriteEntity(info); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,11 +29,12 @@ import (
|
||||
// ClusterWebService cluster manage webservice
|
||||
type ClusterWebService struct {
|
||||
clusterUsecase usecase.ClusterUsecase
|
||||
rbacUsecase usecase.RBACUsecase
|
||||
}
|
||||
|
||||
// NewClusterWebService new cluster webservice
|
||||
func NewClusterWebService(clusterUsecase usecase.ClusterUsecase) *ClusterWebService {
|
||||
return &ClusterWebService{clusterUsecase: clusterUsecase}
|
||||
func NewClusterWebService(clusterUsecase usecase.ClusterUsecase, rbacUsecase usecase.RBACUsecase) *ClusterWebService {
|
||||
return &ClusterWebService{clusterUsecase: clusterUsecase, rbacUsecase: rbacUsecase}
|
||||
}
|
||||
|
||||
// GetWebService -
|
||||
@@ -49,6 +50,7 @@ func (c *ClusterWebService) GetWebService() *restful.WebService {
|
||||
ws.Route(ws.GET("/").To(c.listKubeClusters).
|
||||
Doc("list all clusters").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.rbacUsecase.CheckPerm("cluster", "list")).
|
||||
Param(ws.QueryParameter("query", "Fuzzy search based on name or description").DataType("string")).
|
||||
Param(ws.QueryParameter("page", "Page for paging").DataType("integer").DefaultValue("0")).
|
||||
Param(ws.QueryParameter("pageSize", "PageSize for paging").DataType("integer").DefaultValue("20")).
|
||||
@@ -60,6 +62,7 @@ func (c *ClusterWebService) GetWebService() *restful.WebService {
|
||||
Doc("create cluster").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Reads(apis.CreateClusterRequest{}).
|
||||
Filter(c.rbacUsecase.CheckPerm("cluster", "create")).
|
||||
Returns(200, "OK", apis.ClusterBase{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.ClusterBase{}))
|
||||
@@ -67,6 +70,7 @@ func (c *ClusterWebService) GetWebService() *restful.WebService {
|
||||
ws.Route(ws.GET("/{clusterName}").To(c.getKubeCluster).
|
||||
Doc("detail cluster info").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.rbacUsecase.CheckPerm("cluster", "detail")).
|
||||
Param(ws.PathParameter("clusterName", "identifier of the cluster").DataType("string")).
|
||||
Returns(200, "OK", apis.DetailClusterResponse{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
@@ -75,6 +79,7 @@ func (c *ClusterWebService) GetWebService() *restful.WebService {
|
||||
ws.Route(ws.PUT("/{clusterName}").To(c.modifyKubeCluster).
|
||||
Doc("modify cluster").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.rbacUsecase.CheckPerm("cluster", "update")).
|
||||
Param(ws.PathParameter("clusterName", "identifier of the cluster").DataType("string")).
|
||||
Reads(apis.CreateClusterRequest{}).
|
||||
Returns(200, "OK", apis.ClusterBase{}).
|
||||
@@ -84,6 +89,7 @@ func (c *ClusterWebService) GetWebService() *restful.WebService {
|
||||
ws.Route(ws.DELETE("/{clusterName}").To(c.deleteKubeCluster).
|
||||
Doc("delete cluster").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.rbacUsecase.CheckPerm("cluster", "delete")).
|
||||
Param(ws.PathParameter("clusterName", "identifier of the cluster").DataType("string")).
|
||||
Returns(200, "OK", apis.ClusterBase{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
@@ -94,6 +100,7 @@ func (c *ClusterWebService) GetWebService() *restful.WebService {
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Param(ws.PathParameter("clusterName", "name of the target cluster").DataType("string")).
|
||||
Reads(apis.CreateClusterNamespaceRequest{}).
|
||||
Filter(c.rbacUsecase.CheckPerm("cluster/namespace", "create")).
|
||||
Returns(200, "OK", apis.CreateClusterNamespaceResponse{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.CreateClusterNamespaceResponse{}))
|
||||
@@ -101,6 +108,7 @@ func (c *ClusterWebService) GetWebService() *restful.WebService {
|
||||
ws.Route(ws.POST("/cloud_clusters/{provider}").To(c.listCloudClusters).
|
||||
Doc("list cloud clusters").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.rbacUsecase.CheckPerm("cluster", "create")).
|
||||
Param(ws.PathParameter("provider", "identifier of the cloud provider").DataType("string")).
|
||||
Param(ws.QueryParameter("page", "Page for paging").DataType("integer").DefaultValue("0")).
|
||||
Param(ws.QueryParameter("pageSize", "PageSize for paging").DataType("integer").DefaultValue("20")).
|
||||
@@ -112,6 +120,7 @@ func (c *ClusterWebService) GetWebService() *restful.WebService {
|
||||
ws.Route(ws.POST("/cloud_clusters/{provider}/connect").To(c.connectCloudCluster).
|
||||
Doc("create cluster from cloud cluster").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.rbacUsecase.CheckPerm("cluster", "create")).
|
||||
Param(ws.PathParameter("provider", "identifier of the cloud provider").DataType("string")).
|
||||
Reads(apis.ConnectCloudClusterRequest{}).
|
||||
Returns(200, "OK", apis.ClusterBase{}).
|
||||
@@ -121,6 +130,7 @@ func (c *ClusterWebService) GetWebService() *restful.WebService {
|
||||
ws.Route(ws.POST("/cloud_clusters/{provider}/create").To(c.createCloudCluster).
|
||||
Doc("create cloud cluster").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.rbacUsecase.CheckPerm("cluster", "create")).
|
||||
Param(ws.PathParameter("provider", "identifier of the cloud provider").DataType("string").Required(true)).
|
||||
Reads(apis.CreateCloudClusterRequest{}).
|
||||
Returns(200, "OK", apis.CreateCloudClusterResponse{}).
|
||||
@@ -130,6 +140,7 @@ func (c *ClusterWebService) GetWebService() *restful.WebService {
|
||||
ws.Route(ws.GET("/cloud_clusters/{provider}/creation/{cloudClusterName}").To(c.getCloudClusterCreationStatus).
|
||||
Doc("check cloud cluster create status").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.rbacUsecase.CheckPerm("cluster", "create")).
|
||||
Param(ws.PathParameter("provider", "identifier of the cloud provider").DataType("string")).
|
||||
Param(ws.PathParameter("cloudClusterName", "identifier for cloud cluster which is creating").DataType("string")).
|
||||
Returns(200, "OK", apis.CreateCloudClusterResponse{}).
|
||||
@@ -138,6 +149,7 @@ func (c *ClusterWebService) GetWebService() *restful.WebService {
|
||||
|
||||
ws.Route(ws.GET("/cloud_clusters/{provider}/creation").To(c.listCloudClusterCreation).
|
||||
Doc("list cloud cluster creation").
|
||||
Filter(c.rbacUsecase.CheckPerm("cluster", "create")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Param(ws.PathParameter("provider", "identifier of the cloud provider").DataType("string")).
|
||||
Returns(200, "OK", apis.ListCloudClusterCreationResponse{}).
|
||||
@@ -147,6 +159,7 @@ func (c *ClusterWebService) GetWebService() *restful.WebService {
|
||||
ws.Route(ws.DELETE("/cloud_clusters/{provider}/creation/{cloudClusterName}").To(c.deleteCloudClusterCreation).
|
||||
Doc("delete cloud cluster creation").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.rbacUsecase.CheckPerm("cluster", "create")).
|
||||
Param(ws.PathParameter("provider", "identifier of the cloud provider").DataType("string")).
|
||||
Param(ws.PathParameter("cloudClusterName", "identifier for cloud cluster which is creating").DataType("string")).
|
||||
Returns(200, "OK", apis.CreateCloudClusterResponse{}).
|
||||
|
||||
@@ -27,6 +27,7 @@ import (
|
||||
|
||||
type definitionWebservice struct {
|
||||
definitionUsecase usecase.DefinitionUsecase
|
||||
rbacUsecase usecase.RBACUsecase
|
||||
}
|
||||
|
||||
func (d *definitionWebservice) GetWebService() *restful.WebService {
|
||||
@@ -41,15 +42,18 @@ func (d *definitionWebservice) GetWebService() *restful.WebService {
|
||||
ws.Route(ws.GET("/").To(d.listDefinitions).
|
||||
Doc("list all definitions").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
// TODO: provide project scope api for query definition list
|
||||
// Filter(d.rbacUsecase.CheckPerm("definition", "list")).
|
||||
Param(ws.QueryParameter("type", "query the definition type").DataType("string").Required(true).AllowableValues(map[string]string{"component": "", "trait": "", "workflowstep": ""})).
|
||||
Param(ws.QueryParameter("envName", "if specified, query the definition supported by the env.").DataType("string")).
|
||||
Param(ws.QueryParameter("appliedWorkload", "if specified, query the trait definition applied to the workload.").DataType("string")).
|
||||
Returns(200, "OK", apis.ListDefinitionResponse{}).
|
||||
Writes(apis.ListDefinitionResponse{}).Do(returns200, returns500))
|
||||
|
||||
ws.Route(ws.GET("/{name}").To(d.detailDefinition).
|
||||
ws.Route(ws.GET("/{definitionName}").To(d.detailDefinition).
|
||||
Doc("detail definition").
|
||||
Param(ws.PathParameter("name", "identifier of the definition").DataType("string")).
|
||||
// Filter(d.rbacUsecase.CheckPerm("definition", "detail")).
|
||||
Param(ws.PathParameter("definitionName", "identifier of the definition").DataType("string")).
|
||||
Param(ws.QueryParameter("type", "query the definition type").DataType("string")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Returns(200, "create success", apis.DetailDefinitionResponse{}).
|
||||
@@ -60,9 +64,10 @@ func (d *definitionWebservice) GetWebService() *restful.WebService {
|
||||
}
|
||||
|
||||
// NewDefinitionWebservice new definition webservice
|
||||
func NewDefinitionWebservice(du usecase.DefinitionUsecase) WebService {
|
||||
func NewDefinitionWebservice(du usecase.DefinitionUsecase, rbacUsecase usecase.RBACUsecase) WebService {
|
||||
return &definitionWebservice{
|
||||
definitionUsecase: du,
|
||||
rbacUsecase: rbacUsecase,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +84,7 @@ func (d *definitionWebservice) listDefinitions(req *restful.Request, res *restfu
|
||||
}
|
||||
|
||||
func (d *definitionWebservice) detailDefinition(req *restful.Request, res *restful.Response) {
|
||||
definition, err := d.definitionUsecase.DetailDefinition(req.Request.Context(), req.PathParameter("name"), req.QueryParameter("type"))
|
||||
definition, err := d.definitionUsecase.DetailDefinition(req.Request.Context(), req.PathParameter("definitionName"), req.QueryParameter("type"))
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
|
||||
@@ -28,13 +28,14 @@ import (
|
||||
)
|
||||
|
||||
type envWebService struct {
|
||||
envUsecase usecase.EnvUsecase
|
||||
appUsecase usecase.ApplicationUsecase
|
||||
envUsecase usecase.EnvUsecase
|
||||
appUsecase usecase.ApplicationUsecase
|
||||
rbacUsecase usecase.RBACUsecase
|
||||
}
|
||||
|
||||
// NewEnvWebService new env webservice
|
||||
func NewEnvWebService(envUsecase usecase.EnvUsecase, appUseCase usecase.ApplicationUsecase) WebService {
|
||||
return &envWebService{envUsecase: envUsecase, appUsecase: appUseCase}
|
||||
func NewEnvWebService(envUsecase usecase.EnvUsecase, appUseCase usecase.ApplicationUsecase, rbacUsecase usecase.RBACUsecase) WebService {
|
||||
return &envWebService{envUsecase: envUsecase, appUsecase: appUseCase, rbacUsecase: rbacUsecase}
|
||||
}
|
||||
|
||||
func (n *envWebService) GetWebService() *restful.WebService {
|
||||
@@ -49,6 +50,8 @@ func (n *envWebService) GetWebService() *restful.WebService {
|
||||
ws.Route(ws.GET("/").To(n.list).
|
||||
Operation("envlist").
|
||||
Doc("list all envs").
|
||||
// This api will filter the environments by user's permissions
|
||||
// Filter(n.rbacUsecase.CheckPerm("environment", "list")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Returns(200, "OK", apis.ListEnvResponse{}).
|
||||
Writes(apis.ListEnvResponse{}))
|
||||
@@ -56,25 +59,28 @@ func (n *envWebService) GetWebService() *restful.WebService {
|
||||
ws.Route(ws.POST("/").To(n.create).
|
||||
Operation("envcreate").
|
||||
Doc("create an env").
|
||||
Filter(n.rbacUsecase.CheckPerm("environment", "create")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Reads(apis.CreateEnvRequest{}).
|
||||
Returns(200, "OK", apis.Env{}).
|
||||
Writes(apis.Env{}))
|
||||
|
||||
ws.Route(ws.PUT("/{name}").To(n.update).
|
||||
ws.Route(ws.PUT("/{envName}").To(n.update).
|
||||
Operation("envupdate").
|
||||
Doc("update an env").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Param(ws.PathParameter("name", "identifier of the application ").DataType("string")).
|
||||
Filter(n.rbacUsecase.CheckPerm("environment", "update")).
|
||||
Param(ws.PathParameter("envName", "identifier of the application ").DataType("string")).
|
||||
Reads(apis.CreateEnvRequest{}).
|
||||
Returns(200, "OK", apis.Env{}).
|
||||
Writes(apis.Env{}))
|
||||
|
||||
ws.Route(ws.DELETE("/{name}").To(n.delete).
|
||||
ws.Route(ws.DELETE("/{envName}").To(n.delete).
|
||||
Operation("envdelete").
|
||||
Doc("delete one env").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Param(ws.PathParameter("name", "identifier of the application ").DataType("string")).
|
||||
Filter(n.rbacUsecase.CheckPerm("environment", "delete")).
|
||||
Param(ws.PathParameter("envName", "identifier of the application ").DataType("string")).
|
||||
Returns(200, "OK", apis.EmptyResponse{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.EmptyResponse{}))
|
||||
@@ -103,7 +109,7 @@ func (n *envWebService) list(req *restful.Request, res *restful.Response) {
|
||||
|
||||
// it will prevent the deletion if there's still application in it.
|
||||
func (n *envWebService) delete(req *restful.Request, res *restful.Response) {
|
||||
envname := req.PathParameter("name")
|
||||
envname := req.PathParameter("envName")
|
||||
|
||||
ctx := req.Request.Context()
|
||||
lists, err := n.appUsecase.ListApplications(ctx, apis.ListApplicationOptions{Env: envname})
|
||||
@@ -166,7 +172,7 @@ func (n *envWebService) update(req *restful.Request, res *restful.Response) {
|
||||
return
|
||||
}
|
||||
|
||||
env, err := n.envUsecase.UpdateEnv(req.Request.Context(), req.PathParameter("name"), updateReq)
|
||||
env, err := n.envUsecase.UpdateEnv(req.Request.Context(), req.PathParameter("envName"), updateReq)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
|
||||
@@ -28,12 +28,14 @@ import (
|
||||
|
||||
type oamApplicationWebService struct {
|
||||
oamApplicationUsecase usecase.OAMApplicationUsecase
|
||||
rbacUsecase usecase.RBACUsecase
|
||||
}
|
||||
|
||||
// NewOAMApplication new oam application
|
||||
func NewOAMApplication(oamApplicationUsecase usecase.OAMApplicationUsecase) WebService {
|
||||
func NewOAMApplication(oamApplicationUsecase usecase.OAMApplicationUsecase, rbacUsecase usecase.RBACUsecase) WebService {
|
||||
return &oamApplicationWebService{
|
||||
oamApplicationUsecase: oamApplicationUsecase,
|
||||
rbacUsecase: rbacUsecase,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +51,7 @@ func (c *oamApplicationWebService) GetWebService() *restful.WebService {
|
||||
ws.Route(ws.GET("/namespaces/{namespace}/applications/{appname}").To(c.getApplication).
|
||||
Doc("get the specified oam application in the specified namespace").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.rbacUsecase.CheckPerm("application", "detail")).
|
||||
Param(ws.PathParameter("namespace", "identifier of the namespace").DataType("string")).
|
||||
Param(ws.PathParameter("appname", "identifier of the oam application").DataType("string")).
|
||||
Returns(200, "OK", apis.ApplicationResponse{}).
|
||||
@@ -57,6 +60,7 @@ func (c *oamApplicationWebService) GetWebService() *restful.WebService {
|
||||
ws.Route(ws.POST("/namespaces/{namespace}/applications/{appname}").To(c.createOrUpdateApplication).
|
||||
Doc("create or update oam application in the specified namespace").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.rbacUsecase.CheckPerm("application", "deploy")).
|
||||
Param(ws.PathParameter("namespace", "identifier of the namespace").DataType("string")).
|
||||
Param(ws.PathParameter("appname", "identifier of the oam application").DataType("string")).
|
||||
Reads(apis.ApplicationRequest{}))
|
||||
@@ -65,9 +69,11 @@ func (c *oamApplicationWebService) GetWebService() *restful.WebService {
|
||||
Operation("deleteOAMApplication").
|
||||
Doc("create or update oam application in the specified namespace").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.rbacUsecase.CheckPerm("application", "delete")).
|
||||
Param(ws.PathParameter("namespace", "identifier of the namespace").DataType("string")).
|
||||
Param(ws.PathParameter("appname", "identifier of the oam application").DataType("string")))
|
||||
|
||||
ws.Filter(authCheckFilter)
|
||||
return ws
|
||||
}
|
||||
|
||||
|
||||
@@ -28,12 +28,14 @@ import (
|
||||
)
|
||||
|
||||
type projectWebService struct {
|
||||
rbacUsecase usecase.RBACUsecase
|
||||
projectUsecase usecase.ProjectUsecase
|
||||
targetUsecase usecase.TargetUsecase
|
||||
}
|
||||
|
||||
// NewProjectWebService new project webservice
|
||||
func NewProjectWebService(projectUsecase usecase.ProjectUsecase) WebService {
|
||||
return &projectWebService{projectUsecase: projectUsecase}
|
||||
func NewProjectWebService(projectUsecase usecase.ProjectUsecase, rbacUsecase usecase.RBACUsecase, targetUsecase usecase.TargetUsecase) WebService {
|
||||
return &projectWebService{projectUsecase: projectUsecase, rbacUsecase: rbacUsecase, targetUsecase: targetUsecase}
|
||||
}
|
||||
|
||||
func (n *projectWebService) GetWebService() *restful.WebService {
|
||||
@@ -48,16 +50,132 @@ func (n *projectWebService) GetWebService() *restful.WebService {
|
||||
ws.Route(ws.GET("/").To(n.listprojects).
|
||||
Doc("list all projects").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(n.rbacUsecase.CheckPerm("project", "list")).
|
||||
Returns(200, "OK", apis.ListProjectResponse{}).
|
||||
Writes(apis.ListProjectResponse{}))
|
||||
|
||||
ws.Route(ws.POST("/").To(n.createproject).
|
||||
Doc("create a project").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(n.rbacUsecase.CheckPerm("project", "create")).
|
||||
Reads(apis.CreateProjectRequest{}).
|
||||
Returns(200, "OK", apis.ProjectBase{}).
|
||||
Writes(apis.ProjectBase{}))
|
||||
|
||||
ws.Route(ws.GET("/{projectName}").To(n.detailProject).
|
||||
Doc("detail a project").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Param(ws.PathParameter("projectName", "identifier of the project").DataType("string")).
|
||||
Filter(n.rbacUsecase.CheckPerm("project", "detail")).
|
||||
Returns(200, "OK", apis.ProjectBase{}).
|
||||
Writes(apis.ProjectBase{}))
|
||||
|
||||
ws.Route(ws.PUT("/{projectName}").To(n.updateProject).
|
||||
Doc("update a project").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Param(ws.PathParameter("projectName", "identifier of the project").DataType("string")).
|
||||
Filter(n.rbacUsecase.CheckPerm("project", "update")).
|
||||
Reads(apis.UpdateProjectRequest{}).
|
||||
Returns(200, "OK", apis.ProjectBase{}).
|
||||
Writes(apis.ProjectBase{}))
|
||||
|
||||
ws.Route(ws.DELETE("/{projectName}").To(n.deleteProject).
|
||||
Doc("delete a project").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Param(ws.PathParameter("projectName", "identifier of the project").DataType("string")).
|
||||
Filter(n.rbacUsecase.CheckPerm("project", "delete")).
|
||||
Returns(200, "OK", apis.EmptyResponse{}).
|
||||
Writes(apis.EmptyResponse{}))
|
||||
|
||||
ws.Route(ws.GET("/{projectName}/targets").To(n.listProjectTargets).
|
||||
Doc("get targets list belong to a project").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Param(ws.PathParameter("projectName", "identifier of the project").DataType("string")).
|
||||
Filter(n.rbacUsecase.CheckPerm("project", "detail")).
|
||||
Returns(200, "OK", apis.EmptyResponse{}).
|
||||
Writes(apis.EmptyResponse{}))
|
||||
|
||||
ws.Route(ws.POST("/{projectName}/users").To(n.createProjectUser).
|
||||
Doc("add a user to a project").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Param(ws.PathParameter("projectName", "identifier of the project").DataType("string")).
|
||||
Filter(n.rbacUsecase.CheckPerm("project/projectUser", "create")).
|
||||
Reads(apis.AddProjectUserRequest{}).
|
||||
Returns(200, "OK", apis.ProjectUserBase{}).
|
||||
Writes(apis.ProjectUserBase{}))
|
||||
|
||||
ws.Route(ws.GET("/{projectName}/users").To(n.listProjectUser).
|
||||
Doc("list all users belong to a project").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Param(ws.PathParameter("projectName", "identifier of the project").DataType("string")).
|
||||
Filter(n.rbacUsecase.CheckPerm("project/projectUser", "list")).
|
||||
Returns(200, "OK", apis.ListProjectUsersResponse{}).
|
||||
Writes(apis.ListProjectUsersResponse{}))
|
||||
|
||||
ws.Route(ws.PUT("/{projectName}/users/{userName}").To(n.updateProjectUser).
|
||||
Doc("add a user to a project").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Reads(apis.UpdateProjectUserRequest{}).
|
||||
Param(ws.PathParameter("projectName", "identifier of the project").DataType("string")).
|
||||
Param(ws.PathParameter("userName", "identifier of the project user").DataType("string")).
|
||||
Filter(n.rbacUsecase.CheckPerm("project/projectUser", "create")).
|
||||
Returns(200, "OK", apis.ProjectUserBase{}).
|
||||
Writes(apis.ProjectUserBase{}))
|
||||
|
||||
ws.Route(ws.DELETE("/{projectName}/users/{userName}").To(n.deleteProjectUser).
|
||||
Doc("delete a user from a project").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Reads(apis.UpdateProjectUserRequest{}).
|
||||
Param(ws.PathParameter("projectName", "identifier of the project").DataType("string")).
|
||||
Param(ws.PathParameter("userName", "identifier of the project user").DataType("string")).
|
||||
Filter(n.rbacUsecase.CheckPerm("project/projectUser", "delete")).
|
||||
Returns(200, "OK", apis.EmptyResponse{}).
|
||||
Writes(apis.EmptyResponse{}))
|
||||
|
||||
ws.Route(ws.GET("/{projectName}/roles").To(n.listProjectRoles).
|
||||
Doc("list all project level roles").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Param(ws.PathParameter("projectName", "identifier of the project").DataType("string")).
|
||||
Filter(n.rbacUsecase.CheckPerm("project/role", "list")).
|
||||
Returns(200, "OK", apis.ListRolesResponse{}).
|
||||
Writes(apis.ListRolesResponse{}))
|
||||
|
||||
ws.Route(ws.POST("/{projectName}/roles").To(n.createProjectRole).
|
||||
Doc("create project level role").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Param(ws.PathParameter("projectName", "identifier of the project").DataType("string")).
|
||||
Filter(n.rbacUsecase.CheckPerm("project/role", "create")).
|
||||
Returns(200, "OK", apis.RoleBase{}).
|
||||
Reads(apis.CreateRoleRequest{}).
|
||||
Writes(apis.RoleBase{}))
|
||||
|
||||
ws.Route(ws.PUT("/{projectName}/roles/{roleName}").To(n.updateProjectRole).
|
||||
Doc("update project level role").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Param(ws.PathParameter("projectName", "identifier of the project").DataType("string")).
|
||||
Param(ws.PathParameter("roleName", "identifier of the project role").DataType("string")).
|
||||
Filter(n.rbacUsecase.CheckPerm("project/role", "update")).
|
||||
Reads(apis.UpdateRoleRequest{}).
|
||||
Returns(200, "OK", apis.RoleBase{}).
|
||||
Writes(apis.RoleBase{}))
|
||||
|
||||
ws.Route(ws.DELETE("/{projectName}/roles/{roleName}").To(n.deleteProjectRole).
|
||||
Doc("delete project level role").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Param(ws.PathParameter("projectName", "identifier of the project").DataType("string")).
|
||||
Param(ws.PathParameter("roleName", "identifier of the project role").DataType("string")).
|
||||
Filter(n.rbacUsecase.CheckPerm("project/role", "delete")).
|
||||
Returns(200, "OK", apis.EmptyResponse{}).
|
||||
Writes(apis.EmptyResponse{}))
|
||||
|
||||
ws.Route(ws.GET("/{projectName}/permissions").To(n.listProjectPermissions).
|
||||
Doc("list all project level perm policies").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Param(ws.PathParameter("projectName", "identifier of the project").DataType("string")).
|
||||
Filter(n.rbacUsecase.CheckPerm("project/permission", "list")).
|
||||
Returns(200, "OK", []apis.PermissionBase{}).
|
||||
Writes([]apis.PermissionBase{}))
|
||||
|
||||
ws.Filter(authCheckFilter)
|
||||
return ws
|
||||
}
|
||||
@@ -93,7 +211,7 @@ func (n *projectWebService) createproject(req *restful.Request, res *restful.Res
|
||||
// Call the usecase layer code
|
||||
projectBase, err := n.projectUsecase.CreateProject(req.Request.Context(), createReq)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("create application failure %s", err.Error())
|
||||
log.Logger.Errorf("create project failure %s", err.Error())
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
@@ -104,3 +222,284 @@ func (n *projectWebService) createproject(req *restful.Request, res *restful.Res
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (n *projectWebService) updateProject(req *restful.Request, res *restful.Response) {
|
||||
// Verify the validity of parameters
|
||||
var updateReq apis.UpdateProjectRequest
|
||||
if err := req.ReadEntity(&updateReq); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
if err := validate.Struct(&updateReq); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
// Call the usecase layer code
|
||||
projectBase, err := n.projectUsecase.UpdateProject(req.Request.Context(), req.PathParameter("projectName"), updateReq)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("update project failure %s", err.Error())
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Write back response data
|
||||
if err := res.WriteEntity(projectBase); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (n *projectWebService) detailProject(req *restful.Request, res *restful.Response) {
|
||||
project, err := n.projectUsecase.DetailProject(req.Request.Context(), req.PathParameter("projectName"))
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
// Write back response data
|
||||
if err := res.WriteEntity(project); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (n *projectWebService) deleteProject(req *restful.Request, res *restful.Response) {
|
||||
err := n.projectUsecase.DeleteProject(req.Request.Context(), req.PathParameter("projectName"))
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
// Write back response data
|
||||
if err := res.WriteEntity(apis.EmptyResponse{}); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (n *projectWebService) listProjectTargets(req *restful.Request, res *restful.Response) {
|
||||
project, err := n.projectUsecase.GetProject(req.Request.Context(), req.PathParameter("projectName"))
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
projects, err := n.targetUsecase.ListTargets(req.Request.Context(), 0, 0, project.Name)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
// Write back response data
|
||||
if err := res.WriteEntity(projects); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (n *projectWebService) createProjectUser(req *restful.Request, res *restful.Response) {
|
||||
// Verify the validity of parameters
|
||||
var createReq apis.AddProjectUserRequest
|
||||
if err := req.ReadEntity(&createReq); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
if err := validate.Struct(&createReq); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
if len(createReq.UserRoles) == 0 {
|
||||
bcode.ReturnError(req, res, bcode.ErrProjectRoleCheckFailure)
|
||||
return
|
||||
}
|
||||
// Call the usecase layer code
|
||||
userBase, err := n.projectUsecase.AddProjectUser(req.Request.Context(), req.PathParameter("projectName"), createReq)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("create project user failure %s", err.Error())
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Write back response data
|
||||
if err := res.WriteEntity(userBase); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (n *projectWebService) listProjectUser(req *restful.Request, res *restful.Response) {
|
||||
page, pageSize, err := utils.ExtractPagingParams(req, minPageSize, maxPageSize)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
// Call the usecase layer code
|
||||
users, err := n.projectUsecase.ListProjectUser(req.Request.Context(), req.PathParameter("projectName"), page, pageSize)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("list project users failure %s", err.Error())
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Write back response data
|
||||
if err := res.WriteEntity(users); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (n *projectWebService) updateProjectUser(req *restful.Request, res *restful.Response) {
|
||||
// Verify the validity of parameters
|
||||
var updateReq apis.UpdateProjectUserRequest
|
||||
if err := req.ReadEntity(&updateReq); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
if err := validate.Struct(&updateReq); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
if len(updateReq.UserRoles) == 0 {
|
||||
bcode.ReturnError(req, res, bcode.ErrProjectRoleCheckFailure)
|
||||
return
|
||||
}
|
||||
// Call the usecase layer code
|
||||
userBase, err := n.projectUsecase.UpdateProjectUser(req.Request.Context(), req.PathParameter("projectName"), req.PathParameter("userName"), updateReq)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("update project user failure %s", err.Error())
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Write back response data
|
||||
if err := res.WriteEntity(userBase); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (n *projectWebService) deleteProjectUser(req *restful.Request, res *restful.Response) {
|
||||
// Call the usecase layer code
|
||||
err := n.projectUsecase.DeleteProjectUser(req.Request.Context(), req.PathParameter("projectName"), req.PathParameter("userName"))
|
||||
if err != nil {
|
||||
log.Logger.Errorf("delete project user failure %s", err.Error())
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Write back response data
|
||||
if err := res.WriteEntity(apis.EmptyResponse{}); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (n *projectWebService) listProjectRoles(req *restful.Request, res *restful.Response) {
|
||||
if req.PathParameter("projectName") == "" {
|
||||
bcode.ReturnError(req, res, bcode.ErrProjectIsNotExist)
|
||||
return
|
||||
}
|
||||
page, pageSize, err := utils.ExtractPagingParams(req, minPageSize, maxPageSize)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
roles, err := n.rbacUsecase.ListRole(req.Request.Context(), req.PathParameter("projectName"), page, pageSize)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
if err := res.WriteEntity(roles); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (n *projectWebService) createProjectRole(req *restful.Request, res *restful.Response) {
|
||||
if req.PathParameter("projectName") == "" {
|
||||
bcode.ReturnError(req, res, bcode.ErrProjectIsNotExist)
|
||||
return
|
||||
}
|
||||
// Verify the validity of parameters
|
||||
var createReq apis.CreateRoleRequest
|
||||
if err := req.ReadEntity(&createReq); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
if err := validate.Struct(&createReq); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
// Call the usecase layer code
|
||||
projectBase, err := n.rbacUsecase.CreateRole(req.Request.Context(), req.PathParameter("projectName"), createReq)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("create role failure %s", err.Error())
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Write back response data
|
||||
if err := res.WriteEntity(projectBase); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (n *projectWebService) updateProjectRole(req *restful.Request, res *restful.Response) {
|
||||
if req.PathParameter("projectName") == "" {
|
||||
bcode.ReturnError(req, res, bcode.ErrProjectIsNotExist)
|
||||
return
|
||||
}
|
||||
// Verify the validity of parameters
|
||||
var updateReq apis.UpdateRoleRequest
|
||||
if err := req.ReadEntity(&updateReq); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
if err := validate.Struct(&updateReq); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
// Call the usecase layer code
|
||||
roleBase, err := n.rbacUsecase.UpdateRole(req.Request.Context(), req.PathParameter("projectName"), req.PathParameter("roleName"), updateReq)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("update role failure %s", err.Error())
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Write back response data
|
||||
if err := res.WriteEntity(roleBase); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (n *projectWebService) deleteProjectRole(req *restful.Request, res *restful.Response) {
|
||||
if req.PathParameter("projectName") == "" {
|
||||
bcode.ReturnError(req, res, bcode.ErrProjectIsNotExist)
|
||||
return
|
||||
}
|
||||
err := n.rbacUsecase.DeleteRole(req.Request.Context(), req.PathParameter("projectName"), req.PathParameter("roleName"))
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
// Write back response data
|
||||
if err := res.WriteEntity(apis.EmptyResponse{}); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (n *projectWebService) listProjectPermissions(req *restful.Request, res *restful.Response) {
|
||||
if req.PathParameter("projectName") == "" {
|
||||
bcode.ReturnError(req, res, bcode.ErrProjectIsNotExist)
|
||||
return
|
||||
}
|
||||
policies, err := n.rbacUsecase.ListPermissions(req.Request.Context(), req.PathParameter("projectName"))
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
if err := res.WriteEntity(policies); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
Copyright 2022 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package webservice
|
||||
|
||||
import (
|
||||
restfulspec "github.com/emicklei/go-restful-openapi/v2"
|
||||
"github.com/emicklei/go-restful/v3"
|
||||
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/log"
|
||||
apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/usecase"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode"
|
||||
)
|
||||
|
||||
type rbacWebService struct {
|
||||
rbacUsecase usecase.RBACUsecase
|
||||
}
|
||||
|
||||
// NewRBACWebService new rbac webservice
|
||||
func NewRBACWebService(rbacUsecase usecase.RBACUsecase) WebService {
|
||||
return &rbacWebService{rbacUsecase: rbacUsecase}
|
||||
}
|
||||
|
||||
func (r *rbacWebService) GetWebService() *restful.WebService {
|
||||
ws := new(restful.WebService)
|
||||
ws.Path(versionPrefix).
|
||||
Consumes(restful.MIME_XML, restful.MIME_JSON).
|
||||
Produces(restful.MIME_JSON, restful.MIME_XML).
|
||||
Doc("api for rbac")
|
||||
|
||||
tags := []string{"rbac"}
|
||||
|
||||
ws.Route(ws.GET("/roles").To(r.listPlatformRoles).
|
||||
Doc("list all platform level roles").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(r.rbacUsecase.CheckPerm("role", "list")).
|
||||
Returns(200, "OK", apis.ListRolesResponse{}).
|
||||
Writes(apis.ListRolesResponse{}))
|
||||
|
||||
ws.Route(ws.POST("/roles").To(r.createPlatformRole).
|
||||
Doc("create platform level role").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(r.rbacUsecase.CheckPerm("role", "create")).
|
||||
Returns(200, "OK", apis.RoleBase{}).
|
||||
Reads(apis.CreateRoleRequest{}).
|
||||
Writes(apis.RoleBase{}))
|
||||
|
||||
ws.Route(ws.PUT("/roles/{roleName}").To(r.updatePlatformRole).
|
||||
Doc("update platform level role").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(r.rbacUsecase.CheckPerm("role", "update")).
|
||||
Reads(apis.UpdateRoleRequest{}).
|
||||
Returns(200, "OK", apis.RoleBase{}).
|
||||
Writes(apis.RoleBase{}))
|
||||
|
||||
ws.Route(ws.DELETE("/roles/{roleName}").To(r.deletePlatformRole).
|
||||
Doc("update platform level role").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(r.rbacUsecase.CheckPerm("role", "delete")).
|
||||
Returns(200, "OK", apis.EmptyResponse{}).
|
||||
Writes(apis.EmptyResponse{}))
|
||||
|
||||
ws.Route(ws.GET("/permissions").To(r.listPlatformPermissions).
|
||||
Doc("list all project level perm policies").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(r.rbacUsecase.CheckPerm("permission", "list")).
|
||||
Returns(200, "OK", []apis.PermissionBase{}).
|
||||
Writes([]apis.PermissionBase{}))
|
||||
|
||||
ws.Filter(authCheckFilter)
|
||||
return ws
|
||||
}
|
||||
|
||||
func (r *rbacWebService) listPlatformRoles(req *restful.Request, res *restful.Response) {
|
||||
page, pageSize, err := utils.ExtractPagingParams(req, minPageSize, maxPageSize)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
roles, err := r.rbacUsecase.ListRole(req.Request.Context(), "", page, pageSize)
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
if err := res.WriteEntity(roles); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (r *rbacWebService) createPlatformRole(req *restful.Request, res *restful.Response) {
|
||||
// Verify the validity of parameters
|
||||
var createReq apis.CreateRoleRequest
|
||||
if err := req.ReadEntity(&createReq); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
if err := validate.Struct(&createReq); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
// Call the usecase layer code
|
||||
projectBase, err := r.rbacUsecase.CreateRole(req.Request.Context(), "", createReq)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("create role failure %s", err.Error())
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Write back response data
|
||||
if err := res.WriteEntity(projectBase); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (r *rbacWebService) updatePlatformRole(req *restful.Request, res *restful.Response) {
|
||||
// Verify the validity of parameters
|
||||
var updateReq apis.UpdateRoleRequest
|
||||
if err := req.ReadEntity(&updateReq); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
if err := validate.Struct(&updateReq); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
// Call the usecase layer code
|
||||
roleBase, err := r.rbacUsecase.UpdateRole(req.Request.Context(), "", req.PathParameter("roleName"), updateReq)
|
||||
if err != nil {
|
||||
log.Logger.Errorf("update role failure %s", err.Error())
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Write back response data
|
||||
if err := res.WriteEntity(roleBase); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (r *rbacWebService) deletePlatformRole(req *restful.Request, res *restful.Response) {
|
||||
err := r.rbacUsecase.DeleteRole(req.Request.Context(), "", req.PathParameter("roleName"))
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
// Write back response data
|
||||
if err := res.WriteEntity(apis.EmptyResponse{}); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (r *rbacWebService) listPlatformPermissions(req *restful.Request, res *restful.Response) {
|
||||
policies, err := r.rbacUsecase.ListPermissions(req.Request.Context(), "")
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
if err := res.WriteEntity(policies); err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -26,12 +26,13 @@ import (
|
||||
)
|
||||
|
||||
type systemInfoWebService struct {
|
||||
useCase usecase.SystemInfoUsecase
|
||||
useCase usecase.SystemInfoUsecase
|
||||
rbacUsecase usecase.RBACUsecase
|
||||
}
|
||||
|
||||
// NewSystemInfoWebService return systemInfo webservice
|
||||
func NewSystemInfoWebService(systemInfoUseCase usecase.SystemInfoUsecase) WebService {
|
||||
return &systemInfoWebService{useCase: systemInfoUseCase}
|
||||
func NewSystemInfoWebService(systemInfoUseCase usecase.SystemInfoUsecase, rbacUsecase usecase.RBACUsecase) WebService {
|
||||
return &systemInfoWebService{useCase: systemInfoUseCase, rbacUsecase: rbacUsecase}
|
||||
}
|
||||
|
||||
// GetWebService return systemInfo webservice
|
||||
@@ -50,17 +51,11 @@ func (u systemInfoWebService) GetWebService() *restful.WebService {
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.SystemInfoResponse{}))
|
||||
|
||||
// Delete
|
||||
ws.Route(ws.DELETE("/").To(u.deleteSystemInfo).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Returns(200, "OK", apis.SystemInfoResponse{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.SystemInfoResponse{}))
|
||||
|
||||
// Post
|
||||
ws.Route(ws.PUT("/").To(u.updateSystemInfo).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Reads(apis.SystemInfoRequest{}).
|
||||
Filter(u.rbacUsecase.CheckPerm("systemSetting", "update")).
|
||||
Returns(200, "OK", apis.SystemInfoResponse{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.SystemInfoResponse{}))
|
||||
@@ -107,11 +102,3 @@ func (u systemInfoWebService) updateSystemInfo(req *restful.Request, res *restfu
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (u systemInfoWebService) deleteSystemInfo(req *restful.Request, res *restful.Response) {
|
||||
err := u.useCase.DeleteSystemInfo(req.Request.Context())
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,10 +35,11 @@ import (
|
||||
)
|
||||
|
||||
// NewTargetWebService new Target webservice
|
||||
func NewTargetWebService(targetUsecase usecase.TargetUsecase, applicationUsecase usecase.ApplicationUsecase) WebService {
|
||||
func NewTargetWebService(targetUsecase usecase.TargetUsecase, applicationUsecase usecase.ApplicationUsecase, rbacUsecase usecase.RBACUsecase) WebService {
|
||||
return &TargetWebService{
|
||||
TargetUsecase: targetUsecase,
|
||||
applicationUsecase: applicationUsecase,
|
||||
rbacUsecase: rbacUsecase,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,6 +47,7 @@ func NewTargetWebService(targetUsecase usecase.TargetUsecase, applicationUsecase
|
||||
type TargetWebService struct {
|
||||
TargetUsecase usecase.TargetUsecase
|
||||
applicationUsecase usecase.ApplicationUsecase
|
||||
rbacUsecase usecase.RBACUsecase
|
||||
}
|
||||
|
||||
// GetWebService get web service
|
||||
@@ -61,8 +63,10 @@ func (dt *TargetWebService) GetWebService() *restful.WebService {
|
||||
ws.Route(ws.GET("/").To(dt.listTargets).
|
||||
Doc("list Target").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(dt.rbacUsecase.CheckPerm("target", "list")).
|
||||
Param(ws.QueryParameter("page", "Page for paging").DataType("integer")).
|
||||
Param(ws.QueryParameter("pageSize", "PageSize for paging").DataType("integer")).
|
||||
Param(ws.QueryParameter("project", "list targets by project name").DataType("string")).
|
||||
Returns(200, "OK", apis.ListTargetResponse{}).
|
||||
Writes(apis.ListTargetResponse{}).Do(returns200, returns500))
|
||||
|
||||
@@ -70,32 +74,36 @@ func (dt *TargetWebService) GetWebService() *restful.WebService {
|
||||
Doc("create Target").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Reads(apis.CreateTargetRequest{}).
|
||||
Filter(dt.rbacUsecase.CheckPerm("target", "create")).
|
||||
Returns(200, "create success", apis.DetailTargetResponse{}).
|
||||
Returns(400, "create failure", bcode.Bcode{}).
|
||||
Writes(apis.DetailTargetResponse{}).Do(returns200, returns500))
|
||||
|
||||
ws.Route(ws.GET("/{name}").To(dt.detailTarget).
|
||||
ws.Route(ws.GET("/{targetName}").To(dt.detailTarget).
|
||||
Doc("detail Target").
|
||||
Param(ws.PathParameter("name", "identifier of the Target.").DataType("string")).
|
||||
Param(ws.PathParameter("targetName", "identifier of the Target.").DataType("string")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(dt.targetCheckFilter).
|
||||
Filter(dt.rbacUsecase.CheckPerm("target", "detail")).
|
||||
Returns(200, "create success", apis.DetailTargetResponse{}).
|
||||
Writes(apis.DetailTargetResponse{}).Do(returns200, returns500))
|
||||
|
||||
ws.Route(ws.PUT("/{name}").To(dt.updateTarget).
|
||||
ws.Route(ws.PUT("/{targetName}").To(dt.updateTarget).
|
||||
Doc("update application Target config").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(dt.targetCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the Target").DataType("string")).
|
||||
Param(ws.PathParameter("targetName", "identifier of the Target").DataType("string")).
|
||||
Reads(apis.UpdateTargetRequest{}).
|
||||
Filter(dt.rbacUsecase.CheckPerm("target", "update")).
|
||||
Returns(200, "OK", apis.DetailTargetResponse{}).
|
||||
Writes(apis.DetailTargetResponse{}).Do(returns200, returns500))
|
||||
|
||||
ws.Route(ws.DELETE("/{name}").To(dt.deleteTarget).
|
||||
ws.Route(ws.DELETE("/{targetName}").To(dt.deleteTarget).
|
||||
Doc("deletet Target").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(dt.targetCheckFilter).
|
||||
Param(ws.PathParameter("name", "identifier of the Target").DataType("string")).
|
||||
Filter(dt.rbacUsecase.CheckPerm("target", "delete")).
|
||||
Param(ws.PathParameter("targetName", "identifier of the Target").DataType("string")).
|
||||
Returns(200, "OK", apis.EmptyResponse{}).
|
||||
Writes(apis.EmptyResponse{}).Do(returns200, returns500))
|
||||
|
||||
@@ -129,7 +137,7 @@ func (dt *TargetWebService) createTarget(req *restful.Request, res *restful.Resp
|
||||
}
|
||||
|
||||
func (dt *TargetWebService) targetCheckFilter(req *restful.Request, res *restful.Response, chain *restful.FilterChain) {
|
||||
Target, err := dt.TargetUsecase.GetTarget(req.Request.Context(), req.PathParameter("name"))
|
||||
Target, err := dt.TargetUsecase.GetTarget(req.Request.Context(), req.PathParameter("targetName"))
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
@@ -175,7 +183,7 @@ func (dt *TargetWebService) updateTarget(req *restful.Request, res *restful.Resp
|
||||
}
|
||||
|
||||
func (dt *TargetWebService) deleteTarget(req *restful.Request, res *restful.Response) {
|
||||
TargetName := req.PathParameter("name")
|
||||
TargetName := req.PathParameter("targetName")
|
||||
// Target in use, can't be deleted
|
||||
applications, err := dt.applicationUsecase.ListApplications(req.Request.Context(), apis.ListApplicationOptions{TargetName: TargetName})
|
||||
if err != nil {
|
||||
@@ -204,7 +212,7 @@ func (dt *TargetWebService) listTargets(req *restful.Request, res *restful.Respo
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
}
|
||||
Targets, err := dt.TargetUsecase.ListTargets(req.Request.Context(), page, pageSize)
|
||||
Targets, err := dt.TargetUsecase.ListTargets(req.Request.Context(), page, pageSize, req.QueryParameter("project"))
|
||||
if err != nil {
|
||||
bcode.ReturnError(req, res, err)
|
||||
return
|
||||
|
||||
@@ -31,12 +31,14 @@ import (
|
||||
|
||||
type userWebService struct {
|
||||
userUsecase usecase.UserUsecase
|
||||
rbacUsecase usecase.RBACUsecase
|
||||
}
|
||||
|
||||
// NewUserWebService is the webservice of user
|
||||
func NewUserWebService(userUsecase usecase.UserUsecase) WebService {
|
||||
func NewUserWebService(userUsecase usecase.UserUsecase, rbacUsecase usecase.RBACUsecase) WebService {
|
||||
return &userWebService{
|
||||
userUsecase: userUsecase,
|
||||
rbacUsecase: rbacUsecase,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +54,7 @@ func (c *userWebService) GetWebService() *restful.WebService {
|
||||
ws.Route(ws.GET("/").To(c.listUser).
|
||||
Doc("list users").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.rbacUsecase.CheckPerm("user", "list")).
|
||||
Param(ws.QueryParameter("page", "query the page number").DataType("integer")).
|
||||
Param(ws.QueryParameter("pageSize", "query the page size number").DataType("integer")).
|
||||
Param(ws.QueryParameter("name", "fuzzy search based on name").DataType("string")).
|
||||
@@ -63,6 +66,7 @@ func (c *userWebService) GetWebService() *restful.WebService {
|
||||
|
||||
ws.Route(ws.POST("/").To(c.createUser).
|
||||
Doc("create a user").
|
||||
Filter(c.rbacUsecase.CheckPerm("user", "create")).
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Reads(apis.CreateUserRequest{}).
|
||||
Returns(200, "OK", apis.UserBase{}).
|
||||
@@ -72,6 +76,7 @@ func (c *userWebService) GetWebService() *restful.WebService {
|
||||
ws.Route(ws.GET("/{username}").To(c.detailUser).
|
||||
Doc("get user detail").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.rbacUsecase.CheckPerm("user", "detail")).
|
||||
Filter(c.userCheckFilter).
|
||||
Returns(200, "OK", apis.DetailUserResponse{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
@@ -80,6 +85,7 @@ func (c *userWebService) GetWebService() *restful.WebService {
|
||||
ws.Route(ws.PUT("/{username}").To(c.updateUser).
|
||||
Doc("update a user's alias or password").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.rbacUsecase.CheckPerm("user", "update")).
|
||||
Filter(c.userCheckFilter).
|
||||
Returns(200, "OK", apis.UserBase{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
@@ -88,6 +94,7 @@ func (c *userWebService) GetWebService() *restful.WebService {
|
||||
ws.Route(ws.DELETE("/{username}").To(c.deleteUser).
|
||||
Doc("delete a user").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.rbacUsecase.CheckPerm("user", "delete")).
|
||||
Returns(200, "OK", apis.EmptyResponse{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
Writes(apis.EmptyResponse{}))
|
||||
@@ -95,6 +102,7 @@ func (c *userWebService) GetWebService() *restful.WebService {
|
||||
ws.Route(ws.GET("/{username}/disable").To(c.disableUser).
|
||||
Doc("disable a user").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.rbacUsecase.CheckPerm("user", "disable")).
|
||||
Filter(c.userCheckFilter).
|
||||
Returns(200, "OK", apis.EmptyResponse{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
@@ -103,6 +111,7 @@ func (c *userWebService) GetWebService() *restful.WebService {
|
||||
ws.Route(ws.GET("/{username}/enable").To(c.enableUser).
|
||||
Doc("enable a user").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
Filter(c.rbacUsecase.CheckPerm("user", "enable")).
|
||||
Filter(c.userCheckFilter).
|
||||
Returns(200, "OK", apis.EmptyResponse{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
|
||||
@@ -27,12 +27,14 @@ import (
|
||||
|
||||
type velaQLWebService struct {
|
||||
velaQLUsecase usecase.VelaQLUsecase
|
||||
rbacUsecase usecase.RBACUsecase
|
||||
}
|
||||
|
||||
// NewVelaQLWebService new velaQL webservice
|
||||
func NewVelaQLWebService(velaQLUsecase usecase.VelaQLUsecase) WebService {
|
||||
func NewVelaQLWebService(velaQLUsecase usecase.VelaQLUsecase, rbacUsecase usecase.RBACUsecase) WebService {
|
||||
return &velaQLWebService{
|
||||
velaQLUsecase: velaQLUsecase,
|
||||
rbacUsecase: rbacUsecase,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +50,8 @@ func (v *velaQLWebService) GetWebService() *restful.WebService {
|
||||
ws.Route(ws.GET("/").To(v.queryView).
|
||||
Doc("use velaQL to query resource status").
|
||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||
// TODO: VelaQL is an open data query API that is currently not compatible with RBAC.
|
||||
// Filter(v.rbacUsecase.CheckPerm("application", "detail")).
|
||||
Param(ws.QueryParameter("velaql", "velaql query statement").DataType("string")).
|
||||
Returns(200, "OK", apis.VelaQLViewResponse{}).
|
||||
Returns(400, "Bad Request", bcode.Bcode{}).
|
||||
|
||||
@@ -17,6 +17,8 @@ limitations under the License.
|
||||
package webservice
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
@@ -37,7 +39,7 @@ type WebService interface {
|
||||
|
||||
var registeredWebService []WebService
|
||||
|
||||
// RegisterWebService regist webservice
|
||||
// RegisterWebService register webservice
|
||||
func RegisterWebService(ws WebService) {
|
||||
registeredWebService = append(registeredWebService, ws)
|
||||
}
|
||||
@@ -59,12 +61,13 @@ func returns500(b *restful.RouteBuilder) {
|
||||
|
||||
// Init init all webservice, pass in the required parameter object.
|
||||
// It can be implemented using the idea of dependency injection.
|
||||
func Init(ds datastore.DataStore, addonCacheTime time.Duration) {
|
||||
func Init(ctx context.Context, ds datastore.DataStore, addonCacheTime time.Duration, initDatabase bool) map[string]interface{} {
|
||||
clusterUsecase := usecase.NewClusterUsecase(ds)
|
||||
envUsecase := usecase.NewEnvUsecase(ds)
|
||||
workflowUsecase := usecase.NewWorkflowUsecase(ds, envUsecase)
|
||||
projectUsecase := usecase.NewProjectUsecase(ds)
|
||||
rbacUsecase := usecase.NewRBACUsecase(ds)
|
||||
projectUsecase := usecase.NewProjectUsecase(ds, rbacUsecase)
|
||||
envUsecase := usecase.NewEnvUsecase(ds, projectUsecase)
|
||||
targetUsecase := usecase.NewTargetUsecase(ds)
|
||||
workflowUsecase := usecase.NewWorkflowUsecase(ds, envUsecase)
|
||||
oamApplicationUsecase := usecase.NewOAMApplicationUsecase()
|
||||
velaQLUsecase := usecase.NewVelaQLUsecase()
|
||||
definitionUsecase := usecase.NewDefinitionUsecase()
|
||||
@@ -74,36 +77,55 @@ func Init(ds datastore.DataStore, addonCacheTime time.Duration) {
|
||||
webhookUsecase := usecase.NewWebhookUsecase(ds, applicationUsecase)
|
||||
systemInfoUsecase := usecase.NewSystemInfoUsecase(ds)
|
||||
helmUsecase := usecase.NewHelmUsecase()
|
||||
userUsecase := usecase.NewUserUsecase(ds, projectUsecase, systemInfoUsecase)
|
||||
userUsecase := usecase.NewUserUsecase(ds, projectUsecase, systemInfoUsecase, rbacUsecase)
|
||||
authenticationUsecase := usecase.NewAuthenticationUsecase(ds, systemInfoUsecase, userUsecase)
|
||||
|
||||
// init for default values
|
||||
// Modules that require default data initialization, Call it here in order
|
||||
if initDatabase {
|
||||
initData(ctx, userUsecase, rbacUsecase, projectUsecase, targetUsecase)
|
||||
}
|
||||
|
||||
// Application
|
||||
RegisterWebService(NewApplicationWebService(applicationUsecase, envBindingUsecase, workflowUsecase))
|
||||
RegisterWebService(NewProjectWebService(projectUsecase))
|
||||
RegisterWebService(NewEnvWebService(envUsecase, applicationUsecase))
|
||||
RegisterWebService(NewApplicationWebService(applicationUsecase, envBindingUsecase, workflowUsecase, rbacUsecase))
|
||||
RegisterWebService(NewProjectWebService(projectUsecase, rbacUsecase, targetUsecase))
|
||||
RegisterWebService(NewEnvWebService(envUsecase, applicationUsecase, rbacUsecase))
|
||||
|
||||
// Extension
|
||||
RegisterWebService(NewDefinitionWebservice(definitionUsecase))
|
||||
RegisterWebService(NewAddonWebService(addonUsecase))
|
||||
RegisterWebService(NewEnabledAddonWebService(addonUsecase))
|
||||
RegisterWebService(NewAddonRegistryWebService(addonUsecase))
|
||||
RegisterWebService(NewDefinitionWebservice(definitionUsecase, rbacUsecase))
|
||||
RegisterWebService(NewAddonWebService(addonUsecase, rbacUsecase))
|
||||
RegisterWebService(NewEnabledAddonWebService(addonUsecase, rbacUsecase))
|
||||
RegisterWebService(NewAddonRegistryWebService(addonUsecase, rbacUsecase))
|
||||
|
||||
// Resources
|
||||
RegisterWebService(NewClusterWebService(clusterUsecase))
|
||||
RegisterWebService(NewOAMApplication(oamApplicationUsecase))
|
||||
RegisterWebService(NewClusterWebService(clusterUsecase, rbacUsecase))
|
||||
RegisterWebService(NewOAMApplication(oamApplicationUsecase, rbacUsecase))
|
||||
RegisterWebService(&policyDefinitionWebservice{})
|
||||
RegisterWebService(&payloadTypesWebservice{})
|
||||
RegisterWebService(NewTargetWebService(targetUsecase, applicationUsecase))
|
||||
RegisterWebService(NewVelaQLWebService(velaQLUsecase))
|
||||
RegisterWebService(NewTargetWebService(targetUsecase, applicationUsecase, rbacUsecase))
|
||||
RegisterWebService(NewVelaQLWebService(velaQLUsecase, rbacUsecase))
|
||||
RegisterWebService(NewWebhookWebService(webhookUsecase, applicationUsecase))
|
||||
RegisterWebService(NewHelmWebService(helmUsecase))
|
||||
|
||||
// Authentication
|
||||
RegisterWebService(NewAuthenticationWebService(authenticationUsecase))
|
||||
RegisterWebService(NewUserWebService(userUsecase))
|
||||
RegisterWebService(NewAuthenticationWebService(authenticationUsecase, userUsecase))
|
||||
RegisterWebService(NewUserWebService(userUsecase, rbacUsecase))
|
||||
RegisterWebService(NewSystemInfoWebService(systemInfoUsecase, rbacUsecase))
|
||||
|
||||
RegisterWebService(NewSystemInfoWebService(systemInfoUsecase))
|
||||
// RBAC
|
||||
RegisterWebService(NewRBACWebService(rbacUsecase))
|
||||
|
||||
RegisterWebService(NewHelmWebService(helmUsecase))
|
||||
// return some usecase instance
|
||||
return map[string]interface{}{"workflow": workflowUsecase, "project": projectUsecase}
|
||||
}
|
||||
|
||||
// InitUsecase the usecase set that needs init data
|
||||
type InitUsecase interface {
|
||||
Init(ctx context.Context) error
|
||||
}
|
||||
|
||||
func initData(ctx context.Context, inits ...InitUsecase) {
|
||||
for _, init := range inits {
|
||||
if err := init.Init(ctx); err != nil {
|
||||
log.Fatalf("database init failure %s", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+14
-138
@@ -1,17 +1,17 @@
|
||||
/*
|
||||
Copyright 2022 The KubeVela Authors.
|
||||
Copyright 2022 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package sync
|
||||
@@ -20,138 +20,14 @@ import (
|
||||
"context"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha1"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/model"
|
||||
"github.com/oam-dev/kubevela/pkg/multicluster"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/sync/convert"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
"github.com/oam-dev/kubevela/pkg/workflow/step"
|
||||
)
|
||||
|
||||
// ConvertFromCRComponent concerts Application CR Component object into velaux data store component
|
||||
func ConvertFromCRComponent(appPrimaryKey string, component common.ApplicationComponent) (model.ApplicationComponent, error) {
|
||||
bc := model.ApplicationComponent{
|
||||
AppPrimaryKey: appPrimaryKey,
|
||||
Name: component.Name,
|
||||
Type: component.Type,
|
||||
ExternalRevision: component.ExternalRevision,
|
||||
DependsOn: component.DependsOn,
|
||||
Inputs: component.Inputs,
|
||||
Outputs: component.Outputs,
|
||||
Scopes: component.Scopes,
|
||||
Creator: model.AutoGenComp,
|
||||
}
|
||||
if component.Properties != nil {
|
||||
properties, err := model.NewJSONStruct(component.Properties)
|
||||
if err != nil {
|
||||
return bc, err
|
||||
}
|
||||
bc.Properties = properties
|
||||
}
|
||||
for _, trait := range component.Traits {
|
||||
properties, err := model.NewJSONStruct(trait.Properties)
|
||||
if err != nil {
|
||||
return bc, err
|
||||
}
|
||||
bc.Traits = append(bc.Traits, model.ApplicationTrait{CreateTime: time.Now(), UpdateTime: time.Now(), Properties: properties, Type: trait.Type, Alias: trait.Type, Description: "auto gen"})
|
||||
}
|
||||
return bc, nil
|
||||
}
|
||||
|
||||
// ConvertFromCRPolicy converts Application CR Policy object into velaux data store policy
|
||||
func ConvertFromCRPolicy(appPrimaryKey string, policyCR v1beta1.AppPolicy, creator string) (model.ApplicationPolicy, error) {
|
||||
plc := model.ApplicationPolicy{
|
||||
AppPrimaryKey: appPrimaryKey,
|
||||
Name: policyCR.Name,
|
||||
Type: policyCR.Type,
|
||||
Creator: creator,
|
||||
}
|
||||
if policyCR.Properties != nil {
|
||||
properties, err := model.NewJSONStruct(policyCR.Properties)
|
||||
if err != nil {
|
||||
return plc, err
|
||||
}
|
||||
plc.Properties = properties
|
||||
}
|
||||
return plc, nil
|
||||
}
|
||||
|
||||
// ConvertFromCRWorkflow converts Application CR Workflow section into velaux data store workflow
|
||||
func ConvertFromCRWorkflow(ctx context.Context, cli client.Client, appPrimaryKey string, app *v1beta1.Application) (model.Workflow, []v1beta1.WorkflowStep, error) {
|
||||
dataWf := model.Workflow{
|
||||
AppPrimaryKey: appPrimaryKey,
|
||||
// every namespace has a synced env
|
||||
EnvName: model.AutoGenEnvNamePrefix + app.Namespace,
|
||||
// every application has a synced workflow
|
||||
Name: model.AutoGenWorkflowNamePrefix + appPrimaryKey,
|
||||
Alias: model.AutoGenWorkflowNamePrefix + app.Name,
|
||||
Description: model.AutoGenDesc,
|
||||
}
|
||||
if app.Spec.Workflow == nil {
|
||||
return dataWf, nil, nil
|
||||
}
|
||||
var steps []v1beta1.WorkflowStep
|
||||
if app.Spec.Workflow.Ref != "" {
|
||||
dataWf.Name = app.Spec.Workflow.Ref
|
||||
wf := &v1alpha1.Workflow{}
|
||||
if err := cli.Get(ctx, types.NamespacedName{Namespace: app.GetNamespace(), Name: app.Spec.Workflow.Ref}, wf); err != nil {
|
||||
return dataWf, nil, err
|
||||
}
|
||||
steps = step.ConvertSteps(wf.Steps)
|
||||
} else {
|
||||
steps = app.Spec.Workflow.Steps
|
||||
}
|
||||
for _, s := range steps {
|
||||
if s.Properties == nil {
|
||||
continue
|
||||
}
|
||||
properties, err := model.NewJSONStruct(s.Properties)
|
||||
if err != nil {
|
||||
return dataWf, nil, err
|
||||
}
|
||||
dataWf.Steps = append(dataWf.Steps, model.WorkflowStep{
|
||||
Name: s.Name,
|
||||
Type: s.Type,
|
||||
Inputs: s.Inputs,
|
||||
Outputs: s.Outputs,
|
||||
DependsOn: s.DependsOn,
|
||||
Properties: properties,
|
||||
})
|
||||
}
|
||||
return dataWf, steps, nil
|
||||
}
|
||||
|
||||
// ConvertFromCRTargets converts deployed Cluster/Namespace from Application CR Status into velaux data store
|
||||
func ConvertFromCRTargets(targetApp *v1beta1.Application) []*model.Target {
|
||||
var targets []*model.Target
|
||||
nc := make(map[string]struct{})
|
||||
for _, v := range targetApp.Status.AppliedResources {
|
||||
var cluster = v.Cluster
|
||||
if cluster == "" {
|
||||
cluster = multicluster.ClusterLocalName
|
||||
}
|
||||
name := model.AutoGenTargetNamePrefix + cluster + "-" + v.Namespace
|
||||
if _, ok := nc[name]; ok {
|
||||
continue
|
||||
}
|
||||
nc[name] = struct{}{}
|
||||
targets = append(targets, &model.Target{
|
||||
Name: name,
|
||||
Cluster: &model.ClusterTarget{
|
||||
ClusterName: cluster,
|
||||
Namespace: v.Namespace,
|
||||
},
|
||||
})
|
||||
}
|
||||
return targets
|
||||
}
|
||||
|
||||
// ConvertApp2DatastoreApp will convert Application CR to datastore application related resources
|
||||
func (c *CR2UX) ConvertApp2DatastoreApp(ctx context.Context, targetApp *v1beta1.Application) (*model.DataStoreApp, error) {
|
||||
cli := c.cli
|
||||
@@ -193,7 +69,7 @@ func (c *CR2UX) ConvertApp2DatastoreApp(ctx context.Context, targetApp *v1beta1.
|
||||
|
||||
// 2. convert component and trait
|
||||
for _, cmp := range targetApp.Spec.Components {
|
||||
compModel, err := ConvertFromCRComponent(appMeta.PrimaryKey(), cmp)
|
||||
compModel, err := convert.FromCRComponent(appMeta.PrimaryKey(), cmp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -201,7 +77,7 @@ func (c *CR2UX) ConvertApp2DatastoreApp(ctx context.Context, targetApp *v1beta1.
|
||||
}
|
||||
|
||||
// 3. convert workflow
|
||||
wf, steps, err := ConvertFromCRWorkflow(ctx, cli, appMeta.PrimaryKey(), targetApp)
|
||||
wf, steps, err := convert.FromCRWorkflow(ctx, cli, appMeta.PrimaryKey(), targetApp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -217,7 +93,7 @@ func (c *CR2UX) ConvertApp2DatastoreApp(ctx context.Context, targetApp *v1beta1.
|
||||
return nil, err
|
||||
}
|
||||
for _, plc := range outsidePLC {
|
||||
plcModel, err := ConvertFromCRPolicy(appMeta.PrimaryKey(), plc, model.AutoGenRefPolicy)
|
||||
plcModel, err := convert.FromCRPolicy(appMeta.PrimaryKey(), plc, model.AutoGenRefPolicy)
|
||||
if _, ok := innerPlc[plc.Name]; ok {
|
||||
plcModel.Creator = model.AutoGenPolicy
|
||||
}
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
Copyright 2022 The KubeVela Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package convert
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/common"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1alpha1"
|
||||
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/model"
|
||||
"github.com/oam-dev/kubevela/pkg/multicluster"
|
||||
"github.com/oam-dev/kubevela/pkg/workflow/step"
|
||||
)
|
||||
|
||||
// FromCRComponent concerts Application CR Component object into velaux data store component
|
||||
func FromCRComponent(appPrimaryKey string, component common.ApplicationComponent) (model.ApplicationComponent, error) {
|
||||
bc := model.ApplicationComponent{
|
||||
AppPrimaryKey: appPrimaryKey,
|
||||
Name: component.Name,
|
||||
Type: component.Type,
|
||||
ExternalRevision: component.ExternalRevision,
|
||||
DependsOn: component.DependsOn,
|
||||
Inputs: component.Inputs,
|
||||
Outputs: component.Outputs,
|
||||
Scopes: component.Scopes,
|
||||
Creator: model.AutoGenComp,
|
||||
}
|
||||
if component.Properties != nil {
|
||||
properties, err := model.NewJSONStruct(component.Properties)
|
||||
if err != nil {
|
||||
return bc, err
|
||||
}
|
||||
bc.Properties = properties
|
||||
}
|
||||
for _, trait := range component.Traits {
|
||||
properties, err := model.NewJSONStruct(trait.Properties)
|
||||
if err != nil {
|
||||
return bc, err
|
||||
}
|
||||
bc.Traits = append(bc.Traits, model.ApplicationTrait{CreateTime: time.Now(), UpdateTime: time.Now(), Properties: properties, Type: trait.Type, Alias: trait.Type, Description: "auto gen"})
|
||||
}
|
||||
return bc, nil
|
||||
}
|
||||
|
||||
// FromCRPolicy converts Application CR Policy object into velaux data store policy
|
||||
func FromCRPolicy(appPrimaryKey string, policyCR v1beta1.AppPolicy, creator string) (model.ApplicationPolicy, error) {
|
||||
plc := model.ApplicationPolicy{
|
||||
AppPrimaryKey: appPrimaryKey,
|
||||
Name: policyCR.Name,
|
||||
Type: policyCR.Type,
|
||||
Creator: creator,
|
||||
}
|
||||
if policyCR.Properties != nil {
|
||||
properties, err := model.NewJSONStruct(policyCR.Properties)
|
||||
if err != nil {
|
||||
return plc, err
|
||||
}
|
||||
plc.Properties = properties
|
||||
}
|
||||
return plc, nil
|
||||
}
|
||||
|
||||
// FromCRWorkflow converts Application CR Workflow section into velaux data store workflow
|
||||
func FromCRWorkflow(ctx context.Context, cli client.Client, appPrimaryKey string, app *v1beta1.Application) (model.Workflow, []v1beta1.WorkflowStep, error) {
|
||||
dataWf := model.Workflow{
|
||||
AppPrimaryKey: appPrimaryKey,
|
||||
// every namespace has a synced env
|
||||
EnvName: model.AutoGenEnvNamePrefix + app.Namespace,
|
||||
// every application has a synced workflow
|
||||
Name: model.AutoGenWorkflowNamePrefix + appPrimaryKey,
|
||||
Alias: model.AutoGenWorkflowNamePrefix + app.Name,
|
||||
Description: model.AutoGenDesc,
|
||||
}
|
||||
if app.Spec.Workflow == nil {
|
||||
return dataWf, nil, nil
|
||||
}
|
||||
var steps []v1beta1.WorkflowStep
|
||||
if app.Spec.Workflow.Ref != "" {
|
||||
dataWf.Name = app.Spec.Workflow.Ref
|
||||
wf := &v1alpha1.Workflow{}
|
||||
if err := cli.Get(ctx, types.NamespacedName{Namespace: app.GetNamespace(), Name: app.Spec.Workflow.Ref}, wf); err != nil {
|
||||
return dataWf, nil, err
|
||||
}
|
||||
steps = step.ConvertSteps(wf.Steps)
|
||||
} else {
|
||||
steps = app.Spec.Workflow.Steps
|
||||
}
|
||||
for _, s := range steps {
|
||||
if s.Properties == nil {
|
||||
continue
|
||||
}
|
||||
properties, err := model.NewJSONStruct(s.Properties)
|
||||
if err != nil {
|
||||
return dataWf, nil, err
|
||||
}
|
||||
dataWf.Steps = append(dataWf.Steps, model.WorkflowStep{
|
||||
Name: s.Name,
|
||||
Type: s.Type,
|
||||
Inputs: s.Inputs,
|
||||
Outputs: s.Outputs,
|
||||
DependsOn: s.DependsOn,
|
||||
Properties: properties,
|
||||
})
|
||||
}
|
||||
return dataWf, steps, nil
|
||||
}
|
||||
|
||||
// FromCRTargets converts deployed Cluster/Namespace from Application CR Status into velaux data store
|
||||
func FromCRTargets(targetApp *v1beta1.Application) []*model.Target {
|
||||
var targets []*model.Target
|
||||
nc := make(map[string]struct{})
|
||||
for _, v := range targetApp.Status.AppliedResources {
|
||||
var cluster = v.Cluster
|
||||
if cluster == "" {
|
||||
cluster = multicluster.ClusterLocalName
|
||||
}
|
||||
name := model.AutoGenTargetNamePrefix + cluster + "-" + v.Namespace
|
||||
if _, ok := nc[name]; ok {
|
||||
continue
|
||||
}
|
||||
nc[name] = struct{}{}
|
||||
targets = append(targets, &model.Target{
|
||||
Name: name,
|
||||
Cluster: &model.ClusterTarget{
|
||||
ClusterName: cluster,
|
||||
Namespace: v.Namespace,
|
||||
},
|
||||
})
|
||||
}
|
||||
return targets
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/datastore"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/log"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/model"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/usecase"
|
||||
"github.com/oam-dev/kubevela/pkg/oam"
|
||||
)
|
||||
|
||||
@@ -79,9 +80,10 @@ func (c *CR2UX) getApp(ctx context.Context, name, namespace string) (*model.Appl
|
||||
|
||||
// CR2UX provides the Add/Update/Delete method
|
||||
type CR2UX struct {
|
||||
ds datastore.DataStore
|
||||
cli client.Client
|
||||
cache sync.Map
|
||||
ds datastore.DataStore
|
||||
cli client.Client
|
||||
cache sync.Map
|
||||
usecases map[string]interface{}
|
||||
}
|
||||
|
||||
func formatAppComposedName(name, namespace string) string {
|
||||
@@ -106,8 +108,11 @@ func (c *CR2UX) AddOrUpdate(ctx context.Context, targetApp *v1beta1.Application)
|
||||
log.Logger.Errorf("Convert App to data store err %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err = StoreProject(ctx, dsApp.AppMeta.Project, ds); err != nil {
|
||||
pu, ok := c.usecases["project"].(usecase.ProjectUsecase)
|
||||
if !ok {
|
||||
log.Logger.Warnf("not provide project usecase instance")
|
||||
}
|
||||
if err = StoreProject(ctx, dsApp.AppMeta.Project, ds, pu); err != nil {
|
||||
log.Logger.Errorf("get or create project for sync process err %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -24,11 +24,13 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/datastore"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/log"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/model"
|
||||
v1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1"
|
||||
"github.com/oam-dev/kubevela/pkg/apiserver/rest/usecase"
|
||||
"github.com/oam-dev/kubevela/pkg/utils"
|
||||
)
|
||||
|
||||
// StoreProject will create project for synced application
|
||||
func StoreProject(ctx context.Context, name string, ds datastore.DataStore) error {
|
||||
func StoreProject(ctx context.Context, name string, ds datastore.DataStore, projectUsecase usecase.ProjectUsecase) error {
|
||||
err := ds.Get(ctx, &model.Project{Name: name})
|
||||
if err == nil {
|
||||
// it means the record already exists, don't need to add anything
|
||||
@@ -38,12 +40,15 @@ func StoreProject(ctx context.Context, name string, ds datastore.DataStore) erro
|
||||
// other database error, return it
|
||||
return err
|
||||
}
|
||||
proj := &model.Project{
|
||||
Name: name,
|
||||
Description: model.AutoGenProj,
|
||||
Alias: strings.Title(name),
|
||||
if projectUsecase != nil {
|
||||
_, err := projectUsecase.CreateProject(ctx, v1.CreateProjectRequest{
|
||||
Name: name,
|
||||
Alias: strings.Title(name),
|
||||
Owner: model.DefaultAdminUserName,
|
||||
Description: model.AutoGenProj})
|
||||
return err
|
||||
}
|
||||
return ds.Add(ctx, proj)
|
||||
return nil
|
||||
}
|
||||
|
||||
// StoreAppMeta will sync application metadata from CR to datastore
|
||||
|
||||
@@ -37,7 +37,7 @@ import (
|
||||
)
|
||||
|
||||
// Start prepares watchers and run their controllers, then waits for process termination signals
|
||||
func Start(ctx context.Context, ds datastore.DataStore, cfg *rest.Config) {
|
||||
func Start(ctx context.Context, ds datastore.DataStore, cfg *rest.Config, usecases map[string]interface{}) {
|
||||
k8sClient, err := clients.GetKubeClient()
|
||||
if err != nil {
|
||||
logrus.Fatal(err)
|
||||
@@ -50,10 +50,10 @@ func Start(ctx context.Context, ds datastore.DataStore, cfg *rest.Config) {
|
||||
|
||||
f := dynamicinformer.NewFilteredDynamicSharedInformerFactory(dynamicClient, 0, v1.NamespaceAll, nil)
|
||||
|
||||
startAppSyncing(ctx, f, ds, k8sClient)
|
||||
startAppSyncing(ctx, f, ds, k8sClient, usecases)
|
||||
}
|
||||
|
||||
func startAppSyncing(ctx context.Context, factory dynamicinformer.DynamicSharedInformerFactory, ds datastore.DataStore, cli client.Client) {
|
||||
func startAppSyncing(ctx context.Context, factory dynamicinformer.DynamicSharedInformerFactory, ds datastore.DataStore, cli client.Client, usecases map[string]interface{}) {
|
||||
var err error
|
||||
informer := factory.ForResource(v1beta1.SchemeGroupVersion.WithResource("applications")).Informer()
|
||||
getApp := func(obj interface{}) *v1beta1.Application {
|
||||
@@ -62,10 +62,14 @@ func startAppSyncing(ctx context.Context, factory dynamicinformer.DynamicSharedI
|
||||
_ = json.Unmarshal(bs, app)
|
||||
return app
|
||||
}
|
||||
if usecases == nil {
|
||||
usecases = make(map[string]interface{})
|
||||
}
|
||||
cu := &CR2UX{
|
||||
ds: ds,
|
||||
cli: cli,
|
||||
cache: sync.Map{},
|
||||
ds: ds,
|
||||
cli: cli,
|
||||
cache: sync.Map{},
|
||||
usecases: usecases,
|
||||
}
|
||||
if err = cu.initCache(ctx); err != nil {
|
||||
klog.Fatal("sync app init err", err)
|
||||
|
||||
@@ -61,7 +61,7 @@ var _ = Describe("Test Worker CR sync to datastore", func() {
|
||||
By("Start syncing")
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
go Start(ctx, ds, cfg)
|
||||
go Start(ctx, ds, cfg, nil)
|
||||
|
||||
By("create test app1 and check the syncing results")
|
||||
app1 := &v1beta1.Application{}
|
||||
|
||||
Reference in New Issue
Block a user