-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsession_selector.go
More file actions
118 lines (98 loc) · 3.38 KB
/
session_selector.go
File metadata and controls
118 lines (98 loc) · 3.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
package ui
import (
"errors"
"fmt"
"os"
"sort"
"github.com/Iilun/survey/v2"
"github.com/aaearon/grant-cli/internal/sca/models"
)
// FormatSessionOption formats a session for display in the multi-select UI.
func FormatSessionOption(session models.SessionInfo, nameMap map[string]string) string {
durationMin := session.SessionDuration / 60
var durationStr string
if durationMin >= 60 {
durationStr = fmt.Sprintf("%dh %dm", durationMin/60, durationMin%60)
} else {
durationStr = fmt.Sprintf("%dm", durationMin)
}
if session.IsGroupSession() {
directory := session.WorkspaceID
if nameMap != nil {
if name, ok := nameMap[session.WorkspaceID]; ok {
directory = name
}
}
return fmt.Sprintf("Group: %s in %s - duration: %s (session: %s)", session.Target.ID, directory, durationStr, session.SessionID)
}
workspace := session.WorkspaceID
if nameMap != nil {
if name, ok := nameMap[session.WorkspaceID]; ok {
workspace = fmt.Sprintf("%s (%s)", name, session.WorkspaceID)
}
}
return fmt.Sprintf("%s on %s - duration: %s (session: %s)", session.RoleID, workspace, durationStr, session.SessionID)
}
// BuildSessionOptions builds a sorted list of display options from sessions.
func BuildSessionOptions(sessions []models.SessionInfo, nameMap map[string]string) []string {
options := make([]string, len(sessions))
for i, s := range sessions {
options[i] = FormatSessionOption(s, nameMap)
}
sort.Strings(options)
return options
}
// FindSessionByDisplay finds a session by its formatted display string.
func FindSessionByDisplay(sessions []models.SessionInfo, nameMap map[string]string, display string) (*models.SessionInfo, error) {
for i := range sessions {
if FormatSessionOption(sessions[i], nameMap) == display {
return &sessions[i], nil
}
}
return nil, fmt.Errorf("session not found: %s", display)
}
// SelectSessions presents a multi-select prompt for choosing sessions to revoke.
func SelectSessions(sessions []models.SessionInfo, nameMap map[string]string) ([]models.SessionInfo, error) {
if !IsInteractive() {
return nil, fmt.Errorf("%w; use --all or provide session IDs as arguments", ErrNotInteractive)
}
if len(sessions) == 0 {
return nil, errors.New("no sessions available")
}
options := BuildSessionOptions(sessions, nameMap)
var selected []string
prompt := &survey.MultiSelect{
Message: "Select sessions to revoke:",
Options: options,
}
if err := survey.AskOne(prompt, &selected, survey.WithStdio(os.Stdin, os.Stderr, os.Stderr)); err != nil {
return nil, fmt.Errorf("session selection failed: %w", err)
}
if len(selected) == 0 {
return nil, errors.New("no sessions selected")
}
var result []models.SessionInfo
for _, display := range selected {
s, err := FindSessionByDisplay(sessions, nameMap, display)
if err != nil {
return nil, err
}
result = append(result, *s)
}
return result, nil
}
// ConfirmRevocation prompts the user to confirm session revocation.
func ConfirmRevocation(count int) (bool, error) {
if !IsInteractive() {
return false, fmt.Errorf("%w; use --yes to skip confirmation", ErrNotInteractive)
}
var confirmed bool
prompt := &survey.Confirm{
Message: fmt.Sprintf("Revoke %d session(s)?", count),
Default: false,
}
if err := survey.AskOne(prompt, &confirmed, survey.WithStdio(os.Stdin, os.Stderr, os.Stderr)); err != nil {
return false, fmt.Errorf("confirmation failed: %w", err)
}
return confirmed, nil
}