code.gitea.io/gitea@v1.22.3/tests/integration/org_team_invite_test.go (about)

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