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.

auth.go 8.4 kB

10 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
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  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 auth
  5. import (
  6. "reflect"
  7. "strings"
  8. "github.com/Unknwon/com"
  9. "github.com/go-macaron/binding"
  10. "github.com/go-macaron/session"
  11. gouuid "github.com/satori/go.uuid"
  12. "gopkg.in/macaron.v1"
  13. "code.gitea.io/gitea/models"
  14. "code.gitea.io/gitea/modules/base"
  15. "code.gitea.io/gitea/modules/log"
  16. "code.gitea.io/gitea/modules/setting"
  17. "code.gitea.io/gitea/modules/util"
  18. "code.gitea.io/gitea/modules/validation"
  19. )
  20. // IsAPIPath if URL is an api path
  21. func IsAPIPath(url string) bool {
  22. return strings.HasPrefix(url, "/api/")
  23. }
  24. // SignedInID returns the id of signed in user.
  25. func SignedInID(ctx *macaron.Context, sess session.Store) int64 {
  26. if !models.HasEngine {
  27. return 0
  28. }
  29. // Check access token.
  30. if IsAPIPath(ctx.Req.URL.Path) {
  31. tokenSHA := ctx.Query("token")
  32. if len(tokenSHA) == 0 {
  33. tokenSHA = ctx.Query("access_token")
  34. }
  35. if len(tokenSHA) == 0 {
  36. // Well, check with header again.
  37. auHead := ctx.Req.Header.Get("Authorization")
  38. if len(auHead) > 0 {
  39. auths := strings.Fields(auHead)
  40. if len(auths) == 2 && auths[0] == "token" {
  41. tokenSHA = auths[1]
  42. }
  43. }
  44. }
  45. // Let's see if token is valid.
  46. if len(tokenSHA) > 0 {
  47. t, err := models.GetAccessTokenBySHA(tokenSHA)
  48. if err != nil {
  49. if models.IsErrAccessTokenNotExist(err) || models.IsErrAccessTokenEmpty(err) {
  50. log.Error(4, "GetAccessTokenBySHA: %v", err)
  51. }
  52. return 0
  53. }
  54. t.UpdatedUnix = util.TimeStampNow()
  55. if err = models.UpdateAccessToken(t); err != nil {
  56. log.Error(4, "UpdateAccessToken: %v", err)
  57. }
  58. ctx.Data["IsApiToken"] = true
  59. return t.UID
  60. }
  61. }
  62. uid := sess.Get("uid")
  63. if uid == nil {
  64. return 0
  65. } else if id, ok := uid.(int64); ok {
  66. return id
  67. }
  68. return 0
  69. }
  70. // SignedInUser returns the user object of signed user.
  71. // It returns a bool value to indicate whether user uses basic auth or not.
  72. func SignedInUser(ctx *macaron.Context, sess session.Store) (*models.User, bool) {
  73. if !models.HasEngine {
  74. return nil, false
  75. }
  76. if uid := SignedInID(ctx, sess); uid > 0 {
  77. user, err := models.GetUserByID(uid)
  78. if err == nil {
  79. return user, false
  80. } else if !models.IsErrUserNotExist(err) {
  81. log.Error(4, "GetUserById: %v", err)
  82. }
  83. }
  84. if setting.Service.EnableReverseProxyAuth {
  85. webAuthUser := ctx.Req.Header.Get(setting.ReverseProxyAuthUser)
  86. if len(webAuthUser) > 0 {
  87. u, err := models.GetUserByName(webAuthUser)
  88. if err != nil {
  89. if !models.IsErrUserNotExist(err) {
  90. log.Error(4, "GetUserByName: %v", err)
  91. return nil, false
  92. }
  93. // Check if enabled auto-registration.
  94. if setting.Service.EnableReverseProxyAutoRegister {
  95. email := gouuid.NewV4().String() + "@localhost"
  96. if setting.Service.EnableReverseProxyEmail {
  97. webAuthEmail := ctx.Req.Header.Get(setting.ReverseProxyAuthEmail)
  98. if len(webAuthEmail) > 0 {
  99. email = webAuthEmail
  100. }
  101. }
  102. u := &models.User{
  103. Name: webAuthUser,
  104. Email: email,
  105. Passwd: webAuthUser,
  106. IsActive: true,
  107. }
  108. if err = models.CreateUser(u); err != nil {
  109. // FIXME: should I create a system notice?
  110. log.Error(4, "CreateUser: %v", err)
  111. return nil, false
  112. }
  113. return u, false
  114. }
  115. }
  116. return u, false
  117. }
  118. }
  119. // Check with basic auth.
  120. baHead := ctx.Req.Header.Get("Authorization")
  121. if len(baHead) > 0 {
  122. auths := strings.Fields(baHead)
  123. if len(auths) == 2 && auths[0] == "Basic" {
  124. var u *models.User
  125. uname, passwd, _ := base.BasicAuthDecode(auths[1])
  126. // Check if username or password is a token
  127. isUsernameToken := len(passwd) == 0 || passwd == "x-oauth-basic"
  128. // Assume username is token
  129. authToken := uname
  130. if !isUsernameToken {
  131. // Assume password is token
  132. authToken = passwd
  133. }
  134. token, err := models.GetAccessTokenBySHA(authToken)
  135. if err == nil {
  136. if isUsernameToken {
  137. u, err = models.GetUserByID(token.UID)
  138. if err != nil {
  139. log.Error(4, "GetUserByID: %v", err)
  140. return nil, false
  141. }
  142. } else {
  143. u, err = models.GetUserByName(uname)
  144. if err != nil {
  145. log.Error(4, "GetUserByID: %v", err)
  146. return nil, false
  147. }
  148. if u.ID != token.UID {
  149. return nil, false
  150. }
  151. }
  152. token.UpdatedUnix = util.TimeStampNow()
  153. if err = models.UpdateAccessToken(token); err != nil {
  154. log.Error(4, "UpdateAccessToken: %v", err)
  155. }
  156. } else {
  157. if !models.IsErrAccessTokenNotExist(err) && !models.IsErrAccessTokenEmpty(err) {
  158. log.Error(4, "GetAccessTokenBySha: %v", err)
  159. }
  160. }
  161. if u == nil {
  162. u, err = models.UserSignIn(uname, passwd)
  163. if err != nil {
  164. if !models.IsErrUserNotExist(err) {
  165. log.Error(4, "UserSignIn: %v", err)
  166. }
  167. return nil, false
  168. }
  169. }
  170. ctx.Data["IsApiToken"] = true
  171. return u, true
  172. }
  173. }
  174. return nil, false
  175. }
  176. // Form form binding interface
  177. type Form interface {
  178. binding.Validator
  179. }
  180. func init() {
  181. binding.SetNameMapper(com.ToSnakeCase)
  182. }
  183. // AssignForm assign form values back to the template data.
  184. func AssignForm(form interface{}, data map[string]interface{}) {
  185. typ := reflect.TypeOf(form)
  186. val := reflect.ValueOf(form)
  187. if typ.Kind() == reflect.Ptr {
  188. typ = typ.Elem()
  189. val = val.Elem()
  190. }
  191. for i := 0; i < typ.NumField(); i++ {
  192. field := typ.Field(i)
  193. fieldName := field.Tag.Get("form")
  194. // Allow ignored fields in the struct
  195. if fieldName == "-" {
  196. continue
  197. } else if len(fieldName) == 0 {
  198. fieldName = com.ToSnakeCase(field.Name)
  199. }
  200. data[fieldName] = val.Field(i).Interface()
  201. }
  202. }
  203. func getRuleBody(field reflect.StructField, prefix string) string {
  204. for _, rule := range strings.Split(field.Tag.Get("binding"), ";") {
  205. if strings.HasPrefix(rule, prefix) {
  206. return rule[len(prefix) : len(rule)-1]
  207. }
  208. }
  209. return ""
  210. }
  211. // GetSize get size int form tag
  212. func GetSize(field reflect.StructField) string {
  213. return getRuleBody(field, "Size(")
  214. }
  215. // GetMinSize get minimal size in form tag
  216. func GetMinSize(field reflect.StructField) string {
  217. return getRuleBody(field, "MinSize(")
  218. }
  219. // GetMaxSize get max size in form tag
  220. func GetMaxSize(field reflect.StructField) string {
  221. return getRuleBody(field, "MaxSize(")
  222. }
  223. // GetInclude get include in form tag
  224. func GetInclude(field reflect.StructField) string {
  225. return getRuleBody(field, "Include(")
  226. }
  227. // FIXME: struct contains a struct
  228. func validateStruct(obj interface{}) binding.Errors {
  229. return nil
  230. }
  231. func validate(errs binding.Errors, data map[string]interface{}, f Form, l macaron.Locale) binding.Errors {
  232. if errs.Len() == 0 {
  233. return errs
  234. }
  235. data["HasError"] = true
  236. AssignForm(f, data)
  237. typ := reflect.TypeOf(f)
  238. val := reflect.ValueOf(f)
  239. if typ.Kind() == reflect.Ptr {
  240. typ = typ.Elem()
  241. val = val.Elem()
  242. }
  243. for i := 0; i < typ.NumField(); i++ {
  244. field := typ.Field(i)
  245. fieldName := field.Tag.Get("form")
  246. // Allow ignored fields in the struct
  247. if fieldName == "-" {
  248. continue
  249. }
  250. if errs[0].FieldNames[0] == field.Name {
  251. data["Err_"+field.Name] = true
  252. trName := field.Tag.Get("locale")
  253. if len(trName) == 0 {
  254. trName = l.Tr("form." + field.Name)
  255. } else {
  256. trName = l.Tr(trName)
  257. }
  258. switch errs[0].Classification {
  259. case binding.ERR_REQUIRED:
  260. data["ErrorMsg"] = trName + l.Tr("form.require_error")
  261. case binding.ERR_ALPHA_DASH:
  262. data["ErrorMsg"] = trName + l.Tr("form.alpha_dash_error")
  263. case binding.ERR_ALPHA_DASH_DOT:
  264. data["ErrorMsg"] = trName + l.Tr("form.alpha_dash_dot_error")
  265. case validation.ErrGitRefName:
  266. data["ErrorMsg"] = trName + l.Tr("form.git_ref_name_error")
  267. case binding.ERR_SIZE:
  268. data["ErrorMsg"] = trName + l.Tr("form.size_error", GetSize(field))
  269. case binding.ERR_MIN_SIZE:
  270. data["ErrorMsg"] = trName + l.Tr("form.min_size_error", GetMinSize(field))
  271. case binding.ERR_MAX_SIZE:
  272. data["ErrorMsg"] = trName + l.Tr("form.max_size_error", GetMaxSize(field))
  273. case binding.ERR_EMAIL:
  274. data["ErrorMsg"] = trName + l.Tr("form.email_error")
  275. case binding.ERR_URL:
  276. data["ErrorMsg"] = trName + l.Tr("form.url_error")
  277. case binding.ERR_INCLUDE:
  278. data["ErrorMsg"] = trName + l.Tr("form.include_error", GetInclude(field))
  279. default:
  280. data["ErrorMsg"] = l.Tr("form.unknown_error") + " " + errs[0].Classification
  281. }
  282. return errs
  283. }
  284. }
  285. return errs
  286. }