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