From 1a3bff6cd6c4876fe1dcbc5cbed8944e2bf323b3 Mon Sep 17 00:00:00 2001 From: Dominik Richter Date: Mon, 11 Dec 2023 20:30:03 -0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20aws.iam.loginProfile=20added=20for?= =?UTF-8?q?=20IAM=20users?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Dominik Richter --- providers/aws/resources/aws.lr | 8 ++ providers/aws/resources/aws.lr.go | 83 ++++++++++++++++++++ providers/aws/resources/aws.lr.manifest.yaml | 10 +++ providers/aws/resources/aws_iam.go | 46 +++++++++++ 4 files changed, 147 insertions(+) diff --git a/providers/aws/resources/aws.lr b/providers/aws/resources/aws.lr index 725ba89254..6fc86df68d 100644 --- a/providers/aws/resources/aws.lr +++ b/providers/aws/resources/aws.lr @@ -777,6 +777,14 @@ private aws.iam.user @defaults("arn name") { groups() []string // List of access keys metadata associated with the user accessKeys() []dict + // Login profile for the user + loginProfile() aws.iam.loginProfile +} + +// AWS IAM login profile for a user +private aws.iam.loginProfile @defaults("createDate") { + // Time when the login profile was created + createDate time } // AWS IAM policy diff --git a/providers/aws/resources/aws.lr.go b/providers/aws/resources/aws.lr.go index 9cd79185c5..0c8230fed9 100644 --- a/providers/aws/resources/aws.lr.go +++ b/providers/aws/resources/aws.lr.go @@ -222,6 +222,10 @@ func init() { Init: initAwsIamUser, Create: createAwsIamUser, }, + "aws.iam.loginProfile": { + // to override args, implement: initAwsIamLoginProfile(runtime *plugin.Runtime, args map[string]*llx.RawData) (map[string]*llx.RawData, plugin.Resource, error) + Create: createAwsIamLoginProfile, + }, "aws.iam.policy": { // to override args, implement: initAwsIamPolicy(runtime *plugin.Runtime, args map[string]*llx.RawData) (map[string]*llx.RawData, plugin.Resource, error) Create: createAwsIamPolicy, @@ -1545,6 +1549,12 @@ var getDataFields = map[string]func(r plugin.Resource) *plugin.DataRes{ "aws.iam.user.accessKeys": func(r plugin.Resource) *plugin.DataRes { return (r.(*mqlAwsIamUser).GetAccessKeys()).ToDataRes(types.Array(types.Dict)) }, + "aws.iam.user.loginProfile": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlAwsIamUser).GetLoginProfile()).ToDataRes(types.Resource("aws.iam.loginProfile")) + }, + "aws.iam.loginProfile.createDate": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlAwsIamLoginProfile).GetCreateDate()).ToDataRes(types.Time) + }, "aws.iam.policy.arn": func(r plugin.Resource) *plugin.DataRes { return (r.(*mqlAwsIamPolicy).GetArn()).ToDataRes(types.String) }, @@ -4943,6 +4953,18 @@ var setDataFields = map[string]func(r plugin.Resource, v *llx.RawData) bool { r.(*mqlAwsIamUser).AccessKeys, ok = plugin.RawToTValue[[]interface{}](v.Value, v.Error) return }, + "aws.iam.user.loginProfile": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlAwsIamUser).LoginProfile, ok = plugin.RawToTValue[*mqlAwsIamLoginProfile](v.Value, v.Error) + return + }, + "aws.iam.loginProfile.__id": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlAwsIamLoginProfile).__id, ok = v.Value.(string) + return + }, + "aws.iam.loginProfile.createDate": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlAwsIamLoginProfile).CreateDate, ok = plugin.RawToTValue[*time.Time](v.Value, v.Error) + return + }, "aws.iam.policy.__id": func(r plugin.Resource, v *llx.RawData) (ok bool) { r.(*mqlAwsIamPolicy).__id, ok = v.Value.(string) return @@ -11992,6 +12014,7 @@ type mqlAwsIamUser struct { AttachedPolicies plugin.TValue[[]interface{}] Groups plugin.TValue[[]interface{}] AccessKeys plugin.TValue[[]interface{}] + LoginProfile plugin.TValue[*mqlAwsIamLoginProfile] } // createAwsIamUser creates a new instance of this resource @@ -12089,6 +12112,66 @@ func (c *mqlAwsIamUser) GetAccessKeys() *plugin.TValue[[]interface{}] { }) } +func (c *mqlAwsIamUser) GetLoginProfile() *plugin.TValue[*mqlAwsIamLoginProfile] { + return plugin.GetOrCompute[*mqlAwsIamLoginProfile](&c.LoginProfile, func() (*mqlAwsIamLoginProfile, error) { + if c.MqlRuntime.HasRecording { + d, err := c.MqlRuntime.FieldResourceFromRecording("aws.iam.user", c.__id, "loginProfile") + if err != nil { + return nil, err + } + if d != nil { + return d.Value.(*mqlAwsIamLoginProfile), nil + } + } + + return c.loginProfile() + }) +} + +// mqlAwsIamLoginProfile for the aws.iam.loginProfile resource +type mqlAwsIamLoginProfile struct { + MqlRuntime *plugin.Runtime + __id string + // optional: if you define mqlAwsIamLoginProfileInternal it will be used here + CreateDate plugin.TValue[*time.Time] +} + +// createAwsIamLoginProfile creates a new instance of this resource +func createAwsIamLoginProfile(runtime *plugin.Runtime, args map[string]*llx.RawData) (plugin.Resource, error) { + res := &mqlAwsIamLoginProfile{ + MqlRuntime: runtime, + } + + err := SetAllData(res, args) + if err != nil { + return res, err + } + + // to override __id implement: id() (string, error) + + if runtime.HasRecording { + args, err = runtime.ResourceFromRecording("aws.iam.loginProfile", res.__id) + if err != nil || args == nil { + return res, err + } + return res, SetAllData(res, args) + } + + return res, nil +} + +func (c *mqlAwsIamLoginProfile) MqlName() string { + return "aws.iam.loginProfile" +} + +func (c *mqlAwsIamLoginProfile) MqlID() string { + return c.__id +} + +func (c *mqlAwsIamLoginProfile) GetCreateDate() *plugin.TValue[*time.Time] { + return &c.CreateDate +} + // mqlAwsIamPolicy for the aws.iam.policy resource type mqlAwsIamPolicy struct { MqlRuntime *plugin.Runtime diff --git a/providers/aws/resources/aws.lr.manifest.yaml b/providers/aws/resources/aws.lr.manifest.yaml index 185548aa49..20bb312e44 100755 --- a/providers/aws/resources/aws.lr.manifest.yaml +++ b/providers/aws/resources/aws.lr.manifest.yaml @@ -1703,6 +1703,14 @@ resources: platform: name: - aws + aws.iam.loginProfile: + fields: + createDate: {} + is_private: true + min_mondoo_version: latest + platform: + name: + - aws aws.iam.policy: docs: desc: | @@ -1769,6 +1777,8 @@ resources: createDate: {} groups: {} id: {} + loginProfile: + min_mondoo_version: latest name: {} passwordLastUsed: {} policies: {} diff --git a/providers/aws/resources/aws_iam.go b/providers/aws/resources/aws_iam.go index 68f9842e62..5b002025e8 100644 --- a/providers/aws/resources/aws_iam.go +++ b/providers/aws/resources/aws_iam.go @@ -1310,3 +1310,49 @@ func (a *mqlAwsIamUser) groups() ([]interface{}, error) { return res, nil } + +func (a *mqlAwsIamUser) loginProfile() (*mqlAwsIamLoginProfile, error) { + conn := a.MqlRuntime.Connection.(*connection.AwsConnection) + + svc := conn.Iam("") + ctx := context.Background() + name := a.Name.Data + + profile, err := svc.GetLoginProfile(ctx, &iam.GetLoginProfileInput{ + UserName: &name, + }) + + var ae smithy.APIError + if errors.As(err, &ae) { + if ae.ErrorCode() == "NoSuchEntity" { + a.LoginProfile.State = plugin.StateIsSet | plugin.StateIsNull + return nil, nil + } + } + if err != nil { + return nil, err + } + + date := profile.LoginProfile.CreateDate + if date == nil { + return nil, errors.New("login profile doesn't have a createDate") + } + + o, err := CreateResource(a.MqlRuntime, "aws.iam.loginProfile", map[string]*llx.RawData{ + "createDate": llx.TimeData(*date), + }) + if err != nil { + return nil, err + } + return o.(*mqlAwsIamLoginProfile), nil +} + +func (a *mqlAwsIamLoginProfile) init() (string, error) { + date := a.CreateDate.Data + if date == nil { + return "", nil + } + // Note: the precision of AWS logins is in seconds. Current AWS docs don't + // specify a precision. Using seconds is reasonable. + return strconv.FormatInt(date.Unix(), 10), nil +}