AuthorizedKeysCommand should not query db directly (#9371)

* AuthorizedKeysCommand should not query db directly

* Update routers/private/internal.go

* Fix import order
tokarchuk/v1.17
zeripath 5 years ago committed by techknowlogick
parent 1707f59966
commit d1a49977b0
  1. 10
      cmd/keys.go
  2. 1
      docs/content/doc/usage/command-line.en-us.md
  3. 81
      integrations/cmd_keys_test.go
  4. 25
      modules/private/key.go
  5. 1
      routers/private/internal.go
  6. 25
      routers/private/key.go

@ -9,7 +9,7 @@ import (
"fmt" "fmt"
"strings" "strings"
"code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/private"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
@ -62,14 +62,12 @@ func runKeys(c *cli.Context) error {
return errors.New("No key type and content provided") return errors.New("No key type and content provided")
} }
if err := initDBDisableConsole(true); err != nil { setup("keys.log")
return err
}
publicKey, err := models.SearchPublicKeyByContent(content) authorizedString, err := private.AuthorizedPublicKeyByContent(content)
if err != nil { if err != nil {
return err return err
} }
fmt.Println(publicKey.AuthorizedString()) fmt.Println(strings.TrimSpace(authorizedString))
return nil return nil
} }

@ -281,6 +281,7 @@ provided key. You should also set the value
NB: opensshd requires the gitea program to be owned by root and not NB: opensshd requires the gitea program to be owned by root and not
writable by group or others. The program must be specified by an absolute writable by group or others. The program must be specified by an absolute
path. path.
NB: Gitea must be running for this command to succeed.
#### migrate #### migrate
Migrates the database. This command can be used to run other commands before starting the server for the first time. Migrates the database. This command can be used to run other commands before starting the server for the first time.

