Skip to content

Commit cc20ad0

Browse files
authored
Merge pull request #74 from DevopsArtFactory/feat/iam-user-browser
feat: add IAM user browser
2 parents 4a87424 + b0b66c5 commit cc20ad0

15 files changed

Lines changed: 1395 additions & 100 deletions

File tree

PLAN.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -145,9 +145,9 @@ unic/
145145
- List hosted zones → drill into DNS records → record detail view
146146
- Filter support on zone list and record list
147147

148-
**M3.9 — IAM Users**
149-
- List IAM users with metadata (creation date, last activity, MFA status)
150-
- View user details (attached policies, groups, access keys)
148+
**M3.9 — IAM Users**
149+
- List IAM users with metadata (creation date, last activity, MFA status)
150+
- View user details (attached policies, groups, access keys)
151151

152152
**M3.7 — CloudWatch Logs**
153153
- List log groups → log streams
@@ -267,7 +267,7 @@ Note: M5.2 (textinput) provides the foundation for M6 (enhanced search) — they
267267
- M1 is complete; M2 is deferred (relying on AWS SDK default credential chain)
268268
- M2.1 (Context-based auth with SSO, credential, assume-role) is complete
269269
- M3 services are independent of each other, build in any order
270-
- M3.1 (VPC), M3.2 (RDS), M3.3 (IAM Credentials), M3.4 (SSM Sessions), M3.5 (Security Groups), M3.6.1 (Route53 phase 1), and M3.9 (Secrets Manager) are complete
270+
- M3.1 (VPC), M3.2 (RDS), M3.3 (IAM Credentials), M3.4 (SSM Sessions), M3.5 (Security Groups), M3.6.1 (Route53 phase 1), IAM Users, and Secrets Manager are complete
271271
- M4.3 (Distribution) is partially done (GoReleaser + GitHub Actions)
272272
- M5.1 (File Extraction) is complete — app.go split into per-screen files
273273
- M5 and M6 can begin independently of remaining M3/M4 work

README.md

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ contexts:
125125
| Route53 | DNS Browser (Hosted Zones → Records → Record Detail, create/edit/delete A/CNAME, change status polling) | ✅ Implemented |
126126
| CloudWatch Logs | Log Browser (Log Groups → Streams → Events, live tail with 2s poll, time range presets, filter patterns, log level highlighting) | ✅ Implemented |
127127
| Secrets Manager | Secrets Browser (list secrets, view key-value pairs or raw values) | ✅ Implemented |
128+
| IAM | IAM User Browser (lightweight username pages, background filter expansion, detail drill-down) | ✅ Implemented |
128129
| IAM | Access Key Browser (list keys with status, age, last used) | ✅ Implemented |
129130
| IAM | Access Key Rotation (create → verify/apply → deactivate → delete) | ✅ Implemented |
130131

@@ -171,6 +172,14 @@ contexts:
171172
| `f` | Failover database | Multi-AZ standalone or Aurora cluster |
172173
| `r` | Refresh status | Always |
173174

175+
### IAM Users
176+
177+
| Key | Action | Screen |
178+
|-----|--------|--------|
179+
| `/` | Filter IAM users and continue loading remaining usernames in background | List |
180+
| `n` | Load next page of IAM users | List |
181+
| `Enter` | View groups, policies, and access keys | List |
182+
174183
### IAM Access Key Rotation
175184

176185
| Key | Action | Screen |
@@ -204,17 +213,7 @@ contexts:
204213

205214
### Filtering
206215

207-
Available on: EC2 instances, VPC/Subnets, RDS instances, Route53 zones/records, CloudWatch Log Groups/Streams, Secrets Manager, Context Switcher. Press `/` to enter filter mode, type to search, `Esc` or `Enter` to exit filter mode.
208-
209-
### IAM Access Key Rotation
210-
211-
| Key | Action | Screen |
212-
|-----|--------|--------|
213-
| `r` | Rotate access key | Key detail |
214-
| `c` | Copy new key as export commands | Rotation result |
215-
| `a` | Apply new key to ~/.aws/credentials | Rotation result |
216-
| `d` | Deactivate old key | Rotation result |
217-
| `x` | Delete old inactive key | Rotation result |
216+
Available on: EC2 instances, IAM users, VPC/Subnets, RDS instances, Route53 zones/records, CloudWatch Log Groups/Streams, Secrets Manager, Context Switcher. Press `/` to enter filter mode, type to search, `Esc` or `Enter` to exit filter mode.
218217

219218
## Documentation
220219

internal/app/app.go

