diff --git a/providers/aws/resources/aws.lr b/providers/aws/resources/aws.lr index 725ba89254..8f8d55282f 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("createdAt") { + // Time when the login profile was created + createdAt time } // AWS IAM policy diff --git a/providers/aws/resources/aws.lr.go b/providers/aws/resources/aws.lr.go index 9cd79185c5..328ad01d09 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.createdAt": func(r plugin.Resource) *plugin.DataRes { + return (r.(*mqlAwsIamLoginProfile).GetCreatedAt()).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.createdAt": func(r plugin.Resource, v *llx.RawData) (ok bool) { + r.(*mqlAwsIamLoginProfile).CreatedAt, 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 + CreatedAt 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) GetCreatedAt() *plugin.TValue[*time.Time] { + return &c.CreatedAt +} + // 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..e0dcfe8af5 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: + createdAt: {} + is_private: true + min_mondoo_version: 10.0 + platform: + name: + - aws aws.iam.policy: docs: desc: | @@ -1769,6 +1777,8 @@ resources: createDate: {} groups: {} id: {} + loginProfile: + min_mondoo_version: 10.0 name: {} passwordLastUsed: {} policies: {} diff --git a/providers/aws/resources/aws_iam.go b/providers/aws/resources/aws_iam.go index 68f9842e62..971b01ed1e 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{ + "createdAt": llx.TimeData(*date), + }) + if err != nil { + return nil, err + } + return o.(*mqlAwsIamLoginProfile), nil +} + +func (a *mqlAwsIamLoginProfile) init() (string, error) { + date := a.CreatedAt.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 +}