Skip to content

Commit d3f2ab6

Browse files
authored
Merge pull request #71 from thand-io/new-approval-tui-p2
Enhanced session management to support provider filtering for multi-provider authentication scenarios Improved workflow status UI with better formatting, authorization tracking, and error handling Added utility functions for formatting durations in both ISO 8601 and human-readable formats
2 parents ae3b95c + 2895883 commit d3f2ab6

12 files changed

Lines changed: 308 additions & 151 deletions

File tree

.vscode/launch.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@
5555
"mode": "auto",
5656
"program": "${workspaceFolder}",
5757
"cwd": "${workspaceFolder}",
58-
"args": ["--login-server", "https://agent-717249053981.europe-west1.run.app"],
5958
"env": {},
59+
"envFile": "${workspaceFolder}/.env.remote.local",
6060
"showLog": true
6161
},
6262
{
@@ -77,7 +77,7 @@
7777
"mode": "auto",
7878
"program": "${workspaceFolder}",
7979
"cwd": "${workspaceFolder}",
80-
"args": ["request", "access", "--provider", "aws-prod", "--role", "aws_admin", "--duration", "PT1M", "--reason", "Need access for demo", "--login-server", "http://localhost:9090"],
80+
"args": ["request", "access", "--provider", "aws-thand-dev", "--role", "aws_admin", "--duration", "PT1M", "--reason", "Need access for demo", "--login-server", "http://localhost:9090"],
8181
"env": {},
8282
"showLog": true
8383
},
@@ -88,8 +88,9 @@
8888
"mode": "auto",
8989
"program": "${workspaceFolder}",
9090
"cwd": "${workspaceFolder}",
91-
"args": ["request", "access", "--provider", "aws-prod", "--role", "aws_admin", "--duration", "PT1M", "--reason", "Need access for demo", "--login-server", "https://agent-717249053981.europe-west1.run.app"],
91+
"args": ["request", "access", "--provider", "aws-thand-dev", "--role", "aws_admin", "--duration", "PT1M", "--reason", "Need access for demo"],
9292
"env": {},
93+
"envFile": "${workspaceFolder}/.env.remote.local",
9394
"showLog": true
9495
}
9596
]

