From c8c8e9758bc176f3d6cc502377574e594c77597c Mon Sep 17 00:00:00 2001 From: John Olheiser Date: Wed, 14 Feb 2024 16:49:29 +0000 Subject: [PATCH] Add OTP and scopes to login (#546) Resolves #542 Co-authored-by: 6543 <6543@obermui.de> Reviewed-on: https://gitea.com/gitea/tea/pulls/546 Reviewed-by: 6543 <6543@obermui.de> Co-authored-by: John Olheiser Co-committed-by: John Olheiser --- cmd/login/add.go | 12 ++++++++ docs/CLI.md | 4 +++ go.sum | 18 ++---------- modules/interact/login.go | 55 ++++++++++++++++++++++++++++++++++-- modules/task/login_create.go | 23 +++++++++++---- 5 files changed, 89 insertions(+), 23 deletions(-) diff --git a/cmd/login/add.go b/cmd/login/add.go index 3f91ed7..0677b67 100644 --- a/cmd/login/add.go +++ b/cmd/login/add.go @@ -54,6 +54,16 @@ var CmdLoginAdd = cli.Command{ EnvVars: []string{"GITEA_SERVER_PASSWORD"}, Usage: "Password for basic auth (will create token)", }, + &cli.StringFlag{ + Name: "otp", + EnvVars: []string{"GITEA_SERVER_OTP"}, + Usage: "OTP token for auth, if necessary", + }, + &cli.StringFlag{ + Name: "scopes", + EnvVars: []string{"GITEA_SCOPES"}, + Usage: "Token scopes to add when creating a new token, separated by a comma", + }, &cli.StringFlag{ Name: "ssh-key", Aliases: []string{"s"}, @@ -95,6 +105,8 @@ func runLoginAdd(ctx *cli.Context) error { ctx.String("token"), ctx.String("user"), ctx.String("password"), + ctx.String("otp"), + ctx.String("scopes"), ctx.String("ssh-key"), ctx.String("url"), ctx.String("ssh-agent-principal"), diff --git a/docs/CLI.md b/docs/CLI.md index 6224a92..aa3da52 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -57,8 +57,12 @@ Add a Gitea login **--no-version-check, --nv**: Do not check version of Gitea instance +**--otp**="": OTP token for auth, if necessary + **--password, --pwd**="": Password for basic auth (will create token) +**--scopes**="": Token scopes to add when creating a new token, separated by a comma + **--ssh-agent-key, -a**="": Use SSH public key or SSH fingerprint to login (needs a running ssh-agent with ssh key loaded) **--ssh-agent-principal, -c**="": Use SSH certificate with specified principal to login (needs a running ssh-agent with certificate loaded) diff --git a/go.sum b/go.sum index 33f94a4..b7c5ed5 100644 --- a/go.sum +++ b/go.sum @@ -17,8 +17,6 @@ github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migc github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w= -github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 h1:KLq8BE0KwCL+mmXnjLWEAOYO+2l2AE4YMmqG1ZpZHBs= -github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg= github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ= @@ -43,7 +41,6 @@ github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEM github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI= github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= @@ -56,7 +53,7 @@ github.com/davidmz/go-pageant v1.0.2/go.mod h1:P2EDDnMqIwG5Rrp05dTRITj9z2zpGcD9e github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo= github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819 h1:RIB4cRk+lBqKK3Oy0r2gRX4ui7tuhiZq2SuTtTCi0/0= +github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/enescakir/emoji v1.0.0 h1:W+HsNql8swfCQFtioDGDHCHri8nudlK1n5p2rHCJoog= @@ -66,13 +63,9 @@ github.com/go-fed/httpsig v1.1.0 h1:9M+hb0jkEICD8/cAiNqEB66R87tTINszBRTjwjQzWcI= github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= -github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4= -github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg= github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f h1:Pz0DHeFij3XFhoBRGUDPzSJ+w2UcK5/0JvF8DRI58r8= -github.com/go-git/go-git/v5 v5.8.1 h1:Zo79E4p7TRk0xoRgMq0RShiTHGKcKI4+DI6BfJc/Q+A= -github.com/go-git/go-git/v5 v5.8.1/go.mod h1:FHFuoD6yGz5OSKEBK+aWN9Oah0q54Jxl0abmj6GnqAo= github.com/go-git/go-git/v5 v5.9.0 h1:cD9SFA7sHVRdJ7AYck1ZaAa/yeuBvGPxwXDL8cxrObY= github.com/go-git/go-git/v5 v5.9.0/go.mod h1:RKIqga24sWdMGZF+1Ekv9kylsDz6LzdTSI2s/OsZWE0= github.com/go-git/go-git/v5 v5.10.0 h1:F0x3xXrAWmhwtzoCokU4IMPcBdncG+HAAqi9FcOOjbQ= @@ -96,12 +89,10 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:C github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= @@ -128,9 +119,9 @@ github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKt github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc= github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -141,7 +132,7 @@ github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8= github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg= @@ -163,8 +154,6 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/urfave/cli/v2 v2.16.3 h1:gHoFIwpPjoyIMbJp/VFd+/vuD0dAgFK4B6DpEMFJfQk= -github.com/urfave/cli/v2 v2.16.3/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xMa7jMwI= github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= @@ -294,7 +283,6 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/modules/interact/login.go b/modules/interact/login.go index a1e54fc..732d9b7 100644 --- a/modules/interact/login.go +++ b/modules/interact/login.go @@ -8,6 +8,7 @@ import ( "regexp" "strings" + "code.gitea.io/sdk/gitea" "code.gitea.io/tea/modules/task" "github.com/AlecAivazis/survey/v2" @@ -16,8 +17,8 @@ import ( // CreateLogin create an login interactive func CreateLogin() error { var ( - name, token, user, passwd, sshKey, giteaURL, sshCertPrincipal, sshKeyFingerprint string - insecure, sshAgent, versionCheck bool + name, token, user, passwd, otp, scopes, sshKey, giteaURL, sshCertPrincipal, sshKeyFingerprint string + insecure, sshAgent, versionCheck bool ) versionCheck = true @@ -73,6 +74,19 @@ func CreateLogin() error { if err = survey.AskOne(promptPW, &passwd, survey.WithValidator(survey.Required)); err != nil { return err } + + var tokenScopes []string + promptS := &survey.MultiSelect{Message: "Token Scopes:", Options: tokenScopeOpts} + if err := survey.AskOne(promptS, &tokenScopes, survey.WithValidator(survey.Required)); err != nil { + return err + } + scopes = strings.Join(tokenScopes, ",") + + // Ask for OTP last so it's less likely to timeout + promptO := &survey.Input{Message: "OTP (if applicable)"} + if err := survey.AskOne(promptO, &otp); err != nil { + return err + } } case "ssh-key/certificate": promptI = &survey.Input{Message: "SSH Key/Certificate Path (leave empty for auto-discovery in ~/.ssh and ssh-agent):"} @@ -141,5 +155,40 @@ func CreateLogin() error { } - return task.CreateLogin(name, token, user, passwd, sshKey, giteaURL, sshCertPrincipal, sshKeyFingerprint, insecure, sshAgent, versionCheck) + return task.CreateLogin(name, token, user, passwd, otp, scopes, sshKey, giteaURL, sshCertPrincipal, sshKeyFingerprint, insecure, sshAgent, versionCheck) +} + +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), } diff --git a/modules/task/login_create.go b/modules/task/login_create.go index 5e63329..527a4d4 100644 --- a/modules/task/login_create.go +++ b/modules/task/login_create.go @@ -6,6 +6,7 @@ package task import ( "fmt" "os" + "strings" "time" "code.gitea.io/tea/modules/config" @@ -15,7 +16,7 @@ import ( ) // CreateLogin create a login to be stored in config -func CreateLogin(name, token, user, passwd, sshKey, giteaURL, sshCertPrincipal, sshKeyFingerprint string, insecure, sshAgent, versionCheck bool) error { +func CreateLogin(name, token, user, passwd, otp, scopes, sshKey, giteaURL, sshCertPrincipal, sshKeyFingerprint string, insecure, sshAgent, versionCheck bool) error { // checks ... // ... if we have a url if len(giteaURL) == 0 { @@ -68,7 +69,7 @@ func CreateLogin(name, token, user, passwd, sshKey, giteaURL, sshCertPrincipal, } if len(token) == 0 && sshCertPrincipal == "" && !sshAgent && sshKey == "" { - if login.Token, err = generateToken(login, user, passwd); err != nil { + if login.Token, err = generateToken(login, user, passwd, otp, scopes); err != nil { return err } } @@ -109,8 +110,12 @@ func CreateLogin(name, token, user, passwd, sshKey, giteaURL, sshCertPrincipal, } // generateToken creates a new token when given BasicAuth credentials -func generateToken(login config.Login, user, pass string) (string, error) { - client := login.Client(gitea.SetBasicAuth(user, pass)) +func generateToken(login config.Login, user, pass, otp, scopes string) (string, error) { + opts := []gitea.ClientOption{gitea.SetBasicAuth(user, pass)} + if otp != "" { + opts = append(opts, gitea.SetOTP(otp)) + } + client := login.Client(opts...) tl, _, err := client.ListAccessTokens(gitea.ListAccessTokensOptions{ ListOptions: gitea.ListOptions{Page: -1}, @@ -129,7 +134,15 @@ func generateToken(login config.Login, user, pass string) (string, error) { } } - t, _, err := client.CreateAccessToken(gitea.CreateAccessTokenOption{Name: tokenName}) + var tokenScopes []gitea.AccessTokenScope + for _, scope := range strings.Split(scopes, ",") { + tokenScopes = append(tokenScopes, gitea.AccessTokenScope(strings.TrimSpace(scope))) + } + + t, _, err := client.CreateAccessToken(gitea.CreateAccessTokenOption{ + Name: tokenName, + Scopes: tokenScopes, + }) return t.Token, err }