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.

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