@ -8,6 +8,7 @@ import (
"bytes" "bytes"
"flag" "flag"
"io" "io"
"net/url"
"os" "os"
"testing" "testing"
@ -18,45 +19,45 @@ import (
) )
func Test_CmdKeys(t *testing.T) { func Test_CmdKeys(t *testing.T) {
defer prepareTestEnv(t)() onGiteaRun(t, func(*testing.T, *url.URL) {
tests := []struct {
name string
args []string
wantErr bool
expectedOutput string
}{
{"test_empty_1", []string{"keys", "--username=git", "--type=test", "--content=test"}, true, ""},
{"test_empty_2", []string{"keys", "-e", "git", "-u", "git", "-t", "test", "-k", "test"}, true, ""},
{"with_key",
[]string{"keys", "-e", "git", "-u", "git", "-t", "ssh-rsa", "-k", "AAAAB3NzaC1yc2EAAAADAQABAAABgQDWVj0fQ5N8wNc0LVNA41wDLYJ89ZIbejrPfg/avyj3u/ZohAKsQclxG4Ju0VirduBFF9EOiuxoiFBRr3xRpqzpsZtnMPkWVWb+akZwBFAx8p+jKdy4QXR/SZqbVobrGwip2UjSrri1CtBxpJikojRIZfCnDaMOyd9Jp6KkujvniFzUWdLmCPxUE9zhTaPu0JsEP7MW0m6yx7ZUhHyfss+NtqmFTaDO+QlMR7L2QkDliN2Jl3Xa3PhuWnKJfWhdAq1Cw4oraKUOmIgXLkuiuxVQ6mD3AiFupkmfqdHq6h+uHHmyQqv3gU+/sD8GbGAhf6ftqhTsXjnv1Aj4R8NoDf9BS6KRkzkeun5UisSzgtfQzjOMEiJtmrep2ZQrMGahrXa+q4VKr0aKJfm+KlLfwm/JztfsBcqQWNcTURiCFqz+fgZw0Ey/de0eyMzldYTdXXNRYCKjs9bvBK+6SSXRM7AhftfQ0ZuoW5+gtinPrnmoOaSCEJbAiEiTO/BzOHgowiM="},
false,
"# gitea public key\ncommand=\"" + setting.AppPath + " --config='" + setting.CustomConf + "' serv key-1\",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDWVj0fQ5N8wNc0LVNA41wDLYJ89ZIbejrPfg/avyj3u/ZohAKsQclxG4Ju0VirduBFF9EOiuxoiFBRr3xRpqzpsZtnMPkWVWb+akZwBFAx8p+jKdy4QXR/SZqbVobrGwip2UjSrri1CtBxpJikojRIZfCnDaMOyd9Jp6KkujvniFzUWdLmCPxUE9zhTaPu0JsEP7MW0m6yx7ZUhHyfss+NtqmFTaDO+QlMR7L2QkDliN2Jl3Xa3PhuWnKJfWhdAq1Cw4oraKUOmIgXLkuiuxVQ6mD3AiFupkmfqdHq6h+uHHmyQqv3gU+/sD8GbGAhf6ftqhTsXjnv1Aj4R8NoDf9BS6KRkzkeun5UisSzgtfQzjOMEiJtmrep2ZQrMGahrXa+q4VKr0aKJfm+KlLfwm/JztfsBcqQWNcTURiCFqz+fgZw0Ey/de0eyMzldYTdXXNRYCKjs9bvBK+6SSXRM7AhftfQ0ZuoW5+gtinPrnmoOaSCEJbAiEiTO/BzOHgowiM= user2@localhost\n",
},
{"invalid", []string{"keys", "--not-a-flag=git"}, true, "Incorrect Usage: flag provided but not defined: -not-a-flag\n\n"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
realStdout := os.Stdout //Backup Stdout
r, w, _ := os.Pipe()
os.Stdout = w
tests := []struct { set := flag.NewFlagSet("keys", 0)
name string _ = set.Parse(tt.args)
args []string context := cli.NewContext(&cli.App{Writer: os.Stdout}, set, nil)
wantErr bool err := cmd.CmdKeys.Run(context)
expectedOutput string if (err != nil) != tt.wantErr {
}{ t.Errorf("CmdKeys.Run() error = %v, wantErr %v", err, tt.wantErr)
{"test_empty_1", []string{"keys", "--username=git", "--type=test", "--content=test"}, true, ""}, }
{"test_empty_2", []string{"keys", "-e", "git", "-u", "git", "-t", "test", "-k", "test"}, true, ""}, w.Close()
{"with_key", var buf bytes.Buffer
[]string{"keys", "-e", "git", "-u", "git", "-t", "ssh-rsa", "-k", "AAAAB3NzaC1yc2EAAAADAQABAAABgQDWVj0fQ5N8wNc0LVNA41wDLYJ89ZIbejrPfg/avyj3u/ZohAKsQclxG4Ju0VirduBFF9EOiuxoiFBRr3xRpqzpsZtnMPkWVWb+akZwBFAx8p+jKdy4QXR/SZqbVobrGwip2UjSrri1CtBxpJikojRIZfCnDaMOyd9Jp6KkujvniFzUWdLmCPxUE9zhTaPu0JsEP7MW0m6yx7ZUhHyfss+NtqmFTaDO+QlMR7L2QkDliN2Jl3Xa3PhuWnKJfWhdAq1Cw4oraKUOmIgXLkuiuxVQ6mD3AiFupkmfqdHq6h+uHHmyQqv3gU+/sD8GbGAhf6ftqhTsXjnv1Aj4R8NoDf9BS6KRkzkeun5UisSzgtfQzjOMEiJtmrep2ZQrMGahrXa+q4VKr0aKJfm+KlLfwm/JztfsBcqQWNcTURiCFqz+fgZw0Ey/de0eyMzldYTdXXNRYCKjs9bvBK+6SSXRM7AhftfQ0ZuoW5+gtinPrnmoOaSCEJbAiEiTO/BzOHgowiM="}, io.Copy(&buf, r)
false, commandOutput := buf.String()
"# gitea public key\ncommand=\"" + setting.AppPath + " --config='" + setting.CustomConf + "' serv key-1\",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDWVj0fQ5N8wNc0LVNA41wDLYJ89ZIbejrPfg/avyj3u/ZohAKsQclxG4Ju0VirduBFF9EOiuxoiFBRr3xRpqzpsZtnMPkWVWb+akZwBFAx8p+jKdy4QXR/SZqbVobrGwip2UjSrri1CtBxpJikojRIZfCnDaMOyd9Jp6KkujvniFzUWdLmCPxUE9zhTaPu0JsEP7MW0m6yx7ZUhHyfss+NtqmFTaDO+QlMR7L2QkDliN2Jl3Xa3PhuWnKJfWhdAq1Cw4oraKUOmIgXLkuiuxVQ6mD3AiFupkmfqdHq6h+uHHmyQqv3gU+/sD8GbGAhf6ftqhTsXjnv1Aj4R8NoDf9BS6KRkzkeun5UisSzgtfQzjOMEiJtmrep2ZQrMGahrXa+q4VKr0aKJfm+KlLfwm/JztfsBcqQWNcTURiCFqz+fgZw0Ey/de0eyMzldYTdXXNRYCKjs9bvBK+6SSXRM7AhftfQ0ZuoW5+gtinPrnmoOaSCEJbAiEiTO/BzOHgowiM= user2@localhost\n\n", if tt.expectedOutput != commandOutput {
}, t.Errorf("expectedOutput: %#v, commandOutput: %#v", tt.expectedOutput, commandOutput)
{"invalid", []string{"keys", "--not-a-flag=git"}, true, "Incorrect Usage: flag provided but not defined: -not-a-flag\n\n"}, }
} //Restore stdout
for _, tt := range tests { os.Stdout = realStdout
t.Run(tt.name, func(t *testing.T) { })
realStdout := os.Stdout //Backup Stdout }
r, w, _ := os.Pipe() })
os.Stdout = w
set := flag.NewFlagSet("keys", 0)
_ = set.Parse(tt.args)
context := cli.NewContext(&cli.App{Writer: os.Stdout}, set, nil)
err := cmd.CmdKeys.Run(context)
if (err != nil) != tt.wantErr {
t.Errorf("CmdKeys.Run() error = %v, wantErr %v", err, tt.wantErr)
}
w.Close()
var buf bytes.Buffer
io.Copy(&buf, r)
commandOutput := buf.String()
if tt.expectedOutput != commandOutput {
t.Errorf("expectedOutput: %#v, commandOutput: %#v", tt.expectedOutput, commandOutput)
}
//Restore stdout
os.Stdout = realStdout
})
}
} }

