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

context.go 7.1 kB

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