mirror of
https://gitea.com/gitea/tea.git
synced 2025-09-03 18:38:29 +02:00

Fix #772 Reviewed-on: https://gitea.com/gitea/tea/pulls/786 Reviewed-by: Bo-Yi Wu (吳柏毅) <appleboy.tw@gmail.com>
285 lines
7.9 KiB
Go
285 lines
7.9 KiB
Go
// Copyright 2020 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package interact
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/url"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"code.gitea.io/sdk/gitea"
|
|
"code.gitea.io/tea/modules/auth"
|
|
"code.gitea.io/tea/modules/task"
|
|
"code.gitea.io/tea/modules/theme"
|
|
|
|
"github.com/charmbracelet/huh"
|
|
)
|
|
|
|
// CreateLogin create an login interactive
|
|
func CreateLogin() error {
|
|
var (
|
|
name, token, user, passwd, otp, scopes, sshKey, sshCertPrincipal, sshKeyFingerprint string
|
|
insecure, sshAgent, versionCheck, helper bool
|
|
)
|
|
|
|
versionCheck = true
|
|
helper = false
|
|
|
|
giteaURL := "https://gitea.com"
|
|
if err := huh.NewInput().
|
|
Title("URL of Gitea instance: ").
|
|
Value(&giteaURL).
|
|
Validate(func(s string) error {
|
|
s = strings.TrimSpace(s)
|
|
if len(s) == 0 {
|
|
return fmt.Errorf("URL is required")
|
|
}
|
|
_, err := url.Parse(s)
|
|
if err != nil {
|
|
return fmt.Errorf("Invalid URL: %v", err)
|
|
}
|
|
return nil
|
|
}).
|
|
WithTheme(theme.GetTheme()).
|
|
Run(); err != nil {
|
|
return err
|
|
}
|
|
printTitleAndContent("URL of Gitea instance: ", giteaURL)
|
|
|
|
giteaURL = strings.TrimSuffix(strings.TrimSpace(giteaURL), "/")
|
|
|
|
name, err := task.GenerateLoginName(giteaURL, "")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := huh.NewInput().
|
|
Title("Name of new Login: ").
|
|
Value(&name).
|
|
Validate(huh.ValidateNotEmpty()).
|
|
WithTheme(theme.GetTheme()).
|
|
Run(); err != nil {
|
|
return err
|
|
}
|
|
printTitleAndContent("Name of new Login: ", name)
|
|
|
|
loginMethod, err := promptSelectV2("Login with: ", []string{"token", "ssh-key/certificate", "oauth"})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
printTitleAndContent("Login with: ", loginMethod)
|
|
|
|
switch loginMethod {
|
|
case "oauth":
|
|
if err := huh.NewConfirm().
|
|
Title("Allow Insecure connections:").
|
|
Value(&insecure).
|
|
WithTheme(theme.GetTheme()).
|
|
Run(); err != nil {
|
|
return err
|
|
}
|
|
printTitleAndContent("Allow Insecure connections:", strconv.FormatBool(insecure))
|
|
|
|
return auth.OAuthLoginWithOptions(name, giteaURL, insecure)
|
|
default: // token
|
|
var hasToken bool
|
|
if err := huh.NewConfirm().
|
|
Title("Do you have an access token?").
|
|
Value(&hasToken).
|
|
WithTheme(theme.GetTheme()).
|
|
Run(); err != nil {
|
|
return err
|
|
}
|
|
printTitleAndContent("Do you have an access token?", strconv.FormatBool(hasToken))
|
|
|
|
if hasToken {
|
|
if err := huh.NewInput().
|
|
Title("Token:").
|
|
Value(&token).
|
|
Validate(huh.ValidateNotEmpty()).
|
|
WithTheme(theme.GetTheme()).
|
|
Run(); err != nil {
|
|
return err
|
|
}
|
|
printTitleAndContent("Token:", token)
|
|
} else {
|
|
if err := huh.NewInput().
|
|
Title("Username:").
|
|
Value(&user).
|
|
Validate(huh.ValidateNotEmpty()).
|
|
WithTheme(theme.GetTheme()).
|
|
Run(); err != nil {
|
|
return err
|
|
}
|
|
printTitleAndContent("Username:", user)
|
|
|
|
if err := huh.NewInput().
|
|
Title("Password:").
|
|
Value(&passwd).
|
|
Validate(huh.ValidateNotEmpty()).
|
|
EchoMode(huh.EchoModePassword).
|
|
WithTheme(theme.GetTheme()).
|
|
Run(); err != nil {
|
|
return err
|
|
}
|
|
printTitleAndContent("Password:", "********")
|
|
|
|
var tokenScopes []string
|
|
if err := huh.NewMultiSelect[string]().
|
|
Title("Token Scopes:").
|
|
Options(huh.NewOptions(tokenScopeOpts...)...).
|
|
Value(&tokenScopes).
|
|
Validate(func(s []string) error {
|
|
if len(s) == 0 {
|
|
return errors.New("At least one scope is required")
|
|
}
|
|
return nil
|
|
}).
|
|
WithTheme(theme.GetTheme()).
|
|
Run(); err != nil {
|
|
return err
|
|
}
|
|
printTitleAndContent("Token Scopes:", strings.Join(tokenScopes, "\n"))
|
|
|
|
scopes = strings.Join(tokenScopes, ",")
|
|
|
|
// Ask for OTP last so it's less likely to timeout
|
|
if err := huh.NewInput().
|
|
Title("OTP (if applicable):").
|
|
Value(&otp).
|
|
WithTheme(theme.GetTheme()).
|
|
Run(); err != nil {
|
|
return err
|
|
}
|
|
printTitleAndContent("OTP (if applicable):", otp)
|
|
}
|
|
case "ssh-key/certificate":
|
|
if err := huh.NewInput().
|
|
Title("SSH Key/Certificate Path (leave empty for auto-discovery in ~/.ssh and ssh-agent):").
|
|
Value(&sshKey).
|
|
WithTheme(theme.GetTheme()).
|
|
Run(); err != nil {
|
|
return err
|
|
}
|
|
printTitleAndContent("SSH Key/Certificate Path (leave empty for auto-discovery in ~/.ssh and ssh-agent):", sshKey)
|
|
|
|
if sshKey == "" {
|
|
sshKey, err = promptSelect("Select ssh-key: ", task.ListSSHPubkey(), "", "", "")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
printTitleAndContent("Selected ssh-key:", sshKey)
|
|
|
|
// ssh certificate
|
|
if strings.Contains(sshKey, "principals") {
|
|
sshCertPrincipal = regexp.MustCompile(`.*?principals: (.*?)[,|\s]`).FindStringSubmatch(sshKey)[1]
|
|
if strings.Contains(sshKey, "(ssh-agent)") {
|
|
sshAgent = true
|
|
sshKey = ""
|
|
} else {
|
|
sshKey = regexp.MustCompile(`\((.*?)\)$`).FindStringSubmatch(sshKey)[1]
|
|
sshKey = strings.TrimSuffix(sshKey, "-cert.pub")
|
|
}
|
|
} else {
|
|
sshKeyFingerprint = regexp.MustCompile(`(SHA256:.*?)\s`).FindStringSubmatch(sshKey)[1]
|
|
if strings.Contains(sshKey, "(ssh-agent)") {
|
|
sshAgent = true
|
|
sshKey = ""
|
|
} else {
|
|
sshKey = regexp.MustCompile(`\((.*?)\)$`).FindStringSubmatch(sshKey)[1]
|
|
sshKey = strings.TrimSuffix(sshKey, ".pub")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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 {
|
|
if err := huh.NewInput().
|
|
Title("SSH Key Path (leave empty for auto-discovery):").
|
|
Value(&sshKey).
|
|
WithTheme(theme.GetTheme()).
|
|
Run(); err != nil {
|
|
return err
|
|
}
|
|
printTitleAndContent("SSH Key Path (leave empty for auto-discovery):", sshKey)
|
|
|
|
if err := huh.NewConfirm().
|
|
Title("Allow Insecure connections:").
|
|
Value(&insecure).
|
|
WithTheme(theme.GetTheme()).
|
|
Run(); err != nil {
|
|
return err
|
|
}
|
|
printTitleAndContent("Allow Insecure connections:", strconv.FormatBool(insecure))
|
|
|
|
if err := huh.NewConfirm().
|
|
Title("Add git helper:").
|
|
Value(&helper).
|
|
WithTheme(theme.GetTheme()).
|
|
Run(); err != nil {
|
|
return err
|
|
}
|
|
printTitleAndContent("Add git helper:", strconv.FormatBool(helper))
|
|
|
|
if err := huh.NewConfirm().
|
|
Title("Check version of Gitea instance:").
|
|
Value(&versionCheck).
|
|
WithTheme(theme.GetTheme()).
|
|
Run(); err != nil {
|
|
return err
|
|
}
|
|
printTitleAndContent("Check version of Gitea instance:", strconv.FormatBool(versionCheck))
|
|
}
|
|
|
|
return task.CreateLogin(name, token, user, passwd, otp, scopes, sshKey, giteaURL, sshCertPrincipal, sshKeyFingerprint, insecure, sshAgent, versionCheck, helper)
|
|
}
|
|
|
|
var tokenScopeOpts = []string{
|
|
string(gitea.AccessTokenScopeAll),
|
|
string(gitea.AccessTokenScopeRepo),
|
|
string(gitea.AccessTokenScopeRepoStatus),
|
|
string(gitea.AccessTokenScopePublicRepo),
|
|
string(gitea.AccessTokenScopeAdminOrg),
|
|
string(gitea.AccessTokenScopeWriteOrg),
|
|
string(gitea.AccessTokenScopeReadOrg),
|
|
string(gitea.AccessTokenScopeAdminPublicKey),
|
|
string(gitea.AccessTokenScopeWritePublicKey),
|
|
string(gitea.AccessTokenScopeReadPublicKey),
|
|
string(gitea.AccessTokenScopeAdminRepoHook),
|
|
string(gitea.AccessTokenScopeWriteRepoHook),
|
|
string(gitea.AccessTokenScopeReadRepoHook),
|
|
string(gitea.AccessTokenScopeAdminOrgHook),
|
|
string(gitea.AccessTokenScopeAdminUserHook),
|
|
string(gitea.AccessTokenScopeNotification),
|
|
string(gitea.AccessTokenScopeUser),
|
|
string(gitea.AccessTokenScopeReadUser),
|
|
string(gitea.AccessTokenScopeUserEmail),
|
|
string(gitea.AccessTokenScopeUserFollow),
|
|
string(gitea.AccessTokenScopeDeleteRepo),
|
|
string(gitea.AccessTokenScopePackage),
|
|
string(gitea.AccessTokenScopeWritePackage),
|
|
string(gitea.AccessTokenScopeReadPackage),
|
|
string(gitea.AccessTokenScopeDeletePackage),
|
|
string(gitea.AccessTokenScopeAdminGPGKey),
|
|
string(gitea.AccessTokenScopeWriteGPGKey),
|
|
string(gitea.AccessTokenScopeReadGPGKey),
|
|
string(gitea.AccessTokenScopeAdminApplication),
|
|
string(gitea.AccessTokenScopeWriteApplication),
|
|
string(gitea.AccessTokenScopeReadApplication),
|
|
string(gitea.AccessTokenScopeSudo),
|
|
}
|