mirror of
https://gitea.com/gitea/tea.git
synced 2026-09-10 07:26:33 -04:00
fix(login): configure SSH host explicitly
Add a --ssh-host option to tea login add and pass it through token and OAuth login creation. Expose the same SSH host setting in the interactive login optional settings so users can configure host or host:port when the SSH endpoint differs from the web URL. Update the generated CLI docs and integration helper for the new login creation parameter. Fixes #1032 Signed-off-by: GyeongHo Kim <gyeongho.dev@proton.me>
This commit is contained in:
parent
511884b2af
commit
10a86a301e
|
|
@ -78,6 +78,10 @@ token. Equivalent to running 'tea login helper setup' afterwards.`,
|
|||
Aliases: []string{"s"},
|
||||
Usage: "Path to a SSH key/certificate to use, overrides auto-discovery",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "ssh-host",
|
||||
Usage: "SSH host, optionally with port, e.g. git.example.com or git.example.com:2222",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "insecure",
|
||||
Aliases: []string{"i"},
|
||||
|
|
@ -129,6 +133,7 @@ func runLoginAdd(requestCtx context.Context, cmd *cli.Command) error {
|
|||
opts := auth.OAuthOptions{
|
||||
Name: cmd.String("name"),
|
||||
URL: cmd.String("url"),
|
||||
SSHHost: cmd.String("ssh-host"),
|
||||
Insecure: cmd.Bool("insecure"),
|
||||
}
|
||||
|
||||
|
|
@ -161,6 +166,7 @@ func runLoginAdd(requestCtx context.Context, cmd *cli.Command) error {
|
|||
cmd.String("scopes"),
|
||||
cmd.String("ssh-key"),
|
||||
cmd.String("url"),
|
||||
cmd.String("ssh-host"),
|
||||
cmd.String("ssh-agent-principal"),
|
||||
cmd.String("ssh-agent-key"),
|
||||
cmd.Bool("insecure"),
|
||||
|
|
|
|||
|
|
@ -63,6 +63,8 @@ Add a Gitea login
|
|||
|
||||
**--ssh-agent-principal, -c**="": Use SSH certificate with specified principal to login (needs a running ssh-agent with certificate loaded)
|
||||
|
||||
**--ssh-host**="": SSH host, optionally with port, e.g. git.example.com or git.example.com:2222
|
||||
|
||||
**--ssh-key, -s**="": Path to a SSH key/certificate to use, overrides auto-discovery
|
||||
|
||||
**--token, -t**="": Access token. Can be obtained from Settings > Applications
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ const (
|
|||
type OAuthOptions struct {
|
||||
Name string
|
||||
URL string
|
||||
SSHHost string
|
||||
Insecure bool
|
||||
ClientID string
|
||||
RedirectURL string
|
||||
|
|
@ -72,12 +73,16 @@ func OAuthLoginWithOptions(ctx context.Context, name, giteaURL string, insecure
|
|||
|
||||
// OAuthLoginWithFullOptions performs an OAuth2 PKCE login flow with full options control
|
||||
func OAuthLoginWithFullOptions(ctx context.Context, opts OAuthOptions) error {
|
||||
if _, err := utils.NormalizeSSHHost(opts.SSHHost); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
serverURL, token, err := performBrowserOAuthFlow(ctx, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return createLoginFromToken(ctx, opts.Name, serverURL, token, opts.Insecure)
|
||||
return createLoginFromToken(ctx, opts.Name, serverURL, token, opts.SSHHost, opts.Insecure)
|
||||
}
|
||||
|
||||
// performBrowserOAuthFlow performs the browser-based OAuth2 PKCE flow and returns the token.
|
||||
|
|
@ -370,7 +375,7 @@ var openBrowser = func(url string) error {
|
|||
}
|
||||
|
||||
// createLoginFromToken creates a login entry using the obtained access token
|
||||
func createLoginFromToken(ctx context.Context, name, serverURL string, token *oauth2.Token, insecure bool) error {
|
||||
func createLoginFromToken(ctx context.Context, name, serverURL string, token *oauth2.Token, sshHost string, insecure bool) error {
|
||||
if name == "" {
|
||||
var err error
|
||||
name, err = task.GenerateLoginName(serverURL, "")
|
||||
|
|
@ -408,7 +413,10 @@ func createLoginFromToken(ctx context.Context, name, serverURL string, token *oa
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
login.SSHHost = parsedURL.Host
|
||||
login.SSHHost, err = utils.ResolveSSHHost(parsedURL, sshHost)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Save tokens and add login to config
|
||||
if err := config.AddOAuthLogin(&login, token.AccessToken, token.RefreshToken, token.Expiry); err != nil {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import (
|
|||
"gitea.dev/tea/modules/config"
|
||||
"gitea.dev/tea/modules/task"
|
||||
"gitea.dev/tea/modules/theme"
|
||||
"gitea.dev/tea/modules/utils"
|
||||
|
||||
"charm.land/huh/v2"
|
||||
)
|
||||
|
|
@ -25,8 +26,8 @@ import (
|
|||
// CreateLogin create an login interactive
|
||||
func CreateLogin(ctx context.Context) error {
|
||||
var (
|
||||
name, token, user, passwd, otp, scopes, sshKey, sshCertPrincipal, sshKeyFingerprint string
|
||||
insecure, sshAgent, versionCheck, helper bool
|
||||
name, token, user, passwd, otp, scopes, sshKey, sshHost, sshCertPrincipal, sshKeyFingerprint string
|
||||
insecure, sshAgent, versionCheck, helper bool
|
||||
)
|
||||
|
||||
versionCheck = true
|
||||
|
|
@ -105,7 +106,16 @@ func CreateLogin(ctx context.Context) error {
|
|||
}
|
||||
printTitleAndContent("Allow Insecure connections:", strconv.FormatBool(insecure))
|
||||
|
||||
return auth.OAuthLoginWithOptions(ctx, name, giteaURL, insecure)
|
||||
if err := promptOAuthOptionalSettings(giteaURL, &sshHost); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return auth.OAuthLoginWithFullOptions(ctx, auth.OAuthOptions{
|
||||
Name: name,
|
||||
URL: giteaURL,
|
||||
SSHHost: sshHost,
|
||||
Insecure: insecure,
|
||||
})
|
||||
default: // token
|
||||
var hasToken bool
|
||||
if err := huh.NewConfirm().
|
||||
|
|
@ -243,6 +253,10 @@ func CreateLogin(ctx context.Context) error {
|
|||
}
|
||||
printTitleAndContent("SSH Key Path (leave empty for auto-discovery):", sshKey)
|
||||
|
||||
if err := promptSSHHost(giteaURL, &sshHost); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := huh.NewConfirm().
|
||||
Title("Allow Insecure connections:").
|
||||
Value(&insecure).
|
||||
|
|
@ -271,7 +285,49 @@ func CreateLogin(ctx context.Context) error {
|
|||
printTitleAndContent("Check version of Gitea instance:", strconv.FormatBool(versionCheck))
|
||||
}
|
||||
|
||||
return task.CreateLogin(ctx, name, token, user, passwd, otp, scopes, sshKey, giteaURL, sshCertPrincipal, sshKeyFingerprint, insecure, sshAgent, versionCheck, helper)
|
||||
return task.CreateLogin(ctx, name, token, user, passwd, otp, scopes, sshKey, giteaURL, sshHost, sshCertPrincipal, sshKeyFingerprint, insecure, sshAgent, versionCheck, helper)
|
||||
}
|
||||
|
||||
func promptOAuthOptionalSettings(giteaURL string, sshHost *string) error {
|
||||
var optSettings bool
|
||||
if err := huh.NewConfirm().
|
||||
Title("Set Optional settings:").
|
||||
Value(&optSettings).
|
||||
WithTheme(theme.GetTheme()).
|
||||
Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
printTitleAndContent("Set Optional settings:", strconv.FormatBool(optSettings))
|
||||
|
||||
if !optSettings {
|
||||
return nil
|
||||
}
|
||||
|
||||
return promptSSHHost(giteaURL, sshHost)
|
||||
}
|
||||
|
||||
func promptSSHHost(giteaURL string, sshHost *string) error {
|
||||
if *sshHost == "" {
|
||||
serverURL, err := utils.NormalizeURL(giteaURL)
|
||||
if err == nil {
|
||||
*sshHost = serverURL.Hostname()
|
||||
}
|
||||
}
|
||||
|
||||
if err := huh.NewInput().
|
||||
Title("SSH Host (host or host:port, leave empty to use Gitea URL hostname):").
|
||||
Value(sshHost).
|
||||
Validate(func(s string) error {
|
||||
_, err := utils.NormalizeSSHHost(s)
|
||||
return err
|
||||
}).
|
||||
WithTheme(theme.GetTheme()).
|
||||
Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
printTitleAndContent("SSH Host (host or host:port, leave empty to use Gitea URL hostname):", *sshHost)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var tokenScopeOpts = []string{
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ func SetupHelper(login config.Login) (ok bool, err error) {
|
|||
}
|
||||
|
||||
// CreateLogin create a login to be stored in config
|
||||
func CreateLogin(ctx stdctx.Context, name, token, user, passwd, otp, scopes, sshKey, giteaURL, sshCertPrincipal, sshKeyFingerprint string, insecure, sshAgent, versionCheck, addHelper bool) error {
|
||||
func CreateLogin(ctx stdctx.Context, name, token, user, passwd, otp, scopes, sshKey, giteaURL, sshHost, sshCertPrincipal, sshKeyFingerprint string, insecure, sshAgent, versionCheck, addHelper bool) error {
|
||||
// checks ...
|
||||
// ... if we have a url
|
||||
if len(giteaURL) == 0 {
|
||||
|
|
@ -86,6 +86,11 @@ func CreateLogin(ctx stdctx.Context, name, token, user, passwd, otp, scopes, ssh
|
|||
return err
|
||||
}
|
||||
|
||||
resolvedSSHHost, err := utils.ResolveSSHHost(serverURL, sshHost)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// check if it's a certificate the principal doesn't matter as the user
|
||||
// has explicitly selected this private key
|
||||
if _, err := os.Stat(sshKey + "-cert.pub"); err == nil {
|
||||
|
|
@ -127,8 +132,8 @@ func CreateLogin(ctx stdctx.Context, name, token, user, passwd, otp, scopes, ssh
|
|||
}
|
||||
|
||||
// we do not have a method to get SSH config from api,
|
||||
// so we just use the host
|
||||
login.SSHHost = serverURL.Host
|
||||
// so we use the URL hostname unless explicitly configured
|
||||
login.SSHHost = resolvedSSHHost
|
||||
|
||||
if len(sshKey) == 0 {
|
||||
login.SSHKey, err = findSSHKey(ctx, client)
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ func createIntegrationLogin(t *testing.T) *config.Login {
|
|||
|
||||
require.NotEmpty(t, integrationToken, "integration token setup failed")
|
||||
|
||||
require.NoError(t, task.CreateLogin(t.Context(), "integration", integrationToken, "", "", "", "", "", integrationGiteaURL, "", "", true, false, false, false))
|
||||
require.NoError(t, task.CreateLogin(t.Context(), "integration", integrationToken, "", "", "", "", "", integrationGiteaURL, "", "", "", true, false, false, false))
|
||||
|
||||
login, err := config.GetLoginByName("integration")
|
||||
require.NoError(t, err)
|
||||
|
|
|
|||
Loading…
Reference in a new issue