* add internal routes for ssh hook comands * fix lint * add comment on why package named private not internal but the route name is internal * add comment above package private why package named private not internal but the route name is internal * remove exp time on internal access * move routes from /internal to /api/internal * add comment and defer on UpdatePublicKeyUpdatedtags/v1.21.12.1
| @@ -16,6 +16,7 @@ import ( | |||
| "code.gitea.io/gitea/models" | |||
| "code.gitea.io/gitea/modules/log" | |||
| "code.gitea.io/gitea/modules/private" | |||
| "code.gitea.io/gitea/modules/setting" | |||
| "github.com/Unknwon/com" | |||
| @@ -318,7 +319,7 @@ func runServ(c *cli.Context) error { | |||
| // Update user key activity. | |||
| if keyID > 0 { | |||
| if err = models.UpdatePublicKeyUpdated(keyID); err != nil { | |||
| if err = private.UpdatePublicKeyUpdated(keyID); err != nil { | |||
| fail("Internal error", "UpdatePublicKey: %v", err) | |||
| } | |||
| } | |||
| @@ -29,6 +29,7 @@ import ( | |||
| apiv1 "code.gitea.io/gitea/routers/api/v1" | |||
| "code.gitea.io/gitea/routers/dev" | |||
| "code.gitea.io/gitea/routers/org" | |||
| "code.gitea.io/gitea/routers/private" | |||
| "code.gitea.io/gitea/routers/repo" | |||
| "code.gitea.io/gitea/routers/user" | |||
| @@ -661,6 +662,11 @@ func runWeb(ctx *cli.Context) error { | |||
| apiv1.RegisterRoutes(m) | |||
| }, ignSignIn) | |||
| m.Group("/api/internal", func() { | |||
| // package name internal is ideal but Golang is not allowed, so we use private as package name. | |||
| private.RegisterRoutes(m) | |||
| }) | |||
| // robots.txt | |||
| m.Get("/robots.txt", func(ctx *context.Context) { | |||
| if setting.HasRobotsTxt { | |||
| @@ -502,8 +502,10 @@ func UpdatePublicKey(key *PublicKey) error { | |||
| // UpdatePublicKeyUpdated updates public key use time. | |||
| func UpdatePublicKeyUpdated(id int64) error { | |||
| cnt, err := x.ID(id).Cols("updated").Update(&PublicKey{ | |||
| Updated: time.Now(), | |||
| now := time.Now() | |||
| cnt, err := x.ID(id).Cols("updated_unix").Update(&PublicKey{ | |||
| Updated: now, | |||
| UpdatedUnix: now.Unix(), | |||
| }) | |||
| if err != nil { | |||
| return err | |||
| @@ -62,6 +62,11 @@ func newRequest(url, method string) *Request { | |||
| return &Request{url, &req, map[string]string{}, map[string]string{}, defaultSetting, &resp, nil} | |||
| } | |||
| // NewRequest returns *Request with specific method | |||
| func NewRequest(url, method string) *Request { | |||
| return newRequest(url, method) | |||
| } | |||
| // Get returns *Request with GET method. | |||
| func Get(url string) *Request { | |||
| return newRequest(url, "GET") | |||
| @@ -0,0 +1,53 @@ | |||
| package private | |||
| import ( | |||
| "crypto/tls" | |||
| "encoding/json" | |||
| "fmt" | |||
| "net/http" | |||
| "code.gitea.io/gitea/modules/httplib" | |||
| "code.gitea.io/gitea/modules/log" | |||
| "code.gitea.io/gitea/modules/setting" | |||
| ) | |||
| func newRequest(url, method string) *httplib.Request { | |||
| return httplib.NewRequest(url, method).Header("Authorization", | |||
| fmt.Sprintf("Bearer %s", setting.InternalToken)) | |||
| } | |||
| // Response internal request response | |||
| type Response struct { | |||
| Err string `json:"err"` | |||
| } | |||
| func decodeJSONError(resp *http.Response) *Response { | |||
| var res Response | |||
| err := json.NewDecoder(resp.Body).Decode(&res) | |||
| if err != nil { | |||
| res.Err = err.Error() | |||
| } | |||
| return &res | |||
| } | |||
| // UpdatePublicKeyUpdated update publick key updates | |||
| func UpdatePublicKeyUpdated(keyID int64) error { | |||
| // Ask for running deliver hook and test pull request tasks. | |||
| reqURL := setting.LocalURL + fmt.Sprintf("api/internal/ssh/%d/update", keyID) | |||
| log.GitLogger.Trace("UpdatePublicKeyUpdated: %s", reqURL) | |||
| resp, err := newRequest(reqURL, "POST").SetTLSClientConfig(&tls.Config{ | |||
| InsecureSkipVerify: true, | |||
| }).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/100 != 2 { | |||
| return fmt.Errorf("Failed to update public key: %s", decodeJSONError(resp).Err) | |||
| } | |||
| return nil | |||
| } | |||
| @@ -27,6 +27,7 @@ import ( | |||
| "code.gitea.io/gitea/modules/user" | |||
| "github.com/Unknwon/com" | |||
| "github.com/dgrijalva/jwt-go" | |||
| _ "github.com/go-macaron/cache/memcache" // memcache plugin for cache | |||
| _ "github.com/go-macaron/cache/redis" | |||
| "github.com/go-macaron/session" | |||
| @@ -442,14 +443,15 @@ var ( | |||
| ShowFooterTemplateLoadTime bool | |||
| // Global setting objects | |||
| Cfg *ini.File | |||
| CustomPath string // Custom directory path | |||
| CustomConf string | |||
| CustomPID string | |||
| ProdMode bool | |||
| RunUser string | |||
| IsWindows bool | |||
| HasRobotsTxt bool | |||
| Cfg *ini.File | |||
| CustomPath string // Custom directory path | |||
| CustomConf string | |||
| CustomPID string | |||
| ProdMode bool | |||
| RunUser string | |||
| IsWindows bool | |||
| HasRobotsTxt bool | |||
| InternalToken string // internal access token | |||
| ) | |||
| // DateLang transforms standard language locale name to corresponding value in datetime plugin. | |||
| @@ -764,6 +766,43 @@ please consider changing to GITEA_CUSTOM`) | |||
| ReverseProxyAuthUser = sec.Key("REVERSE_PROXY_AUTHENTICATION_USER").MustString("X-WEBAUTH-USER") | |||
| MinPasswordLength = sec.Key("MIN_PASSWORD_LENGTH").MustInt(6) | |||
| ImportLocalPaths = sec.Key("IMPORT_LOCAL_PATHS").MustBool(false) | |||
| InternalToken = sec.Key("INTERNAL_TOKEN").String() | |||
| if len(InternalToken) == 0 { | |||
| secretBytes := make([]byte, 32) | |||
| _, err := io.ReadFull(rand.Reader, secretBytes) | |||
| if err != nil { | |||
| log.Fatal(4, "Error reading random bytes: %v", err) | |||
| } | |||
| secretKey := base64.RawURLEncoding.EncodeToString(secretBytes) | |||
| now := time.Now() | |||
| InternalToken, err = jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ | |||
| "nbf": now.Unix(), | |||
| }).SignedString([]byte(secretKey)) | |||
| if err != nil { | |||
| log.Fatal(4, "Error generate internal token: %v", err) | |||
| } | |||
| // Save secret | |||
| cfgSave := ini.Empty() | |||
| if com.IsFile(CustomConf) { | |||
| // Keeps custom settings if there is already something. | |||
| if err := cfgSave.Append(CustomConf); err != nil { | |||
| log.Error(4, "Failed to load custom conf '%s': %v", CustomConf, err) | |||
| } | |||
| } | |||
| cfgSave.Section("security").Key("INTERNAL_TOKEN").SetValue(InternalToken) | |||
| if err := os.MkdirAll(filepath.Dir(CustomConf), os.ModePerm); err != nil { | |||
| log.Fatal(4, "Failed to create '%s': %v", CustomConf, err) | |||
| } | |||
| if err := cfgSave.SaveTo(CustomConf); err != nil { | |||
| log.Fatal(4, "Error saving generated JWT Secret to custom config: %v", err) | |||
| } | |||
| } | |||
| sec = Cfg.Section("attachment") | |||
| AttachmentPath = sec.Key("PATH").MustString(path.Join(AppDataPath, "attachments")) | |||
| @@ -940,7 +979,6 @@ var Service struct { | |||
| EnableOpenIDSignUp bool | |||
| OpenIDWhitelist []*regexp.Regexp | |||
| OpenIDBlacklist []*regexp.Regexp | |||
| } | |||
| func newService() { | |||
| @@ -0,0 +1,44 @@ | |||
| // Copyright 2017 The Gitea Authors. All rights reserved. | |||
| // Use of this source code is governed by a MIT-style | |||
| // license that can be found in the LICENSE file. | |||
| // Package private includes all internal routes. The package name internal is ideal but Golang is not allowed, so we use private as package name instead. | |||
| package private | |||
| import ( | |||
| "strings" | |||
| "code.gitea.io/gitea/models" | |||
| "code.gitea.io/gitea/modules/setting" | |||
| macaron "gopkg.in/macaron.v1" | |||
| ) | |||
| // CheckInternalToken check internal token is set | |||
| func CheckInternalToken(ctx *macaron.Context) { | |||
| tokens := ctx.Req.Header.Get("Authorization") | |||
| fields := strings.Fields(tokens) | |||
| if len(fields) != 2 || fields[0] != "Bearer" || fields[1] != setting.InternalToken { | |||
| ctx.Error(403) | |||
| } | |||
| } | |||
| // UpdatePublicKey update publick key updates | |||
| func UpdatePublicKey(ctx *macaron.Context) { | |||
| keyID := ctx.ParamsInt64(":id") | |||
| if err := models.UpdatePublicKeyUpdated(keyID); err != nil { | |||
| ctx.JSON(500, map[string]interface{}{ | |||
| "err": err.Error(), | |||
| }) | |||
| return | |||
| } | |||
| ctx.PlainText(200, []byte("success")) | |||
| } | |||
| // RegisterRoutes registers all internal APIs routes to web application. | |||
| // These APIs will be invoked by internal commands for example `gitea serv` and etc. | |||
| func RegisterRoutes(m *macaron.Macaron) { | |||
| m.Group("/", func() { | |||
| m.Post("/ssh/:id/update", UpdatePublicKey) | |||
| }, CheckInternalToken) | |||
| } | |||