4
0
mirror of https://github.com/cwinfo/matterbridge.git synced 2025-07-03 06:07:45 +00:00

Update vendor (#1265)

This commit is contained in:
Wim
2020-10-19 23:40:00 +02:00
committed by GitHub
parent 950f2759bd
commit 075a84427f
242 changed files with 22338 additions and 1486 deletions

View File

@ -13,9 +13,10 @@ import (
)
const (
BOT_DISPLAY_NAME_MAX_RUNES = USER_FIRST_NAME_MAX_RUNES
BOT_DESCRIPTION_MAX_RUNES = 1024
BOT_CREATOR_ID_MAX_RUNES = KEY_VALUE_PLUGIN_ID_MAX_RUNES // UserId or PluginId
BOT_DISPLAY_NAME_MAX_RUNES = USER_FIRST_NAME_MAX_RUNES
BOT_DESCRIPTION_MAX_RUNES = 1024
BOT_CREATOR_ID_MAX_RUNES = KEY_VALUE_PLUGIN_ID_MAX_RUNES // UserId or PluginId
BOT_WARN_METRIC_BOT_USERNAME = "mattermost-advisor"
)
// Bot is a special type of User meant for programmatic interactions.

View File

@ -120,12 +120,18 @@ type ChannelModeratedRolesPatch struct {
// PerPage number of results per page, if paginated.
//
type ChannelSearchOpts struct {
NotAssociatedToGroup string
ExcludeDefaultChannels bool
IncludeDeleted bool
ExcludeChannelNames []string
Page *int
PerPage *int
NotAssociatedToGroup string
ExcludeDefaultChannels bool
IncludeDeleted bool
Deleted bool
ExcludeChannelNames []string
TeamIds []string
GroupConstrained bool
ExcludeGroupConstrained bool
Public bool
Private bool
Page *int
PerPage *int
}
type ChannelMemberCountByGroup struct {

View File

@ -10,7 +10,8 @@ type ChannelMemberHistoryResult struct {
LeaveTime *int64
// these two fields are never set in the database - when we SELECT, we join on Users to get them
UserEmail string `db:"Email"`
Username string
IsBot bool
UserEmail string `db:"Email"`
Username string
IsBot bool
UserDeleteAt int64
}

View File

@ -11,11 +11,18 @@ import (
const CHANNEL_SEARCH_DEFAULT_LIMIT = 50
type ChannelSearch struct {
Term string `json:"term"`
ExcludeDefaultChannels bool `json:"exclude_default_channels"`
NotAssociatedToGroup string `json:"not_associated_to_group"`
Page *int `json:"page,omitempty"`
PerPage *int `json:"per_page,omitempty"`
Term string `json:"term"`
ExcludeDefaultChannels bool `json:"exclude_default_channels"`
NotAssociatedToGroup string `json:"not_associated_to_group"`
TeamIds []string `json:"team_ids"`
GroupConstrained bool `json:"group_constrained"`
ExcludeGroupConstrained bool `json:"exclude_group_constrained"`
Public bool `json:"public"`
Private bool `json:"private"`
IncludeDeleted bool `json:"include_deleted"`
Deleted bool `json:"deleted"`
Page *int `json:"page,omitempty"`
PerPage *int `json:"per_page,omitempty"`
}
// ToJson convert a Channel to a json string

View File

@ -0,0 +1,111 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package model
import (
"encoding/json"
"io"
)
type SidebarCategoryType string
type SidebarCategorySorting string
const (
// Each sidebar category has a 'type'. System categories are Channels, Favorites and DMs
// All user-created categories will have type Custom
SidebarCategoryChannels SidebarCategoryType = "channels"
SidebarCategoryDirectMessages SidebarCategoryType = "direct_messages"
SidebarCategoryFavorites SidebarCategoryType = "favorites"
SidebarCategoryCustom SidebarCategoryType = "custom"
// Increment to use when adding/reordering things in the sidebar
MinimalSidebarSortDistance = 10
// Default Sort Orders for categories
DefaultSidebarSortOrderFavorites = 0
DefaultSidebarSortOrderChannels = DefaultSidebarSortOrderFavorites + MinimalSidebarSortDistance
DefaultSidebarSortOrderDMs = DefaultSidebarSortOrderChannels + MinimalSidebarSortDistance
// Sorting modes
// default for all categories except DMs (behaves like manual)
SidebarCategorySortDefault SidebarCategorySorting = ""
// sort manually
SidebarCategorySortManual SidebarCategorySorting = "manual"
// sort by recency (default for DMs)
SidebarCategorySortRecent SidebarCategorySorting = "recent"
// sort by display name alphabetically
SidebarCategorySortAlphabetical SidebarCategorySorting = "alpha"
)
// SidebarCategory represents the corresponding DB table
// SortOrder is never returned to the user and only used for queries
type SidebarCategory struct {
Id string `json:"id"`
UserId string `json:"user_id"`
TeamId string `json:"team_id"`
SortOrder int64 `json:"-"`
Sorting SidebarCategorySorting `json:"sorting"`
Type SidebarCategoryType `json:"type"`
DisplayName string `json:"display_name"`
}
// SidebarCategoryWithChannels combines data from SidebarCategory table with the Channel IDs that belong to that category
type SidebarCategoryWithChannels struct {
SidebarCategory
Channels []string `json:"channel_ids"`
}
type SidebarCategoryOrder []string
// OrderedSidebarCategories combines categories, their channel IDs and an array of Category IDs, sorted
type OrderedSidebarCategories struct {
Categories SidebarCategoriesWithChannels `json:"categories"`
Order SidebarCategoryOrder `json:"order"`
}
type SidebarChannel struct {
ChannelId string `json:"channel_id"`
UserId string `json:"user_id"`
CategoryId string `json:"category_id"`
SortOrder int64 `json:"-"`
}
type SidebarChannels []*SidebarChannel
type SidebarCategoriesWithChannels []*SidebarCategoryWithChannels
func SidebarCategoryFromJson(data io.Reader) (*SidebarCategoryWithChannels, error) {
var o *SidebarCategoryWithChannels
err := json.NewDecoder(data).Decode(&o)
return o, err
}
func SidebarCategoriesFromJson(data io.Reader) ([]*SidebarCategoryWithChannels, error) {
var o []*SidebarCategoryWithChannels
err := json.NewDecoder(data).Decode(&o)
return o, err
}
func OrderedSidebarCategoriesFromJson(data io.Reader) (*OrderedSidebarCategories, error) {
var o *OrderedSidebarCategories
err := json.NewDecoder(data).Decode(&o)
return o, err
}
func (o SidebarCategoryWithChannels) ToJson() []byte {
b, _ := json.Marshal(o)
return b
}
func SidebarCategoriesWithChannelsToJson(o []*SidebarCategoryWithChannels) []byte {
if b, err := json.Marshal(o); err != nil {
return []byte("[]")
} else {
return b
}
}
func (o OrderedSidebarCategories) ToJson() []byte {
if b, err := json.Marshal(o); err != nil {
return []byte("[]")
} else {
return b
}
}

View File

@ -10,6 +10,7 @@ import (
"io"
"io/ioutil"
"mime/multipart"
"net"
"net/http"
"net/url"
"strconv"
@ -61,6 +62,40 @@ type Client4 struct {
AuthToken string
AuthType string
HttpHeader map[string]string // Headers to be copied over for each request
// TrueString is the string value sent to the server for true boolean query parameters.
trueString string
// FalseString is the string value sent to the server for false boolean query parameters.
falseString string
}
// SetBoolString is a helper method for overriding how true and false query string parameters are
// sent to the server.
//
// This method is only exposed for testing. It is never necessary to configure these values
// in production.
func (c *Client4) SetBoolString(value bool, valueStr string) {
if value {
c.trueString = valueStr
} else {
c.falseString = valueStr
}
}
// boolString builds the query string parameter for boolean values.
func (c *Client4) boolString(value bool) string {
if value && c.trueString != "" {
return c.trueString
} else if !value && c.falseString != "" {
return c.falseString
}
if value {
return "true"
} else {
return "false"
}
}
func closeBody(r *http.Response) {
@ -81,7 +116,21 @@ func (c *Client4) Must(result interface{}, resp *Response) interface{} {
}
func NewAPIv4Client(url string) *Client4 {
return &Client4{url, url + API_URL_SUFFIX, &http.Client{}, "", "", map[string]string{}}
url = strings.TrimRight(url, "/")
return &Client4{url, url + API_URL_SUFFIX, &http.Client{}, "", "", map[string]string{}, "", ""}
}
func NewAPIv4SocketClient(socketPath string) *Client4 {
tr := &http.Transport{
Dial: func(network, addr string) (net.Conn, error) {
return net.Dial("unix", socketPath)
},
}
client := NewAPIv4Client("http://_")
client.HttpClient = &http.Client{Transport: tr}
return client
}
func BuildErrorResponse(r *http.Response, err *AppError) *Response {
@ -140,6 +189,10 @@ func (c *Client4) GetUserRoute(userId string) string {
return fmt.Sprintf(c.GetUsersRoute()+"/%v", userId)
}
func (c *Client4) GetUserCategoryRoute(userID, teamID string) string {
return c.GetUserRoute(userID) + c.GetTeamRoute(teamID) + "/channels/categories"
}
func (c *Client4) GetUserAccessTokensRoute() string {
return fmt.Sprintf(c.GetUsersRoute() + "/tokens")
}
@ -261,6 +314,14 @@ func (c *Client4) GetFileRoute(fileId string) string {
return fmt.Sprintf(c.GetFilesRoute()+"/%v", fileId)
}
func (c *Client4) GetUploadsRoute() string {
return "/uploads"
}
func (c *Client4) GetUploadRoute(uploadId string) string {
return fmt.Sprintf("%s/%s", c.GetUploadsRoute(), uploadId)
}
func (c *Client4) GetPluginsRoute() string {
return "/plugins"
}
@ -453,6 +514,10 @@ func (c *Client4) GetGroupsRoute() string {
return "/groups"
}
func (c *Client4) GetPublishUserTypingRoute(userId string) string {
return c.GetUserRoute(userId) + "/typing"
}
func (c *Client4) GetGroupRoute(groupID string) string {
return fmt.Sprintf("%s/%s", c.GetGroupsRoute(), groupID)
}
@ -650,7 +715,7 @@ func (c *Client4) LoginByLdap(loginId string, password string) (*User, *Response
m := make(map[string]string)
m["login_id"] = loginId
m["password"] = password
m["ldap_only"] = "true"
m["ldap_only"] = c.boolString(true)
return c.login(m)
}
@ -967,6 +1032,17 @@ func (c *Client4) GetUsersWithoutTeam(page int, perPage int, etag string) ([]*Us
return UserListFromJson(r.Body), BuildResponse(r)
}
// GetUsersInGroup returns a page of users in a group. Page counting starts at 0.
func (c *Client4) GetUsersInGroup(groupID string, page int, perPage int, etag string) ([]*User, *Response) {
query := fmt.Sprintf("?in_group=%v&page=%v&per_page=%v", groupID, page, perPage)
r, err := c.DoApiGet(c.GetUsersRoute()+query, etag)
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return UserListFromJson(r.Body), BuildResponse(r)
}
// GetUsersByIds returns a list of users based on the provided user ids.
func (c *Client4) GetUsersByIds(userIds []string) ([]*User, *Response) {
r, err := c.DoApiPost(c.GetUsersRoute()+"/ids", ArrayToJson(userIds))
@ -1119,6 +1195,17 @@ func (c *Client4) UpdateUserPassword(userId, currentPassword, newPassword string
return CheckStatusOK(r), BuildResponse(r)
}
// UpdateUserHashedPassword updates a user's password with an already-hashed password. Must be a system administrator.
func (c *Client4) UpdateUserHashedPassword(userId, newHashedPassword string) (bool, *Response) {
requestBody := map[string]string{"already_hashed": "true", "new_password": newHashedPassword}
r, err := c.DoApiPut(c.GetUserRoute(userId)+"/password", MapToJson(requestBody))
if err != nil {
return false, BuildErrorResponse(r, err)
}
defer closeBody(r)
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", "")
@ -1173,6 +1260,50 @@ func (c *Client4) DeleteUser(userId string) (bool, *Response) {
return CheckStatusOK(r), BuildResponse(r)
}
// PermanentDeleteUser deletes a user in the system based on the provided user id string.
func (c *Client4) PermanentDeleteUser(userId string) (bool, *Response) {
r, err := c.DoApiDelete(c.GetUserRoute(userId) + "?permanent=" + c.boolString(true))
if err != nil {
return false, BuildErrorResponse(r, err)
}
defer closeBody(r)
return CheckStatusOK(r), BuildResponse(r)
}
// ConvertUserToBot converts a user to a bot user.
func (c *Client4) ConvertUserToBot(userId string) (*Bot, *Response) {
r, err := c.DoApiPost(c.GetUserRoute(userId)+"/convert_to_bot", "")
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return BotFromJson(r.Body), BuildResponse(r)
}
// ConvertBotToUser converts a bot user to a user.
func (c *Client4) ConvertBotToUser(userId string, userPatch *UserPatch, setSystemAdmin bool) (*User, *Response) {
var query string
if setSystemAdmin {
query = "?set_system_admin=true"
}
r, err := c.DoApiPost(c.GetBotRoute(userId)+"/convert_to_user"+query, userPatch.ToJson())
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return UserFromJson(r.Body), BuildResponse(r)
}
// PermanentDeleteAll permanently deletes all users in the system. This is a local only endpoint
func (c *Client4) PermanentDeleteAllUsers() (bool, *Response) {
r, err := c.DoApiDelete(c.GetUsersRoute())
if err != nil {
return false, BuildErrorResponse(r, err)
}
defer closeBody(r)
return CheckStatusOK(r), BuildResponse(r)
}
// SendPasswordResetEmail will send a link for password resetting to a user with the
// provided email.
func (c *Client4) SendPasswordResetEmail(email string) (bool, *Response) {
@ -1287,6 +1418,16 @@ func (c *Client4) VerifyUserEmail(token string) (bool, *Response) {
return CheckStatusOK(r), BuildResponse(r)
}
// VerifyUserEmailWithoutToken will verify a user's email by its Id. (Requires manage system role)
func (c *Client4) VerifyUserEmailWithoutToken(userId string) (*User, *Response) {
r, err := c.DoApiPost(c.GetUserRoute(userId)+"/email/verify/member", "")
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return UserFromJson(r.Body), BuildResponse(r)
}
// SendVerificationEmail will send an email to the user with the provided email address, if
// that user exists. The email will contain a link that can be used to verify the user's
// email address.
@ -1487,7 +1628,7 @@ func (c *Client4) GetBot(userId string, etag string) (*Bot, *Response) {
// GetBot fetches the given bot, even if it is deleted.
func (c *Client4) GetBotIncludeDeleted(userId string, etag string) (*Bot, *Response) {
r, err := c.DoApiGet(c.GetBotRoute(userId)+"?include_deleted=true", etag)
r, err := c.DoApiGet(c.GetBotRoute(userId)+"?include_deleted="+c.boolString(true), etag)
if err != nil {
return nil, BuildErrorResponse(r, err)
}
@ -1508,7 +1649,7 @@ func (c *Client4) GetBots(page, perPage int, etag string) ([]*Bot, *Response) {
// GetBotsIncludeDeleted fetches the given page of bots, including deleted.
func (c *Client4) GetBotsIncludeDeleted(page, perPage int, etag string) ([]*Bot, *Response) {
query := fmt.Sprintf("?page=%v&per_page=%v&include_deleted=true", page, perPage)
query := fmt.Sprintf("?page=%v&per_page=%v&include_deleted="+c.boolString(true), page, perPage)
r, err := c.DoApiGet(c.GetBotsRoute()+query, etag)
if err != nil {
return nil, BuildErrorResponse(r, err)
@ -1519,7 +1660,7 @@ func (c *Client4) GetBotsIncludeDeleted(page, perPage int, etag string) ([]*Bot,
// GetBotsOrphaned fetches the given page of bots, only including orphanded bots.
func (c *Client4) GetBotsOrphaned(page, perPage int, etag string) ([]*Bot, *Response) {
query := fmt.Sprintf("?page=%v&per_page=%v&only_orphaned=true", page, perPage)
query := fmt.Sprintf("?page=%v&per_page=%v&only_orphaned="+c.boolString(true), page, perPage)
r, err := c.DoApiGet(c.GetBotsRoute()+query, etag)
if err != nil {
return nil, BuildErrorResponse(r, err)
@ -1659,7 +1800,7 @@ func (c *Client4) GetAllTeams(etag string, page int, perPage int) ([]*Team, *Res
// GetAllTeamsWithTotalCount returns all teams based on permissions.
func (c *Client4) GetAllTeamsWithTotalCount(etag string, page int, perPage int) ([]*Team, int64, *Response) {
query := fmt.Sprintf("?page=%v&per_page=%v&include_total_count=true", page, perPage)
query := fmt.Sprintf("?page=%v&per_page=%v&include_total_count="+c.boolString(true), page, perPage)
r, err := c.DoApiGet(c.GetTeamsRoute()+query, etag)
if err != nil {
return nil, 0, BuildErrorResponse(r, err)
@ -1811,7 +1952,7 @@ func (c *Client4) SoftDeleteTeam(teamId string) (bool, *Response) {
// PermanentDeleteTeam deletes the team, should only be used when needed for
// compliance and the like.
func (c *Client4) PermanentDeleteTeam(teamId string) (bool, *Response) {
r, err := c.DoApiDelete(c.GetTeamRoute(teamId) + "?permanent=true")
r, err := c.DoApiDelete(c.GetTeamRoute(teamId) + "?permanent=" + c.boolString(true))
if err != nil {
return false, BuildErrorResponse(r, err)
}
@ -1931,7 +2072,7 @@ func (c *Client4) AddTeamMembersGracefully(teamId string, userIds []string) ([]*
members = append(members, member)
}
r, err := c.DoApiPost(c.GetTeamMembersRoute(teamId)+"/batch?graceful=true", TeamMembersToJson(members))
r, err := c.DoApiPost(c.GetTeamMembersRoute(teamId)+"/batch?graceful="+c.boolString(true), TeamMembersToJson(members))
if err != nil {
return nil, BuildErrorResponse(r, err)
}
@ -2049,7 +2190,7 @@ func (c *Client4) InviteGuestsToTeam(teamId string, userEmails []string, channel
// InviteUsersToTeam invite users by email to the team.
func (c *Client4) InviteUsersToTeamGracefully(teamId string, userEmails []string) ([]*EmailInviteWithError, *Response) {
r, err := c.DoApiPost(c.GetTeamRoute(teamId)+"/invite/email?graceful=true", ArrayToJson(userEmails))
r, err := c.DoApiPost(c.GetTeamRoute(teamId)+"/invite/email?graceful="+c.boolString(true), ArrayToJson(userEmails))
if err != nil {
return nil, BuildErrorResponse(r, err)
}
@ -2064,7 +2205,7 @@ func (c *Client4) InviteGuestsToTeamGracefully(teamId string, userEmails []strin
Channels: channels,
Message: message,
}
r, err := c.DoApiPost(c.GetTeamRoute(teamId)+"/invite-guests/email?graceful=true", guestsInvite.ToJson())
r, err := c.DoApiPost(c.GetTeamRoute(teamId)+"/invite-guests/email?graceful="+c.boolString(true), guestsInvite.ToJson())
if err != nil {
return nil, BuildErrorResponse(r, err)
}
@ -2163,7 +2304,16 @@ func (c *Client4) RemoveTeamIcon(teamId string) (bool, *Response) {
// GetAllChannels get all the channels. Must be a system administrator.
func (c *Client4) GetAllChannels(page int, perPage int, etag string) (*ChannelListWithTeamData, *Response) {
query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage)
return c.getAllChannels(page, perPage, etag, false)
}
// GetAllChannelsIncludeDeleted get all the channels. Must be a system administrator.
func (c *Client4) GetAllChannelsIncludeDeleted(page int, perPage int, etag string) (*ChannelListWithTeamData, *Response) {
return c.getAllChannels(page, perPage, etag, true)
}
func (c *Client4) getAllChannels(page int, perPage int, etag string, includeDeleted bool) (*ChannelListWithTeamData, *Response) {
query := fmt.Sprintf("?page=%v&per_page=%v&include_deleted=%v", page, perPage, includeDeleted)
r, err := c.DoApiGet(c.GetChannelsRoute()+query, etag)
if err != nil {
return nil, BuildErrorResponse(r, err)
@ -2174,7 +2324,7 @@ func (c *Client4) GetAllChannels(page int, perPage int, etag string) (*ChannelLi
// GetAllChannelsWithCount get all the channels including the total count. Must be a system administrator.
func (c *Client4) GetAllChannelsWithCount(page int, perPage int, etag string) (*ChannelListWithTeamData, int64, *Response) {
query := fmt.Sprintf("?page=%v&per_page=%v&include_total_count=true", page, perPage)
query := fmt.Sprintf("?page=%v&per_page=%v&include_total_count="+c.boolString(true), page, perPage)
r, err := c.DoApiGet(c.GetChannelsRoute()+query, etag)
if err != nil {
return nil, 0, BuildErrorResponse(r, err)
@ -2307,6 +2457,17 @@ func (c *Client4) GetPinnedPosts(channelId string, etag string) (*PostList, *Res
return PostListFromJson(r.Body), BuildResponse(r)
}
// GetPrivateChannelsForTeam returns a list of private channels based on the provided team id string.
func (c *Client4) GetPrivateChannelsForTeam(teamId string, page int, perPage int, etag string) ([]*Channel, *Response) {
query := fmt.Sprintf("/private?page=%v&per_page=%v", page, perPage)
r, err := c.DoApiGet(c.GetChannelsForTeamRoute(teamId)+query, etag)
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return ChannelSliceFromJson(r.Body), BuildResponse(r)
}
// GetPublicChannelsForTeam returns a list of public channels based on the provided team id string.
func (c *Client4) GetPublicChannelsForTeam(teamId string, page int, perPage int, etag string) ([]*Channel, *Response) {
query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage)
@ -2349,6 +2510,18 @@ func (c *Client4) GetChannelsForTeamForUser(teamId, userId string, includeDelete
return ChannelSliceFromJson(r.Body), BuildResponse(r)
}
// GetChannelsForTeamAndUserWithLastDeleteAt returns a list channels of a team for a user, additionally filtered with lastDeleteAt. This does not have any effect if includeDeleted is set to false.
func (c *Client4) GetChannelsForTeamAndUserWithLastDeleteAt(teamId, userId string, includeDeleted bool, lastDeleteAt int, etag string) ([]*Channel, *Response) {
route := fmt.Sprintf(c.GetUserRoute(userId) + c.GetTeamRoute(teamId) + "/channels")
route += fmt.Sprintf("?include_deleted=%v&last_delete_at=%d", includeDeleted, lastDeleteAt)
r, err := c.DoApiGet(route, etag)
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return ChannelSliceFromJson(r.Body), BuildResponse(r)
}
// SearchChannels returns the channels on a team matching the provided search term.
func (c *Client4) SearchChannels(teamId string, search *ChannelSearch) ([]*Channel, *Response) {
r, err := c.DoApiPost(c.GetChannelsForTeamRoute(teamId)+"/search", search.ToJson())
@ -2409,6 +2582,30 @@ func (c *Client4) DeleteChannel(channelId string) (bool, *Response) {
return CheckStatusOK(r), BuildResponse(r)
}
// PermanentDeleteChannel deletes a channel based on the provided channel id string.
func (c *Client4) PermanentDeleteChannel(channelId string) (bool, *Response) {
r, err := c.DoApiDelete(c.GetChannelRoute(channelId) + "?permanent=" + c.boolString(true))
if err != nil {
return false, BuildErrorResponse(r, err)
}
defer closeBody(r)
return CheckStatusOK(r), BuildResponse(r)
}
// MoveChannel moves the channel to the destination team.
func (c *Client4) MoveChannel(channelId, teamId string, force bool) (*Channel, *Response) {
requestBody := map[string]interface{}{
"team_id": teamId,
"force": force,
}
r, err := c.DoApiPost(c.GetChannelRoute(channelId)+"/move", StringInterfaceToJson(requestBody))
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return ChannelFromJson(r.Body), BuildResponse(r)
}
// GetChannelByName returns a channel based on the provided channel name and team id strings.
func (c *Client4) GetChannelByName(channelName, teamId string, etag string) (*Channel, *Response) {
r, err := c.DoApiGet(c.GetChannelByNameRoute(channelName, teamId), etag)
@ -2421,7 +2618,7 @@ func (c *Client4) GetChannelByName(channelName, teamId string, etag string) (*Ch
// GetChannelByNameIncludeDeleted returns a channel based on the provided channel name and team id strings. Other then GetChannelByName it will also return deleted channels.
func (c *Client4) GetChannelByNameIncludeDeleted(channelName, teamId string, etag string) (*Channel, *Response) {
r, err := c.DoApiGet(c.GetChannelByNameRoute(channelName, teamId)+"?include_deleted=true", etag)
r, err := c.DoApiGet(c.GetChannelByNameRoute(channelName, teamId)+"?include_deleted="+c.boolString(true), etag)
if err != nil {
return nil, BuildErrorResponse(r, err)
}
@ -2441,7 +2638,7 @@ func (c *Client4) GetChannelByNameForTeamName(channelName, teamName string, etag
// GetChannelByNameForTeamNameIncludeDeleted returns a channel based on the provided channel name and team name strings. Other then GetChannelByNameForTeamName it will also return deleted channels.
func (c *Client4) GetChannelByNameForTeamNameIncludeDeleted(channelName, teamName string, etag string) (*Channel, *Response) {
r, err := c.DoApiGet(c.GetChannelByNameForTeamNameRoute(channelName, teamName)+"?include_deleted=true", etag)
r, err := c.DoApiGet(c.GetChannelByNameForTeamNameRoute(channelName, teamName)+"?include_deleted="+c.boolString(true), etag)
if err != nil {
return nil, BuildErrorResponse(r, err)
}
@ -3059,7 +3256,7 @@ func (c *Client4) GetPing() (string, *Response) {
// GetPingWithServerStatus will return ok if several basic server health checks
// all pass successfully.
func (c *Client4) GetPingWithServerStatus() (string, *Response) {
r, err := c.DoApiGet(c.GetSystemRoute()+"/ping?get_server_status=true", "")
r, err := c.DoApiGet(c.GetSystemRoute()+"/ping?get_server_status="+c.boolString(true), "")
if r != nil && r.StatusCode == 500 {
defer r.Body.Close()
return STATUS_UNHEALTHY, BuildErrorResponse(r, err)
@ -3187,6 +3384,19 @@ func (c *Client4) UpdateConfig(config *Config) (*Config, *Response) {
return ConfigFromJson(r.Body), BuildResponse(r)
}
// MigrateConfig will migrate existing config to the new one.
func (c *Client4) MigrateConfig(from, to string) (bool, *Response) {
m := make(map[string]string, 2)
m["from"] = from
m["to"] = to
r, err := c.DoApiPost(c.GetConfigRoute()+"/migrate", MapToJson(m))
if err != nil {
return false, BuildErrorResponse(r, err)
}
defer closeBody(r)
return true, BuildResponse(r)
}
// UploadLicenseFile will add a license file to the system.
func (c *Client4) UploadLicenseFile(data []byte) (bool, *Response) {
body := &bytes.Buffer{}
@ -3470,7 +3680,7 @@ func (c *Client4) GetSamlMetadata() (string, *Response) {
return buf.String(), BuildResponse(r)
}
func samlFileToMultipart(data []byte, filename string) ([]byte, *multipart.Writer, error) {
func fileToMultipart(data []byte, filename string) ([]byte, *multipart.Writer, error) {
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
@ -3493,7 +3703,7 @@ func samlFileToMultipart(data []byte, filename string) ([]byte, *multipart.Write
// UploadSamlIdpCertificate will upload an IDP certificate for SAML and set the config to use it.
// The filename parameter is deprecated and ignored: the server will pick a hard-coded filename when writing to disk.
func (c *Client4) UploadSamlIdpCertificate(data []byte, filename string) (bool, *Response) {
body, writer, err := samlFileToMultipart(data, filename)
body, writer, err := fileToMultipart(data, filename)
if err != nil {
return false, &Response{Error: NewAppError("UploadSamlIdpCertificate", "model.client.upload_saml_cert.app_error", nil, err.Error(), http.StatusBadRequest)}
}
@ -3505,7 +3715,7 @@ func (c *Client4) UploadSamlIdpCertificate(data []byte, filename string) (bool,
// UploadSamlPublicCertificate will upload a public certificate for SAML and set the config to use it.
// The filename parameter is deprecated and ignored: the server will pick a hard-coded filename when writing to disk.
func (c *Client4) UploadSamlPublicCertificate(data []byte, filename string) (bool, *Response) {
body, writer, err := samlFileToMultipart(data, filename)
body, writer, err := fileToMultipart(data, filename)
if err != nil {
return false, &Response{Error: NewAppError("UploadSamlPublicCertificate", "model.client.upload_saml_cert.app_error", nil, err.Error(), http.StatusBadRequest)}
}
@ -3517,7 +3727,7 @@ func (c *Client4) UploadSamlPublicCertificate(data []byte, filename string) (boo
// UploadSamlPrivateCertificate will upload a private key for SAML and set the config to use it.
// The filename parameter is deprecated and ignored: the server will pick a hard-coded filename when writing to disk.
func (c *Client4) UploadSamlPrivateCertificate(data []byte, filename string) (bool, *Response) {
body, writer, err := samlFileToMultipart(data, filename)
body, writer, err := fileToMultipart(data, filename)
if err != nil {
return false, &Response{Error: NewAppError("UploadSamlPrivateCertificate", "model.client.upload_saml_cert.app_error", nil, err.Error(), http.StatusBadRequest)}
}
@ -3685,7 +3895,19 @@ func (c *Client4) GetLdapGroups() ([]*Group, *Response) {
}
defer closeBody(r)
return GroupsFromJson(r.Body), BuildResponse(r)
responseData := struct {
Count int `json:"count"`
Groups []*Group `json:"groups"`
}{}
if err := json.NewDecoder(r.Body).Decode(&responseData); err != nil {
appErr := NewAppError("Api4.GetLdapGroups", "api.marshal_error", nil, err.Error(), http.StatusInternalServerError)
return nil, BuildErrorResponse(r, appErr)
}
for i := range responseData.Groups {
responseData.Groups[i].DisplayName = *responseData.Groups[i].Name
}
return responseData.Groups, BuildResponse(r)
}
// LinkLdapGroup creates or undeletes a Mattermost group and associates it to the given LDAP group DN.
@ -3714,6 +3936,18 @@ func (c *Client4) UnlinkLdapGroup(dn string) (*Group, *Response) {
return GroupFromJson(r.Body), BuildResponse(r)
}
// MigrateIdLdap migrates the LDAP enabled users to given attribute
func (c *Client4) MigrateIdLdap(toAttribute string) (bool, *Response) {
r, err := c.DoApiPost(c.GetLdapRoute()+"/migrateid", MapToJson(map[string]string{
"toAttribute": toAttribute,
}))
if err != nil {
return false, BuildErrorResponse(r, err)
}
defer closeBody(r)
return CheckStatusOK(r), BuildResponse(r)
}
// GetGroupsByChannel retrieves the Mattermost Groups associated with a given channel
func (c *Client4) GetGroupsByChannel(channelId string, opts GroupSearchOpts) ([]*GroupWithSchemeAdmin, int, *Response) {
path := fmt.Sprintf("%s/groups?q=%v&include_member_count=%v&filter_allow_reference=%v", c.GetChannelRoute(channelId), opts.Q, opts.IncludeMemberCount, opts.FilterAllowReference)
@ -3828,6 +4062,74 @@ func (c *Client4) GetGroupsByUserId(userId string) ([]*Group, *Response) {
return GroupsFromJson(r.Body), BuildResponse(r)
}
func (c *Client4) MigrateAuthToLdap(fromAuthService string, matchField string, force bool) (bool, *Response) {
r, err := c.DoApiPost(c.GetUsersRoute()+"/migrate_auth/ldap", StringInterfaceToJson(map[string]interface{}{
"from": fromAuthService,
"force": force,
"match_field": matchField,
}))
if err != nil {
return false, BuildErrorResponse(r, err)
}
defer closeBody(r)
return CheckStatusOK(r), BuildResponse(r)
}
func (c *Client4) MigrateAuthToSaml(fromAuthService string, usersMap map[string]string, auto bool) (bool, *Response) {
r, err := c.DoApiPost(c.GetUsersRoute()+"/migrate_auth/saml", StringInterfaceToJson(map[string]interface{}{
"from": fromAuthService,
"auto": auto,
"matches": usersMap,
}))
if err != nil {
return false, BuildErrorResponse(r, err)
}
defer closeBody(r)
return CheckStatusOK(r), BuildResponse(r)
}
// UploadLdapPublicCertificate will upload a public certificate for LDAP and set the config to use it.
func (c *Client4) UploadLdapPublicCertificate(data []byte) (bool, *Response) {
body, writer, err := fileToMultipart(data, LDAP_PUBIC_CERTIFICATE_NAME)
if err != nil {
return false, &Response{Error: NewAppError("UploadLdapPublicCertificate", "model.client.upload_ldap_cert.app_error", nil, err.Error(), http.StatusBadRequest)}
}
_, resp := c.DoUploadFile(c.GetLdapRoute()+"/certificate/public", body, writer.FormDataContentType())
return resp.Error == nil, resp
}
// UploadLdapPrivateCertificate will upload a private key for LDAP and set the config to use it.
func (c *Client4) UploadLdapPrivateCertificate(data []byte) (bool, *Response) {
body, writer, err := fileToMultipart(data, LDAP_PRIVATE_KEY_NAME)
if err != nil {
return false, &Response{Error: NewAppError("UploadLdapPrivateCertificate", "model.client.upload_Ldap_cert.app_error", nil, err.Error(), http.StatusBadRequest)}
}
_, resp := c.DoUploadFile(c.GetLdapRoute()+"/certificate/private", body, writer.FormDataContentType())
return resp.Error == nil, resp
}
// DeleteLdapPublicCertificate deletes the LDAP IDP certificate from the server and updates the config to not use it and disable LDAP.
func (c *Client4) DeleteLdapPublicCertificate() (bool, *Response) {
r, err := c.DoApiDelete(c.GetLdapRoute() + "/certificate/public")
if err != nil {
return false, BuildErrorResponse(r, err)
}
defer closeBody(r)
return CheckStatusOK(r), BuildResponse(r)
}
// DeleteLDAPPrivateCertificate deletes the LDAP IDP certificate from the server and updates the config to not use it and disable LDAP.
func (c *Client4) DeleteLdapPrivateCertificate() (bool, *Response) {
r, err := c.DoApiDelete(c.GetLdapRoute() + "/certificate/private")
if err != nil {
return false, BuildErrorResponse(r, err)
}
defer closeBody(r)
return CheckStatusOK(r), BuildResponse(r)
}
// Audits Section
// GetAudits returns a list of audits for the whole system.
@ -4519,6 +4821,21 @@ func (c *Client4) CancelJob(jobId string) (bool, *Response) {
return CheckStatusOK(r), BuildResponse(r)
}
// DownloadJob downloads the results of the job
func (c *Client4) DownloadJob(jobId string) ([]byte, *Response) {
r, appErr := c.DoApiGet(c.GetJobsRoute()+fmt.Sprintf("/%v/download", jobId), "")
if appErr != nil {
return nil, BuildErrorResponse(r, appErr)
}
defer closeBody(r)
data, err := ioutil.ReadAll(r.Body)
if err != nil {
return nil, BuildErrorResponse(r, NewAppError("GetFile", "model.client.read_job_result_file.app_error", nil, err.Error(), r.StatusCode))
}
return data, BuildResponse(r)
}
// Roles Section
// GetRole gets a single role by ID.
@ -4650,7 +4967,7 @@ func (c *Client4) uploadPlugin(file io.Reader, force bool) (*Manifest, *Response
writer := multipart.NewWriter(body)
if force {
err := writer.WriteField("force", "true")
err := writer.WriteField("force", c.boolString(true))
if err != nil {
return nil, &Response{Error: NewAppError("UploadPlugin", "model.client.writer.app_error", nil, err.Error(), 0)}
}
@ -4693,10 +5010,7 @@ func (c *Client4) uploadPlugin(file io.Reader, force bool) (*Manifest, *Response
}
func (c *Client4) InstallPluginFromUrl(downloadUrl string, force bool) (*Manifest, *Response) {
forceStr := "false"
if force {
forceStr = "true"
}
forceStr := c.boolString(force)
url := fmt.Sprintf("%s?plugin_download_url=%s&force=%s", c.GetPluginsRoute()+"/install_from_url", url.QueryEscape(downloadUrl), forceStr)
r, err := c.DoApiPost(url, "")
@ -5074,6 +5388,16 @@ func (c *Client4) GetKnownUsers() ([]string, *Response) {
return userIds, BuildResponse(r)
}
// PublishUserTyping publishes a user is typing websocket event based on the provided TypingRequest.
func (c *Client4) PublishUserTyping(userID string, typingRequest TypingRequest) (bool, *Response) {
r, err := c.DoApiPost(c.GetPublishUserTypingRoute(userID), typingRequest.ToJson())
if err != nil {
return false, BuildErrorResponse(r, err)
}
defer closeBody(r)
return CheckStatusOK(r), BuildResponse(r)
}
func (c *Client4) GetChannelMemberCountsByGroup(channelID string, includeTimezones bool, etag string) ([]*ChannelMemberCountByGroup, *Response) {
r, err := c.DoApiGet(c.GetChannelRoute(channelID)+"/member_counts_by_group?include_timezones="+strconv.FormatBool(includeTimezones), etag)
if err != nil {
@ -5093,3 +5417,202 @@ func (c *Client4) RequestTrialLicense(users int) (bool, *Response) {
defer closeBody(r)
return CheckStatusOK(r), BuildResponse(r)
}
// GetGroupStats retrieves stats for a Mattermost Group
func (c *Client4) GetGroupStats(groupID string) (*GroupStats, *Response) {
r, appErr := c.DoApiGet(c.GetGroupRoute(groupID)+"/stats", "")
if appErr != nil {
return nil, BuildErrorResponse(r, appErr)
}
defer closeBody(r)
return GroupStatsFromJson(r.Body), BuildResponse(r)
}
func (c *Client4) GetSidebarCategoriesForTeamForUser(userID, teamID, etag string) (*OrderedSidebarCategories, *Response) {
route := c.GetUserCategoryRoute(userID, teamID)
r, appErr := c.DoApiGet(route, etag)
if appErr != nil {
return nil, BuildErrorResponse(r, appErr)
}
cat, err := OrderedSidebarCategoriesFromJson(r.Body)
if err != nil {
return nil, BuildErrorResponse(r, NewAppError("Client4.GetSidebarCategoriesForTeamForUser", "model.utils.decode_json.app_error", nil, err.Error(), r.StatusCode))
}
return cat, BuildResponse(r)
}
func (c *Client4) CreateSidebarCategoryForTeamForUser(userID, teamID string, category *SidebarCategoryWithChannels) (*SidebarCategoryWithChannels, *Response) {
payload, _ := json.Marshal(category)
route := c.GetUserCategoryRoute(userID, teamID)
r, appErr := c.doApiPostBytes(route, payload)
if appErr != nil {
return nil, BuildErrorResponse(r, appErr)
}
defer closeBody(r)
cat, err := SidebarCategoryFromJson(r.Body)
if err != nil {
return nil, BuildErrorResponse(r, NewAppError("Client4.CreateSidebarCategoryForTeamForUser", "model.utils.decode_json.app_error", nil, err.Error(), r.StatusCode))
}
return cat, BuildResponse(r)
}
func (c *Client4) UpdateSidebarCategoriesForTeamForUser(userID, teamID string, categories []*SidebarCategoryWithChannels) ([]*SidebarCategoryWithChannels, *Response) {
payload, _ := json.Marshal(categories)
route := c.GetUserCategoryRoute(userID, teamID)
r, appErr := c.doApiPutBytes(route, payload)
if appErr != nil {
return nil, BuildErrorResponse(r, appErr)
}
defer closeBody(r)
categories, err := SidebarCategoriesFromJson(r.Body)
if err != nil {
return nil, BuildErrorResponse(r, NewAppError("Client4.UpdateSidebarCategoriesForTeamForUser", "model.utils.decode_json.app_error", nil, err.Error(), r.StatusCode))
}
return categories, BuildResponse(r)
}
func (c *Client4) GetSidebarCategoryOrderForTeamForUser(userID, teamID, etag string) ([]string, *Response) {
route := c.GetUserCategoryRoute(userID, teamID) + "/order"
r, err := c.DoApiGet(route, etag)
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return ArrayFromJson(r.Body), BuildResponse(r)
}
func (c *Client4) UpdateSidebarCategoryOrderForTeamForUser(userID, teamID string, order []string) ([]string, *Response) {
payload, _ := json.Marshal(order)
route := c.GetUserCategoryRoute(userID, teamID) + "/order"
r, err := c.doApiPutBytes(route, payload)
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return ArrayFromJson(r.Body), BuildResponse(r)
}
func (c *Client4) GetSidebarCategoryForTeamForUser(userID, teamID, categoryID, etag string) (*SidebarCategoryWithChannels, *Response) {
route := c.GetUserCategoryRoute(userID, teamID) + "/" + categoryID
r, appErr := c.DoApiGet(route, etag)
if appErr != nil {
return nil, BuildErrorResponse(r, appErr)
}
defer closeBody(r)
cat, err := SidebarCategoryFromJson(r.Body)
if err != nil {
return nil, BuildErrorResponse(r, NewAppError("Client4.UpdateSidebarCategoriesForTeamForUser", "model.utils.decode_json.app_error", nil, err.Error(), r.StatusCode))
}
return cat, BuildResponse(r)
}
func (c *Client4) UpdateSidebarCategoryForTeamForUser(userID, teamID, categoryID string, category *SidebarCategoryWithChannels) (*SidebarCategoryWithChannels, *Response) {
payload, _ := json.Marshal(category)
route := c.GetUserCategoryRoute(userID, teamID) + "/" + categoryID
r, appErr := c.doApiPutBytes(route, payload)
if appErr != nil {
return nil, BuildErrorResponse(r, appErr)
}
defer closeBody(r)
cat, err := SidebarCategoryFromJson(r.Body)
if err != nil {
return nil, BuildErrorResponse(r, NewAppError("Client4.UpdateSidebarCategoriesForTeamForUser", "model.utils.decode_json.app_error", nil, err.Error(), r.StatusCode))
}
return cat, BuildResponse(r)
}
// CheckIntegrity performs a database integrity check.
func (c *Client4) CheckIntegrity() ([]IntegrityCheckResult, *Response) {
r, err := c.DoApiPost("/integrity", "")
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
var results []IntegrityCheckResult
if err := json.NewDecoder(r.Body).Decode(&results); err != nil {
appErr := NewAppError("Api4.CheckIntegrity", "api.marshal_error", nil, err.Error(), http.StatusInternalServerError)
return nil, BuildErrorResponse(r, appErr)
}
return results, BuildResponse(r)
}
func (c *Client4) GetNotices(lastViewed int64, teamId string, client NoticeClientType, clientVersion, locale, etag string) (NoticeMessages, *Response) {
url := fmt.Sprintf("/system/notices/%s?lastViewed=%d&client=%s&clientVersion=%s&locale=%s", teamId, lastViewed, client, clientVersion, locale)
r, appErr := c.DoApiGet(url, etag)
if appErr != nil {
return nil, BuildErrorResponse(r, appErr)
}
defer closeBody(r)
notices, err := UnmarshalProductNoticeMessages(r.Body)
if err != nil {
return nil, &Response{StatusCode: http.StatusBadRequest, Error: NewAppError(url, "model.client.connecting.app_error", nil, err.Error(), http.StatusForbidden)}
}
return notices, BuildResponse(r)
}
func (c *Client4) MarkNoticesViewed(ids []string) *Response {
r, err := c.DoApiPut("/system/notices/view", ArrayToJson(ids))
if err != nil {
return BuildErrorResponse(r, err)
}
defer closeBody(r)
return BuildResponse(r)
}
// CreateUpload creates a new upload session.
func (c *Client4) CreateUpload(us *UploadSession) (*UploadSession, *Response) {
r, err := c.DoApiPost(c.GetUploadsRoute(), us.ToJson())
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return UploadSessionFromJson(r.Body), BuildResponse(r)
}
// GetUpload returns the upload session for the specified uploadId.
func (c *Client4) GetUpload(uploadId string) (*UploadSession, *Response) {
r, err := c.DoApiGet(c.GetUploadRoute(uploadId), "")
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return UploadSessionFromJson(r.Body), BuildResponse(r)
}
// GetUploadsForUser returns the upload sessions created by the specified
// userId.
func (c *Client4) GetUploadsForUser(userId string) ([]*UploadSession, *Response) {
r, err := c.DoApiGet(c.GetUserRoute(userId)+"/uploads", "")
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return UploadSessionsFromJson(r.Body), BuildResponse(r)
}
// UploadData performs an upload. On success it returns
// a FileInfo object.
func (c *Client4) UploadData(uploadId string, data io.Reader) (*FileInfo, *Response) {
url := c.GetUploadRoute(uploadId)
r, err := c.doApiRequestReader("POST", c.ApiUrl+url, data, "")
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return FileInfoFromJson(r.Body), BuildResponse(r)
}
func (c *Client4) UpdatePassword(userId, currentPassword, newPassword string) *Response {
requestBody := map[string]string{"current_password": currentPassword, "new_password": newPassword}
r, err := c.DoApiPut(c.GetUserRoute(userId)+"/password", MapToJson(requestBody))
if err != nil {
return BuildErrorResponse(r, err)
}
defer closeBody(r)
return BuildResponse(r)
}

View File

@ -43,6 +43,16 @@ const (
CLUSTER_EVENT_INVALIDATE_CACHE_FOR_TERMS_OF_SERVICE = "inv_terms_of_service"
CLUSTER_EVENT_BUSY_STATE_CHANGED = "busy_state_change"
// Gossip communication
CLUSTER_GOSSIP_EVENT_REQUEST_GET_LOGS = "gossip_request_get_logs"
CLUSTER_GOSSIP_EVENT_RESPONSE_GET_LOGS = "gossip_response_get_logs"
CLUSTER_GOSSIP_EVENT_REQUEST_GET_CLUSTER_STATS = "gossip_request_cluster_stats"
CLUSTER_GOSSIP_EVENT_RESPONSE_GET_CLUSTER_STATS = "gossip_response_cluster_stats"
CLUSTER_GOSSIP_EVENT_REQUEST_GET_PLUGIN_STATUSES = "gossip_request_plugin_statuses"
CLUSTER_GOSSIP_EVENT_RESPONSE_GET_PLUGIN_STATUSES = "gossip_response_plugin_statuses"
CLUSTER_GOSSIP_EVENT_REQUEST_SAVE_CONFIG = "gossip_request_save_config"
CLUSTER_GOSSIP_EVENT_RESPONSE_SAVE_CONFIG = "gossip_response_save_config"
// SendTypes for ClusterMessage.
CLUSTER_SEND_BEST_EFFORT = "best_effort"
CLUSTER_SEND_RELIABLE = "reliable"

View File

@ -18,23 +18,26 @@ const (
)
type Command struct {
Id string `json:"id"`
Token string `json:"token"`
CreateAt int64 `json:"create_at"`
UpdateAt int64 `json:"update_at"`
DeleteAt int64 `json:"delete_at"`
CreatorId string `json:"creator_id"`
TeamId string `json:"team_id"`
Trigger string `json:"trigger"`
Method string `json:"method"`
Username string `json:"username"`
IconURL string `json:"icon_url"`
AutoComplete bool `json:"auto_complete"`
AutoCompleteDesc string `json:"auto_complete_desc"`
AutoCompleteHint string `json:"auto_complete_hint"`
DisplayName string `json:"display_name"`
Description string `json:"description"`
URL string `json:"url"`
Id string `json:"id"`
Token string `json:"token"`
CreateAt int64 `json:"create_at"`
UpdateAt int64 `json:"update_at"`
DeleteAt int64 `json:"delete_at"`
CreatorId string `json:"creator_id"`
TeamId string `json:"team_id"`
Trigger string `json:"trigger"`
Method string `json:"method"`
Username string `json:"username"`
IconURL string `json:"icon_url"`
AutoComplete bool `json:"auto_complete"`
AutoCompleteDesc string `json:"auto_complete_desc"`
AutoCompleteHint string `json:"auto_complete_hint"`
DisplayName string `json:"display_name"`
Description string `json:"description"`
URL string `json:"url"`
// PluginId records the id of the plugin that created this Command. If it is blank, the Command
// was not created by a plugin.
PluginId string `json:"plugin_id"`
AutocompleteData *AutocompleteData `db:"-" json:"autocomplete_data,omitempty"`
// AutocompleteIconData is a base64 encoded svg
AutocompleteIconData string `db:"-" json:"autocomplete_icon_data,omitempty"`
@ -80,10 +83,20 @@ func (o *Command) IsValid() *AppError {
return NewAppError("Command.IsValid", "model.command.is_valid.update_at.app_error", nil, "", http.StatusBadRequest)
}
if !IsValidId(o.CreatorId) {
// If the CreatorId is blank, this should be a command created by a plugin.
if o.CreatorId == "" && !IsValidPluginId(o.PluginId) {
return NewAppError("Command.IsValid", "model.command.is_valid.plugin_id.app_error", nil, "", http.StatusBadRequest)
}
// If the PluginId is blank, this should be a command associated with a userId.
if o.PluginId == "" && !IsValidId(o.CreatorId) {
return NewAppError("Command.IsValid", "model.command.is_valid.user_id.app_error", nil, "", http.StatusBadRequest)
}
if o.CreatorId != "" && o.PluginId != "" {
return NewAppError("Command.IsValid", "model.command.is_valid.plugin_id.app_error", nil, "command cannot have both a CreatorId and a PluginId", http.StatusBadRequest)
}
if !IsValidId(o.TeamId) {
return NewAppError("Command.IsValid", "model.command.is_valid.team_id.app_error", nil, "", http.StatusBadRequest)
}

View File

@ -20,9 +20,11 @@ type CommandArgs struct {
Command string `json:"command"`
SiteURL string `json:"-"`
T goi18n.TranslateFunc `json:"-"`
Session Session `json:"-"`
UserMentions UserMentionMap `json:"-"`
ChannelMentions ChannelMentionMap `json:"-"`
// DO NOT USE Session field is deprecated. MM-26398
Session Session `json:"-"`
}
func (o *CommandArgs) ToJson() string {

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,6 @@
package model
import (
"bytes"
"encoding/json"
"image"
"image/gif"
@ -151,10 +150,10 @@ func NewInfo(name string) *FileInfo {
return info
}
func GetInfoForBytes(name string, data []byte) (*FileInfo, *AppError) {
func GetInfoForBytes(name string, data io.ReadSeeker, size int) (*FileInfo, *AppError) {
info := &FileInfo{
Name: name,
Size: int64(len(data)),
Size: int64(size),
}
var err *AppError
@ -170,16 +169,17 @@ func GetInfoForBytes(name string, data []byte) (*FileInfo, *AppError) {
if info.IsImage() {
// Only set the width and height if it's actually an image that we can understand
if config, _, err := image.DecodeConfig(bytes.NewReader(data)); err == nil {
if config, _, err := image.DecodeConfig(data); err == nil {
info.Width = config.Width
info.Height = config.Height
if info.MimeType == "image/gif" {
// Just show the gif itself instead of a preview image for animated gifs
if gifConfig, err := gif.DecodeAll(bytes.NewReader(data)); err != nil {
data.Seek(0, io.SeekStart)
if gifConfig, err := gif.DecodeAll(data); err != nil {
// Still return the rest of the info even though it doesn't appear to be an actual gif
info.HasPreviewImage = true
return info, NewAppError("GetInfoForBytes", "model.file_info.get.gif.app_error", nil, "name="+name, http.StatusBadRequest)
return info, NewAppError("GetInfoForBytes", "model.file_info.get.gif.app_error", nil, err.Error(), http.StatusBadRequest)
} else {
info.HasPreviewImage = len(gifConfig.Image) == 1
}

View File

@ -94,6 +94,11 @@ type PageOpts struct {
PerPage int
}
type GroupStats struct {
GroupID string `json:"group_id"`
TotalMemberCount int64 `json:"total_member_count"`
}
func (group *Group) Patch(patch *GroupPatch) {
if patch.Name != nil {
group.Name = patch.Name
@ -208,3 +213,9 @@ func GroupPatchFromJson(data io.Reader) *GroupPatch {
json.NewDecoder(data).Decode(&groupPatch)
return groupPatch
}
func GroupStatsFromJson(data io.Reader) *GroupStats {
var groupStats *GroupStats
json.NewDecoder(data).Decode(&groupStats)
return groupStats
}

View File

@ -0,0 +1,58 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package model
import (
"encoding/json"
"errors"
)
type OrphanedRecord struct {
ParentId *string `json:"parent_id"`
ChildId *string `json:"child_id"`
}
type RelationalIntegrityCheckData struct {
ParentName string `json:"parent_name"`
ChildName string `json:"child_name"`
ParentIdAttr string `json:"parent_id_attr"`
ChildIdAttr string `json:"child_id_attr"`
Records []OrphanedRecord `json:"records"`
}
type IntegrityCheckResult struct {
Data interface{} `json:"data"`
Err error `json:"err"`
}
func (r *IntegrityCheckResult) UnmarshalJSON(b []byte) error {
var data map[string]interface{}
if err := json.Unmarshal(b, &data); err != nil {
return err
}
if d, ok := data["data"]; ok && d != nil {
var rdata RelationalIntegrityCheckData
m := d.(map[string]interface{})
rdata.ParentName = m["parent_name"].(string)
rdata.ChildName = m["child_name"].(string)
rdata.ParentIdAttr = m["parent_id_attr"].(string)
rdata.ChildIdAttr = m["child_id_attr"].(string)
for _, recData := range m["records"].([]interface{}) {
var record OrphanedRecord
m := recData.(map[string]interface{})
if val := m["parent_id"]; val != nil {
record.ParentId = NewString(val.(string))
}
if val := m["child_id"]; val != nil {
record.ChildId = NewString(val.(string))
}
rdata.Records = append(rdata.Records, record)
}
r.Data = rdata
}
if err, ok := data["err"]; ok && err != nil {
r.Err = errors.New(data["err"].(string))
}
return nil
}

View File

@ -19,6 +19,9 @@ const (
JOB_TYPE_LDAP_SYNC = "ldap_sync"
JOB_TYPE_MIGRATIONS = "migrations"
JOB_TYPE_PLUGINS = "plugins"
JOB_TYPE_EXPIRY_NOTIFY = "expiry_notify"
JOB_TYPE_PRODUCT_NOTICES = "product_notices"
JOB_TYPE_ACTIVE_USERS = "active_users"
JOB_STATUS_PENDING = "pending"
JOB_STATUS_IN_PROGRESS = "in_progress"
@ -59,6 +62,9 @@ func (j *Job) IsValid() *AppError {
case JOB_TYPE_MESSAGE_EXPORT:
case JOB_TYPE_MIGRATIONS:
case JOB_TYPE_PLUGINS:
case JOB_TYPE_PRODUCT_NOTICES:
case JOB_TYPE_EXPIRY_NOTIFY:
case JOB_TYPE_ACTIVE_USERS:
default:
return NewAppError("Job.IsValid", "model.job.is_valid.type.app_error", nil, "id="+j.Id, http.StatusBadRequest)
}

View File

@ -4,5 +4,7 @@
package model
const (
USER_AUTH_SERVICE_LDAP = "ldap"
USER_AUTH_SERVICE_LDAP = "ldap"
LDAP_PUBIC_CERTIFICATE_NAME = "ldap-public.crt"
LDAP_PRIVATE_KEY_NAME = "ldap-private.key"
)

View File

@ -13,7 +13,7 @@ const (
EXPIRED_LICENSE_ERROR = "api.license.add_license.expired.app_error"
INVALID_LICENSE_ERROR = "api.license.add_license.invalid.app_error"
LICENSE_GRACE_PERIOD = 1000 * 60 * 60 * 24 * 10 //10 days
LICENSE_RENEWAL_LINK = "https://licensing.mattermost.com/renew"
LICENSE_RENEWAL_LINK = "https://mattermost.com/renew/"
)
type LicenseRecord struct {
@ -81,6 +81,8 @@ type Features struct {
IDLoadedPushNotifications *bool `json:"id_loaded"`
LockTeammateNameDisplay *bool `json:"lock_teammate_name_display"`
EnterprisePlugins *bool `json:"enterprise_plugins"`
AdvancedLogging *bool `json:"advanced_logging"`
Cloud *bool `json:"cloud"`
// after we enabled more features we'll need to control them with this
FutureFeatures *bool `json:"future_features"`
@ -108,6 +110,8 @@ func (f *Features) ToMap() map[string]interface{} {
"id_loaded": *f.IDLoadedPushNotifications,
"lock_teammate_name_display": *f.LockTeammateNameDisplay,
"enterprise_plugins": *f.EnterprisePlugins,
"advanced_logging": *f.AdvancedLogging,
"cloud": *f.Cloud,
"future": *f.FutureFeatures,
}
}
@ -212,6 +216,14 @@ func (f *Features) SetDefaults() {
if f.EnterprisePlugins == nil {
f.EnterprisePlugins = NewBool(*f.FutureFeatures)
}
if f.AdvancedLogging == nil {
f.AdvancedLogging = NewBool(*f.FutureFeatures)
}
if f.Cloud == nil {
f.Cloud = NewBool(false)
}
}
func (l *License) IsExpired() bool {

View File

@ -171,9 +171,9 @@ func (o *LinkMetadata) DeserializeDataToConcreteType() error {
// FloorToNearestHour takes a timestamp (in milliseconds) and returns it rounded to the previous hour in UTC.
func FloorToNearestHour(ms int64) int64 {
t := time.Unix(0, ms*int64(1000*1000))
t := time.Unix(0, ms*int64(1000*1000)).UTC()
return time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), 0, 0, 0, t.Location()).UnixNano() / int64(time.Millisecond)
return time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), 0, 0, 0, time.UTC).UnixNano() / int64(time.Millisecond)
}
// isRoundedToNearestHour returns true if the given timestamp (in milliseconds) has been rounded to the nearest hour in UTC.

View File

@ -4,6 +4,7 @@
package model
const (
ADVANCED_PERMISSIONS_MIGRATION_KEY = "AdvancedPermissionsMigrationComplete"
MIGRATION_KEY_ADVANCED_PERMISSIONS_PHASE_2 = "migration_advanced_permissions_phase_2"
MIGRATION_KEY_EMOJI_PERMISSIONS_SPLIT = "emoji_permissions_split"
@ -17,4 +18,7 @@ const (
MIGRATION_KEY_ADD_MANAGE_GUESTS_PERMISSIONS = "add_manage_guests_permissions"
MIGRATION_KEY_CHANNEL_MODERATIONS_PERMISSIONS = "channel_moderations_permissions"
MIGRATION_KEY_ADD_USE_GROUP_MENTIONS_PERMISSION = "add_use_group_mentions_permission"
MIGRATION_KEY_ADD_SYSTEM_CONSOLE_PERMISSIONS = "add_system_console_permissions"
MIGRATION_KEY_SIDEBAR_CATEGORIES_PHASE_2 = "migration_sidebar_categories_phase_2"
MIGRATION_KEY_ADD_CONVERT_CHANNEL_PERMISSIONS = "add_convert_channel_permissions"
)

View File

@ -112,6 +112,9 @@ func (o *OutgoingWebhookResponse) ToJson() string {
func OutgoingWebhookResponseFromJson(data io.Reader) (*OutgoingWebhookResponse, error) {
var o *OutgoingWebhookResponse
err := json.NewDecoder(data).Decode(&o)
if err == io.EOF {
return nil, nil
}
return o, err
}

File diff suppressed because it is too large Load Diff

View File

@ -64,6 +64,7 @@ const (
POST_PROPS_MENTION_HIGHLIGHT_DISABLED = "mentionHighlightDisabled"
POST_PROPS_GROUP_HIGHLIGHT_DISABLED = "disable_group_highlight"
POST_SYSTEM_WARN_METRIC_STATUS = "warn_metric_status"
)
var AT_MENTION_PATTEN = regexp.MustCompile(`\B@`)
@ -312,7 +313,8 @@ func (o *Post) IsValid(maxPostSize int) *AppError {
POST_CHANNEL_RESTORED,
POST_CHANGE_CHANNEL_PRIVACY,
POST_ME,
POST_ADD_BOT_TEAMS_CHANNELS:
POST_ADD_BOT_TEAMS_CHANNELS,
POST_SYSTEM_WARN_METRIC_STATUS:
default:
if !strings.HasPrefix(o.Type, POST_CUSTOM_TYPE_PREFIX) {
return NewAppError("Post.IsValid", "model.post.is_valid.type.app_error", nil, "id="+o.Type, http.StatusBadRequest)
@ -495,15 +497,14 @@ func (o *SearchParameter) SearchParameterToJson() string {
return string(b)
}
func SearchParameterFromJson(data io.Reader) *SearchParameter {
func SearchParameterFromJson(data io.Reader) (*SearchParameter, error) {
decoder := json.NewDecoder(data)
var searchParam SearchParameter
err := decoder.Decode(&searchParam)
if err != nil {
return nil
if err := decoder.Decode(&searchParam); err != nil {
return nil, err
}
return &searchParam
return &searchParam, nil
}
func (o *Post) ChannelMentions() []string {
@ -521,6 +522,9 @@ func (o *Post) DisableMentionHighlights() string {
// DisableMentionHighlights disables mention highlighting for a post patch if required.
func (o *PostPatch) DisableMentionHighlights() {
if o.Message == nil {
return
}
if _, hasMentions := findAtChannelMention(*o.Message); hasMentions {
if o.Props == nil {
o.Props = &StringInterface{}

View File

@ -14,6 +14,7 @@ import (
const (
PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW = "direct_channel_show"
PREFERENCE_CATEGORY_GROUP_CHANNEL_SHOW = "group_channel_show"
PREFERENCE_CATEGORY_TUTORIAL_STEPS = "tutorial_step"
PREFERENCE_CATEGORY_ADVANCED_SETTINGS = "advanced_settings"
PREFERENCE_CATEGORY_FLAGGED_POST = "flagged_post"

View File

@ -0,0 +1,213 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package model
import (
"encoding/json"
"github.com/pkg/errors"
"io"
)
type ProductNotices []ProductNotice
func (r *ProductNotices) Marshal() ([]byte, error) {
return json.Marshal(r)
}
func UnmarshalProductNotices(data []byte) (ProductNotices, error) {
var r ProductNotices
err := json.Unmarshal(data, &r)
return r, err
}
// List of product notices. Order is important and is used to resolve priorities.
// Each notice will only be show if conditions are met.
type ProductNotice struct {
Conditions Conditions `json:"conditions"`
ID string `json:"id"` // Unique identifier for this notice. Can be a running number. Used for storing 'viewed'; state on the server.
LocalizedMessages map[string]NoticeMessageInternal `json:"localizedMessages"` // Notice message data, organized by locale.; Example:; "localizedMessages": {; "en": { "title": "English", description: "English description"},; "frFR": { "title": "Frances", description: "French description"}; }
Repeatable *bool `json:"repeatable,omitempty"` // Configurable flag if the notice should reappear after its seen and dismissed
}
func (n *ProductNotice) SysAdminOnly() bool {
return n.Conditions.Audience != nil && *n.Conditions.Audience == NoticeAudience_Sysadmin
}
func (n *ProductNotice) TeamAdminOnly() bool {
return n.Conditions.Audience != nil && *n.Conditions.Audience == NoticeAudience_TeamAdmin
}
type Conditions struct {
Audience *NoticeAudience `json:"audience,omitempty"`
ClientType *NoticeClientType `json:"clientType,omitempty"` // Only show the notice on specific clients. Defaults to 'all'
DesktopVersion []string `json:"desktopVersion,omitempty"` // What desktop client versions does this notice apply to.; Format: semver ranges (https://devhints.io/semver); Example: [">=1.2.3 < ~2.4.x"]; Example: ["<v5.19", "v5.20-v5.22"]
DisplayDate *string `json:"displayDate,omitempty"` // When to display the notice.; Examples:; "2020-03-01T00:00:00Z" - show on specified date; ">= 2020-03-01T00:00:00Z" - show after specified date; "< 2020-03-01T00:00:00Z" - show before the specified date; "> 2020-03-01T00:00:00Z <= 2020-04-01T00:00:00Z" - show only between the specified dates
InstanceType *NoticeInstanceType `json:"instanceType,omitempty"`
MobileVersion []string `json:"mobileVersion,omitempty"` // What mobile client versions does this notice apply to.; Format: semver ranges (https://devhints.io/semver); Example: [">=1.2.3 < ~2.4.x"]; Example: ["<v5.19", "v5.20-v5.22"]
NumberOfPosts *int64 `json:"numberOfPosts,omitempty"` // Only show the notice when server has more than specified number of posts
NumberOfUsers *int64 `json:"numberOfUsers,omitempty"` // Only show the notice when server has more than specified number of users
ServerConfig map[string]interface{} `json:"serverConfig,omitempty"` // Map of mattermost server config paths and their values. Notice will be displayed only if; the values match the target server config; Example: serverConfig: { "PluginSettings.Enable": true, "GuestAccountsSettings.Enable":; false }
ServerVersion []string `json:"serverVersion,omitempty"` // What server versions does this notice apply to.; Format: semver ranges (https://devhints.io/semver); Example: [">=1.2.3 < ~2.4.x"]; Example: ["<v5.19", "v5.20-v5.22"]
Sku *NoticeSKU `json:"sku,omitempty"`
UserConfig map[string]interface{} `json:"userConfig,omitempty"` // Map of user's settings and their values. Notice will be displayed only if the values; match the viewing users' config; Example: userConfig: { "new_sidebar.disabled": true }
}
type NoticeMessageInternal struct {
Action *NoticeAction `json:"action,omitempty"` // Optional action to perform on action button click. (defaults to closing the notice)
ActionParam *string `json:"actionParam,omitempty"` // Optional action parameter.; Example: {"action": "url", actionParam: "/console/some-page"}
ActionText *string `json:"actionText,omitempty"` // Optional override for the action button text (defaults to OK)
Description string `json:"description"` // Notice content. Use {{Mattermost}} instead of plain text to support white-labeling. Text; supports Markdown.
Image *string `json:"image,omitempty"`
Title string `json:"title"` // Notice title. Use {{Mattermost}} instead of plain text to support white-labeling. Text; supports Markdown.
}
type NoticeMessages []NoticeMessage
type NoticeMessage struct {
NoticeMessageInternal
ID string `json:"id"`
SysAdminOnly bool `json:"sysAdminOnly"`
TeamAdminOnly bool `json:"teamAdminOnly"`
}
func (r *NoticeMessages) Marshal() ([]byte, error) {
return json.Marshal(r)
}
func UnmarshalProductNoticeMessages(data io.Reader) (NoticeMessages, error) {
var r NoticeMessages
err := json.NewDecoder(data).Decode(&r)
return r, err
}
// User role, i.e. who will see the notice. Defaults to "all"
type NoticeAudience string
func NewNoticeAudience(s NoticeAudience) *NoticeAudience {
return &s
}
func (a *NoticeAudience) Matches(sysAdmin bool, teamAdmin bool) bool {
switch *a {
case NoticeAudience_All:
return true
case NoticeAudience_Member:
return !sysAdmin && !teamAdmin
case NoticeAudience_Sysadmin:
return sysAdmin
case NoticeAudience_TeamAdmin:
return teamAdmin
}
return false
}
const (
NoticeAudience_All NoticeAudience = "all"
NoticeAudience_Member NoticeAudience = "member"
NoticeAudience_Sysadmin NoticeAudience = "sysadmin"
NoticeAudience_TeamAdmin NoticeAudience = "teamadmin"
)
// Only show the notice on specific clients. Defaults to 'all'
//
// Client type. Defaults to "all"
type NoticeClientType string
func NewNoticeClientType(s NoticeClientType) *NoticeClientType { return &s }
func (c *NoticeClientType) Matches(other NoticeClientType) bool {
switch *c {
case NoticeClientType_All:
return true
case NoticeClientType_Mobile:
return other == NoticeClientType_MobileIos || other == NoticeClientType_MobileAndroid
default:
return *c == other
}
}
const (
NoticeClientType_All NoticeClientType = "all"
NoticeClientType_Desktop NoticeClientType = "desktop"
NoticeClientType_Mobile NoticeClientType = "mobile"
NoticeClientType_MobileAndroid NoticeClientType = "mobile-android"
NoticeClientType_MobileIos NoticeClientType = "mobile-ios"
NoticeClientType_Web NoticeClientType = "web"
)
func NoticeClientTypeFromString(s string) (NoticeClientType, error) {
switch s {
case "web":
return NoticeClientType_Web, nil
case "mobile-ios":
return NoticeClientType_MobileIos, nil
case "mobile-android":
return NoticeClientType_MobileAndroid, nil
case "desktop":
return NoticeClientType_Desktop, nil
}
return NoticeClientType_All, errors.New("Invalid client type supplied")
}
// Instance type. Defaults to "both"
type NoticeInstanceType string
func NewNoticeInstanceType(n NoticeInstanceType) *NoticeInstanceType { return &n }
func (t *NoticeInstanceType) Matches(isCloud bool) bool {
if *t == NoticeInstanceType_Both {
return true
}
if *t == NoticeInstanceType_Cloud && !isCloud {
return false
}
if *t == NoticeInstanceType_OnPrem && isCloud {
return false
}
return true
}
const (
NoticeInstanceType_Both NoticeInstanceType = "both"
NoticeInstanceType_Cloud NoticeInstanceType = "cloud"
NoticeInstanceType_OnPrem NoticeInstanceType = "onprem"
)
// SKU. Defaults to "all"
type NoticeSKU string
func NewNoticeSKU(s NoticeSKU) *NoticeSKU { return &s }
func (c *NoticeSKU) Matches(s string) bool {
switch *c {
case NoticeSKU_All:
return true
case NoticeSKU_E0, NoticeSKU_Team:
return s == ""
default:
return s == string(*c)
}
}
const (
NoticeSKU_E0 NoticeSKU = "e0"
NoticeSKU_E10 NoticeSKU = "e10"
NoticeSKU_E20 NoticeSKU = "e20"
NoticeSKU_All NoticeSKU = "all"
NoticeSKU_Team NoticeSKU = "team"
)
// Optional action to perform on action button click. (defaults to closing the notice)
//
// Possible actions to execute on button press
type NoticeAction string
const (
URL NoticeAction = "url"
)
// Definition of the table keeping the 'viewed' state of each in-product notice per user
type ProductNoticeViewState struct {
UserId string
NoticeId string
Viewed int32
Timestamp int64
}

View File

@ -19,6 +19,7 @@ const (
PUSH_TYPE_MESSAGE = "message"
PUSH_TYPE_CLEAR = "clear"
PUSH_TYPE_UPDATE_BADGE = "update_badge"
PUSH_TYPE_SESSION = "session"
PUSH_MESSAGE_V2 = "v2"
PUSH_SOUND_NONE = "none"

View File

@ -9,10 +9,24 @@ import (
"strings"
)
// SysconsoleAncillaryPermissions maps the non-sysconsole permissions required by each sysconsole view.
var SysconsoleAncillaryPermissions map[string][]*Permission
var SystemManagerDefaultPermissions []string
var SystemUserManagerDefaultPermissions []string
var SystemReadOnlyAdminDefaultPermissions []string
var BuiltInSchemeManagedRoleIDs []string
var NewSystemRoleIDs []string
func init() {
BuiltInSchemeManagedRoleIDs = []string{
NewSystemRoleIDs = []string{
SYSTEM_USER_MANAGER_ROLE_ID,
SYSTEM_READ_ONLY_ADMIN_ROLE_ID,
SYSTEM_MANAGER_ROLE_ID,
}
BuiltInSchemeManagedRoleIDs = append([]string{
SYSTEM_GUEST_ROLE_ID,
SYSTEM_USER_ROLE_ID,
SYSTEM_ADMIN_ROLE_ID,
@ -29,7 +43,125 @@ func init() {
CHANNEL_GUEST_ROLE_ID,
CHANNEL_USER_ROLE_ID,
CHANNEL_ADMIN_ROLE_ID,
}, NewSystemRoleIDs...)
// When updating the values here, the values in mattermost-redux must also be updated.
SysconsoleAncillaryPermissions = map[string][]*Permission{
PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_CHANNELS.Id: {
PERMISSION_READ_PUBLIC_CHANNEL,
PERMISSION_READ_CHANNEL,
PERMISSION_READ_PUBLIC_CHANNEL_GROUPS,
PERMISSION_READ_PRIVATE_CHANNEL_GROUPS,
},
PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_USERS.Id: {
PERMISSION_READ_OTHER_USERS_TEAMS,
},
PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_TEAMS.Id: {
PERMISSION_LIST_PRIVATE_TEAMS,
PERMISSION_LIST_PUBLIC_TEAMS,
PERMISSION_VIEW_TEAM,
},
PERMISSION_SYSCONSOLE_READ_ENVIRONMENT.Id: {
PERMISSION_READ_JOBS,
},
PERMISSION_SYSCONSOLE_READ_AUTHENTICATION.Id: {
PERMISSION_READ_JOBS,
},
PERMISSION_SYSCONSOLE_READ_REPORTING.Id: {
PERMISSION_VIEW_TEAM,
},
PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_USERS.Id: {
PERMISSION_EDIT_OTHER_USERS,
PERMISSION_DEMOTE_TO_GUEST,
PERMISSION_PROMOTE_GUEST,
},
PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_CHANNELS.Id: {
PERMISSION_MANAGE_TEAM,
PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES,
PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES,
PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS,
PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS,
PERMISSION_DELETE_PRIVATE_CHANNEL,
PERMISSION_DELETE_PUBLIC_CHANNEL,
PERMISSION_MANAGE_CHANNEL_ROLES,
PERMISSION_CONVERT_PUBLIC_CHANNEL_TO_PRIVATE,
PERMISSION_CONVERT_PRIVATE_CHANNEL_TO_PUBLIC,
},
PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_TEAMS.Id: {
PERMISSION_MANAGE_TEAM,
PERMISSION_MANAGE_TEAM_ROLES,
PERMISSION_REMOVE_USER_FROM_TEAM,
PERMISSION_JOIN_PRIVATE_TEAMS,
PERMISSION_JOIN_PUBLIC_TEAMS,
PERMISSION_ADD_USER_TO_TEAM,
},
PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_GROUPS.Id: {
PERMISSION_MANAGE_TEAM,
PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS,
PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS,
PERMISSION_CONVERT_PUBLIC_CHANNEL_TO_PRIVATE,
PERMISSION_CONVERT_PRIVATE_CHANNEL_TO_PUBLIC,
},
PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT.Id: {
PERMISSION_MANAGE_JOBS,
},
PERMISSION_SYSCONSOLE_WRITE_SITE.Id: {
PERMISSION_EDIT_BRAND,
},
}
SystemUserManagerDefaultPermissions = []string{
PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_GROUPS.Id,
PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_TEAMS.Id,
PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_CHANNELS.Id,
PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_PERMISSIONS.Id,
PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_GROUPS.Id,
PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_TEAMS.Id,
PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_CHANNELS.Id,
PERMISSION_SYSCONSOLE_READ_AUTHENTICATION.Id,
}
SystemReadOnlyAdminDefaultPermissions = []string{
PERMISSION_SYSCONSOLE_READ_ABOUT.Id,
PERMISSION_SYSCONSOLE_READ_REPORTING.Id,
PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_USERS.Id,
PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_GROUPS.Id,
PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_TEAMS.Id,
PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_CHANNELS.Id,
PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_PERMISSIONS.Id,
PERMISSION_SYSCONSOLE_READ_ENVIRONMENT.Id,
PERMISSION_SYSCONSOLE_READ_SITE.Id,
PERMISSION_SYSCONSOLE_READ_AUTHENTICATION.Id,
PERMISSION_SYSCONSOLE_READ_PLUGINS.Id,
PERMISSION_SYSCONSOLE_READ_INTEGRATIONS.Id,
PERMISSION_SYSCONSOLE_READ_EXPERIMENTAL.Id,
}
SystemManagerDefaultPermissions = []string{
PERMISSION_SYSCONSOLE_READ_ABOUT.Id,
PERMISSION_SYSCONSOLE_READ_REPORTING.Id,
PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_GROUPS.Id,
PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_TEAMS.Id,
PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_CHANNELS.Id,
PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_PERMISSIONS.Id,
PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_GROUPS.Id,
PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_TEAMS.Id,
PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_CHANNELS.Id,
PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_PERMISSIONS.Id,
PERMISSION_SYSCONSOLE_READ_ENVIRONMENT.Id,
PERMISSION_SYSCONSOLE_WRITE_ENVIRONMENT.Id,
PERMISSION_SYSCONSOLE_READ_SITE.Id,
PERMISSION_SYSCONSOLE_WRITE_SITE.Id,
PERMISSION_SYSCONSOLE_READ_AUTHENTICATION.Id,
PERMISSION_SYSCONSOLE_READ_PLUGINS.Id,
PERMISSION_SYSCONSOLE_READ_INTEGRATIONS.Id,
PERMISSION_SYSCONSOLE_WRITE_INTEGRATIONS.Id,
}
// Add the ancillary permissions to each system role
SystemUserManagerDefaultPermissions = addAncillaryPermissions(SystemUserManagerDefaultPermissions)
SystemReadOnlyAdminDefaultPermissions = addAncillaryPermissions(SystemReadOnlyAdminDefaultPermissions)
SystemManagerDefaultPermissions = addAncillaryPermissions(SystemManagerDefaultPermissions)
}
type RoleType string
@ -42,6 +174,9 @@ const (
SYSTEM_POST_ALL_ROLE_ID = "system_post_all"
SYSTEM_POST_ALL_PUBLIC_ROLE_ID = "system_post_all_public"
SYSTEM_USER_ACCESS_TOKEN_ROLE_ID = "system_user_access_token"
SYSTEM_USER_MANAGER_ROLE_ID = "system_user_manager"
SYSTEM_READ_ONLY_ADMIN_ROLE_ID = "system_read_only_admin"
SYSTEM_MANAGER_ROLE_ID = "system_manager"
TEAM_GUEST_ROLE_ID = "team_guest"
TEAM_USER_ROLE_ID = "team_user"
@ -135,8 +270,8 @@ func (r *Role) MergeChannelHigherScopedPermissions(higherScopedPermissions *Role
higherScopedPermissionsMap := AsStringBoolMap(higherScopedPermissions.Permissions)
rolePermissionsMap := AsStringBoolMap(r.Permissions)
for _, cp := range ALL_PERMISSIONS {
if cp.Scope != PERMISSION_SCOPE_CHANNEL {
for _, cp := range AllPermissions {
if cp.Scope != PermissionScopeChannel {
continue
}
@ -150,7 +285,7 @@ func (r *Role) MergeChannelHigherScopedPermissions(higherScopedPermissions *Role
continue
}
_, permissionIsModerated := CHANNEL_MODERATED_PERMISSIONS_MAP[cp.Id]
_, permissionIsModerated := ChannelModeratedPermissionsMap[cp.Id]
if permissionIsModerated {
_, presentOnRole := rolePermissionsMap[cp.Id]
if presentOnRole && presentOnHigherScope {
@ -216,13 +351,13 @@ func ChannelModeratedPermissionsChangedByPatch(role *Role, patch *RolePatch) []s
patchMap := make(map[string]bool)
for _, permission := range role.Permissions {
if channelModeratedPermissionName, found := CHANNEL_MODERATED_PERMISSIONS_MAP[permission]; found {
if channelModeratedPermissionName, found := ChannelModeratedPermissionsMap[permission]; found {
roleMap[channelModeratedPermissionName] = true
}
}
for _, permission := range *patch.Permissions {
if channelModeratedPermissionName, found := CHANNEL_MODERATED_PERMISSIONS_MAP[permission]; found {
if channelModeratedPermissionName, found := ChannelModeratedPermissionsMap[permission]; found {
patchMap[channelModeratedPermissionName] = true
}
}
@ -246,11 +381,11 @@ func ChannelModeratedPermissionsChangedByPatch(role *Role, patch *RolePatch) []s
func (r *Role) GetChannelModeratedPermissions(channelType string) map[string]bool {
moderatedPermissions := make(map[string]bool)
for _, permission := range r.Permissions {
if _, found := CHANNEL_MODERATED_PERMISSIONS_MAP[permission]; !found {
if _, found := ChannelModeratedPermissionsMap[permission]; !found {
continue
}
for moderated, moderatedPermissionValue := range CHANNEL_MODERATED_PERMISSIONS_MAP {
for moderated, moderatedPermissionValue := range ChannelModeratedPermissionsMap {
// the moderated permission has already been found to be true so skip this iteration
if moderatedPermissions[moderatedPermissionValue] {
continue
@ -279,14 +414,14 @@ func (r *Role) RolePatchFromChannelModerationsPatch(channelModerationsPatch []*C
// Iterate through the list of existing permissions on the role and append permissions that we want to keep.
for _, permission := range r.Permissions {
// Permission is not moderated so dont add it to the patch and skip the channelModerationsPatch
if _, isModerated := CHANNEL_MODERATED_PERMISSIONS_MAP[permission]; !isModerated {
if _, isModerated := ChannelModeratedPermissionsMap[permission]; !isModerated {
continue
}
permissionEnabled := true
// Check if permission has a matching moderated permission name inside the channel moderation patch
for _, channelModerationPatch := range channelModerationsPatch {
if *channelModerationPatch.Name == CHANNEL_MODERATED_PERMISSIONS_MAP[permission] {
if *channelModerationPatch.Name == ChannelModeratedPermissionsMap[permission] {
// Permission key exists in patch with a value of false so skip over it
if roleName == "members" {
if channelModerationPatch.Roles.Members != nil && !*channelModerationPatch.Roles.Members {
@ -307,7 +442,7 @@ func (r *Role) RolePatchFromChannelModerationsPatch(channelModerationsPatch []*C
// Iterate through the patch and add any permissions that dont already exist on the role
for _, channelModerationPatch := range channelModerationsPatch {
for permission, moderatedPermissionName := range CHANNEL_MODERATED_PERMISSIONS_MAP {
for permission, moderatedPermissionName := range ChannelModeratedPermissionsMap {
if roleName == "members" && channelModerationPatch.Roles.Members != nil && *channelModerationPatch.Roles.Members && *channelModerationPatch.Name == moderatedPermissionName {
permissionsToAddToPatch[permission] = true
}
@ -349,7 +484,7 @@ func (r *Role) IsValidWithoutId() bool {
for _, permission := range r.Permissions {
permissionValidated := false
for _, p := range ALL_PERMISSIONS {
for _, p := range append(AllPermissions, DeprecatedPermissions...) {
if permission == p.Id {
permissionValidated = true
break
@ -364,6 +499,23 @@ func (r *Role) IsValidWithoutId() bool {
return true
}
func CleanRoleNames(roleNames []string) ([]string, bool) {
var cleanedRoleNames []string
for _, roleName := range roleNames {
if strings.TrimSpace(roleName) == "" {
continue
}
if !IsValidRoleName(roleName) {
return roleNames, false
}
cleanedRoleNames = append(cleanedRoleNames, roleName)
}
return cleanedRoleNames, true
}
func IsValidRoleName(roleName string) bool {
if len(roleName) <= 0 || len(roleName) > ROLE_NAME_MAX_LENGTH {
return false
@ -493,6 +645,8 @@ func MakeDefaultRoles() map[string]*Role {
PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS.Id,
PERMISSION_MANAGE_INCOMING_WEBHOOKS.Id,
PERMISSION_MANAGE_OUTGOING_WEBHOOKS.Id,
PERMISSION_CONVERT_PUBLIC_CHANNEL_TO_PRIVATE.Id,
PERMISSION_CONVERT_PRIVATE_CHANNEL_TO_PUBLIC.Id,
},
SchemeManaged: true,
BuiltIn: true,
@ -562,6 +716,38 @@ func MakeDefaultRoles() map[string]*Role {
BuiltIn: true,
}
roles[SYSTEM_USER_MANAGER_ROLE_ID] = &Role{
Name: "system_user_manager",
DisplayName: "authentication.roles.system_user_manager.name",
Description: "authentication.roles.system_user_manager.description",
Permissions: SystemUserManagerDefaultPermissions,
SchemeManaged: false,
BuiltIn: true,
}
roles[SYSTEM_READ_ONLY_ADMIN_ROLE_ID] = &Role{
Name: "system_read_only_admin",
DisplayName: "authentication.roles.system_read_only_admin.name",
Description: "authentication.roles.system_read_only_admin.description",
Permissions: SystemReadOnlyAdminDefaultPermissions,
SchemeManaged: false,
BuiltIn: true,
}
roles[SYSTEM_MANAGER_ROLE_ID] = &Role{
Name: "system_manager",
DisplayName: "authentication.roles.system_manager.name",
Description: "authentication.roles.system_manager.description",
Permissions: SystemManagerDefaultPermissions,
SchemeManaged: false,
BuiltIn: true,
}
allPermissionIDs := []string{}
for _, permission := range AllPermissions {
allPermissionIDs = append(allPermissionIDs, permission.Id)
}
roles[SYSTEM_ADMIN_ROLE_ID] = &Role{
Name: "system_admin",
DisplayName: "authentication.roles.global_admin.name",
@ -569,64 +755,21 @@ func MakeDefaultRoles() map[string]*Role {
// System admins can do anything channel and team admins can do
// plus everything members of teams and channels can do to all teams
// and channels on the system
Permissions: append(
append(
append(
append(
[]string{
PERMISSION_ASSIGN_SYSTEM_ADMIN_ROLE.Id,
PERMISSION_MANAGE_SYSTEM.Id,
PERMISSION_MANAGE_ROLES.Id,
PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES.Id,
PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS.Id,
PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS.Id,
PERMISSION_DELETE_PUBLIC_CHANNEL.Id,
PERMISSION_CREATE_PUBLIC_CHANNEL.Id,
PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES.Id,
PERMISSION_DELETE_PRIVATE_CHANNEL.Id,
PERMISSION_CREATE_PRIVATE_CHANNEL.Id,
PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH.Id,
PERMISSION_MANAGE_OTHERS_INCOMING_WEBHOOKS.Id,
PERMISSION_MANAGE_OTHERS_OUTGOING_WEBHOOKS.Id,
PERMISSION_EDIT_OTHER_USERS.Id,
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,
PERMISSION_ADD_USER_TO_TEAM.Id,
PERMISSION_LIST_USERS_WITHOUT_TEAM.Id,
PERMISSION_MANAGE_JOBS.Id,
PERMISSION_CREATE_POST_PUBLIC.Id,
PERMISSION_CREATE_POST_EPHEMERAL.Id,
PERMISSION_CREATE_USER_ACCESS_TOKEN.Id,
PERMISSION_READ_USER_ACCESS_TOKEN.Id,
PERMISSION_REVOKE_USER_ACCESS_TOKEN.Id,
PERMISSION_CREATE_BOT.Id,
PERMISSION_READ_BOTS.Id,
PERMISSION_READ_OTHERS_BOTS.Id,
PERMISSION_MANAGE_BOTS.Id,
PERMISSION_MANAGE_OTHERS_BOTS.Id,
PERMISSION_REMOVE_OTHERS_REACTIONS.Id,
PERMISSION_LIST_PRIVATE_TEAMS.Id,
PERMISSION_JOIN_PRIVATE_TEAMS.Id,
PERMISSION_VIEW_MEMBERS.Id,
},
roles[TEAM_USER_ROLE_ID].Permissions...,
),
roles[CHANNEL_USER_ROLE_ID].Permissions...,
),
roles[TEAM_ADMIN_ROLE_ID].Permissions...,
),
roles[CHANNEL_ADMIN_ROLE_ID].Permissions...,
),
Permissions: allPermissionIDs,
SchemeManaged: true,
BuiltIn: true,
}
return roles
}
func addAncillaryPermissions(permissions []string) []string {
for _, permission := range permissions {
if ancillaryPermissions, ok := SysconsoleAncillaryPermissions[permission]; ok {
for _, ancillaryPermission := range ancillaryPermissions {
permissions = append(permissions, ancillaryPermission.Id)
}
}
}
return permissions
}

View File

@ -15,6 +15,7 @@ const (
USER_AUTH_SERVICE_SAML_TEXT = "SAML"
USER_AUTH_SERVICE_IS_SAML = "isSaml"
USER_AUTH_SERVICE_IS_MOBILE = "isMobile"
USER_AUTH_SERVICE_IS_OAUTH = "isOAuthUser"
)
type SamlAuthRequest struct {

View File

@ -4,6 +4,7 @@
package model
import (
"net/http"
"regexp"
"strings"
"time"
@ -367,3 +368,13 @@ func ParseSearchParams(text string, timeZoneOffset int) []*SearchParams {
return paramsList
}
func IsSearchParamsListValid(paramsList []*SearchParams) *AppError {
// All SearchParams should have same IncludeDeletedChannels value.
for _, params := range paramsList {
if params.IncludeDeletedChannels != paramsList[0].IncludeDeletedChannels {
return NewAppError("IsSearchParamsListValid", "model.search_params_list.is_valid.include_deleted_channels.app_error", nil, "", http.StatusInternalServerError)
}
}
return nil
}

File diff suppressed because it is too large Load Diff

View File

@ -30,6 +30,11 @@ const (
SESSION_USER_ACCESS_TOKEN_EXPIRY = 100 * 365 // 100 years
)
//msgp:tuple Session
// Session contains the user session details.
// This struct's serializer methods are auto-generated. If a new field is added/removed,
// please run make gen-serialized.
type Session struct {
Id string `json:"id"`
Token string `json:"token"`
@ -40,6 +45,7 @@ type Session struct {
DeviceId string `json:"device_id"`
Roles string `json:"roles"`
IsOAuth bool `json:"is_oauth"`
ExpiredNotify bool `json:"expired_notify"`
Props StringMap `json:"props"`
TeamMembers []*TeamMember `json:"team_members" db:"-"`
Local bool `json:"local" db:"-"`
@ -114,6 +120,9 @@ func (me *Session) IsExpired() bool {
return false
}
// Deprecated: SetExpireInDays is deprecated and should not be used.
// Use (*App).SetSessionExpireInDays instead which handles the
// cases where the new ExpiresAt is not relative to CreateAt.
func (me *Session) SetExpireInDays(days int) {
if me.CreateAt == 0 {
me.ExpiresAt = GetMillis() + (1000 * 60 * 60 * 24 * int64(days))
@ -171,8 +180,21 @@ func (me *Session) IsSaml() bool {
return isSaml
}
func (me *Session) IsOAuthUser() bool {
val, ok := me.Props[USER_AUTH_SERVICE_IS_OAUTH]
if !ok {
return false
}
isOAuthUser, err := strconv.ParseBool(val)
if err != nil {
mlog.Error("Error parsing boolean property from Session", mlog.Err(err))
return false
}
return isOAuthUser
}
func (me *Session) IsSSOLogin() bool {
return me.IsOAuth || me.IsSaml()
return me.IsOAuthUser() || me.IsSaml()
}
func (me *Session) GetUserRoles() []string {

View File

@ -35,7 +35,8 @@ func (o *Status) ToJson() string {
}
func (o *Status) ToClusterJson() string {
b, _ := json.Marshal(o)
oCopy := *o
b, _ := json.Marshal(oCopy)
return string(b)
}

View File

@ -10,15 +10,37 @@ import (
)
const (
SYSTEM_DIAGNOSTIC_ID = "DiagnosticId"
SYSTEM_RAN_UNIT_TESTS = "RanUnitTests"
SYSTEM_LAST_SECURITY_TIME = "LastSecurityTime"
SYSTEM_ACTIVE_LICENSE_ID = "ActiveLicenseId"
SYSTEM_LAST_COMPLIANCE_TIME = "LastComplianceTime"
SYSTEM_ASYMMETRIC_SIGNING_KEY = "AsymmetricSigningKey"
SYSTEM_POST_ACTION_COOKIE_SECRET = "PostActionCookieSecret"
SYSTEM_INSTALLATION_DATE_KEY = "InstallationDate"
SYSTEM_FIRST_SERVER_RUN_TIMESTAMP_KEY = "FirstServerRunTimestamp"
SYSTEM_TELEMETRY_ID = "DiagnosticId"
SYSTEM_RAN_UNIT_TESTS = "RanUnitTests"
SYSTEM_LAST_SECURITY_TIME = "LastSecurityTime"
SYSTEM_ACTIVE_LICENSE_ID = "ActiveLicenseId"
SYSTEM_LAST_COMPLIANCE_TIME = "LastComplianceTime"
SYSTEM_ASYMMETRIC_SIGNING_KEY = "AsymmetricSigningKey"
SYSTEM_POST_ACTION_COOKIE_SECRET = "PostActionCookieSecret"
SYSTEM_INSTALLATION_DATE_KEY = "InstallationDate"
SYSTEM_FIRST_SERVER_RUN_TIMESTAMP_KEY = "FirstServerRunTimestamp"
SYSTEM_CLUSTER_ENCRYPTION_KEY = "ClusterEncryptionKey"
SYSTEM_UPGRADED_FROM_TE_ID = "UpgradedFromTE"
SYSTEM_WARN_METRIC_NUMBER_OF_TEAMS_5 = "warn_metric_number_of_teams_5"
SYSTEM_WARN_METRIC_NUMBER_OF_CHANNELS_50 = "warn_metric_number_of_channels_50"
SYSTEM_WARN_METRIC_MFA = "warn_metric_mfa"
SYSTEM_WARN_METRIC_EMAIL_DOMAIN = "warn_metric_email_domain"
SYSTEM_WARN_METRIC_NUMBER_OF_ACTIVE_USERS_100 = "warn_metric_number_of_active_users_100"
SYSTEM_WARN_METRIC_NUMBER_OF_ACTIVE_USERS_200 = "warn_metric_number_of_active_users_200"
SYSTEM_WARN_METRIC_NUMBER_OF_ACTIVE_USERS_300 = "warn_metric_number_of_active_users_300"
SYSTEM_WARN_METRIC_NUMBER_OF_ACTIVE_USERS_500 = "warn_metric_number_of_active_users_500"
SYSTEM_WARN_METRIC_NUMBER_OF_POSTS_2M = "warn_metric_number_of_posts_2M"
SYSTEM_WARN_METRIC_LAST_RUN_TIMESTAMP_KEY = "LastWarnMetricRunTimestamp"
)
const (
WARN_METRIC_STATUS_LIMIT_REACHED = "true"
WARN_METRIC_STATUS_RUNONCE = "runonce"
WARN_METRIC_STATUS_ACK = "ack"
WARN_METRIC_STATUS_STORE_PREFIX = "warn_metric_"
WARN_METRIC_JOB_INTERVAL = 24 * 7
WARN_METRIC_NUMBER_OF_ACTIVE_USERS_25 = 25
WARN_METRIC_JOB_WAIT_TIME = 1000 * 3600 * 24 * 7 // 7 days
)
type System struct {
@ -69,3 +91,114 @@ func ServerBusyStateFromJson(r io.Reader) *ServerBusyState {
json.NewDecoder(r).Decode(&sbs)
return sbs
}
var WarnMetricsTable = map[string]WarnMetric{
SYSTEM_WARN_METRIC_MFA: {
Id: SYSTEM_WARN_METRIC_MFA,
Limit: -1,
IsBotOnly: true,
IsRunOnce: true,
},
SYSTEM_WARN_METRIC_EMAIL_DOMAIN: {
Id: SYSTEM_WARN_METRIC_EMAIL_DOMAIN,
Limit: -1,
IsBotOnly: true,
IsRunOnce: true,
},
SYSTEM_WARN_METRIC_NUMBER_OF_TEAMS_5: {
Id: SYSTEM_WARN_METRIC_NUMBER_OF_TEAMS_5,
Limit: 5,
IsBotOnly: true,
IsRunOnce: true,
},
SYSTEM_WARN_METRIC_NUMBER_OF_CHANNELS_50: {
Id: SYSTEM_WARN_METRIC_NUMBER_OF_CHANNELS_50,
Limit: 50,
IsBotOnly: true,
IsRunOnce: true,
},
SYSTEM_WARN_METRIC_NUMBER_OF_ACTIVE_USERS_100: {
Id: SYSTEM_WARN_METRIC_NUMBER_OF_ACTIVE_USERS_100,
Limit: 100,
IsBotOnly: true,
IsRunOnce: true,
},
SYSTEM_WARN_METRIC_NUMBER_OF_ACTIVE_USERS_200: {
Id: SYSTEM_WARN_METRIC_NUMBER_OF_ACTIVE_USERS_200,
Limit: 200,
IsBotOnly: true,
IsRunOnce: true,
},
SYSTEM_WARN_METRIC_NUMBER_OF_ACTIVE_USERS_300: {
Id: SYSTEM_WARN_METRIC_NUMBER_OF_ACTIVE_USERS_300,
Limit: 300,
IsBotOnly: true,
IsRunOnce: true,
},
SYSTEM_WARN_METRIC_NUMBER_OF_ACTIVE_USERS_500: {
Id: SYSTEM_WARN_METRIC_NUMBER_OF_ACTIVE_USERS_500,
Limit: 500,
IsBotOnly: false,
IsRunOnce: true,
},
SYSTEM_WARN_METRIC_NUMBER_OF_POSTS_2M: {
Id: SYSTEM_WARN_METRIC_NUMBER_OF_POSTS_2M,
Limit: 2000000,
IsBotOnly: false,
IsRunOnce: true,
},
}
type WarnMetric struct {
Id string
Limit int64
IsBotOnly bool
IsRunOnce bool
}
type WarnMetricDisplayTexts struct {
BotTitle string
BotMessageBody string
BotSuccessMessage string
EmailBody string
}
type WarnMetricStatus struct {
Id string `json:"id"`
Limit int64 `json:"limit"`
Acked bool `json:"acked"`
StoreStatus string `json:"store_status,omitempty"`
}
func (wms *WarnMetricStatus) ToJson() string {
b, _ := json.Marshal(wms)
return string(b)
}
func WarnMetricStatusFromJson(data io.Reader) *WarnMetricStatus {
var o WarnMetricStatus
if err := json.NewDecoder(data).Decode(&o); err != nil {
return nil
} else {
return &o
}
}
func MapWarnMetricStatusToJson(o map[string]*WarnMetricStatus) string {
b, _ := json.Marshal(o)
return string(b)
}
type SendWarnMetricAck struct {
ForceAck bool `json:"forceAck"`
}
func (swma *SendWarnMetricAck) ToJson() string {
b, _ := json.Marshal(swma)
return string(b)
}
func SendWarnMetricAckFromJson(r io.Reader) *SendWarnMetricAck {
var swma *SendWarnMetricAck
json.NewDecoder(r).Decode(&swma)
return swma
}

View File

@ -9,9 +9,12 @@ import (
)
type TeamSearch struct {
Term string `json:"term"`
Page *int `json:"page,omitempty"`
PerPage *int `json:"per_page,omitempty"`
Term string `json:"term"`
Page *int `json:"page,omitempty"`
PerPage *int `json:"per_page,omitempty"`
AllowOpenInvite *bool `json:"allow_open_invite,omitempty"`
GroupConstrained *bool `json:"group_constrained,omitempty"`
IncludeGroupConstrained *bool `json:"include_group_constrained,omitempty"`
}
func (t *TeamSearch) IsPaginated() bool {

View File

@ -0,0 +1,25 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package model
import (
"encoding/json"
"io"
)
type TypingRequest struct {
ChannelId string `json:"channel_id"`
ParentId string `json:"parent_id"`
}
func (o *TypingRequest) ToJson() string {
b, _ := json.Marshal(o)
return string(b)
}
func TypingRequestFromJson(data io.Reader) *TypingRequest {
var o *TypingRequest
json.NewDecoder(data).Decode(&o)
return o
}

View File

@ -0,0 +1,141 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package model
import (
"encoding/json"
"fmt"
"io"
"net/http"
)
// UploadType defines the type of an upload.
type UploadType string
const (
UploadTypeAttachment UploadType = "attachment"
UploadTypeImport UploadType = "import"
)
// UploadSession contains information used to keep track of a file upload.
type UploadSession struct {
// The unique identifier for the session.
Id string `json:"id"`
// The type of the upload.
Type UploadType `json:"type"`
// The timestamp of creation.
CreateAt int64 `json:"create_at"`
// The id of the user performing the upload.
UserId string `json:"user_id"`
// The id of the channel to upload to.
ChannelId string `json:"channel_id"`
// The name of the file to upload.
Filename string `json:"filename"`
// The path where the file is stored.
Path string `json:"-"`
// The size of the file to upload.
FileSize int64 `json:"file_size"`
// The amount of received data in bytes. If equal to FileSize it means the
// upload has finished.
FileOffset int64 `json:"file_offset"`
}
// ToJson serializes the UploadSession into JSON and returns it as string.
func (us *UploadSession) ToJson() string {
b, _ := json.Marshal(us)
return string(b)
}
// UploadSessionsToJson serializes a list of UploadSession into JSON and
// returns it as string.
func UploadSessionsToJson(uss []*UploadSession) string {
b, _ := json.Marshal(uss)
return string(b)
}
// UploadSessionsFromJson deserializes a list of UploadSession from JSON data.
func UploadSessionsFromJson(data io.Reader) []*UploadSession {
decoder := json.NewDecoder(data)
var uss []*UploadSession
if err := decoder.Decode(&uss); err != nil {
return nil
}
return uss
}
// UploadSessionFromJson deserializes the UploadSession from JSON data.
func UploadSessionFromJson(data io.Reader) *UploadSession {
decoder := json.NewDecoder(data)
var us UploadSession
if err := decoder.Decode(&us); err != nil {
return nil
}
return &us
}
// PreSave is a utility function used to fill required information.
func (us *UploadSession) PreSave() {
if us.Id == "" {
us.Id = NewId()
}
if us.CreateAt == 0 {
us.CreateAt = GetMillis()
}
}
// IsValid validates an UploadType. It returns an error in case of
// failure.
func (t UploadType) IsValid() error {
switch t {
case UploadTypeAttachment:
return nil
case UploadTypeImport:
return nil
default:
}
return fmt.Errorf("invalid UploadType %s", t)
}
// IsValid validates an UploadSession. It returns an error in case of
// failure.
func (us *UploadSession) IsValid() *AppError {
if !IsValidId(us.Id) {
return NewAppError("UploadSession.IsValid", "model.upload_session.is_valid.id.app_error", nil, "", http.StatusBadRequest)
}
if err := us.Type.IsValid(); err != nil {
return NewAppError("UploadSession.IsValid", "model.upload_session.is_valid.type.app_error", nil, err.Error(), http.StatusBadRequest)
}
if !IsValidId(us.UserId) {
return NewAppError("UploadSession.IsValid", "model.upload_session.is_valid.user_id.app_error", nil, "id="+us.Id, http.StatusBadRequest)
}
if us.Type == UploadTypeAttachment && !IsValidId(us.ChannelId) {
return NewAppError("UploadSession.IsValid", "model.upload_session.is_valid.channel_id.app_error", nil, "id="+us.Id, http.StatusBadRequest)
}
if us.CreateAt == 0 {
return NewAppError("UploadSession.IsValid", "model.upload_session.is_valid.create_at.app_error", nil, "id="+us.Id, http.StatusBadRequest)
}
if us.Filename == "" {
return NewAppError("UploadSession.IsValid", "model.upload_session.is_valid.filename.app_error", nil, "id="+us.Id, http.StatusBadRequest)
}
if us.FileSize <= 0 {
return NewAppError("UploadSession.IsValid", "model.upload_session.is_valid.file_size.app_error", nil, "id="+us.Id, http.StatusBadRequest)
}
if us.FileOffset < 0 || us.FileOffset > us.FileSize {
return NewAppError("UploadSession.IsValid", "model.upload_session.is_valid.file_offset.app_error", nil, "id="+us.Id, http.StatusBadRequest)
}
if us.Path == "" {
return NewAppError("UploadSession.IsValid", "model.upload_session.is_valid.path.app_error", nil, "id="+us.Id, http.StatusBadRequest)
}
return nil
}

View File

@ -59,6 +59,11 @@ const (
USER_LOCALE_MAX_LENGTH = 5
)
//msgp:tuple User
// User contains the details about the user.
// This struct's serializer methods are auto-generated. If a new field is added/removed,
// please run make gen-serialized.
type User struct {
Id string `json:"id"`
CreateAt int64 `json:"create_at,omitempty"`
@ -124,6 +129,7 @@ type UserForIndexing struct {
Nickname string `json:"nickname"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Roles string `json:"roles"`
CreateAt int64 `json:"create_at"`
DeleteAt int64 `json:"delete_at"`
TeamsIds []string `json:"team_id"`

View File

@ -13,6 +13,14 @@ type UserCountOptions struct {
ExcludeRegularUsers bool
// Only include users on a specific team. "" for any team.
TeamId string
// Only include users on a specific channel. "" for any channel.
ChannelId string
// Restrict to search in a list of teams and channels
ViewRestrictions *ViewUsersRestrictions
// Only include users matching any of the given system wide roles.
Roles []string
// Only include users matching any of the given channel roles, must be used with ChannelId.
ChannelRoles []string
// Only include users matching any of the given team roles, must be used with TeamId.
TeamRoles []string
}

View File

@ -12,6 +12,8 @@ type UserGetOptions struct {
InChannelId string
// Filters the users not in the channel
NotInChannelId string
// Filters the users in the group
InGroupId string
// Filters the users group constrained
GroupConstrained bool
// Filters the users without a team
@ -22,6 +24,12 @@ type UserGetOptions struct {
Active bool
// Filters for the given role
Role string
// Filters for users matching any of the given system wide roles
Roles []string
// Filters for users matching any of the given channel roles, must be used with InChannelId
ChannelRoles []string
// Filters for users matching any of the given team roles, must be used with InTeamId
TeamRoles []string
// Sorting option
Sort string
// Restrict to search in a list of teams and channels

View File

@ -13,16 +13,20 @@ const USER_SEARCH_DEFAULT_LIMIT = 100
// UserSearch captures the parameters provided by a client for initiating a user search.
type UserSearch struct {
Term string `json:"term"`
TeamId string `json:"team_id"`
NotInTeamId string `json:"not_in_team_id"`
InChannelId string `json:"in_channel_id"`
NotInChannelId string `json:"not_in_channel_id"`
GroupConstrained bool `json:"group_constrained"`
AllowInactive bool `json:"allow_inactive"`
WithoutTeam bool `json:"without_team"`
Limit int `json:"limit"`
Role string `json:"role"`
Term string `json:"term"`
TeamId string `json:"team_id"`
NotInTeamId string `json:"not_in_team_id"`
InChannelId string `json:"in_channel_id"`
NotInChannelId string `json:"not_in_channel_id"`
InGroupId string `json:"in_group_id"`
GroupConstrained bool `json:"group_constrained"`
AllowInactive bool `json:"allow_inactive"`
WithoutTeam bool `json:"without_team"`
Limit int `json:"limit"`
Role string `json:"role"`
Roles []string `json:"roles"`
ChannelRoles []string `json:"channel_roles"`
TeamRoles []string `json:"team_roles"`
}
// ToJson convert a User to a json string
@ -60,6 +64,12 @@ type UserSearchOptions struct {
Limit int
// Filters for the given role
Role string
// Filters for users that have any of the given system roles
Roles []string
// Filters for users that have the given channel roles to be used when searching in a channel
ChannelRoles []string
// Filters for users that have the given team roles to be used when searching in a team
TeamRoles []string
// Restrict to search in a list of teams and channels
ViewRestrictions *ViewUsersRestrictions
// List of allowed channels

View File

@ -659,12 +659,12 @@ func AsStringBoolMap(list []string) map[string]bool {
// SanitizeUnicode will remove undesirable Unicode characters from a string.
func SanitizeUnicode(s string) string {
return strings.Map(filterBlacklist, s)
return strings.Map(filterBlocklist, s)
}
// filterBlacklist returns `r` if it is not in the blacklist, otherwise drop (-1).
// Blacklist is taken from https://www.w3.org/TR/unicode-xml/#Charlist
func filterBlacklist(r rune) rune {
// filterBlocklist returns `r` if it is not in the blocklist, otherwise drop (-1).
// Blocklist is taken from https://www.w3.org/TR/unicode-xml/#Charlist
func filterBlocklist(r rune) rune {
const drop = -1
switch r {
case '\u0340', '\u0341': // clones of grave and acute; deprecated in Unicode

View File

@ -13,6 +13,9 @@ import (
// It should be maintained in chronological order with most current
// release at the front of the list.
var versions = []string{
"5.28.0",
"5.27.0",
"5.26.0",
"5.25.0",
"5.24.0",
"5.23.0",

View File

@ -62,6 +62,12 @@ const (
WEBSOCKET_EVENT_RECEIVED_GROUP_NOT_ASSOCIATED_TO_TEAM = "received_group_not_associated_to_team"
WEBSOCKET_EVENT_RECEIVED_GROUP_ASSOCIATED_TO_CHANNEL = "received_group_associated_to_channel"
WEBSOCKET_EVENT_RECEIVED_GROUP_NOT_ASSOCIATED_TO_CHANNEL = "received_group_not_associated_to_channel"
WEBSOCKET_EVENT_SIDEBAR_CATEGORY_CREATED = "sidebar_category_created"
WEBSOCKET_EVENT_SIDEBAR_CATEGORY_UPDATED = "sidebar_category_updated"
WEBSOCKET_EVENT_SIDEBAR_CATEGORY_DELETED = "sidebar_category_deleted"
WEBSOCKET_EVENT_SIDEBAR_CATEGORY_ORDER_UPDATED = "sidebar_category_order_updated"
WEBSOCKET_WARN_METRIC_STATUS_RECEIVED = "warn_metric_status_received"
WEBSOCKET_WARN_METRIC_STATUS_REMOVED = "warn_metric_status_removed"
)
type WebSocketMessage interface {