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 6.3 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
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  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. if form != nil {
  81. auth.AssignForm(form, ctx.Data)
  82. }
  83. ctx.HTML(200, tpl)
  84. }
  85. // Handle handles and logs error by given status.
  86. func (ctx *Context) Handle(status int, title string, err error) {
  87. log.Error("%s: %v", title, err)
  88. if martini.Dev == martini.Prod {
  89. ctx.HTML(500, "status/500")
  90. return
  91. }
  92. ctx.Data["ErrorMsg"] = err
  93. ctx.HTML(status, fmt.Sprintf("status/%d", status))
  94. }
  95. func (ctx *Context) Debug(msg string, args ...interface{}) {
  96. log.Debug(msg, args...)
  97. }
  98. func (ctx *Context) GetCookie(name string) string {
  99. cookie, err := ctx.Req.Cookie(name)
  100. if err != nil {
  101. return ""
  102. }
  103. return cookie.Value
  104. }
  105. func (ctx *Context) SetCookie(name string, value string, others ...interface{}) {
  106. cookie := http.Cookie{}
  107. cookie.Name = name
  108. cookie.Value = value
  109. if len(others) > 0 {
  110. switch v := others[0].(type) {
  111. case int:
  112. cookie.MaxAge = v
  113. case int64:
  114. cookie.MaxAge = int(v)
  115. case int32:
  116. cookie.MaxAge = int(v)
  117. }
  118. }
  119. // default "/"
  120. if len(others) > 1 {
  121. if v, ok := others[1].(string); ok && len(v) > 0 {
  122. cookie.Path = v
  123. }
  124. } else {
  125. cookie.Path = "/"
  126. }
  127. // default empty
  128. if len(others) > 2 {
  129. if v, ok := others[2].(string); ok && len(v) > 0 {
  130. cookie.Domain = v
  131. }
  132. }
  133. // default empty
  134. if len(others) > 3 {
  135. switch v := others[3].(type) {
  136. case bool:
  137. cookie.Secure = v
  138. default:
  139. if others[3] != nil {
  140. cookie.Secure = true
  141. }
  142. }
  143. }
  144. // default false. for session cookie default true
  145. if len(others) > 4 {
  146. if v, ok := others[4].(bool); ok && v {
  147. cookie.HttpOnly = true
  148. }
  149. }
  150. ctx.Res.Header().Add("Set-Cookie", cookie.String())
  151. }
  152. // Get secure cookie from request by a given key.
  153. func (ctx *Context) GetSecureCookie(Secret, key string) (string, bool) {
  154. val := ctx.GetCookie(key)
  155. if val == "" {
  156. return "", false
  157. }
  158. parts := strings.SplitN(val, "|", 3)
  159. if len(parts) != 3 {
  160. return "", false
  161. }
  162. vs := parts[0]
  163. timestamp := parts[1]
  164. sig := parts[2]
  165. h := hmac.New(sha1.New, []byte(Secret))
  166. fmt.Fprintf(h, "%s%s", vs, timestamp)
  167. if fmt.Sprintf("%02x", h.Sum(nil)) != sig {
  168. return "", false
  169. }
  170. res, _ := base64.URLEncoding.DecodeString(vs)
  171. return string(res), true
  172. }
  173. // Set Secure cookie for response.
  174. func (ctx *Context) SetSecureCookie(Secret, name, value string, others ...interface{}) {
  175. vs := base64.URLEncoding.EncodeToString([]byte(value))
  176. timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
  177. h := hmac.New(sha1.New, []byte(Secret))
  178. fmt.Fprintf(h, "%s%s", vs, timestamp)
  179. sig := fmt.Sprintf("%02x", h.Sum(nil))
  180. cookie := strings.Join([]string{vs, timestamp, sig}, "|")
  181. ctx.SetCookie(name, cookie, others...)
  182. }
  183. func (ctx *Context) CsrfToken() string {
  184. if len(ctx.csrfToken) > 0 {
  185. return ctx.csrfToken
  186. }
  187. token := ctx.GetCookie("_csrf")
  188. if len(token) == 0 {
  189. token = base.GetRandomString(30)
  190. ctx.SetCookie("_csrf", token)
  191. }
  192. ctx.csrfToken = token
  193. return token
  194. }
  195. func (ctx *Context) CsrfTokenValid() bool {
  196. token := ctx.Query("_csrf")
  197. if token == "" {
  198. token = ctx.Req.Header.Get("X-Csrf-Token")
  199. }
  200. if token == "" {
  201. return false
  202. } else if ctx.csrfToken != token {
  203. return false
  204. }
  205. return true
  206. }
  207. // InitContext initializes a classic context for a request.
  208. func InitContext() martini.Handler {
  209. return func(res http.ResponseWriter, r *http.Request, c martini.Context, rd *Render) {
  210. ctx := &Context{
  211. c: c,
  212. // p: p,
  213. Req: r,
  214. Res: res,
  215. Cache: base.Cache,
  216. Render: rd,
  217. }
  218. ctx.Data["PageStartTime"] = time.Now()
  219. // start session
  220. ctx.Session = base.SessionManager.SessionStart(res, r)
  221. rw := res.(martini.ResponseWriter)
  222. rw.Before(func(martini.ResponseWriter) {
  223. ctx.Session.SessionRelease(res)
  224. })
  225. // Get user from session if logined.
  226. user := auth.SignedInUser(ctx.Session)
  227. ctx.User = user
  228. ctx.IsSigned = user != nil
  229. ctx.Data["IsSigned"] = ctx.IsSigned
  230. if user != nil {
  231. ctx.Data["SignedUser"] = user
  232. ctx.Data["SignedUserId"] = user.Id
  233. ctx.Data["SignedUserName"] = user.Name
  234. ctx.Data["IsAdmin"] = ctx.User.IsAdmin
  235. }
  236. // get or create csrf token
  237. ctx.Data["CsrfToken"] = ctx.CsrfToken()
  238. ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + ctx.csrfToken + `">`)
  239. c.Map(ctx)
  240. c.Next()
  241. }
  242. }