847 lines · 28.0 KB · Go
RawDownload
// Copyright 2017 The Gitea Authors. All rights reserved.// Copyright 2024 The Forgejo Authors. All rights reserved.// SPDX-License-Identifier: MIT package user_test import (	"crypto/rand"	"encoding/hex"	"fmt"	"strings"	"testing"	"time" 	"forgejo.org/models/auth"	"forgejo.org/models/db"	"forgejo.org/models/unittest"	user_model "forgejo.org/models/user"	"forgejo.org/modules/auth/password/hash"	"forgejo.org/modules/container"	"forgejo.org/modules/optional"	"forgejo.org/modules/setting"	"forgejo.org/modules/structs"	"forgejo.org/modules/test"	"forgejo.org/modules/timeutil"	"forgejo.org/modules/util"	"forgejo.org/modules/validation" 	"github.com/stretchr/testify/assert"	"github.com/stretchr/testify/require") func TestOAuth2Application_LoadUser(t *testing.T) {	require.NoError(t, unittest.PrepareTestDatabase())	app := unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ID: 1})	user, err := user_model.GetUserByID(db.DefaultContext, app.UID)	require.NoError(t, err)	assert.NotNil(t, user)} func TestIsValidUserID(t *testing.T) {	assert.False(t, user_model.IsValidUserID(-30))	assert.False(t, user_model.IsValidUserID(0))	assert.True(t, user_model.IsValidUserID(user_model.GhostUserID))	assert.True(t, user_model.IsValidUserID(user_model.ActionsUserID))	assert.True(t, user_model.IsValidUserID(200))} func TestGetUserFromMap(t *testing.T) {	id := int64(200)	idMap := map[int64]*user_model.User{		id: {ID: id},	} 	ghostID := int64(user_model.GhostUserID)	actionsID := int64(user_model.ActionsUserID)	actualID, actualUser := user_model.GetUserFromMap(-20, idMap)	assert.Equal(t, ghostID, actualID)	assert.Equal(t, ghostID, actualUser.ID) 	actualID, actualUser = user_model.GetUserFromMap(0, idMap)	assert.Equal(t, ghostID, actualID)	assert.Equal(t, ghostID, actualUser.ID) 	actualID, actualUser = user_model.GetUserFromMap(ghostID, idMap)	assert.Equal(t, ghostID, actualID)	assert.Equal(t, ghostID, actualUser.ID) 	actualID, actualUser = user_model.GetUserFromMap(actionsID, idMap)	assert.Equal(t, actionsID, actualID)	assert.Equal(t, actionsID, actualUser.ID)} func TestGetUserByName(t *testing.T) {	defer unittest.OverrideFixtures("models/user/fixtures")()	require.NoError(t, unittest.PrepareTestDatabase()) 	{		_, err := user_model.GetUserByName(db.DefaultContext, "")		assert.True(t, user_model.IsErrUserNotExist(err), err)	}	{		_, err := user_model.GetUserByName(db.DefaultContext, "UNKNOWN")		assert.True(t, user_model.IsErrUserNotExist(err), err)	}	{		user, err := user_model.GetUserByName(db.DefaultContext, "USER2")		require.NoError(t, err)		assert.Equal(t, "user2", user.Name)	}	{		user, err := user_model.GetUserByName(db.DefaultContext, "org3")		require.NoError(t, err)		assert.Equal(t, "org3", user.Name)	}	{		user, err := user_model.GetUserByName(db.DefaultContext, "remote01")		require.NoError(t, err)		assert.Equal(t, "remote01", user.Name)	}} func TestCanCreateOrganization(t *testing.T) {	require.NoError(t, unittest.PrepareTestDatabase()) 	admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})	assert.True(t, admin.CanCreateOrganization()) 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})	assert.True(t, user.CanCreateOrganization())	// Disable user create organization permission.	user.AllowCreateOrganization = false	assert.False(t, user.CanCreateOrganization()) 	setting.Admin.DisableRegularOrgCreation = true	user.AllowCreateOrganization = true	assert.True(t, admin.CanCreateOrganization())	assert.False(t, user.CanCreateOrganization())} func TestGetAllUsers(t *testing.T) {	defer unittest.OverrideFixtures("models/user/fixtures")()	require.NoError(t, unittest.PrepareTestDatabase()) 	users, err := user_model.GetAllUsers(db.DefaultContext)	require.NoError(t, err) 	found := make(map[user_model.UserType]bool, 0)	for _, user := range users {		found[user.Type] = true	}	assert.True(t, found[user_model.UserTypeIndividual], users)	assert.True(t, found[user_model.UserTypeRemoteUser], users)	assert.False(t, found[user_model.UserTypeOrganization], users)} func TestAPActorID(t *testing.T) {	user := user_model.User{ID: 1}	url := user.APActorID()	expected := "https://try.gitea.io/api/v1/activitypub/user-id/1"	assert.Equal(t, expected, url)} func TestAPActorID_APActorID(t *testing.T) {	user := user_model.User{ID: user_model.APServerActorUserID}	url := user.APActorID()	expected := "https://try.gitea.io/api/v1/activitypub/actor"	assert.Equal(t, expected, url)} func TestAPActorKeyID(t *testing.T) {	user := user_model.User{ID: 1}	url := user.APActorKeyID()	expected := "https://try.gitea.io/api/v1/activitypub/user-id/1#main-key"	assert.Equal(t, expected, url)} func TestSearchUsers(t *testing.T) {	defer unittest.OverrideFixtures("models/user/fixtures")()	require.NoError(t, unittest.PrepareTestDatabase())	testSuccess := func(opts *user_model.SearchUserOptions, expectedUserOrOrgIDs []int64) {		users, _, err := user_model.SearchUsers(db.DefaultContext, opts)		require.NoError(t, err)		cassText := fmt.Sprintf("ids: %v, opts: %v", expectedUserOrOrgIDs, opts)		if assert.Len(t, users, len(expectedUserOrOrgIDs), "case: %s", cassText) {			for i, expectedID := range expectedUserOrOrgIDs {				assert.Equal(t, expectedID, users[i].ID, "case: %s", cassText)			}		}	} 	// test orgs	testOrgSuccess := func(opts *user_model.SearchUserOptions, expectedOrgIDs []int64) {		opts.Type = user_model.UserTypeOrganization		testSuccess(opts, expectedOrgIDs)	} 	testOrgSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1, PageSize: 2}},		[]int64{3, 6}) 	testOrgSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 2, PageSize: 2}},		[]int64{7, 17}) 	testOrgSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 3, PageSize: 2}},		[]int64{19, 25}) 	testOrgSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 4, PageSize: 2}},		[]int64{26, 41}) 	testOrgSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 5, PageSize: 2}},		[]int64{}) 	// test users	testUserSuccess := func(opts *user_model.SearchUserOptions, expectedUserIDs []int64) {		opts.Type = user_model.UserTypeIndividual		testSuccess(opts, expectedUserIDs)	} 	testUserSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}},		[]int64{1, 2, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28, 29, 30, 32, 34, 37, 38, 39, 40, 1041}) 	testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(false)},		[]int64{9}) 	testUserSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)},		[]int64{1, 2, 4, 5, 8, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28, 29, 30, 32, 34, 37, 38, 39, 40, 1041}) 	testUserSuccess(&user_model.SearchUserOptions{Keyword: "user1", OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)},		[]int64{1, 10, 11, 12, 13, 14, 15, 16, 18}) 	// order by name asc default	testUserSuccess(&user_model.SearchUserOptions{Keyword: "user1", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)},		[]int64{1, 10, 11, 12, 13, 14, 15, 16, 18}) 	testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsAdmin: optional.Some(true)},		[]int64{1}) 	testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsRestricted: optional.Some(true)},		[]int64{29}) 	testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsProhibitLogin: optional.Some(true)},		[]int64{1041, 37}) 	testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsTwoFactorEnabled: optional.Some(true)},		[]int64{24, 32})} func TestEmailNotificationPreferences(t *testing.T) {	require.NoError(t, unittest.PrepareTestDatabase()) 	for _, test := range []struct {		expected string		userID   int64	}{		{user_model.EmailNotificationsEnabled, 1},		{user_model.EmailNotificationsEnabled, 2},		{user_model.EmailNotificationsOnMention, 3},		{user_model.EmailNotificationsOnMention, 4},		{user_model.EmailNotificationsEnabled, 5},		{user_model.EmailNotificationsEnabled, 6},		{user_model.EmailNotificationsDisabled, 7},		{user_model.EmailNotificationsEnabled, 8},		{user_model.EmailNotificationsOnMention, 9},	} {		user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: test.userID})		assert.Equal(t, test.expected, user.EmailNotificationsPreference)	}} func TestHashPasswordDeterministic(t *testing.T) {	b := make([]byte, 16)	u := &user_model.User{}	algos := hash.RecommendedHashAlgorithms	for j := 0; j < len(algos); j++ {		u.PasswdHashAlgo = algos[j]		for i := 0; i < 50; i++ {			// generate a random password			rand.Read(b)			pass := string(b) 			// save the current password in the user - hash it and store the result			u.SetPassword(pass)			r1 := u.Passwd 			// run again			u.SetPassword(pass)			r2 := u.Passwd 			assert.NotEqual(t, r1, r2)			assert.True(t, u.ValidatePassword(pass))		}	}} func BenchmarkHashPassword(b *testing.B) {	// BenchmarkHashPassword ensures that it takes a reasonable amount of time	// to hash a password - in order to protect from brute-force attacks.	pass := "password1337"	u := &user_model.User{Passwd: pass}	b.ResetTimer()	for i := 0; i < b.N; i++ {		u.SetPassword(pass)	}} func TestNewGitSig(t *testing.T) {	users := make([]*user_model.User, 0, 20)	err := db.GetEngine(db.DefaultContext).Find(&users)	require.NoError(t, err) 	for _, user := range users {		sig := user.NewGitSig()		assert.NotContains(t, sig.Name, "<")		assert.NotContains(t, sig.Name, ">")		assert.NotContains(t, sig.Name, "\n")		assert.NotEmpty(t, strings.TrimSpace(sig.Name))	}} func TestDisplayName(t *testing.T) {	users := make([]*user_model.User, 0, 20)	err := db.GetEngine(db.DefaultContext).Find(&users)	require.NoError(t, err) 	for _, user := range users {		displayName := user.DisplayName()		assert.Equal(t, strings.TrimSpace(displayName), displayName)		if len(strings.TrimSpace(user.FullName)) == 0 {			assert.Equal(t, user.Name, displayName)		}		assert.NotEmpty(t, strings.TrimSpace(displayName))	}} func TestCreateUserInvalidEmail(t *testing.T) {	user := &user_model.User{		Name:               "GiteaBot",		Email:              "GiteaBot@gitea.io\r\n",		Passwd:             ";p['////..-++']",		IsAdmin:            false,		Theme:              setting.UI.DefaultTheme,		MustChangePassword: false,	} 	err := user_model.CreateUser(db.DefaultContext, user)	require.Error(t, err)	assert.True(t, validation.IsErrEmailCharIsNotSupported(err))} func TestCreateUserEmailAlreadyUsed(t *testing.T) {	require.NoError(t, unittest.PrepareTestDatabase()) 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) 	// add new user with user2's email	user.Name = "testuser"	user.LowerName = strings.ToLower(user.Name)	user.ID = 0	err := user_model.CreateUser(db.DefaultContext, user)	require.Error(t, err)	assert.True(t, user_model.IsErrEmailAlreadyUsed(err))} func TestCreateUserCustomTimestamps(t *testing.T) {	require.NoError(t, unittest.PrepareTestDatabase()) 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) 	// Add new user with a custom creation timestamp.	var creationTimestamp timeutil.TimeStamp = 12345	user.Name = "testuser"	user.LowerName = strings.ToLower(user.Name)	user.ID = 0	user.Email = "unique@example.com"	user.CreatedUnix = creationTimestamp	err := user_model.CreateUser(db.DefaultContext, user)	require.NoError(t, err) 	fetched, err := user_model.GetUserByID(t.Context(), user.ID)	require.NoError(t, err)	assert.Equal(t, creationTimestamp, fetched.CreatedUnix)	assert.Equal(t, creationTimestamp, fetched.UpdatedUnix)} func TestCreateUserWithoutCustomTimestamps(t *testing.T) {	require.NoError(t, unittest.PrepareTestDatabase()) 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) 	// There is no way to use a mocked time for the XORM auto-time functionality,	// so use the real clock to approximate the expected timestamp.	timestampStart := time.Now().Unix() 	// Add new user without a custom creation timestamp.	user.Name = "Testuser"	user.LowerName = strings.ToLower(user.Name)	user.ID = 0	user.Email = "unique@example.com"	user.CreatedUnix = 0	user.UpdatedUnix = 0	err := user_model.CreateUser(db.DefaultContext, user)	require.NoError(t, err) 	timestampEnd := time.Now().Unix() 	fetched, err := user_model.GetUserByID(t.Context(), user.ID)	require.NoError(t, err) 	assert.LessOrEqual(t, timestampStart, fetched.CreatedUnix)	assert.LessOrEqual(t, fetched.CreatedUnix, timestampEnd) 	assert.LessOrEqual(t, timestampStart, fetched.UpdatedUnix)	assert.LessOrEqual(t, fetched.UpdatedUnix, timestampEnd)} func TestCreateUserClaimingUsername(t *testing.T) {	require.NoError(t, unittest.PrepareTestDatabase())	defer test.MockVariableValue(&setting.Service.UsernameCooldownPeriod, 1)() 	_, err := db.GetEngine(db.DefaultContext).NoAutoTime().Insert(&user_model.Redirect{RedirectUserID: 1, LowerName: "redirecting", CreatedUnix: timeutil.TimeStampNow()})	require.NoError(t, err) 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) 	user.Name = "redirecting"	user.LowerName = strings.ToLower(user.Name)	user.ID = 0	user.Email = "unique@example.com" 	t.Run("Normal creation", func(t *testing.T) {		err = user_model.CreateUser(db.DefaultContext, user)		assert.True(t, user_model.IsErrCooldownPeriod(err))	}) 	t.Run("Creation as admin", func(t *testing.T) {		err = user_model.AdminCreateUser(db.DefaultContext, user)		require.NoError(t, err)	})} func TestGetUserIDsByNames(t *testing.T) {	require.NoError(t, unittest.PrepareTestDatabase()) 	// ignore non existing	IDs, err := user_model.GetUserIDsByNames(db.DefaultContext, []string{"user1", "user2", "none_existing_user"}, true)	require.NoError(t, err)	assert.Equal(t, []int64{1, 2}, IDs) 	// ignore non existing	IDs, err = user_model.GetUserIDsByNames(db.DefaultContext, []string{"user1", "do_not_exist"}, false)	require.Error(t, err)	assert.Equal(t, []int64(nil), IDs)} func TestGetMaileableUsersByIDs(t *testing.T) {	require.NoError(t, unittest.PrepareTestDatabase()) 	results, err := user_model.GetMaileableUsersByIDs(db.DefaultContext, []int64{1, 4}, false)	require.NoError(t, err)	assert.Len(t, results, 1)	if len(results) > 1 {		assert.Equal(t, 1, results[0].ID)	} 	results, err = user_model.GetMaileableUsersByIDs(db.DefaultContext, []int64{1, 4}, true)	require.NoError(t, err)	assert.Len(t, results, 2)	if len(results) > 2 {		assert.Equal(t, 1, results[0].ID)		assert.Equal(t, 4, results[1].ID)	}} func TestNewUserRedirect(t *testing.T) {	// redirect to a completely new name	require.NoError(t, unittest.PrepareTestDatabase()) 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})	require.NoError(t, user_model.NewUserRedirect(db.DefaultContext, user.ID, user.Name, "newusername")) 	unittest.AssertExistsAndLoadBean(t, &user_model.Redirect{		LowerName:      user.LowerName,		RedirectUserID: user.ID,	})	unittest.AssertExistsAndLoadBean(t, &user_model.Redirect{		LowerName:      "olduser1",		RedirectUserID: user.ID,	})} func TestNewUserRedirect2(t *testing.T) {	// redirect to previously used name	require.NoError(t, unittest.PrepareTestDatabase()) 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})	require.NoError(t, user_model.NewUserRedirect(db.DefaultContext, user.ID, user.Name, "olduser1")) 	unittest.AssertExistsAndLoadBean(t, &user_model.Redirect{		LowerName:      user.LowerName,		RedirectUserID: user.ID,	})	unittest.AssertNotExistsBean(t, &user_model.Redirect{		LowerName:      "olduser1",		RedirectUserID: user.ID,	})} func TestNewUserRedirect3(t *testing.T) {	// redirect for a previously-unredirected user	require.NoError(t, unittest.PrepareTestDatabase()) 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})	require.NoError(t, user_model.NewUserRedirect(db.DefaultContext, user.ID, user.Name, "newusername")) 	unittest.AssertExistsAndLoadBean(t, &user_model.Redirect{		LowerName:      user.LowerName,		RedirectUserID: user.ID,	})} 

Lines 1–500 of 847.