fix(region): set public for keypair

This commit is contained in:
ioito
2023-09-05 18:11:06 +08:00
parent 9e158441d7
commit 28738f03ac
8 changed files with 335 additions and 179 deletions

View File

@@ -15,167 +15,20 @@
package compute
import (
"fmt"
"io/ioutil"
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/cmd/climc/shell"
modules "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
"yunion.io/x/onecloud/pkg/mcclient/options"
"yunion.io/x/onecloud/pkg/mcclient/options/compute"
)
func init() {
type KeypairList struct {
options.BaseListOptions
}
R(&KeypairList{}, "keypair-list", "List keypairs.", func(s *mcclient.ClientSession, args *KeypairList) error {
var params *jsonutils.JSONDict
{
var err error
params, err = args.BaseListOptions.Params()
if err != nil {
return err
}
}
result, err := modules.Keypairs.List(s, params)
if err != nil {
return err
}
printList(result, modules.Keypairs.GetColumns(s))
return nil
})
type KeypairCreate struct {
NAME string `help:"Name of keypair to be created"`
Scheme string `help:"Scheme of keypair, default is RSA" choices:"RSA" default:"RSA"`
PublicKey string `help:"Publickey of keypair"`
Desc string `help:"Short description of keypair"`
}
R(&KeypairCreate{}, "keypair-create", "Create a new keypair", func(s *mcclient.ClientSession, args *KeypairCreate) error {
params := jsonutils.NewDict()
params.Add(jsonutils.NewString(args.NAME), "name")
if len(args.Scheme) > 0 {
params.Add(jsonutils.NewString(args.Scheme), "scheme")
}
if len(args.PublicKey) > 0 {
params.Add(jsonutils.NewString(args.PublicKey), "public_key")
}
if len(args.Desc) > 0 {
params.Add(jsonutils.NewString(args.Desc), "description")
}
result, e := modules.Keypairs.Create(s, params)
if e != nil {
return e
}
printObject(result)
return nil
})
type KeypairUpdate struct {
ID string `help:"ID of keypair to be updated"`
Name string `help:"New name of keypair"`
Desc string `help:"Short description of keypair"`
}
R(&KeypairUpdate{}, "keypair-update", "Update a keypair", func(s *mcclient.ClientSession, args *KeypairUpdate) error {
params := jsonutils.NewDict()
if len(args.Name) > 0 {
params.Add(jsonutils.NewString(args.Name), "name")
}
if len(args.Desc) > 0 {
params.Add(jsonutils.NewString(args.Desc), "description")
}
result, e := modules.Keypairs.Update(s, args.ID, params)
if e != nil {
return e
}
printObject(result)
return nil
})
type KeypairDelete struct {
ID string `help:"ID of keypair to be deleted"`
}
R(&KeypairDelete{}, "keypair-delete", "Delete a keypair", func(s *mcclient.ClientSession, args *KeypairDelete) error {
result, e := modules.Keypairs.Delete(s, args.ID, nil)
if e != nil {
return e
}
printObject(result)
return nil
})
type KeypairShow struct {
ID string `help:"ID of keypair to be shown"`
}
R(&KeypairShow{}, "keypair-show", "Show details of a keypair", func(s *mcclient.ClientSession, args *KeypairShow) error {
result, e := modules.Keypairs.Get(s, args.ID, nil)
if e != nil {
return e
}
printObject(result)
return nil
})
type KeypairImport struct {
NAME string `help:"Name of keypair to be imported"`
PublicKey string `help:"Filename of public key file, or public key can be supplied via stdin"`
Desc string `help:"Short description of keypair"`
}
R(&KeypairImport{}, "keypair-import", "Create a new keypair with a existing public key", func(s *mcclient.ClientSession, args *KeypairImport) error {
params := jsonutils.NewDict()
params.Add(jsonutils.NewString(args.NAME), "name")
if len(args.PublicKey) > 0 {
content, e := ioutil.ReadFile(args.PublicKey)
if e != nil {
params.Add(jsonutils.NewString(args.PublicKey), "public_key")
} else {
params.Add(jsonutils.NewString(string(content)), "public_key")
}
} else {
return fmt.Errorf("no public key provided")
}
if len(args.Desc) > 0 {
params.Add(jsonutils.NewString(args.Desc), "description")
}
result, e := modules.Keypairs.Create(s, params)
if e != nil {
return e
}
printObject(result)
return nil
})
type KeypairPrivateKey struct {
ID string `help:"ID of keypair to fetch"`
}
R(&KeypairPrivateKey{}, "keypair-privatekey", "Fetch the private key of a keypair, this can be done once only", func(s *mcclient.ClientSession, args *KeypairPrivateKey) error {
result, e := modules.Keypairs.GetSpecific(s, args.ID, "privatekey", nil)
if e != nil {
return e
}
key, e := result.GetString("private_key")
if e != nil {
return fmt.Errorf("Private key has been fetched")
}
fmt.Printf("%s", key)
return nil
})
cmd := shell.NewResourceCmd(&modules.Keypairs)
cmd.List(&compute.KeypairList{})
cmd.Create(&compute.KeypairCreate{})
cmd.Update(&compute.KeypairUpdate{})
cmd.Delete(&compute.KeyPairIdOptions{})
cmd.Show(&compute.KeyPairIdOptions{})
cmd.Get("privatekey", &compute.KeyPairIdOptions{})
cmd.Perform("public", &options.BasePublicOptions{})
cmd.Perform("private", &compute.KeyPairIdOptions{})
}

