gitea源码

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. // Copyright 2022 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package web
  4. import (
  5. "fmt"
  6. "net/http"
  7. "net/url"
  8. "strconv"
  9. "strings"
  10. user_model "code.gitea.io/gitea/models/user"
  11. "code.gitea.io/gitea/modules/log"
  12. "code.gitea.io/gitea/modules/setting"
  13. "code.gitea.io/gitea/services/context"
  14. )
  15. // https://datatracker.ietf.org/doc/html/draft-ietf-appsawg-webfinger-14#section-4.4
  16. type webfingerJRD struct {
  17. Subject string `json:"subject,omitempty"`
  18. Aliases []string `json:"aliases,omitempty"`
  19. Properties map[string]any `json:"properties,omitempty"`
  20. Links []*webfingerLink `json:"links,omitempty"`
  21. }
  22. type webfingerLink struct {
  23. Rel string `json:"rel,omitempty"`
  24. Type string `json:"type,omitempty"`
  25. Href string `json:"href,omitempty"`
  26. Titles map[string]string `json:"titles,omitempty"`
  27. Properties map[string]any `json:"properties,omitempty"`
  28. }
  29. // WebfingerQuery returns information about a resource
  30. // https://datatracker.ietf.org/doc/html/rfc7565
  31. func WebfingerQuery(ctx *context.Context) {
  32. appURL, _ := url.Parse(setting.AppURL)
  33. resource, err := url.Parse(ctx.FormTrim("resource"))
  34. if err != nil {
  35. ctx.HTTPError(http.StatusBadRequest)
  36. return
  37. }
  38. var u *user_model.User
  39. switch resource.Scheme {
  40. case "acct":
  41. // allow only the current host
  42. parts := strings.SplitN(resource.Opaque, "@", 2)
  43. if len(parts) != 2 {
  44. ctx.HTTPError(http.StatusBadRequest)
  45. return
  46. }
  47. if parts[1] != appURL.Host {
  48. ctx.HTTPError(http.StatusBadRequest)
  49. return
  50. }
  51. u, err = user_model.GetUserByName(ctx, parts[0])
  52. case "mailto":
  53. u, err = user_model.GetUserByEmail(ctx, resource.Opaque)
  54. if u != nil && u.KeepEmailPrivate {
  55. err = user_model.ErrUserNotExist{}
  56. }
  57. default:
  58. ctx.HTTPError(http.StatusBadRequest)
  59. return
  60. }
  61. if err != nil {
  62. if user_model.IsErrUserNotExist(err) {
  63. ctx.HTTPError(http.StatusNotFound)
  64. } else {
  65. log.Error("Error getting user: %s Error: %v", resource.Opaque, err)
  66. ctx.HTTPError(http.StatusInternalServerError)
  67. }
  68. return
  69. }
  70. if !user_model.IsUserVisibleToViewer(ctx, u, ctx.Doer) {
  71. ctx.HTTPError(http.StatusNotFound)
  72. return
  73. }
  74. aliases := []string{
  75. u.HTMLURL(ctx),
  76. appURL.String() + "api/v1/activitypub/user-id/" + strconv.FormatInt(u.ID, 10),
  77. }
  78. if !u.KeepEmailPrivate {
  79. aliases = append(aliases, "mailto:"+u.Email)
  80. }
  81. links := []*webfingerLink{
  82. {
  83. Rel: "http://webfinger.net/rel/profile-page",
  84. Type: "text/html",
  85. Href: u.HTMLURL(ctx),
  86. },
  87. {
  88. Rel: "http://webfinger.net/rel/avatar",
  89. Href: u.AvatarLink(ctx),
  90. },
  91. {
  92. Rel: "self",
  93. Type: "application/activity+json",
  94. Href: appURL.String() + "api/v1/activitypub/user-id/" + strconv.FormatInt(u.ID, 10),
  95. },
  96. {
  97. Rel: "http://openid.net/specs/connect/1.0/issuer",
  98. Href: appURL.String(),
  99. },
  100. }
  101. ctx.Resp.Header().Add("Access-Control-Allow-Origin", "*")
  102. ctx.JSON(http.StatusOK, &webfingerJRD{
  103. Subject: fmt.Sprintf("acct:%s@%s", url.QueryEscape(u.Name), appURL.Host),
  104. Aliases: aliases,
  105. Links: links,
  106. })
  107. }