* MM-14139: Creating permissions for invite/promote/demote guests (#10778)

* MM-14139: Creating permissions for invite/promote/demote guests

* Fixing tests

* Adding invite guest api endpoint (#10792)

* Adding invite guest api endpoint

* Adding i18n

* Adding some tests

* WIP

* Migrating Token.Extra info to bigger size (2048)

* Fixing tests

* Adding client function for invite guests

* Adding send guests invites tests

* Renaming file from guest to guest_invite

* Adding Promote/Demote users from/to guest endpoints (#10791)

* Adding Promote/Demote users from/to guest endpoints

* Adding i18n translations

* Adding the client functions

* Using getQueryBuilder function

* Addressing PR review comments

* Adding default channels to users on promte from guest (#10851)

* Adding default channels to users on promte from guest

* Addressing PR review comments

* Fixing merge problems

* Sending websockets events on promote/demote (#11403)

* Sending websockets events on promote/demote

* Fixing merge problems

* Fixing govet shadowing problem

* Fixing feature branch tests

* Avoiding leaking users data through websockets for guest accounts (#11489)

* Avoiding leaking users data through websockets for guest accounts

* Adding tests and fixing code error

* Fixing i18n

* Allow to enable/disable guests and other extra config settings (#11481)

* Allow to enable/disable guests and other extra config settings

* Fixing tests and moving license and config validation to api level

* Update api4/role_test.go

Co-Authored-By: George Goldberg <george@gberg.me>

* Update api4/role_test.go

Co-Authored-By: George Goldberg <george@gberg.me>

* Fixing typo

* fixing tests

* Managing correctly the guest channel leave behavior (#11578)

* MM-15134: Removing guests from teams or system on leave channels if needed

* WIP

* No deactivating the guest user when leave the last team

* Adding a couple of tests

* Fixing shadow variables

* Fixing tests

* fixing tests

* fixing shadow variables

* Adding guest counts for channel stats (#11646)

* Adding guest counts for channel stats

* Adding tests

* Fixing tests

* Fixing guest domain restrictions (#11660)

* Adding needed migration for the database

* Fixing migration
Этот коммит содержится в:
Jesús Espino
2019-07-22 22:13:39 +02:00
коммит произвёл GitHub
родитель fdde7c8287
Коммит fe8a0f6485
48 изменённых файлов: 2630 добавлений и 133 удалений

Просмотреть файл

@@ -11,6 +11,7 @@ import (
type ChannelStats struct {
ChannelId string `json:"channel_id"`
MemberCount int64 `json:"member_count"`
GuestCount int64 `json:"guest_count"`
}
func (o *ChannelStats) ToJson() string {

Просмотреть файл

@@ -1078,6 +1078,26 @@ func (c *Client4) UpdateUserPassword(userId, currentPassword, newPassword string
return CheckStatusOK(r), BuildResponse(r)
}
// PromoteGuestToUser convert a guest into a regular user
func (c *Client4) PromoteGuestToUser(guestId string) (bool, *Response) {
r, err := c.DoApiPost(c.GetUserRoute(guestId)+"/promote", "")
if err != nil {
return false, BuildErrorResponse(r, err)
}
defer closeBody(r)
return CheckStatusOK(r), BuildResponse(r)
}
// DemoteUserToGuest convert a regular user into a guest
func (c *Client4) DemoteUserToGuest(guestId string) (bool, *Response) {
r, err := c.DoApiPost(c.GetUserRoute(guestId)+"/demote", "")
if err != nil {
return false, BuildErrorResponse(r, err)
}
defer closeBody(r)
return CheckStatusOK(r), BuildResponse(r)
}
// UpdateUserRoles updates a user's roles in the system. A user can have "system_user" and "system_admin" roles.
func (c *Client4) UpdateUserRoles(userId, roles string) (bool, *Response) {
requestBody := map[string]string{"roles": roles}
@@ -1904,6 +1924,21 @@ func (c *Client4) InviteUsersToTeam(teamId string, userEmails []string) (bool, *
return CheckStatusOK(r), BuildResponse(r)
}
// InviteGuestsToTeam invite guest by email to some channels in a team.
func (c *Client4) InviteGuestsToTeam(teamId string, userEmails []string, channels []string, message string) (bool, *Response) {
guestsInvite := GuestsInvite{
Emails: userEmails,
Channels: channels,
Message: message,
}
r, err := c.DoApiPost(c.GetTeamRoute(teamId)+"/invite-guests/email", guestsInvite.ToJson())
if err != nil {
return false, BuildErrorResponse(r, err)
}
defer closeBody(r)
return CheckStatusOK(r), BuildResponse(r)
}
// InvalidateEmailInvites will invalidate active email invitations that have not been accepted by the user.
func (c *Client4) InvalidateEmailInvites() (bool, *Response) {
r, err := c.DoApiDelete(c.GetTeamsRoute() + "/invites/email")

Просмотреть файл

@@ -2312,6 +2312,31 @@ func (s *DisplaySettings) SetDefaults() {
}
}
type GuestAccountsSettings struct {
Enable *bool
AllowEmailAccounts *bool
EnforceMultifactorAuthentication *bool
RestrictCreationToDomains *string
}
func (s *GuestAccountsSettings) SetDefaults() {
if s.Enable == nil {
s.Enable = NewBool(false)
}
if s.AllowEmailAccounts == nil {
s.AllowEmailAccounts = NewBool(true)
}
if s.EnforceMultifactorAuthentication == nil {
s.EnforceMultifactorAuthentication = NewBool(false)
}
if s.RestrictCreationToDomains == nil {
s.RestrictCreationToDomains = NewString("")
}
}
type ImageProxySettings struct {
Enable *bool
ImageProxyType *string
@@ -2388,6 +2413,7 @@ type Config struct {
JobSettings JobSettings
PluginSettings PluginSettings
DisplaySettings DisplaySettings
GuestAccountsSettings GuestAccountsSettings
ImageProxySettings ImageProxySettings
}
@@ -2471,6 +2497,7 @@ func (o *Config) SetDefaults() {
o.JobSettings.SetDefaults()
o.MessageExportSettings.SetDefaults()
o.DisplaySettings.SetDefaults()
o.GuestAccountsSettings.SetDefaults()
o.ImageProxySettings.SetDefaults(o.ServiceSettings)
}

53
model/guest_invite.go Обычный файл
Просмотреть файл

@@ -0,0 +1,53 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package model
import (
"encoding/json"
"io"
"net/http"
)
type GuestsInvite struct {
Emails []string `json:"emails"`
Channels []string `json:"channels"`
Message string `json:"message"`
}
// IsValid validates the user and returns an error if it isn't configured
// correctly.
func (i *GuestsInvite) IsValid() *AppError {
if len(i.Emails) == 0 {
return NewAppError("GuestsInvite.IsValid", "model.guest.is_valid.emails.app_error", nil, "", http.StatusBadRequest)
}
for _, email := range i.Emails {
if len(email) > USER_EMAIL_MAX_LENGTH || len(email) == 0 || !IsValidEmail(email) {
return NewAppError("GuestsInvite.IsValid", "model.guest.is_valid.email.app_error", nil, "email="+email, http.StatusBadRequest)
}
}
if len(i.Channels) == 0 {
return NewAppError("GuestsInvite.IsValid", "model.guest.is_valid.channels.app_error", nil, "", http.StatusBadRequest)
}
for _, channel := range i.Channels {
if len(channel) != 26 {
return NewAppError("GuestsInvite.IsValid", "model.guest.is_valid.channel.app_error", nil, "channel="+channel, http.StatusBadRequest)
}
}
return nil
}
// GuestsInviteFromJson will decode the input and return a GuestsInvite
func GuestsInviteFromJson(data io.Reader) *GuestsInvite {
var invite *GuestsInvite
json.NewDecoder(data).Decode(&invite)
return invite
}
func (invite *GuestsInvite) ToJson() string {
b, _ := json.Marshal(invite)
return string(b)
}

Просмотреть файл

@@ -59,6 +59,7 @@ type Features struct {
MessageExport *bool `json:"message_export"`
CustomPermissionsSchemes *bool `json:"custom_permissions_schemes"`
CustomTermsOfService *bool `json:"custom_terms_of_service"`
GuestAccountsPermissions *bool `json:"guest_accounts_permissions"`
// after we enabled more features we'll need to control them with this
FutureFeatures *bool `json:"future_features"`
@@ -81,6 +82,7 @@ func (f *Features) ToMap() map[string]interface{} {
"data_retention": *f.DataRetention,
"message_export": *f.MessageExport,
"custom_permissions_schemes": *f.CustomPermissionsSchemes,
"guest_accounts_permissions": *f.GuestAccountsPermissions,
"future": *f.FutureFeatures,
}
}
@@ -162,6 +164,10 @@ func (f *Features) SetDefaults() {
f.CustomPermissionsSchemes = NewBool(*f.FutureFeatures)
}
if f.GuestAccountsPermissions == nil {
f.GuestAccountsPermissions = NewBool(*f.FutureFeatures)
}
if f.CustomTermsOfService == nil {
f.CustomTermsOfService = NewBool(*f.FutureFeatures)
}

Просмотреть файл

@@ -52,6 +52,7 @@ func TestLicenseFeaturesSetDefaults(t *testing.T) {
CheckTrue(t, *f.DataRetention)
CheckTrue(t, *f.MessageExport)
CheckTrue(t, *f.CustomPermissionsSchemes)
CheckTrue(t, *f.GuestAccountsPermissions)
CheckTrue(t, *f.FutureFeatures)
f = Features{}
@@ -73,6 +74,7 @@ func TestLicenseFeaturesSetDefaults(t *testing.T) {
*f.DataRetention = true
*f.MessageExport = true
*f.CustomPermissionsSchemes = true
*f.GuestAccountsPermissions = true
*f.EmailNotificationContents = true
f.SetDefaults()
@@ -93,6 +95,7 @@ func TestLicenseFeaturesSetDefaults(t *testing.T) {
CheckTrue(t, *f.DataRetention)
CheckTrue(t, *f.MessageExport)
CheckTrue(t, *f.CustomPermissionsSchemes)
CheckTrue(t, *f.GuestAccountsPermissions)
CheckFalse(t, *f.FutureFeatures)
}
@@ -176,6 +179,7 @@ func TestLicenseToFromJson(t *testing.T) {
CheckBool(t, *f1.DataRetention, *f.DataRetention)
CheckBool(t, *f1.MessageExport, *f.MessageExport)
CheckBool(t, *f1.CustomPermissionsSchemes, *f.CustomPermissionsSchemes)
CheckBool(t, *f1.GuestAccountsPermissions, *f.GuestAccountsPermissions)
CheckBool(t, *f1.FutureFeatures, *f.FutureFeatures)
invalid := `{"asdf`

Просмотреть файл

@@ -87,6 +87,9 @@ var PERMISSION_READ_OTHERS_BOTS *Permission
var PERMISSION_MANAGE_BOTS *Permission
var PERMISSION_MANAGE_OTHERS_BOTS *Permission
var PERMISSION_VIEW_MEMBERS *Permission
var PERMISSION_INVITE_GUEST *Permission
var PERMISSION_PROMOTE_GUEST *Permission
var PERMISSION_DEMOTE_TO_GUEST *Permission
// General permission that encompasses all system admin functions
// in the future this could be broken up to allow access to some
@@ -533,6 +536,25 @@ func initializePermissions() {
"authentication.permisssions.view_members.description",
PERMISSION_SCOPE_TEAM,
}
PERMISSION_INVITE_GUEST = &Permission{
"invite_guest",
"authentication.permissions.invite_guest.name",
"authentication.permissions.invite_guest.description",
PERMISSION_SCOPE_TEAM,
}
PERMISSION_PROMOTE_GUEST = &Permission{
"promote_guest",
"authentication.permissions.promote_guest.name",
"authentication.permissions.promote_guest.description",
PERMISSION_SCOPE_SYSTEM,
}
PERMISSION_DEMOTE_TO_GUEST = &Permission{
"demote_to_guest",
"authentication.permissions.demote_to_guest.name",
"authentication.permissions.demote_to_guest.description",
PERMISSION_SCOPE_SYSTEM,
}
ALL_PERMISSIONS = []*Permission{
PERMISSION_INVITE_USER,
@@ -606,6 +628,9 @@ func initializePermissions() {
PERMISSION_MANAGE_OTHERS_BOTS,
PERMISSION_MANAGE_SYSTEM,
PERMISSION_VIEW_MEMBERS,
PERMISSION_INVITE_GUEST,
PERMISSION_PROMOTE_GUEST,
PERMISSION_DEMOTE_TO_GUEST,
}
}

Просмотреть файл

@@ -382,6 +382,9 @@ func MakeDefaultRoles() map[string]*Role {
PERMISSION_EDIT_OTHERS_POSTS.Id,
PERMISSION_MANAGE_OAUTH.Id,
PERMISSION_INVITE_USER.Id,
PERMISSION_INVITE_GUEST.Id,
PERMISSION_PROMOTE_GUEST.Id,
PERMISSION_DEMOTE_TO_GUEST.Id,
PERMISSION_DELETE_POST.Id,
PERMISSION_DELETE_OTHERS_POSTS.Id,
PERMISSION_CREATE_TEAM.Id,

Просмотреть файл

@@ -22,6 +22,7 @@ const (
SESSION_PROP_IS_BOT = "is_bot"
SESSION_PROP_IS_BOT_VALUE = "true"
SESSION_TYPE_USER_ACCESS_TOKEN = "UserAccessToken"
SESSION_PROP_IS_GUEST = "is_guest"
SESSION_ACTIVITY_TIMEOUT = 1000 * 60 * 5 // 5 minutes
SESSION_USER_ACCESS_TOKEN_EXPIRY = 100 * 365 // 100 years
)