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.

org_team_invite_test.go 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. // Copyright 2022 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package integration
  4. import (
  5. "fmt"
  6. "net/http"
  7. "net/url"
  8. "strings"
  9. "testing"
  10. "code.gitea.io/gitea/models/db"
  11. "code.gitea.io/gitea/models/organization"
  12. "code.gitea.io/gitea/models/unittest"
  13. user_model "code.gitea.io/gitea/models/user"
  14. "code.gitea.io/gitea/modules/setting"
  15. "code.gitea.io/gitea/modules/test"
  16. "code.gitea.io/gitea/tests"
  17. "github.com/stretchr/testify/assert"
  18. )
  19. func TestOrgTeamEmailInvite(t *testing.T) {
  20. if setting.MailService == nil {
  21. t.Skip()
  22. return
  23. }
  24. defer tests.PrepareTestEnv(t)()
  25. org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
  26. team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 2})
  27. user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
  28. isMember, err := organization.IsTeamMember(db.DefaultContext, team.OrgID, team.ID, user.ID)
  29. assert.NoError(t, err)
  30. assert.False(t, isMember)
  31. session := loginUser(t, "user1")
  32. teamURL := fmt.Sprintf("/org/%s/teams/%s", org.Name, team.Name)
  33. csrf := GetCSRF(t, session, teamURL)
  34. req := NewRequestWithValues(t, "POST", teamURL+"/action/add", map[string]string{
  35. "_csrf": csrf,
  36. "uid": "1",
  37. "uname": user.Email,
  38. })
  39. resp := session.MakeRequest(t, req, http.StatusSeeOther)
  40. req = NewRequest(t, "GET", test.RedirectURL(resp))
  41. session.MakeRequest(t, req, http.StatusOK)
  42. // get the invite token
  43. invites, err := organization.GetInvitesByTeamID(db.DefaultContext, team.ID)
  44. assert.NoError(t, err)
  45. assert.Len(t, invites, 1)
  46. session = loginUser(t, user.Name)
  47. // join the team
  48. inviteURL := fmt.Sprintf("/org/invite/%s", invites[0].Token)
  49. csrf = GetCSRF(t, session, inviteURL)
  50. req = NewRequestWithValues(t, "POST", inviteURL, map[string]string{
  51. "_csrf": csrf,
  52. })
  53. resp = session.MakeRequest(t, req, http.StatusSeeOther)
  54. req = NewRequest(t, "GET", test.RedirectURL(resp))
  55. session.MakeRequest(t, req, http.StatusOK)
  56. isMember, err = organization.IsTeamMember(db.DefaultContext, team.OrgID, team.ID, user.ID)
  57. assert.NoError(t, err)
  58. assert.True(t, isMember)
  59. }
  60. // Check that users are redirected to accept the invitation correctly after login
  61. func TestOrgTeamEmailInviteRedirectsExistingUser(t *testing.T) {
  62. if setting.MailService == nil {
  63. t.Skip()
  64. return
  65. }
  66. defer tests.PrepareTestEnv(t)()
  67. org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
  68. team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 2})
  69. user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
  70. isMember, err := organization.IsTeamMember(db.DefaultContext, team.OrgID, team.ID, user.ID)
  71. assert.NoError(t, err)
  72. assert.False(t, isMember)
  73. // create the invite
  74. session := loginUser(t, "user1")
  75. teamURL := fmt.Sprintf("/org/%s/teams/%s", org.Name, team.Name)
  76. req := NewRequestWithValues(t, "POST", teamURL+"/action/add", map[string]string{
  77. "_csrf": GetCSRF(t, session, teamURL),
  78. "uid": "1",
  79. "uname": user.Email,
  80. })
  81. resp := session.MakeRequest(t, req, http.StatusSeeOther)
  82. req = NewRequest(t, "GET", test.RedirectURL(resp))
  83. session.MakeRequest(t, req, http.StatusOK)
  84. // get the invite token
  85. invites, err := organization.GetInvitesByTeamID(db.DefaultContext, team.ID)
  86. assert.NoError(t, err)
  87. assert.Len(t, invites, 1)
  88. // accept the invite
  89. inviteURL := fmt.Sprintf("/org/invite/%s", invites[0].Token)
  90. req = NewRequest(t, "GET", fmt.Sprintf("/user/login?redirect_to=%s", url.QueryEscape(inviteURL)))
  91. resp = MakeRequest(t, req, http.StatusOK)
  92. doc := NewHTMLParser(t, resp.Body)
  93. req = NewRequestWithValues(t, "POST", "/user/login", map[string]string{
  94. "_csrf": doc.GetCSRF(),
  95. "user_name": "user5",
  96. "password": "password",
  97. })
  98. for _, c := range resp.Result().Cookies() {
  99. req.AddCookie(c)
  100. }
  101. resp = MakeRequest(t, req, http.StatusSeeOther)
  102. assert.Equal(t, inviteURL, test.RedirectURL(resp))
  103. // complete the login process
  104. ch := http.Header{}
  105. ch.Add("Cookie", strings.Join(resp.Header()["Set-Cookie"], ";"))
  106. cr := http.Request{Header: ch}
  107. session = emptyTestSession(t)
  108. baseURL, err := url.Parse(setting.AppURL)
  109. assert.NoError(t, err)
  110. session.jar.SetCookies(baseURL, cr.Cookies())
  111. // make the request
  112. req = NewRequestWithValues(t, "POST", test.RedirectURL(resp), map[string]string{
  113. "_csrf": GetCSRF(t, session, test.RedirectURL(resp)),
  114. })
  115. resp = session.MakeRequest(t, req, http.StatusSeeOther)
  116. req = NewRequest(t, "GET", test.RedirectURL(resp))
  117. session.MakeRequest(t, req, http.StatusOK)
  118. isMember, err = organization.IsTeamMember(db.DefaultContext, team.OrgID, team.ID, user.ID)
  119. assert.NoError(t, err)
  120. assert.True(t, isMember)
  121. }
  122. // Check that newly signed up users are redirected to accept the invitation correctly
  123. func TestOrgTeamEmailInviteRedirectsNewUser(t *testing.T) {
  124. if setting.MailService == nil {
  125. t.Skip()
  126. return
  127. }
  128. defer tests.PrepareTestEnv(t)()
  129. org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
  130. team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 2})
  131. // create the invite
  132. session := loginUser(t, "user1")
  133. teamURL := fmt.Sprintf("/org/%s/teams/%s", org.Name, team.Name)
  134. req := NewRequestWithValues(t, "POST", teamURL+"/action/add", map[string]string{
  135. "_csrf": GetCSRF(t, session, teamURL),
  136. "uid": "1",
  137. "uname": "doesnotexist@example.com",
  138. })
  139. resp := session.MakeRequest(t, req, http.StatusSeeOther)
  140. req = NewRequest(t, "GET", test.RedirectURL(resp))
  141. session.MakeRequest(t, req, http.StatusOK)
  142. // get the invite token
  143. invites, err := organization.GetInvitesByTeamID(db.DefaultContext, team.ID)
  144. assert.NoError(t, err)
  145. assert.Len(t, invites, 1)
  146. // accept the invite
  147. inviteURL := fmt.Sprintf("/org/invite/%s", invites[0].Token)
  148. req = NewRequest(t, "GET", fmt.Sprintf("/user/sign_up?redirect_to=%s", url.QueryEscape(inviteURL)))
  149. resp = MakeRequest(t, req, http.StatusOK)
  150. doc := NewHTMLParser(t, resp.Body)
  151. req = NewRequestWithValues(t, "POST", "/user/sign_up", map[string]string{
  152. "_csrf": doc.GetCSRF(),
  153. "user_name": "doesnotexist",
  154. "email": "doesnotexist@example.com",
  155. "password": "examplePassword!1",
  156. "retype": "examplePassword!1",
  157. })
  158. for _, c := range resp.Result().Cookies() {
  159. req.AddCookie(c)
  160. }
  161. resp = MakeRequest(t, req, http.StatusSeeOther)
  162. assert.Equal(t, inviteURL, test.RedirectURL(resp))
  163. // complete the signup process
  164. ch := http.Header{}
  165. ch.Add("Cookie", strings.Join(resp.Header()["Set-Cookie"], ";"))
  166. cr := http.Request{Header: ch}
  167. session = emptyTestSession(t)
  168. baseURL, err := url.Parse(setting.AppURL)
  169. assert.NoError(t, err)
  170. session.jar.SetCookies(baseURL, cr.Cookies())
  171. // make the redirected request
  172. req = NewRequestWithValues(t, "POST", test.RedirectURL(resp), map[string]string{
  173. "_csrf": GetCSRF(t, session, test.RedirectURL(resp)),
  174. })
  175. resp = session.MakeRequest(t, req, http.StatusSeeOther)
  176. req = NewRequest(t, "GET", test.RedirectURL(resp))
  177. session.MakeRequest(t, req, http.StatusOK)
  178. // get the new user
  179. newUser, err := user_model.GetUserByName(db.DefaultContext, "doesnotexist")
  180. assert.NoError(t, err)
  181. isMember, err := organization.IsTeamMember(db.DefaultContext, team.OrgID, team.ID, newUser.ID)
  182. assert.NoError(t, err)
  183. assert.True(t, isMember)
  184. }
  185. // Check that users are redirected correctly after confirming their email
  186. func TestOrgTeamEmailInviteRedirectsNewUserWithActivation(t *testing.T) {
  187. if setting.MailService == nil {
  188. t.Skip()
  189. return
  190. }
  191. // enable email confirmation temporarily
  192. defer func(prevVal bool) {
  193. setting.Service.RegisterEmailConfirm = prevVal
  194. }(setting.Service.RegisterEmailConfirm)
  195. setting.Service.RegisterEmailConfirm = true
  196. defer tests.PrepareTestEnv(t)()
  197. org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
  198. team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 2})
  199. // create the invite
  200. session := loginUser(t, "user1")
  201. teamURL := fmt.Sprintf("/org/%s/teams/%s", org.Name, team.Name)
  202. req := NewRequestWithValues(t, "POST", teamURL+"/action/add", map[string]string{
  203. "_csrf": GetCSRF(t, session, teamURL),
  204. "uid": "1",
  205. "uname": "doesnotexist@example.com",
  206. })
  207. resp := session.MakeRequest(t, req, http.StatusSeeOther)
  208. req = NewRequest(t, "GET", test.RedirectURL(resp))
  209. session.MakeRequest(t, req, http.StatusOK)
  210. // get the invite token
  211. invites, err := organization.GetInvitesByTeamID(db.DefaultContext, team.ID)
  212. assert.NoError(t, err)
  213. assert.Len(t, invites, 1)
  214. // accept the invite
  215. inviteURL := fmt.Sprintf("/org/invite/%s", invites[0].Token)
  216. req = NewRequest(t, "GET", fmt.Sprintf("/user/sign_up?redirect_to=%s", url.QueryEscape(inviteURL)))
  217. inviteResp := MakeRequest(t, req, http.StatusOK)
  218. doc := NewHTMLParser(t, resp.Body)
  219. req = NewRequestWithValues(t, "POST", "/user/sign_up", map[string]string{
  220. "_csrf": doc.GetCSRF(),
  221. "user_name": "doesnotexist",
  222. "email": "doesnotexist@example.com",
  223. "password": "examplePassword!1",
  224. "retype": "examplePassword!1",
  225. })
  226. for _, c := range inviteResp.Result().Cookies() {
  227. req.AddCookie(c)
  228. }
  229. resp = MakeRequest(t, req, http.StatusOK)
  230. user, err := user_model.GetUserByName(db.DefaultContext, "doesnotexist")
  231. assert.NoError(t, err)
  232. ch := http.Header{}
  233. ch.Add("Cookie", strings.Join(resp.Header()["Set-Cookie"], ";"))
  234. cr := http.Request{Header: ch}
  235. session = emptyTestSession(t)
  236. baseURL, err := url.Parse(setting.AppURL)
  237. assert.NoError(t, err)
  238. session.jar.SetCookies(baseURL, cr.Cookies())
  239. activateURL := fmt.Sprintf("/user/activate?code=%s", user.GenerateEmailActivateCode("doesnotexist@example.com"))
  240. req = NewRequestWithValues(t, "POST", activateURL, map[string]string{
  241. "password": "examplePassword!1",
  242. })
  243. // use the cookies set by the signup request
  244. for _, c := range inviteResp.Result().Cookies() {
  245. req.AddCookie(c)
  246. }
  247. resp = session.MakeRequest(t, req, http.StatusSeeOther)
  248. // should be redirected to accept the invite
  249. assert.Equal(t, inviteURL, test.RedirectURL(resp))
  250. req = NewRequestWithValues(t, "POST", test.RedirectURL(resp), map[string]string{
  251. "_csrf": GetCSRF(t, session, test.RedirectURL(resp)),
  252. })
  253. resp = session.MakeRequest(t, req, http.StatusSeeOther)
  254. req = NewRequest(t, "GET", test.RedirectURL(resp))
  255. session.MakeRequest(t, req, http.StatusOK)
  256. isMember, err := organization.IsTeamMember(db.DefaultContext, team.OrgID, team.ID, user.ID)
  257. assert.NoError(t, err)
  258. assert.True(t, isMember)
  259. }
  260. // Test that a logged-in user who navigates to the sign-up link is then redirected using redirect_to
  261. // For example: an invite may have been created before the user account was created, but they may be
  262. // accepting the invite after having created an account separately
  263. func TestOrgTeamEmailInviteRedirectsExistingUserWithLogin(t *testing.T) {
  264. if setting.MailService == nil {
  265. t.Skip()
  266. return
  267. }
  268. defer tests.PrepareTestEnv(t)()
  269. org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
  270. team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 2})
  271. user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
  272. isMember, err := organization.IsTeamMember(db.DefaultContext, team.OrgID, team.ID, user.ID)
  273. assert.NoError(t, err)
  274. assert.False(t, isMember)
  275. // create the invite
  276. session := loginUser(t, "user1")
  277. teamURL := fmt.Sprintf("/org/%s/teams/%s", org.Name, team.Name)
  278. req := NewRequestWithValues(t, "POST", teamURL+"/action/add", map[string]string{
  279. "_csrf": GetCSRF(t, session, teamURL),
  280. "uid": "1",
  281. "uname": user.Email,
  282. })
  283. resp := session.MakeRequest(t, req, http.StatusSeeOther)
  284. req = NewRequest(t, "GET", test.RedirectURL(resp))
  285. session.MakeRequest(t, req, http.StatusOK)
  286. // get the invite token
  287. invites, err := organization.GetInvitesByTeamID(db.DefaultContext, team.ID)
  288. assert.NoError(t, err)
  289. assert.Len(t, invites, 1)
  290. // note: the invited user has logged in
  291. session = loginUser(t, "user5")
  292. // accept the invite (note: this uses the sign_up url)
  293. inviteURL := fmt.Sprintf("/org/invite/%s", invites[0].Token)
  294. req = NewRequest(t, "GET", fmt.Sprintf("/user/sign_up?redirect_to=%s", url.QueryEscape(inviteURL)))
  295. resp = session.MakeRequest(t, req, http.StatusSeeOther)
  296. assert.Equal(t, inviteURL, test.RedirectURL(resp))
  297. // make the request
  298. req = NewRequestWithValues(t, "POST", test.RedirectURL(resp), map[string]string{
  299. "_csrf": GetCSRF(t, session, test.RedirectURL(resp)),
  300. })
  301. resp = session.MakeRequest(t, req, http.StatusSeeOther)
  302. req = NewRequest(t, "GET", test.RedirectURL(resp))
  303. session.MakeRequest(t, req, http.StatusOK)
  304. isMember, err = organization.IsTeamMember(db.DefaultContext, team.OrgID, team.ID, user.ID)
  305. assert.NoError(t, err)
  306. assert.True(t, isMember)
  307. }