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