Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 26 additions & 5 deletions collector/hws/collector/iam/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ package iam
import (
"context"

"github.com/cloudrec/hws/collector"
"github.com/core-sdk/constant"
"github.com/core-sdk/log"
"github.com/core-sdk/schema"
"github.com/cloudrec/hws/collector"
iam "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3"
"github.com/huaweicloud/huaweicloud-sdk-go-v3/services/iam/v3/model"
"go.uber.org/zap"
Expand All @@ -46,6 +46,7 @@ type UserDetail struct {
User model.KeystoneListUsersResult
UserAttribute *model.ShowUserResult
Credentials *[]model.Credentials
CredentialsDetail []*model.ShowCredential
UserGroups []*UserGroup
LoginProtects *model.LoginProtectResult
DomainPasswordPolicy *model.PasswordPolicyResult
Expand All @@ -67,10 +68,12 @@ func GetUserDetail(ctx context.Context, service schema.ServiceInterface, res cha
}

for _, user := range *response.Users {
credentials, CredentialsDetail := listPermanentAccessKeys(ctx, cli, user.Id)
res <- &UserDetail{
User: user,
UserAttribute: showUser(ctx, cli, user.Id),
Credentials: listPermanentAccessKeys(ctx, cli, user.Id),
Credentials: credentials,
CredentialsDetail: CredentialsDetail,
DomainPasswordPolicy: getDomainPasswordPolicy(ctx, cli, user.DomainId),
LoginProtects: showUserLoginProtect(ctx, cli, user.Id),
}
Expand All @@ -79,16 +82,34 @@ func GetUserDetail(ctx context.Context, service schema.ServiceInterface, res cha
return nil
}

func listPermanentAccessKeys(ctx context.Context, cli *iam.IamClient, id string) (credentials *[]model.Credentials) {
func listPermanentAccessKeys(ctx context.Context, cli *iam.IamClient, id string) (*[]model.Credentials, []*model.ShowCredential) {
request := &model.ListPermanentAccessKeysRequest{}
request.UserId = &id
response, err := cli.ListPermanentAccessKeys(request)
if err != nil {
log.CtxLogger(ctx).Warn("ListPermanentAccessKeys error", zap.Error(err))
return
return nil, nil
}

var credentialsDetail []*model.ShowCredential
for _, credential := range *response.Credentials{
credentialDetail := showPermanentAccessKey(ctx, cli, credential.Access)
credentialsDetail = append(credentialsDetail, credentialDetail)
Comment on lines +95 to +97
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The listPermanentAccessKeys function currently makes a separate API call to showPermanentAccessKey for each credential within the loop. This pattern can lead to an N+1 query problem, which might significantly impact performance, especially for users with a large number of access keys. Consider exploring if the Huawei Cloud IAM API offers a bulk retrieval method for access key details or if there's a more efficient way to gather this information.

}

return response.Credentials, credentialsDetail
}

func showPermanentAccessKey(ctx context.Context, cli *iam.IamClient, AccessKey string)(*model.ShowCredential){
request := &model.ShowPermanentAccessKeyRequest{}
request.AccessKey = AccessKey
response, err := cli.ShowPermanentAccessKey(request)
if err != nil {
log.CtxLogger(ctx).Warn("ShowPermanentAccessKey error", zap.Error(err))
return nil
}

return response.Credentials
return response.Credential
}

func getDomainPasswordPolicy(ctx context.Context, cli *iam.IamClient, domainId string) *model.PasswordPolicyResult {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"CredentialsDetail":[{"last_use_time":"VTMl0siGdLtfvi2byXwFtNvIQNx","access":"MvwxL6lnop8WR6WtsVZL","create_time":"0WBWc4SEyXJWqxR9j9b0yUjf6MH","user_id":"UNZEMOqhFHvDLwqIcZ4QtGlJZc9zjbhX","description":"xjjNYokyCZ4Vl4Bm","status":"7zjqk6"},{"last_use_time":"7x1L62kp2gD0DwFV4AueALRKMnr","access":"dCGceTyJTRnTMekZ8Fpo","create_time":"9WOo0sPf8SEXLTfB3cVDN0Xuk28","user_id":"2225vUnkTMIDnRU9PQfkzEB8VqVzC9hF","description":"YumZGdop9gBpbZ7j","status":"13DbY9"}],"User":{"domain_id":"Wx5dWt6AvZpVW6rQc8waxZFneSz6QGxI","pwd_status":false,"name":"G6wxF7042rLQfNFjA","links":{"next":"","previous":"","self":"XYQMeoxznfPVQrSinZ7M0KiXujwAsXpPUEWJYQv2WNZC2F2F9DOZho3cpuLH1yZCKx2YWuvSwfRSgfUiX"},"password_expires_at":"","id":"xPHdIytYxkH9f9YRXovdGCwBb5WLPQTC","enabled":true},"LoginProtects":null,"UserAttribute":{"pwd_strength":"bhb3","create_time":"pwmwLYowO22PLXnbkkZQ6","last_login_time":"cT6oX2bqlK8zDV9O0AR","description":"","access_mode":"cM7F3Pn","areacode":"","enabled":true,"domain_id":"CfQHFZtWztUIbDIr4oyXCViCg2O8qLr4","pwd_status":false,"xuser_id":"","update_time":"2Twx4bZm9o8YvL4CtpKrr","phone":"w","is_domain_owner":false,"name":"kQ0jF4Gw4FHbLlk2i","links":{"next":"","previous":"","self":"Q26kmAZ0EhcwoSlbDRvDopIeOWptPfrdeqzgviXBvF0z26GlH2uB5QJPTckFjuwsFuY8Rw7Z9yQsPqFswKWhvAWEYtO"},"id":"InmZlvmRUGemZI7hR3S3KLaOUeoM3ksr","xuser_type":"","email":""},"DomainPasswordPolicy":{"password_not_username_or_invert":true,"password_char_combination":3,"password_validity_period":90,"minimum_password_age":0,"maximum_password_length":32,"password_requirements":"ml64zq0ePvNjzGepQ3UZBaop8FoMSh2QkBWPOWggRFDxaG3pwy8NOtHug4XoUZohb6uCLSArHJdC9CqTJTZgrSBfwtSKNLF0dVhYFNugJWGJs7iAKhJCY6LBgFrG","maximum_consecutive_identical_chars":0,"minimum_password_length":12,"number_of_recent_passwords_disallowed":4},"Credentials":[{"access":"gV2kBLXcYCmavOfr0Z1g","create_time":"zy7PqjCt8fPonn4i0gTD9e7ZD4H","user_id":"iJlQDdDIeGCmXQ3VwsSLVjo7nLOoTtCR","description":"IKXtmIEUoz9WvOzS","status":"U80yx7"},{"access":"yG0nDtyNpaOcxP1v8ftA","create_time":"hUmGKEC4fX4RamUfloZdvP9zNDG","user_id":"4TBChHgUaTxg211T8TGapK5W89ab1yGd","description":"xyVfA23aVOarzbyV","status":"YfHZzU"}],"UserGroups":null}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"advice":null,"categoryList":["身份安全"],"code":"HUAWEI_CLOUD_IAM User_202511112036_1023138","context":null,"description":"RAM用户长期未使用的AccessKey仍具有效权限,若被泄露或滥用可能导致非授权访问、数据泄露或资源滥用,且违反凭证生命周期管理安全原则。","level":"Medium","link":null,"linkedDataList":null,"name":"IAM 用户 AK 超过一年未使用","platform":"HUAWEI_CLOUD","resourceType":"IAM User"}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package hws_ak_no_use_for_one_year_109
import rego.v1

now_ns := time.now_ns()

default risk := false

risk if {
count(ak_no_use_for_one_year) > 0
}

user_name := input.UserAttribute.name
user_id := input.UserAttribute.domain_id
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The user_id variable is assigned input.UserAttribute.domain_id. Typically, user_id refers to the unique identifier of the user, not the domain. It is likely that input.UserAttribute.id or input.User.id should be used here to correctly identify the user. Please verify the intended source for the user's ID and correct this assignment.

user_id := input.UserAttribute.id


ak_no_use_for_one_year contains p if {
some p in input.CredentialsDetail
p.status == "active"
last_used_date_ns := time.parse_rfc3339_ns(p.last_use_time)
tmp := time.add_date(last_used_date_ns, 0, 0, 365)
tmp < now_ns
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
Loading