-
-
Notifications
You must be signed in to change notification settings - Fork 5.8k
Add team member invite by email #20307
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
37 commits
Select commit
Hold shift + click to select a range
31bd5f3
Added team invite by email.
KN4CK3R 6c84ab0
Merge branch 'main' of https://github.com/go-gitea/gitea into feature…
KN4CK3R bc4ea05
Changed text.
KN4CK3R 7cce63c
check if user added to team by email (#2)
jackHay22 9ba8d28
Merge branch 'main' of https://github.com/go-gitea/gitea into feature…
KN4CK3R bb2aba5
Merge branch 'feature-invite' of https://github.com/KN4CK3R/gitea int…
KN4CK3R b7128f4
Merge branch 'main' of https://github.com/go-gitea/gitea into feature…
KN4CK3R b9c4879
Changed file name.
KN4CK3R 420dd67
Updated tests.
KN4CK3R 981639f
lint
KN4CK3R 8bd324e
Merge branch 'main' into feature-invite
6543 c8d47d1
Escape user.
KN4CK3R f3eaf01
Merge branch 'feature-invite' of https://github.com/KN4CK3R/gitea int…
KN4CK3R 5b2cf17
make fmt
6543 48a709a
Merge branch 'main' into feature-invite
6543 83badcc
Skip test if MailService is not available.
KN4CK3R 4eb81d5
Merge branch 'main' into feature-invite
6543 7b21c06
Merge branch 'main' of https://github.com/go-gitea/gitea into feature…
KN4CK3R 6874979
Merge branch 'main' of https://github.com/go-gitea/gitea into feature…
KN4CK3R eee0639
Merge branch 'main' of https://github.com/go-gitea/gitea into feature…
KN4CK3R bdf4f6c
Merge branch 'main' of https://github.com/go-gitea/gitea into feature…
KN4CK3R 5fc26b7
Add suggestions.
KN4CK3R de9c196
Update regex.
KN4CK3R 9035122
Merge branch 'main' of https://github.com/go-gitea/gitea into feature…
KN4CK3R 960982b
Merge branch 'main' into feature-invite
KN4CK3R 7b13b4a
Merge branch 'main' of https://github.com/go-gitea/gitea into feature…
KN4CK3R 8de58b3
Merge branch 'main' into feature-invite
6543 e9013dc
Merge branch 'main' of https://github.com/go-gitea/gitea into feature…
KN4CK3R 2a1e008
Merge branch 'main' into feature-invite
6543 ffae78c
Merge branch 'main' into feature-invite
6543 e3472c0
Merge branch 'main' into feature-invite
KN4CK3R f6d8c82
Merge branch 'main' into feature-invite
6543 62eb11b
Merge branch 'main' into feature-invite
6543 7794085
Merge branch 'main' into feature-invite
6543 5a4fbe5
Merge branch 'main' of https://github.com/go-gitea/gitea into feature…
KN4CK3R f1875c8
Check membership in test.
KN4CK3R 073cec2
Merge branch 'main' of https://github.com/go-gitea/gitea into feature…
KN4CK3R File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
// Copyright 2022 The Gitea Authors. All rights reserved. | ||
// Use of this source code is governed by a MIT-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package migrations | ||
|
||
import ( | ||
"code.gitea.io/gitea/modules/timeutil" | ||
|
||
"xorm.io/xorm" | ||
) | ||
|
||
func addTeamInviteTable(x *xorm.Engine) error { | ||
type TeamInvite struct { | ||
ID int64 `xorm:"pk autoincr"` | ||
Token string `xorm:"UNIQUE(token) INDEX NOT NULL DEFAULT ''"` | ||
InviterID int64 `xorm:"NOT NULL DEFAULT 0"` | ||
OrgID int64 `xorm:"INDEX NOT NULL DEFAULT 0"` | ||
TeamID int64 `xorm:"UNIQUE(team_mail) INDEX NOT NULL DEFAULT 0"` | ||
Email string `xorm:"UNIQUE(team_mail) NOT NULL DEFAULT ''"` | ||
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` | ||
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` | ||
} | ||
|
||
return x.Sync2(new(TeamInvite)) | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
// Copyright 2022 The Gitea Authors. All rights reserved. | ||
// Use of this source code is governed by a MIT-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package organization | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
"code.gitea.io/gitea/models/db" | ||
user_model "code.gitea.io/gitea/models/user" | ||
"code.gitea.io/gitea/modules/timeutil" | ||
"code.gitea.io/gitea/modules/util" | ||
|
||
"xorm.io/builder" | ||
) | ||
|
||
type ErrTeamInviteAlreadyExist struct { | ||
TeamID int64 | ||
Email string | ||
} | ||
|
||
func IsErrTeamInviteAlreadyExist(err error) bool { | ||
_, ok := err.(ErrTeamInviteAlreadyExist) | ||
return ok | ||
} | ||
|
||
func (err ErrTeamInviteAlreadyExist) Error() string { | ||
return fmt.Sprintf("team invite already exists [team_id: %d, email: %s]", err.TeamID, err.Email) | ||
} | ||
|
||
func (err ErrTeamInviteAlreadyExist) Unwrap() error { | ||
return util.ErrAlreadyExist | ||
} | ||
|
||
type ErrTeamInviteNotFound struct { | ||
Token string | ||
} | ||
|
||
func IsErrTeamInviteNotFound(err error) bool { | ||
_, ok := err.(ErrTeamInviteNotFound) | ||
return ok | ||
} | ||
|
||
func (err ErrTeamInviteNotFound) Error() string { | ||
return fmt.Sprintf("team invite was not found [token: %s]", err.Token) | ||
} | ||
|
||
func (err ErrTeamInviteNotFound) Unwrap() error { | ||
return util.ErrNotExist | ||
} | ||
|
||
// ErrUserEmailAlreadyAdded represents a "user by email already added to team" error. | ||
type ErrUserEmailAlreadyAdded struct { | ||
Email string | ||
} | ||
|
||
// IsErrUserEmailAlreadyAdded checks if an error is a ErrUserEmailAlreadyAdded. | ||
func IsErrUserEmailAlreadyAdded(err error) bool { | ||
_, ok := err.(ErrUserEmailAlreadyAdded) | ||
return ok | ||
} | ||
|
||
func (err ErrUserEmailAlreadyAdded) Error() string { | ||
return fmt.Sprintf("user with email already added [email: %s]", err.Email) | ||
} | ||
|
||
func (err ErrUserEmailAlreadyAdded) Unwrap() error { | ||
return util.ErrAlreadyExist | ||
} | ||
|
||
// TeamInvite represents an invite to a team | ||
type TeamInvite struct { | ||
ID int64 `xorm:"pk autoincr"` | ||
Token string `xorm:"UNIQUE(token) INDEX NOT NULL DEFAULT ''"` | ||
InviterID int64 `xorm:"NOT NULL DEFAULT 0"` | ||
OrgID int64 `xorm:"INDEX NOT NULL DEFAULT 0"` | ||
TeamID int64 `xorm:"UNIQUE(team_mail) INDEX NOT NULL DEFAULT 0"` | ||
Email string `xorm:"UNIQUE(team_mail) NOT NULL DEFAULT ''"` | ||
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` | ||
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` | ||
} | ||
|
||
func CreateTeamInvite(ctx context.Context, doer *user_model.User, team *Team, email string) (*TeamInvite, error) { | ||
KN4CK3R marked this conversation as resolved.
Show resolved
Hide resolved
|
||
has, err := db.GetEngine(ctx).Exist(&TeamInvite{ | ||
TeamID: team.ID, | ||
Email: email, | ||
}) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if has { | ||
return nil, ErrTeamInviteAlreadyExist{ | ||
TeamID: team.ID, | ||
Email: email, | ||
} | ||
} | ||
|
||
// check if the user is already a team member by email | ||
exist, err := db.GetEngine(ctx). | ||
Where(builder.Eq{ | ||
"team_user.org_id": team.OrgID, | ||
"team_user.team_id": team.ID, | ||
"`user`.email": email, | ||
}). | ||
Join("INNER", "`user`", "`user`.id = team_user.uid"). | ||
Table("team_user"). | ||
Exist() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
if exist { | ||
return nil, ErrUserEmailAlreadyAdded{ | ||
Email: email, | ||
} | ||
} | ||
|
||
token, err := util.CryptoRandomString(25) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
invite := &TeamInvite{ | ||
Token: token, | ||
InviterID: doer.ID, | ||
OrgID: team.OrgID, | ||
TeamID: team.ID, | ||
Email: email, | ||
} | ||
|
||
return invite, db.Insert(ctx, invite) | ||
} | ||
|
||
func RemoveInviteByID(ctx context.Context, inviteID, teamID int64) error { | ||
_, err := db.DeleteByBean(ctx, &TeamInvite{ | ||
ID: inviteID, | ||
TeamID: teamID, | ||
}) | ||
return err | ||
} | ||
|
||
func GetInvitesByTeamID(ctx context.Context, teamID int64) ([]*TeamInvite, error) { | ||
invites := make([]*TeamInvite, 0, 10) | ||
return invites, db.GetEngine(ctx). | ||
Where("team_id=?", teamID). | ||
Find(&invites) | ||
} | ||
|
||
func GetInviteByToken(ctx context.Context, token string) (*TeamInvite, error) { | ||
invite := &TeamInvite{} | ||
|
||
has, err := db.GetEngine(ctx).Where("token=?", token).Get(invite) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if !has { | ||
return nil, ErrTeamInviteNotFound{Token: token} | ||
} | ||
return invite, nil | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
// Copyright 2022 The Gitea Authors. All rights reserved. | ||
// Use of this source code is governed by a MIT-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package organization_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"code.gitea.io/gitea/models/db" | ||
"code.gitea.io/gitea/models/organization" | ||
"code.gitea.io/gitea/models/unittest" | ||
user_model "code.gitea.io/gitea/models/user" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestTeamInvite(t *testing.T) { | ||
assert.NoError(t, unittest.PrepareTestDatabase()) | ||
|
||
team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 2}) | ||
|
||
t.Run("MailExistsInTeam", func(t *testing.T) { | ||
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) | ||
|
||
// user 2 already added to team 2, should result in error | ||
_, err := organization.CreateTeamInvite(db.DefaultContext, user2, team, user2.Email) | ||
assert.Error(t, err) | ||
}) | ||
|
||
t.Run("CreateAndRemove", func(t *testing.T) { | ||
user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) | ||
|
||
invite, err := organization.CreateTeamInvite(db.DefaultContext, user1, team, "[email protected]") | ||
assert.NotNil(t, invite) | ||
assert.NoError(t, err) | ||
|
||
// Shouldn't allow duplicate invite | ||
_, err = organization.CreateTeamInvite(db.DefaultContext, user1, team, "[email protected]") | ||
assert.Error(t, err) | ||
|
||
// should remove invite | ||
assert.NoError(t, organization.RemoveInviteByID(db.DefaultContext, invite.ID, invite.TeamID)) | ||
|
||
// invite should not exist | ||
_, err = organization.GetInviteByToken(db.DefaultContext, invite.Token) | ||
assert.Error(t, err) | ||
}) | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.