From 347ee1d205c95f5fd766e206cc65bfb9782a2623 Mon Sep 17 00:00:00 2001 From: Gabe Van Engel Date: Tue, 28 Aug 2018 08:06:57 -0700 Subject: MM-11327: Restrict Teams by Email (#9142) * Check a team's AllowedDomains setting before adding users to the team. * Updated AddUser tests to validate AllowedDomains restriction. * Updated variable name to match convention. * Removed AllowedDomains from team sanitization. * Update AppError's Where to match the calling function. * Added tests for user matching allowedDomains, and multi domain values of allowedDomains. * Added test to make sure we block users who have a subdomain of a whitelisted domain. * Revert "Removed AllowedDomains from team sanitization." This reverts commit 17c2afea584da40c7d769787ae86408e9700510c. * Update sanitization tests to include dockerhost, now that we enforce AllowedDomains. * Added tests to verify the interplay between the global and per team domain restrictions. * Validate AllowedDomains property against RestrictCreationToDomains before updating a team. * Remove team.AllowedDomains from sanitization. * Add i18n string for the team allowed domains restriction app error. --- app/team.go | 86 ++++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 57 insertions(+), 29 deletions(-) (limited to 'app/team.go') diff --git a/app/team.go b/app/team.go index 8d1331823..dd372a99a 100644 --- a/app/team.go +++ b/app/team.go @@ -44,7 +44,7 @@ func (a *App) CreateTeamWithUser(team *model.Team, userId string) (*model.Team, team.Email = user.Email } - if !a.isTeamEmailAllowed(user) { + if !a.isTeamEmailAllowed(user, team) { return nil, model.NewAppError("isTeamEmailAllowed", "api.team.is_team_creation_allowed.domain.app_error", nil, "", http.StatusBadRequest) } @@ -60,35 +60,43 @@ func (a *App) CreateTeamWithUser(team *model.Team, userId string) (*model.Team, return rteam, nil } -func (a *App) isTeamEmailAddressAllowed(email string) bool { - email = strings.ToLower(email) +func (a *App) normalizeDomains(domains string) []string { // commas and @ signs are optional // can be in the form of "@corp.mattermost.com, mattermost.com mattermost.org" -> corp.mattermost.com mattermost.com mattermost.org - domains := strings.Fields(strings.TrimSpace(strings.ToLower(strings.Replace(strings.Replace(a.Config().TeamSettings.RestrictCreationToDomains, "@", " ", -1), ",", " ", -1)))) + return strings.Fields(strings.TrimSpace(strings.ToLower(strings.Replace(strings.Replace(domains, "@", " ", -1), ",", " ", -1)))) +} - matched := false - for _, d := range domains { - if strings.HasSuffix(email, "@"+d) { - matched = true - break +func (a *App) isTeamEmailAddressAllowed(email string, allowedDomains string) bool { + email = strings.ToLower(email) + // First check per team allowedDomains, then app wide restrictions + for _, restriction := range []string{allowedDomains, a.Config().TeamSettings.RestrictCreationToDomains} { + domains := a.normalizeDomains(restriction) + if len(domains) <= 0 { + continue + } + matched := false + for _, d := range domains { + if strings.HasSuffix(email, "@"+d) { + matched = true + break + } + } + if !matched { + return false } - } - - if len(a.Config().TeamSettings.RestrictCreationToDomains) > 0 && !matched { - return false } return true } -func (a *App) isTeamEmailAllowed(user *model.User) bool { +func (a *App) isTeamEmailAllowed(user *model.User, team *model.Team) bool { email := strings.ToLower(user.Email) if len(user.AuthService) > 0 && len(*user.AuthData) > 0 { return true } - return a.isTeamEmailAddressAllowed(email) + return a.isTeamEmailAddressAllowed(email, team.AllowedDomains) } func (a *App) UpdateTeam(team *model.Team) (*model.Team, *model.AppError) { @@ -98,6 +106,23 @@ func (a *App) UpdateTeam(team *model.Team) (*model.Team, *model.AppError) { return nil, err } + validDomains := a.normalizeDomains(a.Config().TeamSettings.RestrictCreationToDomains) + if len(validDomains) > 0 { + for _, domain := range a.normalizeDomains(team.AllowedDomains) { + matched := false + for _, d := range validDomains { + if domain == d { + matched = true + break + } + } + if !matched { + err := model.NewAppError("UpdateTeam", "api.team.update_restricted_domains.mismatch.app_error", map[string]interface{}{"Domain": domain}, "", http.StatusBadRequest) + return nil, err + } + } + } + oldTeam.DisplayName = team.DisplayName oldTeam.Description = team.Description oldTeam.InviteId = team.InviteId @@ -430,6 +455,9 @@ func (a *App) joinUserToTeam(team *model.Team, user *model.User) (*model.TeamMem } func (a *App) JoinUserToTeam(team *model.Team, user *model.User, userRequestorId string) *model.AppError { + if !a.isTeamEmailAllowed(user, team) { + return model.NewAppError("JoinUserToTeam", "api.team.join_user_to_team.allowed_domains.app_error", nil, "", http.StatusBadRequest) + } tm, alreadyAdded, err := a.joinUserToTeam(team, user) if err != nil { return err @@ -843,20 +871,6 @@ func (a *App) InviteNewUsersToTeam(emailList []string, teamId, senderId string) return err } - var invalidEmailList []string - - for _, email := range emailList { - if !a.isTeamEmailAddressAllowed(email) { - invalidEmailList = append(invalidEmailList, email) - } - } - - if len(invalidEmailList) > 0 { - s := strings.Join(invalidEmailList, ", ") - err := model.NewAppError("InviteNewUsersToTeam", "api.team.invite_members.invalid_email.app_error", map[string]interface{}{"Addresses": s}, "", http.StatusBadRequest) - return err - } - tchan := a.Srv.Store.Team().Get(teamId) uchan := a.Srv.Store.User().Get(senderId) @@ -874,6 +888,20 @@ func (a *App) InviteNewUsersToTeam(emailList []string, teamId, senderId string) user = result.Data.(*model.User) } + var invalidEmailList []string + + for _, email := range emailList { + if !a.isTeamEmailAddressAllowed(email, team.AllowedDomains) { + invalidEmailList = append(invalidEmailList, email) + } + } + + if len(invalidEmailList) > 0 { + s := strings.Join(invalidEmailList, ", ") + err := model.NewAppError("InviteNewUsersToTeam", "api.team.invite_members.invalid_email.app_error", map[string]interface{}{"Addresses": s}, "", http.StatusBadRequest) + return err + } + nameFormat := *a.Config().TeamSettings.TeammateNameDisplay a.SendInviteEmails(team, user.GetDisplayName(nameFormat), user.Id, emailList, a.GetSiteURL()) -- cgit v1.2.3-1-g7c22