View File

@@ -38,6 +38,7 @@ type DeletePreventableCreateInput struct {
type KeypairListInput struct {
apis.UserResourceListInput
apis.SharableResourceBaseListInput
// 加密类型
// example: RSA

View File

@@ -40,6 +40,7 @@ type KeypairCreateInput struct {
type KeypairDetails struct {
apis.UserResourceDetails
apis.SharableResourceBaseInfo
SKeypair
// 私钥长度

View File

@@ -217,7 +217,47 @@ func SharableManagerValidateCreateData(
func SharableManagerFilterByOwner(manager IStandaloneModelManager, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, owner mcclient.IIdentityProvider, scope rbacscope.TRbacScope) *sqlchemy.SQuery {
if owner != nil {
resScope := manager.ResourceScope()
if resScope == rbacscope.ScopeProject && scope == rbacscope.ScopeProject {
if resScope == rbacscope.ScopeUser {
targetProjectId := owner.GetProjectId()
if len(targetProjectId) == 0 {
targetProjectId = userCred.GetProjectId()
}
subq := SharedResourceManager.Query("resource_id")
subq = subq.Equals("resource_type", manager.Keyword())
subq = subq.Equals("target_project_id", targetProjectId)
subq = subq.Equals("target_type", SharedTargetProject)
subq2 := SharedResourceManager.Query("resource_id")
subq2 = subq2.Equals("resource_type", manager.Keyword())
subq2 = subq2.Equals("target_project_id", owner.GetProjectDomainId())
subq2 = subq2.Equals("target_type", SharedTargetDomain)
filters := []sqlchemy.ICondition{
sqlchemy.AND(
sqlchemy.IsTrue(q.Field("is_public")),
sqlchemy.Equals(q.Field("public_scope"), rbacscope.ScopeSystem),
),
sqlchemy.AND(
sqlchemy.IsTrue(q.Field("is_public")),
sqlchemy.Equals(q.Field("public_scope"), rbacscope.ScopeDomain),
sqlchemy.OR(
sqlchemy.In(q.Field("id"), subq2.SubQuery()),
),
),
sqlchemy.In(q.Field("id"), subq.SubQuery()),
}
ownerUserId := owner.GetUserId()
if len(ownerUserId) > 0 {
filters = append(filters, sqlchemy.Equals(q.Field("owner_id"), ownerUserId))
}
q = q.Filter(sqlchemy.OR(filters...))
if userCred != nil {
result := policy.PolicyManager.Allow(scope, userCred, consts.GetServiceType(), manager.KeywordPlural(), policy.PolicyActionList)
if !result.ObjectTags.IsEmpty() {
policyTagFilters := tagutils.STagFilters{}
policyTagFilters.AddFilters(result.ObjectTags)
q = ObjectIdQueryWithTagFilters(q, "id", manager.Keyword(), policyTagFilters)
}
}
} else if resScope == rbacscope.ScopeProject && scope == rbacscope.ScopeProject {
ownerProjectId := owner.GetProjectId()
if len(ownerProjectId) > 0 {
subq := SharedResourceManager.Query("resource_id")

View File

@@ -99,6 +99,14 @@ func (manager *SSharedResourceManager) shareToTarget(
var requireScope rbacscope.TRbacScope
resScope := model.GetModelManager().ResourceScope()
switch resScope {
case rbacscope.ScopeUser:
switch targetType {
case SharedTargetDomain:
// should have system-level privileges
requireScope = rbacscope.ScopeSystem
case SharedTargetProject:
requireScope = rbacscope.ScopeDomain
}
case rbacscope.ScopeProject:
switch targetType {
case SharedTargetProject:

View File

@@ -22,10 +22,12 @@ import (
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/gotypes"
"yunion.io/x/pkg/util/rbacscope"
"yunion.io/x/pkg/utils"
"yunion.io/x/sqlchemy"
"yunion.io/x/onecloud/pkg/apis"
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/httperrors"
@@ -37,6 +39,7 @@ import (
type SKeypairManager struct {
db.SUserResourceBaseManager
db.SSharableBaseResourceManager
}
var KeypairManager *SKeypairManager
@@ -55,6 +58,7 @@ func init() {
type SKeypair struct {
db.SUserResourceBase
db.SSharableBaseResource
// 加密类型
// example: RSA
@@ -68,6 +72,14 @@ type SKeypair struct {
PublicKey string `width:"1024" charset:"ascii" nullable:"false" list:"user" create:"required"`
}
func (manager *SKeypairManager) GetISharableVirtualModelManager() db.ISharableVirtualModelManager {
return manager.GetVirtualObject().(db.ISharableVirtualModelManager)
}
func (manager *SKeypairManager) GetIVirtualModelManager() db.IVirtualModelManager {
return manager.GetVirtualObject().(db.IVirtualModelManager)
}
// 列出ssh密钥对
func (manager *SKeypairManager) ListItemFilter(
ctx context.Context,
@@ -75,11 +87,27 @@ func (manager *SKeypairManager) ListItemFilter(
userCred mcclient.TokenCredential,
query api.KeypairListInput,
) (*sqlchemy.SQuery, error) {
q, err := manager.SUserResourceBaseManager.ListItemFilter(ctx, q, userCred, query.UserResourceListInput)
q, err := manager.SStandaloneResourceBaseManager.ListItemFilter(ctx, q, userCred, query.StandaloneResourceListInput)
if err != nil {
return nil, err
}
if ((query.Admin != nil && *query.Admin) || query.Scope == string(rbacscope.ScopeSystem)) && db.IsAdminAllowList(userCred, manager).Result.IsAllow() {
user := query.UserId
if len(user) > 0 {
uc, _ := db.UserCacheManager.FetchUserByIdOrName(ctx, user)
if uc == nil {
return nil, httperrors.NewUserNotFoundError("user %s not found", user)
}
q = q.Equals("owner_id", uc.Id)
}
} else {
q, err = manager.SSharableBaseResourceManager.ListItemFilter(ctx, q, userCred, query.SharableResourceBaseListInput)
if err != nil {
return nil, errors.Wrap(err, "SSharableBaseResourceManager.ListItemFilter")
}
}
if len(query.Scheme) > 0 {
q = q.In("scheme", query.Scheme)
}
@@ -87,6 +115,8 @@ func (manager *SKeypairManager) ListItemFilter(
q = q.In("fingerprint", query.Fingerprint)
}
q.DebugQuery()
return q, nil
}
@@ -113,6 +143,56 @@ func (manager *SKeypairManager) QueryDistinctExtraField(q *sqlchemy.SQuery, fiel
return q, httperrors.ErrNotFound
}
func (km *SKeypairManager) query(manager db.IModelManager, field string, keyIds []string, filter func(*sqlchemy.SQuery) *sqlchemy.SQuery) *sqlchemy.SSubQuery {
q := manager.Query()
if filter != nil {
q = filter(q)
}
sq := q.SubQuery()
return sq.Query(
sq.Field("keypair_id"),
sqlchemy.COUNT(field),
).In("keypair_id", keyIds).GroupBy(sq.Field("keypir_id")).SubQuery()
}
type SKeypairUsageCount struct {
Id string
LinkedGuestCount int
}
func (km *SKeypairManager) TotalResourceCount(keyIds []string) (map[string]SKeypairUsageCount, error) {
ret := map[string]SKeypairUsageCount{}
guestSQ := km.query(GuestManager, "guest_cnt", keyIds, func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
return q.IsNotEmpty("keypair_id")
})
keypairs := km.Query().SubQuery()
keypairsQ := keypairs.Query(
sqlchemy.SUM("linked_guest_count", guestSQ.Field("guest_cnt")),
)
keypairsQ.AppendField(keypairsQ.Field("id"))
keypairsQ = keypairsQ.LeftJoin(guestSQ, sqlchemy.Equals(keypairsQ.Field("id"), guestSQ.Field("keypair_id")))
keypairsQ = keypairsQ.Filter(sqlchemy.In(keypairsQ.Field("id"), keyIds)).GroupBy(keypairsQ.Field("id"))
counts := []SKeypairUsageCount{}
err := keypairsQ.All(&counts)
if err != nil {
return nil, errors.Wrapf(err, "keyparisQ.All")
}
for i := range counts {
ret[counts[i].Id] = counts[i]
}
return ret, nil
}
func (manager *SKeypairManager) FetchCustomizeColumns(
ctx context.Context,
userCred mcclient.TokenCredential,
@@ -123,13 +203,26 @@ func (manager *SKeypairManager) FetchCustomizeColumns(
) []api.KeypairDetails {
rows := make([]api.KeypairDetails, len(objs))
userRows := manager.SUserResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
shareRows := manager.SSharableBaseResourceManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
keyIds := make([]string, len(objs))
for i := range rows {
keypair := objs[i].(*SKeypair)
rows[i] = api.KeypairDetails{
UserResourceDetails: userRows[i],
PrivateKeyLen: len(keypair.PrivateKey),
UserResourceDetails: userRows[i],
SharableResourceBaseInfo: shareRows[i],
PrivateKeyLen: len(keypair.PrivateKey),
}
keyIds[i] = keypair.Id
}
usages, err := manager.TotalResourceCount(keyIds)
if err != nil {
return rows
}
for i := range rows {
if cnt, ok := usages[keyIds[i]]; ok {
rows[i].LinkedGuestCount = cnt.LinkedGuestCount
}
rows[i].LinkedGuestCount, _ = keypair.GetLinkedGuestsCount()
}
return rows
@@ -178,12 +271,16 @@ func (manager *SKeypairManager) ValidateCreateData(ctx context.Context, userCred
return input, nil
}
func (self *SKeypair) ValidateDeleteCondition(ctx context.Context, info jsonutils.JSONObject) error {
guestCnt, err := self.GetLinkedGuestsCount()
if err != nil {
return httperrors.NewInternalServerError("GetLinkedGuestsCount failed %s", err)
func (self *SKeypair) ValidateDeleteCondition(ctx context.Context, info *api.KeypairDetails) error {
if gotypes.IsNil(info) {
info = &api.KeypairDetails{}
var err error
info.LinkedGuestCount, err = self.GetLinkedGuestsCount()
if err != nil {
return httperrors.NewInternalServerError("GetLinkedGuestsCount failed %s", err)
}
}
if guestCnt > 0 {
if info.LinkedGuestCount > 0 {
return httperrors.NewNotEmptyError("Cannot delete keypair used by servers")
}
return self.SStandaloneResourceBase.ValidateDeleteCondition(ctx, nil)
@@ -195,14 +292,7 @@ func totalKeypairCount(userId string) (int, error) {
}
func (manager *SKeypairManager) FilterByOwner(q *sqlchemy.SQuery, man db.FilterByOwnerProvider, userCred mcclient.TokenCredential, owner mcclient.IIdentityProvider, scope rbacscope.TRbacScope) *sqlchemy.SQuery {
if owner != nil {
if scope == rbacscope.ScopeUser {
if len(owner.GetUserId()) > 0 {
q = q.Equals("owner_id", owner.GetUserId())
}
}
}
return q
return db.SharableManagerFilterByOwner(manager.GetISharableVirtualModelManager(), q, userCred, owner, scope)
}
func (keypair *SKeypair) GetDetailsPrivatekey(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
@@ -224,3 +314,82 @@ func (keypair *SKeypair) GetDetailsPrivatekey(ctx context.Context, userCred mccl
}
return retval, nil
}
func (self *SKeypair) GetOwnerId() mcclient.IIdentityProvider {
owner := &db.SOwnerId{UserId: self.OwnerId}
obj, err := db.UserCacheManager.FetchById(self.OwnerId)
if err != nil {
return owner
}
user := obj.(*db.SUser)
owner.DomainId = user.DomainId
return owner
}
func (self *SKeypair) GetProjectDomainId() string {
obj, err := db.UserCacheManager.FetchById(self.OwnerId)
if err != nil {
return ""
}
user := obj.(*db.SUser)
return user.DomainId
}
func (self *SKeypair) GetRequiredSharedDomainIds() []string {
obj, err := db.UserCacheManager.FetchById(self.OwnerId)
if err != nil {
return []string{}
}
user := obj.(*db.SUser)
return []string{user.DomainId}
}
func (self *SKeypair) GetSharableTargetDomainIds() []string {
return []string{}
}
func (self *SKeypair) GetChangeOwnerRequiredDomainIds() []string {
domainId := self.GetProjectDomainId()
if len(domainId) > 0 {
return []string{domainId}
}
return []string{}
}
func (self *SKeypair) GetChangeOwnerCandidateDomainIds() []string {
domains := []db.STenant{}
db.TenantCacheManager.GetDomainQuery().All(&domains)
ret := []string{}
for i := range domains {
ret = append(ret, domains[i].Id)
}
return ret
}
func (self *SKeypair) GetSharedDomains() []string {
return db.SharableGetSharedProjects(self, db.SharedTargetDomain)
}
func (keypair *SKeypair) GetISharableModel() db.ISharableBaseModel {
return keypair.GetVirtualObject().(db.ISharableBaseModel)
}
func (keypair *SKeypair) GetDetailsChangeOwnerCandidateDomains(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (apis.ChangeOwnerCandidateDomainsOutput, error) {
return db.IOwnerResourceBaseModelGetChangeOwnerCandidateDomains(keypair)
}
func (self *SKeypair) PerformPublic(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformPublicProjectInput) (jsonutils.JSONObject, error) {
err := db.SharablePerformPublic(self.GetISharableModel(), ctx, userCred, input)
if err != nil {
return nil, errors.Wrap(err, "SharablePerformPublic")
}
return nil, nil
}
func (self *SKeypair) PerformPrivate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformPrivateInput) (jsonutils.JSONObject, error) {
err := db.SharablePerformPrivate(self.GetISharableModel(), ctx, userCred)
if err != nil {
return nil, errors.Wrap(err, "SharablePerformPrivate")
}
return nil, nil
}

View File

@@ -468,9 +468,10 @@ func (opts *BaseUpdateOptions) Params() (jsonutils.JSONObject, error) {
}
type BasePublicOptions struct {
ID string `help:"ID or name of resource" json:"-"`
Scope string `help:"sharing scope" choices:"system|domain"`
SharedDomains []string `help:"share to domains"`
ID string `help:"ID or name of resource" json:"-"`
Scope string `help:"sharing scope" choices:"system|domain|project"`
SharedDomains []string `help:"share to domains"`
SharedProjects []string `help:"share to projects"`
}
func (opts *BasePublicOptions) GetId() string {

View File

@@ -0,0 +1,83 @@
// Copyright 2019 Yunion
//
// 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 compute
import (
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/mcclient/options"
)
type KeypairList struct {
options.BaseListOptions
}
func (self *KeypairList) Params() (jsonutils.JSONObject, error) {
return options.ListStructToParams(self)
}
type KeypairCreate struct {
NAME string `help:"Name of keypair to be created"`
Scheme string `help:"Scheme of keypair, default is RSA" choices:"RSA" default:"RSA"`
PublicKey string `help:"Publickey of keypair"`
Desc string `help:"Short description of keypair"`
}
func (args *KeypairCreate) Params() (jsonutils.JSONObject, error) {
params := jsonutils.NewDict()
params.Add(jsonutils.NewString(args.NAME), "name")
if len(args.Scheme) > 0 {
params.Add(jsonutils.NewString(args.Scheme), "scheme")
}
if len(args.PublicKey) > 0 {
params.Add(jsonutils.NewString(args.PublicKey), "public_key")
}
if len(args.Desc) > 0 {
params.Add(jsonutils.NewString(args.Desc), "description")
}
return params, nil
}
type KeyPairIdOptions struct {
ID string `help:"ID of keypair"`
}
func (self *KeyPairIdOptions) GetId() string {
return self.ID
}
func (self *KeyPairIdOptions) Params() (jsonutils.JSONObject, error) {
return nil, nil
}
type KeypairUpdate struct {
KeyPairIdOptions
Name string `help:"New name of keypair"`
Desc string `help:"Short description of keypair"`
}
func (args *KeypairUpdate) Params() (jsonutils.JSONObject, error) {
params := jsonutils.NewDict()
if len(args.Name) > 0 {
params.Add(jsonutils.NewString(args.Name), "name")
}
if len(args.Desc) > 0 {
params.Add(jsonutils.NewString(args.Desc), "description")
}
return params, nil
}