cmd/cli/access.go

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,15 @@ var accessCmd = &cobra.Command{
2626
// TODO: use resource, permissions later to let users request specific permissions
2727
// and access to specific resources
2828
// resource, _ := cmd.Flags().GetString("resource")
29-
provider, _ := cmd.Flags().GetString("provider")
29+
identities, _ := cmd.Flags().GetStringArray("identity")
30+
authenticator, _ := cmd.Flags().GetString("authenticator")
31+
workflow, _ := cmd.Flags().GetString("workflow")
32+
providers, _ := cmd.Flags().GetStringArray("provider")
3033
role, _ := cmd.Flags().GetString("role")
3134
duration, _ := cmd.Flags().GetString("duration")
3235
reason, _ := cmd.Flags().GetString("reason")
3336

34-
if len(provider) == 0 || len(role) == 0 || len(duration) == 0 || len(reason) == 0 {
37+
if len(providers) == 0 || len(role) == 0 || len(duration) == 0 || len(reason) == 0 {
3538
fmt.Println("Error: --provider, --role, --duration, and --reason are required")
3639
fmt.Println("Example: agent request access --provider snowflake-prod --role analyst --duration 4h --reason 'Need access for analysis'")
3740
return
@@ -45,11 +48,13 @@ var accessCmd = &cobra.Command{
4548
}
4649

4750
err = MakeElevationRequest(&models.ElevateRequest{
48-
Role: foundRole,
49-
Providers: []string{provider},
50-
// Let the system pick the workflow based on role and provider
51-
Reason: reason,
52-
Duration: duration,
51+
Role: foundRole,
52+
Providers: providers,
53+
Identities: identities,
54+
Authenticator: authenticator,
55+
Workflow: workflow,
56+
Reason: reason,
57+
Duration: duration,
5358
})
5459

5560
if err != nil {
@@ -66,7 +71,10 @@ func init() {
6671

6772
// Add flags for access command
6873
// accessCmd.Flags().StringP("resource", "r", "", "Resource to access (e.g., snowflake-prod, aws-prod)")
69-
accessCmd.Flags().StringP("provider", "p", "", "Provider to access (alias for resource)")
74+
accessCmd.Flags().StringArrayP("identities", "i", []string{}, "Identities to use for access (e.g., user@example.com)")
75+
accessCmd.Flags().StringP("authenticator", "a", "", "Authenticator to use for login (overrides provider selection)")
76+
accessCmd.Flags().StringP("workflow", "w", "", "Workflow to execute (e.g., snowflake-access)")
77+
accessCmd.Flags().StringArrayP("provider", "p", []string{}, "Provider to access (alias for resource)")
7078
accessCmd.Flags().StringP("role", "o", "", "Role to assume (e.g., analyst, admin, readonly)")
7179
accessCmd.Flags().StringP("duration", "d", "", "Duration of access (e.g., 1h, 4h, 8h)")
7280
accessCmd.Flags().StringP("reason", "e", "", "Reason for access request (e.g., 'Need access for analysis')")

cmd/cli/request.go

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ var requestCmd = &cobra.Command{
6060
return
6161
}
6262

63-
sesh, err := loginSessions.GetFirstActiveSession()
63+
_, sesh, err := loginSessions.GetFirstActiveSession()
6464

6565
if err != nil {
6666
return
@@ -125,13 +125,28 @@ func MakeElevationRequest(request *models.ElevateRequest) error {
125125
}
126126

127127
if len(request.Workflow) == 0 {
128+
// Default to first workflow in role
128129
if len(request.Role.Workflows) == 0 {
129130
return fmt.Errorf("no workflow specified and role has no associated workflows")
130131
}
131132

132133
request.Workflow = request.Role.Workflows[0]
133134
}
134135

136+
if len(request.Authenticator) == 0 {
137+
138+
// First try to see if we have an authenticator matching the provider
139+
foundProvider, localSession, err := sessionManager.GetFirstActiveSession(
140+
cfg.GetLoginServerHostname(),
141+
request.Role.Authenticators...)
142+
143+
// If we have a valid session for one of the role's authenticators then use it
144+
if err == nil && localSession != nil {
145+
request.Authenticator = foundProvider
146+
request.Session = localSession
147+
}
148+
}
149+
135150
if err := ensureValidSession(request); err != nil {
136151
return err
137152
}
@@ -164,13 +179,11 @@ func validateElevationRequest(request *models.ElevateRequest) error {
164179
}
165180

166181
func ensureValidSession(request *models.ElevateRequest) error {
167-
session, err := sessionManager.GetSession(
168-
cfg.GetLoginServerHostname(), request.Authenticator)
169-
request.Session = session
170182

171-
if err != nil || isSessionExpired(session) {
183+
if request.Session == nil || isSessionExpired(request.Session) {
172184
return authenticateUser(request)
173185
}
186+
174187
return nil
175188
}
176189

@@ -226,12 +239,13 @@ func authenticateUser(request *models.ElevateRequest) error {
226239
sessionHandler := sessionManager.AwaitRefresh(
227240
cfg.GetLoginServerHostname())
228241

229-
session, err := sessionHandler.GetFirstActiveSession()
242+
foundProvider, session, err := sessionHandler.GetFirstActiveSession()
230243

231244
if err != nil {
232245
return fmt.Errorf("failed to get session: %w", err)
233246
}
234247

248+
request.Authenticator = foundProvider
235249
request.Session = session
236250

237251
}
@@ -354,14 +368,23 @@ func logRedirectWorkflow() resty.RedirectPolicy {
354368
return fmt.Errorf("please complete the authentication request in your browser")
355369

356370
}
371+
372+
urlQuery := req.URL.Query()
373+
357374
// Parse the URL to get the next task name
358-
nextTaskName := req.URL.Query().Get("task")
375+
nextTaskName := urlQuery.Get("taskName")
359376

360377
if len(nextTaskName) == 0 {
361-
nextTaskName = "unknown"
378+
nextTaskName = "initializing"
379+
}
380+
381+
taskStatus := urlQuery.Get("taskStatus")
382+
383+
if len(taskStatus) == 0 {
384+
taskStatus = "running"
362385
}
363386

364-
fmt.Printf("redirecting .. %s\n", nextTaskName)
387+
fmt.Printf("redirecting .. %s (%s)\n", nextTaskName, taskStatus)
365388

366389
return nil
367390
})

0 commit comments

Comments
 (0)