You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

284 lines
6.1 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package middleware
  5. import (
  6. "crypto/hmac"
  7. "crypto/sha1"
  8. "encoding/base64"
  9. "fmt"
  10. "html/template"
  11. "net/http"
  12. "strconv"
  13. "strings"
  14. "time"
  15. "github.com/go-martini/martini"
  16. "github.com/gogits/cache"
  17. "github.com/gogits/git"
  18. "github.com/gogits/session"
  19. "github.com/gogits/gogs/models"
  20. "github.com/gogits/gogs/modules/auth"
  21. "github.com/gogits/gogs/modules/base"
  22. "github.com/gogits/gogs/modules/log"
  23. )
  24. // Context represents context of a request.
  25. type Context struct {
  26. *Render
  27. c martini.Context
  28. p martini.Params
  29. Req *http.Request
  30. Res http.ResponseWriter
  31. Session session.SessionStore
  32. Cache cache.Cache
  33. User *models.User
  34. IsSigned bool
  35. csrfToken string
  36. Repo struct {
  37. IsOwner bool
  38. IsWatching bool
  39. IsBranch bool
  40. IsTag bool
  41. IsCommit bool
  42. Repository *models.Repository
  43. Owner *models.User
  44. Commit *git.Commit
  45. GitRepo *git.Repository
  46. BranchName string
  47. CommitId string
  48. RepoLink string
  49. CloneLink struct {
  50. SSH string
  51. HTTPS string
  52. Git string
  53. }
  54. }
  55. }
  56. // Query querys form parameter.
  57. func (ctx *Context) Query(name string) string {
  58. ctx.Req.ParseForm()
  59. return ctx.Req.Form.Get(name)
  60. }
  61. // func (ctx *Context) Param(name string) string {
  62. // return ctx.p[name]
  63. // }
  64. // HasError returns true if error occurs in form validation.
  65. func (ctx *Context) HasError() bool {
  66. hasErr, ok := ctx.Data["HasError"]
  67. if !ok {
  68. return false
  69. }
  70. return hasErr.(bool)
  71. }
  72. // HTML calls render.HTML underlying but reduce one argument.
  73. func (ctx *Context) HTML(status int, name string, htmlOpt ...HTMLOptions) {
  74. ctx.Render.HTML(status, name, ctx.Data, htmlOpt...)
  75. }
  76. // RenderWithErr used for page has form validation but need to prompt error to users.
  77. func (ctx *Context) RenderWithErr(msg, tpl string, form auth.Form) {
  78. ctx.Data["HasError"] = true
  79. ctx.Data["ErrorMsg"] = msg
  80. auth.AssignForm(form, ctx.Data)
  81. ctx.HTML(200, tpl)
  82. }
  83. // Handle handles and logs error by given status.
  84. func (ctx *Context) Handle(status int, title string, err error) {
  85. log.Error("%s: %v", title, err)
  86. if martini.Dev == martini.Prod {
  87. ctx.HTML(500, "status/500")
  88. return
  89. }
  90. ctx.Data["ErrorMsg"] = err
  91. ctx.HTML(status, fmt.Sprintf("status/%d", status))
  92. }
  93. func (ctx *Context) Debug(msg string, args ...interface{}) {
  94. log.Debug(msg, args...)
  95. }
  96. func (ctx *Context) GetCookie(name string) string {
  97. cookie, err := ctx.Req.Cookie(name)
  98. if err != nil {
  99. return ""
  100. }
  101. return cookie.Value
  102. }
  103. func (ctx *Context) SetCookie(name string, value string, others ...interface{}) {
  104. cookie := http.Cookie{}
  105. cookie.Name = name
  106. cookie.Value = value
  107. if len(others) > 0 {
  108. switch v := others[0].(type) {
  109. case int:
  110. cookie.MaxAge = v
  111. case int64:
  112. cookie.MaxAge = int(v)
  113. case int32:
  114. cookie.MaxAge = int(v)
  115. }
  116. }
  117. // default "/"
  118. if len(others) > 1 {
  119. if v, ok := others[1].(string); ok && len(v) > 0 {
  120. cookie.Path = v
  121. }
  122. } else {
  123. cookie.Path = "/"
  124. }
  125. // default empty
  126. if len(others) > 2 {
  127. if v, ok := others[2].(string); ok && len(v) > 0 {
  128. cookie.Domain = v
  129. }
  130. }
  131. // default empty
  132. if len(others) > 3 {
  133. switch v := others[3].(type) {
  134. case bool:
  135. cookie.Secure = v
  136. default:
  137. if others[3] != nil {
  138. cookie.Secure = true
  139. }
  140. }
  141. }
  142. // default false. for session cookie default true
  143. if len(others) > 4 {
  144. if v, ok := others[4].(bool); ok && v {
  145. cookie.HttpOnly = true
  146. }
  147. }
  148. ctx.Res.Header().Add("Set-Cookie", cookie.String())
  149. }
  150. // Get secure cookie from request by a given key.
  151. func (ctx *Context) GetSecureCookie(Secret, key string) (string, bool) {
  152. val := ctx.GetCookie(key)
  153. if val == "" {
  154. return "", false
  155. }
  156. parts := strings.SplitN(val, "|", 3)
  157. if len(parts) != 3 {
  158. return "", false
  159. }
  160. vs := parts[0]
  161. timestamp := parts[1]
  162. sig := parts[2]
  163. h := hmac.New(sha1.New, []byte(Secret))
  164. fmt.Fprintf(h, "%s%s", vs, timestamp)
  165. if fmt.Sprintf("%02x", h.Sum(nil)) != sig {
  166. return "", false
  167. }
  168. res, _ := base64.URLEncoding.DecodeString(vs)
  169. return string(res), true
  170. }
  171. // Set Secure cookie for response.
  172. func (ctx *Context) SetSecureCookie(Secret, name, value string, others ...interface{}) {
  173. vs := base64.URLEncoding.EncodeToString([]byte(value))
  174. timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
  175. h := hmac.New(sha1.New, []byte(Secret))
  176. fmt.Fprintf(h, "%s%s", vs, timestamp)
  177. sig := fmt.Sprintf("%02x", h.Sum(nil))
  178. cookie := strings.Join([]string{vs, timestamp, sig}, "|")
  179. ctx.SetCookie(name, cookie, others...)
  180. }
  181. func (ctx *Context) CsrfToken() string {
  182. if len(ctx.csrfToken) > 0 {
  183. return ctx.csrfToken
  184. }
  185. token := ctx.GetCookie("_csrf")
  186. if len(token) == 0 {
  187. token = base.GetRandomString(30)
  188. ctx.SetCookie("_csrf", token)
  189. }
  190. ctx.csrfToken = token
  191. return token
  192. }
  193. func (ctx *Context) CsrfTokenValid() bool {
  194. token := ctx.Query("_csrf")
  195. if token == "" {
  196. token = ctx.Req.Header.Get("X-Csrf-Token")
  197. }
  198. if token == "" {
  199. return false
  200. } else if ctx.csrfToken != token {
  201. return false
  202. }
  203. return true
  204. }
  205. // InitContext initializes a classic context for a request.
  206. func InitContext() martini.Handler {
  207. return func(res http.ResponseWriter, r *http.Request, c martini.Context, rd *Render) {
  208. ctx := &Context{
  209. c: c,
  210. // p: p,
  211. Req: r,
  212. Res: res,
  213. Cache: base.Cache,
  214. Render: rd,
  215. }
  216. ctx.Data["PageStartTime"] = time.Now()
  217. // start session
  218. ctx.Session = base.SessionManager.SessionStart(res, r)
  219. rw := res.(martini.ResponseWriter)
  220. rw.Before(func(martini.ResponseWriter) {
  221. ctx.Session.SessionRelease(res)
  222. })
  223. // Get user from session if logined.
  224. user := auth.SignedInUser(ctx.Session)
  225. ctx.User = user
  226. ctx.IsSigned = user != nil
  227. ctx.Data["IsSigned"] = ctx.IsSigned
  228. if user != nil {
  229. ctx.Data["SignedUser"] = user
  230. ctx.Data["SignedUserId"] = user.Id
  231. ctx.Data["SignedUserName"] = user.Name
  232. ctx.Data["IsAdmin"] = ctx.User.IsAdmin
  233. }
  234. // get or create csrf token
  235. ctx.Data["CsrfToken"] = ctx.CsrfToken()
  236. ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + ctx.csrfToken + `">`)
  237. c.Map(ctx)
  238. c.Next()
  239. }
  240. }