diff --git a/cmd/lk/agent_run.go b/cmd/lk/agent_run.go index 03f5c5eb..d9af62f3 100644 --- a/cmd/lk/agent_run.go +++ b/cmd/lk/agent_run.go @@ -212,6 +212,9 @@ func mergeCredentials(explicit, project agentCredentials) agentCredentials { // subprocess running from dir. func resolveCredentials(ctx context.Context, cmd *cli.Command, dir string, loadOpts ...loadOption) ([]string, error) { explicit := explicitCredentials(cmd) + // The agent gets LIVEKIT_* credentials from the environment even under + // --user-auth, so they aren't ignored here. + envCredentialsUsed = true // An explicitly named project always wins, mirroring resolveProject's // priority order. Without this, ambient LIVEKIT_* shell variables (which diff --git a/cmd/lk/testdata/script/env-keys-with-user-auth.txtar b/cmd/lk/testdata/script/env-keys-with-user-auth.txtar index cd04ce1c..27428b1e 100644 --- a/cmd/lk/testdata/script/env-keys-with-user-auth.txtar +++ b/cmd/lk/testdata/script/env-keys-with-user-auth.txtar @@ -1,5 +1,6 @@ # --user-auth wins over API keys in the environment: the command runs -# as the signed-in user, with a one-line note that the env keys are ignored. +# as the signed-in user, with a one-line note that the env credentials are +# ignored. env LIVEKIT_URL=ws://env.test env LIVEKIT_API_KEY=APIenv env LIVEKIT_API_SECRET=secretenv @@ -10,8 +11,7 @@ cp stderr lk.stderr requests cmp stdout want-requests -skip 'known bug: --user-auth ignores LIVEKIT_API_KEY and LIVEKIT_API_SECRET without saying so' -grep 'LIVEKIT_API_KEY.*ignored' lk.stderr +grep '^Ignoring LIVEKIT_URL, LIVEKIT_API_KEY, LIVEKIT_API_SECRET from environment \(--user-auth uses your session\)$' lk.stderr -- home/.livekit/cli-config.yaml -- default_user: u_1 diff --git a/cmd/lk/user_auth.go b/cmd/lk/user_auth.go index c8ea9821..ab5ede08 100644 --- a/cmd/lk/user_auth.go +++ b/cmd/lk/user_auth.go @@ -19,6 +19,7 @@ import ( "context" "errors" "fmt" + "os" "strings" "time" @@ -247,9 +248,16 @@ func requireUserSession(cmd *cli.Command) (*config.CLIConfig, *config.UserConfig // resolves the session more than once still prints the breadcrumb only once. var userAnnounced bool +// envCredentialsUsed is set by commands that hand LIVEKIT_* credentials from +// the environment to what they run (agent runs, see resolveCredentials), so +// announceUser doesn't call them ignored. +var envCredentialsUsed bool + // announceUser surfaces which signed-in user a command acts as — the user-auth // counterpart of the "Using project" breadcrumb (stderr, suppressed by --quiet). -// The API is named when it isn't the default, e.g. for a staging sign-in. +// The API is named when it isn't the default, e.g. for a staging sign-in. It +// also names the LIVEKIT_* credentials in the environment that the command +// ignores, since they don't switch modes (see userAuthEnabled). func announceUser(user *config.UserConfig) { if userAnnounced || user == nil { return @@ -260,6 +268,18 @@ func announceUser(user *config.UserConfig) { } else { out.Statusf("Using user [%s] on [%s]", util.Accented(userLabel(user)), util.Accented(publicAPIURL)) } + if envCredentialsUsed { + return + } + var ignored []string + for _, name := range []string{"LIVEKIT_URL", "LIVEKIT_API_KEY", "LIVEKIT_API_SECRET"} { + if os.Getenv(name) != "" { + ignored = append(ignored, name) + } + } + if len(ignored) > 0 { + out.Statusf("Ignoring %s from environment (--user-auth uses your session)", strings.Join(ignored, ", ")) + } } // resolveSessionUser picks the user to act as: the one named by userRef (the diff --git a/cmd/lk/user_auth_test.go b/cmd/lk/user_auth_test.go index 164e83ef..6ca11477 100644 --- a/cmd/lk/user_auth_test.go +++ b/cmd/lk/user_auth_test.go @@ -263,6 +263,46 @@ func TestCloudAPIError(t *testing.T) { } } +// TestAnnounceUserNamesIgnoredEnvCredentials checks the note announceUser adds +// for LIVEKIT_* credentials in the environment that --user-auth leaves unused, +// and that commands handing them to an agent don't get it. +func TestAnnounceUserNamesIgnoredEnvCredentials(t *testing.T) { + prevAnnounced, prevUsed := userAnnounced, envCredentialsUsed + t.Cleanup(func() { userAnnounced, envCredentialsUsed = prevAnnounced, prevUsed }) + user := &config.UserConfig{Id: "u_1", Email: "dev@example.com"} + + for _, tc := range []struct { + name string + url, key string + secret string + usedByAgent bool + wantIgnoring string + }{ + {name: "all three", url: "ws://env.test", key: "APIenv", secret: "secretenv", + wantIgnoring: "Ignoring LIVEKIT_URL, LIVEKIT_API_KEY, LIVEKIT_API_SECRET from environment (--user-auth uses your session)"}, + {name: "only the key", key: "APIenv", + wantIgnoring: "Ignoring LIVEKIT_API_KEY from environment (--user-auth uses your session)"}, + {name: "none set"}, + {name: "handed to an agent", url: "ws://env.test", key: "APIenv", secret: "secretenv", usedByAgent: true}, + } { + t.Run(tc.name, func(t *testing.T) { + t.Setenv("LIVEKIT_URL", tc.url) + t.Setenv("LIVEKIT_API_KEY", tc.key) + t.Setenv("LIVEKIT_API_SECRET", tc.secret) + userAnnounced, envCredentialsUsed = false, tc.usedByAgent + buf := withCapturedAnnounce(t) + + announceUser(user) + + if tc.wantIgnoring == "" { + assert.NotContains(t, buf.String(), "Ignoring") + } else { + assert.Contains(t, buf.String(), tc.wantIgnoring+"\n") + } + }) + } +} + func TestSetUserAuthHidden(t *testing.T) { root := &cli.Command{Flags: globalFlags} for _, cmds := range [][]*cli.Command{ProjectCommands, WorkspaceCommands, UserCommands, CloudCommands, AgentCommands} {