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.

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