@ -6,6 +6,8 @@ package private
import ( import (
"fmt" "fmt"
"io/ioutil"
"net/http"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
) )
@ -27,3 +29,26 @@ func UpdatePublicKeyInRepo(keyID, repoID int64) error {
} }
return nil return nil
} }
// AuthorizedPublicKeyByContent searches content as prefix (leak e-mail part)
// and returns public key found.
func AuthorizedPublicKeyByContent(content string) (string, error) {
// Ask for running deliver hook and test pull request tasks.
reqURL := setting.LocalURL + fmt.Sprintf("api/internal/ssh/authorized_keys")
req := newInternalRequest(reqURL, "POST")
req.Param("content", content)
resp, err := req.Response()
if err != nil {
return "", err
}
defer resp.Body.Close()
// All 2XX status codes are accepted and others will return an error
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("Failed to update public key: %s", decodeJSONError(resp).Err)
}
bs, err := ioutil.ReadAll(resp.Body)
return string(bs), err
}

@ -76,6 +76,7 @@ func CheckUnitUser(ctx *macaron.Context) {
// These APIs will be invoked by internal commands for example `gitea serv` and etc. // These APIs will be invoked by internal commands for example `gitea serv` and etc.
func RegisterRoutes(m *macaron.Macaron) { func RegisterRoutes(m *macaron.Macaron) {
m.Group("/", func() { m.Group("/", func() {
m.Post("/ssh/authorized_keys", AuthorizedPublicKeyByContent)
m.Post("/ssh/:id/update/:repoid", UpdatePublicKeyInRepo) m.Post("/ssh/:id/update/:repoid", UpdatePublicKeyInRepo)
m.Get("/hook/pre-receive/:owner/:repo", HookPreReceive) m.Get("/hook/pre-receive/:owner/:repo", HookPreReceive)
m.Get("/hook/post-receive/:owner/:repo", HookPostReceive) m.Get("/hook/post-receive/:owner/:repo", HookPostReceive)

@ -6,6 +6,8 @@
package private package private
import ( import (
"net/http"
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/timeutil"
@ -17,7 +19,7 @@ func UpdatePublicKeyInRepo(ctx *macaron.Context) {
keyID := ctx.ParamsInt64(":id") keyID := ctx.ParamsInt64(":id")
repoID := ctx.ParamsInt64(":repoid") repoID := ctx.ParamsInt64(":repoid")
if err := models.UpdatePublicKeyUpdated(keyID); err != nil { if err := models.UpdatePublicKeyUpdated(keyID); err != nil {
ctx.JSON(500, map[string]interface{}{ ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
"err": err.Error(), "err": err.Error(),
}) })
return return
@ -29,18 +31,33 @@ func UpdatePublicKeyInRepo(ctx *macaron.Context) {
ctx.PlainText(200, []byte("success")) ctx.PlainText(200, []byte("success"))
return return
} }
ctx.JSON(500, map[string]interface{}{ ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
"err": err.Error(), "err": err.Error(),
}) })
return return
} }
deployKey.UpdatedUnix = timeutil.TimeStampNow() deployKey.UpdatedUnix = timeutil.TimeStampNow()
if err = models.UpdateDeployKeyCols(deployKey, "updated_unix"); err != nil { if err = models.UpdateDeployKeyCols(deployKey, "updated_unix"); err != nil {
ctx.JSON(500, map[string]interface{}{ ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
"err": err.Error(), "err": err.Error(),
}) })
return return
} }
ctx.PlainText(200, []byte("success")) ctx.PlainText(http.StatusOK, []byte("success"))
}
// AuthorizedPublicKeyByContent searches content as prefix (leak e-mail part)
// and returns public key found.
func AuthorizedPublicKeyByContent(ctx *macaron.Context) {
content := ctx.Query("content")
publicKey, err := models.SearchPublicKeyByContent(content)
if err != nil {
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
"err": err.Error(),
})
return
}
ctx.PlainText(http.StatusOK, []byte(publicKey.AuthorizedString()))
} }

Loading…
Cancel
Save