Lines changed: 49 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ const (
3939
screenSecurityGroupDetail
4040
screenSecurityGroupAddRule
4141
screenSecurityGroupDeleteConfirm
42+
screenIAMUserList
43+
screenIAMUserDetail
4244
screenIAMKeyList
4345
screenIAMKeyDetail
4446
screenIAMKeyRotateConfirm
@@ -117,17 +119,26 @@ type Model struct {
117119
selectedRoute53Record *awsservice.DNSRecord
118120

119121
// Route53 mutation state
120-
route53Action string // "create", "edit", "delete"
121-
route53ConfirmInput string // type-to-confirm for delete
122-
route53EditField int // current form field index
123-
route53EditValues map[string]string // accumulated form values
124-
route53EditInput string // current field text input
125-
route53EditSelectIdx int // index for select-type fields (record type)
126-
route53ChangeID string // for status polling
127-
route53ChangeStatus string // "PENDING" / "INSYNC"
128-
route53Polling bool // polling active
122+
route53Action string // "create", "edit", "delete"
123+
route53ConfirmInput string // type-to-confirm for delete
124+
route53EditField int // current form field index
125+
route53EditValues map[string]string // accumulated form values
126+
route53EditInput string // current field text input
127+
route53EditSelectIdx int // index for select-type fields (record type)
128+
route53ChangeID string // for status polling
129+
route53ChangeStatus string // "PENDING" / "INSYNC"
130+
route53Polling bool // polling active
129131

130132
// IAM credentials state
133+
iamUsers []awsservice.IAMUser
134+
filteredIAMUsers []awsservice.IAMUser
135+
iamUserIdx int
136+
iamUserFilter string
137+
iamUserFilterActive bool
138+
iamUserLoadingMore bool
139+
iamUserHasMore bool
140+
iamUserNextMarker string
141+
selectedIAMUser *awsservice.IAMUserDetail
131142
iamKeys []awsservice.AccessKey
132143
iamKeyIdx int
133144
selectedIAMKey *awsservice.AccessKey
@@ -161,31 +172,31 @@ type Model struct {
161172
sgDeleteConfirm string // type-to-confirm input for rule deletion
162173
sgDeleteRule *awsservice.SecurityGroupRule
163174
sgAddField int // current field in add form (0=direction, 1=protocol, 2=fromPort, 3=toPort, 4=source, 5=description)
164-
sgAddValues map[string]string // accumulated form values
165-
sgAddInput string // current field text input
166-
sgAddSelectIdx int // index for select-type fields (direction, protocol)
175+
sgAddValues map[string]string // accumulated form values
176+
sgAddInput string // current field text input
177+
sgAddSelectIdx int // index for select-type fields (direction, protocol)
167178

168179
// CloudWatch Logs browser state
169-
cwLogGroups []awsservice.LogGroup
170-
filteredCWLogGroups []awsservice.LogGroup
171-
cwLogGroupIdx int
172-
cwLogGroupFilter string
173-
cwLogGroupFilterActive bool
174-
selectedCWLogGroup *awsservice.LogGroup
180+
cwLogGroups []awsservice.LogGroup
181+
filteredCWLogGroups []awsservice.LogGroup
182+
cwLogGroupIdx int
183+
cwLogGroupFilter string
184+
cwLogGroupFilterActive bool
185+
selectedCWLogGroup *awsservice.LogGroup
175186
cwLogStreams []awsservice.LogStream
176187
filteredCWLogStreams []awsservice.LogStream
177188
cwLogStreamIdx int
178189
cwLogStreamFilter string
179190
cwLogStreamFilterActive bool
180191
selectedCWLogStream *awsservice.LogStream
181-
cwLogEvents []awsservice.LogEvent
182-
cwLogScrollOffset int
183-
cwLogNextToken *string
184-
cwLogTimeRange int // index into preset time ranges
185-
cwLogFilterPattern string
186-
cwLogFilterActive bool // filter pattern input active
187-
cwLogTailing bool // live tail active
188-
cwLogTailToken *string
192+
cwLogEvents []awsservice.LogEvent
193+
cwLogScrollOffset int
194+
cwLogNextToken *string
195+
cwLogTimeRange int // index into preset time ranges
196+
cwLogFilterPattern string
197+
cwLogFilterActive bool // filter pattern input active
198+
cwLogTailing bool // live tail active
199+
cwLogTailToken *string
189200

190201
// Context picker
191202
configPath string
@@ -210,7 +221,7 @@ type Model struct {
210221

211222
// Update check
212223
currentVersion string
213-
updateAvailable string // non-empty = new version available
224+
updateAvailable string // non-empty = new version available
214225
installMethod update.InstallMethod
215226

216227
// Error display
@@ -375,6 +386,10 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
375386
return m.updateSecurityGroupAddRule(msg)
376387
case screenSecurityGroupDeleteConfirm:
377388
return m.updateSecurityGroupDeleteConfirm(msg)
389+
case screenIAMUserList:
390+
return m.updateIAMUserList(msg)
391+
case screenIAMUserDetail:
392+
return m.updateIAMUserDetail(msg)
378393
case screenIAMKeyList:
379394
return m.updateIAMKeyList(msg)
380395
case screenIAMKeyDetail:
@@ -475,6 +490,9 @@ func (m Model) updateFeatureList(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
475490
case domain.FeatureSecurityGroupBrowser:
476491
m.screen = screenLoading
477492
return m, m.loadSecurityGroups()
493+
case domain.FeatureIAMUsersBrowser:
494+
m.screen = screenLoading
495+
return m, m.loadIAMUsers()
478496
case domain.FeatureListAccessKeys:
479497
m.iamRotationEnabled = false
480498
m.screen = screenLoading
@@ -556,6 +574,10 @@ func (m Model) View() string {
556574
v = m.viewSecurityGroupAddRule()
557575
case screenSecurityGroupDeleteConfirm:
558576
v = m.viewSecurityGroupDeleteConfirm()
577+
case screenIAMUserList:
578+
v = m.viewIAMUserList()
579+
case screenIAMUserDetail:
580+
v = m.viewIAMUserDetail()
559581
case screenIAMKeyList:
560582
v = m.viewIAMKeyList()
561583
case screenIAMKeyDetail:
@@ -760,7 +782,6 @@ func (m Model) updateSecretDetail(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
760782
return m, nil
761783
}
762784

763-
764785
func (m Model) viewSecretList() string {
765786
var b strings.Builder
766787
b.WriteString(m.renderStatusBar())

0 commit comments

Comments
 (0)