Sync with mattermost 3.5.0

This commit is contained in:
Wim 2016-11-12 22:00:53 +01:00
parent 08ebee6b4f
commit 1d5cd1d7c4
32 changed files with 1476 additions and 430 deletions

View File

@ -153,7 +153,7 @@ func (b *Bmattermost) handleMatterClient(mchan chan *MMMessage) {
for message := range b.mc.MessageChan { for message := range b.mc.MessageChan {
// do not post our own messages back to irc // do not post our own messages back to irc
// only listen to message from our team // only listen to message from our team
if message.Raw.Event == "posted" && b.mc.User.Username != message.Username && message.Raw.TeamId == b.TeamId { if message.Raw.Event == "posted" && b.mc.User.Username != message.Username && message.Raw.Data["team_id"].(string) == b.TeamId {
flog.Debugf("Receiving from matterclient %#v", message) flog.Debugf("Receiving from matterclient %#v", message)
m := &MMMessage{} m := &MMMessage{}
m.Username = message.Username m.Username = message.Username

View File

@ -264,7 +264,7 @@ func (m *MMClient) parseActionPost(rmsg *Message) {
} }
rmsg.Username = m.GetUser(data.UserId).Username rmsg.Username = m.GetUser(data.UserId).Username
rmsg.Channel = m.GetChannelName(data.ChannelId) rmsg.Channel = m.GetChannelName(data.ChannelId)
rmsg.Team = m.GetTeamName(rmsg.Raw.TeamId) rmsg.Team = m.GetTeamName(rmsg.Raw.Data["team_id"].(string))
// direct message // direct message
if rmsg.Raw.Data["channel_type"] == "D" { if rmsg.Raw.Data["channel_type"] == "D" {
rmsg.Channel = m.GetUser(data.UserId).Username rmsg.Channel = m.GetUser(data.UserId).Username
@ -275,7 +275,7 @@ func (m *MMClient) parseActionPost(rmsg *Message) {
} }
func (m *MMClient) UpdateUsers() error { func (m *MMClient) UpdateUsers() error {
mmusers, err := m.Client.GetProfilesForDirectMessageList(m.Team.Id) mmusers, err := m.Client.GetProfiles(0, 1000, "")
if err != nil { if err != nil {
return errors.New(err.DetailedError) return errors.New(err.DetailedError)
} }
@ -305,7 +305,7 @@ func (m *MMClient) GetChannelName(channelId string) string {
m.RLock() m.RLock()
defer m.RUnlock() defer m.RUnlock()
for _, t := range m.OtherTeams { for _, t := range m.OtherTeams {
for _, channel := range append(t.Channels.Channels, t.MoreChannels.Channels...) { for _, channel := range append(*t.Channels, *t.MoreChannels...) {
if channel.Id == channelId { if channel.Id == channelId {
return channel.Name return channel.Name
} }
@ -322,7 +322,7 @@ func (m *MMClient) GetChannelId(name string, teamId string) string {
} }
for _, t := range m.OtherTeams { for _, t := range m.OtherTeams {
if t.Id == teamId { if t.Id == teamId {
for _, channel := range append(t.Channels.Channels, t.MoreChannels.Channels...) { for _, channel := range append(*t.Channels, *t.MoreChannels...) {
if channel.Name == name { if channel.Name == name {
return channel.Id return channel.Id
} }
@ -336,7 +336,7 @@ func (m *MMClient) GetChannelHeader(channelId string) string {
m.RLock() m.RLock()
defer m.RUnlock() defer m.RUnlock()
for _, t := range m.OtherTeams { for _, t := range m.OtherTeams {
for _, channel := range append(t.Channels.Channels, t.MoreChannels.Channels...) { for _, channel := range append(*t.Channels, *t.MoreChannels...) {
if channel.Id == channelId { if channel.Id == channelId {
return channel.Header return channel.Header
} }
@ -354,7 +354,7 @@ func (m *MMClient) PostMessage(channelId string, text string) {
func (m *MMClient) JoinChannel(channelId string) error { func (m *MMClient) JoinChannel(channelId string) error {
m.RLock() m.RLock()
defer m.RUnlock() defer m.RUnlock()
for _, c := range m.Team.Channels.Channels { for _, c := range *m.Team.Channels {
if c.Id == channelId { if c.Id == channelId {
m.log.Debug("Not joining ", channelId, " already joined.") m.log.Debug("Not joining ", channelId, " already joined.")
return nil return nil
@ -397,7 +397,7 @@ func (m *MMClient) GetPublicLink(filename string) string {
if err != nil { if err != nil {
return "" return ""
} }
return res.Data.(string) return res
} }
func (m *MMClient) GetPublicLinks(filenames []string) []string { func (m *MMClient) GetPublicLinks(filenames []string) []string {
@ -407,7 +407,7 @@ func (m *MMClient) GetPublicLinks(filenames []string) []string {
if err != nil { if err != nil {
continue continue
} }
output = append(output, res.Data.(string)) output = append(output, res)
} }
return output return output
} }
@ -432,15 +432,17 @@ func (m *MMClient) UpdateLastViewed(channelId string) {
} }
func (m *MMClient) UsernamesInChannel(channelId string) []string { func (m *MMClient) UsernamesInChannel(channelId string) []string {
ceiRes, err := m.Client.GetChannelExtraInfo(channelId, 5000, "") res, err := m.Client.GetMyChannelMembers()
if err != nil { if err != nil {
m.log.Errorf("UsernamesInChannel(%s) failed: %s", channelId, err) m.log.Errorf("UsernamesInChannel(%s) failed: %s", channelId, err)
return []string{} return []string{}
} }
extra := ceiRes.Data.(*model.ChannelExtra) members := res.Data.(*model.ChannelMembers)
result := []string{} result := []string{}
for _, member := range extra.Members { for _, channel := range *members {
result = append(result, member.Username) if channel.ChannelId == channelId {
result = append(result, m.GetUser(channel.UserId).Username)
}
} }
return result return result
} }
@ -500,10 +502,10 @@ func (m *MMClient) GetChannels() []*model.Channel {
defer m.RUnlock() defer m.RUnlock()
var channels []*model.Channel var channels []*model.Channel
// our primary team channels first // our primary team channels first
channels = append(channels, m.Team.Channels.Channels...) channels = append(channels, *m.Team.Channels...)
for _, t := range m.OtherTeams { for _, t := range m.OtherTeams {
if t.Id != m.Team.Id { if t.Id != m.Team.Id {
channels = append(channels, t.Channels.Channels...) channels = append(channels, *t.Channels...)
} }
} }
return channels return channels
@ -515,7 +517,7 @@ func (m *MMClient) GetMoreChannels() []*model.Channel {
defer m.RUnlock() defer m.RUnlock()
var channels []*model.Channel var channels []*model.Channel
for _, t := range m.OtherTeams { for _, t := range m.OtherTeams {
channels = append(channels, t.MoreChannels.Channels...) channels = append(channels, *t.MoreChannels...)
} }
return channels return channels
} }
@ -526,8 +528,8 @@ func (m *MMClient) GetTeamFromChannel(channelId string) string {
defer m.RUnlock() defer m.RUnlock()
var channels []*model.Channel var channels []*model.Channel
for _, t := range m.OtherTeams { for _, t := range m.OtherTeams {
channels = append(channels, t.Channels.Channels...) channels = append(channels, *t.Channels...)
channels = append(channels, t.MoreChannels.Channels...) channels = append(channels, *t.MoreChannels...)
for _, c := range channels { for _, c := range channels {
if c.Id == channelId { if c.Id == channelId {
return t.Id return t.Id
@ -540,11 +542,13 @@ func (m *MMClient) GetTeamFromChannel(channelId string) string {
func (m *MMClient) GetLastViewedAt(channelId string) int64 { func (m *MMClient) GetLastViewedAt(channelId string) int64 {
m.RLock() m.RLock()
defer m.RUnlock() defer m.RUnlock()
for _, t := range m.OtherTeams { /*
if _, ok := t.Channels.Members[channelId]; ok { for _, t := range m.OtherTeams {
return t.Channels.Members[channelId].LastViewedAt if _, ok := t.Channels.Members[channelId]; ok {
return t.Channels.Members[channelId].LastViewedAt
}
} }
} */
return 0 return 0
} }
@ -619,7 +623,7 @@ func (m *MMClient) initUser() error {
//m.log.Debug("initUser(): loading all team data") //m.log.Debug("initUser(): loading all team data")
for _, v := range initData.Teams { for _, v := range initData.Teams {
m.Client.SetTeamId(v.Id) m.Client.SetTeamId(v.Id)
mmusers, _ := m.Client.GetProfiles(v.Id, "") mmusers, _ := m.Client.GetProfiles(0, 1000, "")
t := &Team{Team: v, Users: mmusers.Data.(map[string]*model.User), Id: v.Id} t := &Team{Team: v, Users: mmusers.Data.(map[string]*model.User), Id: v.Id}
mmchannels, _ := m.Client.GetChannels("") mmchannels, _ := m.Client.GetChannels("")
t.Channels = mmchannels.Data.(*model.ChannelList) t.Channels = mmchannels.Data.(*model.ChannelList)

View File

@ -13,7 +13,6 @@ type ClusterInterface interface {
GetClusterInfos() []*model.ClusterInfo GetClusterInfos() []*model.ClusterInfo
RemoveAllSessionsForUserId(userId string) RemoveAllSessionsForUserId(userId string)
InvalidateCacheForUser(userId string) InvalidateCacheForUser(userId string)
InvalidateCacheForChannel(channelId string)
Publish(event *model.WebSocketEvent) Publish(event *model.WebSocketEvent)
UpdateStatus(status *model.Status) UpdateStatus(status *model.Status)
GetLogs() ([]string, *model.AppError) GetLogs() ([]string, *model.AppError)

View File

@ -8,7 +8,7 @@ import (
) )
type MfaInterface interface { type MfaInterface interface {
GenerateQrCode(user *model.User) ([]byte, *model.AppError) GenerateSecret(user *model.User) (string, []byte, *model.AppError)
Activate(user *model.User, token string) *model.AppError Activate(user *model.User, token string) *model.AppError
Deactivate(userId string) *model.AppError Deactivate(userId string) *model.AppError
ValidateToken(secret, token string) (bool, *model.AppError) ValidateToken(secret, token string) (bool, *model.AppError)

View File

@ -0,0 +1,372 @@
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package model
type Permission struct {
Id string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
}
type Role struct {
Id string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Permissions []string `json:"permissions"`
}
var PERMISSION_INVITE_USER *Permission
var PERMISSION_ADD_USER_TO_TEAM *Permission
var PERMISSION_USE_SLASH_COMMANDS *Permission
var PERMISSION_MANAGE_SLASH_COMMANDS *Permission
var PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS *Permission
var PERMISSION_CREATE_PUBLIC_CHANNEL *Permission
var PERMISSION_CREATE_PRIVATE_CHANNEL *Permission
var PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS *Permission
var PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS *Permission
var PERMISSION_ASSIGN_SYSTEM_ADMIN_ROLE *Permission
var PERMISSION_MANAGE_ROLES *Permission
var PERMISSION_CREATE_DIRECT_CHANNEL *Permission
var PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES *Permission
var PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES *Permission
var PERMISSION_LIST_TEAM_CHANNELS *Permission
var PERMISSION_JOIN_PUBLIC_CHANNELS *Permission
var PERMISSION_DELETE_PUBLIC_CHANNEL *Permission
var PERMISSION_DELETE_PRIVATE_CHANNEL *Permission
var PERMISSION_EDIT_OTHER_USERS *Permission
var PERMISSION_READ_CHANNEL *Permission
var PERMISSION_PERMANENT_DELETE_USER *Permission
var PERMISSION_UPLOAD_FILE *Permission
var PERMISSION_GET_PUBLIC_LINK *Permission
var PERMISSION_MANAGE_WEBHOOKS *Permission
var PERMISSION_MANAGE_OTHERS_WEBHOOKS *Permission
var PERMISSION_MANAGE_OAUTH *Permission
var PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH *Permission
var PERMISSION_CREATE_POST *Permission
var PERMISSION_EDIT_POST *Permission
var PERMISSION_EDIT_OTHERS_POSTS *Permission
var PERMISSION_REMOVE_USER_FROM_TEAM *Permission
var PERMISSION_MANAGE_TEAM *Permission
var PERMISSION_IMPORT_TEAM *Permission
// General permission that encompases all system admin functions
// in the future this could be broken up to allow access to some
// admin functions but not others
var PERMISSION_MANAGE_SYSTEM *Permission
var ROLE_SYSTEM_USER *Role
var ROLE_SYSTEM_ADMIN *Role
var ROLE_TEAM_USER *Role
var ROLE_TEAM_ADMIN *Role
var ROLE_CHANNEL_USER *Role
var ROLE_CHANNEL_ADMIN *Role
var ROLE_CHANNEL_GUEST *Role
var BuiltInRoles map[string]*Role
func InitalizePermissions() {
PERMISSION_INVITE_USER = &Permission{
"invite_user",
"authentication.permissions.team_invite_user.name",
"authentication.permissions.team_invite_user.description",
}
PERMISSION_ADD_USER_TO_TEAM = &Permission{
"add_user_to_team",
"authentication.permissions.add_user_to_team.name",
"authentication.permissions.add_user_to_team.description",
}
PERMISSION_USE_SLASH_COMMANDS = &Permission{
"use_slash_commands",
"authentication.permissions.team_use_slash_commands.name",
"authentication.permissions.team_use_slash_commands.description",
}
PERMISSION_MANAGE_SLASH_COMMANDS = &Permission{
"manage_slash_commands",
"authentication.permissions.manage_slash_commands.name",
"authentication.permissions.manage_slash_commands.description",
}
PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS = &Permission{
"manage_others_slash_commands",
"authentication.permissions.manage_others_slash_commands.name",
"authentication.permissions.manage_others_slash_commands.description",
}
PERMISSION_CREATE_PUBLIC_CHANNEL = &Permission{
"create_public_channel",
"authentication.permissions.create_public_channel.name",
"authentication.permissions.create_public_channel.description",
}
PERMISSION_CREATE_PRIVATE_CHANNEL = &Permission{
"create_private_channel",
"authentication.permissions.create_private_channel.name",
"authentication.permissions.create_private_channel.description",
}
PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS = &Permission{
"manage_public_channel_members",
"authentication.permissions.manage_public_channel_members.name",
"authentication.permissions.manage_public_channel_members.description",
}
PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS = &Permission{
"manage_private_channel_members",
"authentication.permissions.manage_private_channel_members.name",
"authentication.permissions.manage_private_channel_members.description",
}
PERMISSION_ASSIGN_SYSTEM_ADMIN_ROLE = &Permission{
"assign_system_admin_role",
"authentication.permissions.assign_system_admin_role.name",
"authentication.permissions.assign_system_admin_role.description",
}
PERMISSION_MANAGE_ROLES = &Permission{
"manage_roles",
"authentication.permissions.manage_roles.name",
"authentication.permissions.manage_roles.description",
}
PERMISSION_MANAGE_SYSTEM = &Permission{
"manage_system",
"authentication.permissions.manage_system.name",
"authentication.permissions.manage_system.description",
}
PERMISSION_CREATE_DIRECT_CHANNEL = &Permission{
"create_direct_channel",
"authentication.permissions.create_direct_channel.name",
"authentication.permissions.create_direct_channel.description",
}
PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES = &Permission{
"manage__publicchannel_properties",
"authentication.permissions.manage_public_channel_properties.name",
"authentication.permissions.manage_public_channel_properties.description",
}
PERMISSION_MANAGE_PRIVATE_CHANNEL_PROPERTIES = &Permission{
"manage_private_channel_properties",
"authentication.permissions.manage_private_channel_properties.name",
"authentication.permissions.manage_private_channel_properties.description",
}
PERMISSION_LIST_TEAM_CHANNELS = &Permission{
"list_team_channels",
"authentication.permissions.list_team_channels.name",
"authentication.permissions.list_team_channels.description",
}
PERMISSION_JOIN_PUBLIC_CHANNELS = &Permission{
"join_public_channels",
"authentication.permissions.join_public_channels.name",
"authentication.permissions.join_public_channels.description",
}
PERMISSION_DELETE_PUBLIC_CHANNEL = &Permission{
"delete_public_channel",
"authentication.permissions.delete_public_channel.name",
"authentication.permissions.delete_public_channel.description",
}
PERMISSION_DELETE_PRIVATE_CHANNEL = &Permission{
"delete_private_channel",
"authentication.permissions.delete_private_channel.name",
"authentication.permissions.delete_private_channel.description",
}
PERMISSION_EDIT_OTHER_USERS = &Permission{
"edit_other_users",
"authentication.permissions.edit_other_users.name",
"authentication.permissions.edit_other_users.description",
}
PERMISSION_READ_CHANNEL = &Permission{
"read_channel",
"authentication.permissions.read_channel.name",
"authentication.permissions.read_channel.description",
}
PERMISSION_PERMANENT_DELETE_USER = &Permission{
"permanent_delete_user",
"authentication.permissions.permanent_delete_user.name",
"authentication.permissions.permanent_delete_user.description",
}
PERMISSION_UPLOAD_FILE = &Permission{
"upload_file",
"authentication.permissions.upload_file.name",
"authentication.permissions.upload_file.description",
}
PERMISSION_GET_PUBLIC_LINK = &Permission{
"get_public_link",
"authentication.permissions.get_public_link.name",
"authentication.permissions.get_public_link.description",
}
PERMISSION_MANAGE_WEBHOOKS = &Permission{
"manage_webhooks",
"authentication.permissions.manage_webhooks.name",
"authentication.permissions.manage_webhooks.description",
}
PERMISSION_MANAGE_OTHERS_WEBHOOKS = &Permission{
"manage_others_webhooks",
"authentication.permissions.manage_others_webhooks.name",
"authentication.permissions.manage_others_webhooks.description",
}
PERMISSION_MANAGE_OAUTH = &Permission{
"manage_oauth",
"authentication.permissions.manage_oauth.name",
"authentication.permissions.manage_oauth.description",
}
PERMISSION_MANAGE_SYSTEM_WIDE_OAUTH = &Permission{
"manage_sytem_wide_oauth",
"authentication.permissions.manage_sytem_wide_oauth.name",
"authentication.permissions.manage_sytem_wide_oauth.description",
}
PERMISSION_CREATE_POST = &Permission{
"create_post",
"authentication.permissions.create_post.name",
"authentication.permissions.create_post.description",
}
PERMISSION_EDIT_POST = &Permission{
"edit_post",
"authentication.permissions.edit_post.name",
"authentication.permissions.edit_post.description",
}
PERMISSION_EDIT_OTHERS_POSTS = &Permission{
"edit_others_posts",
"authentication.permissions.edit_others_posts.name",
"authentication.permissions.edit_others_posts.description",
}
PERMISSION_REMOVE_USER_FROM_TEAM = &Permission{
"remove_user_from_team",
"authentication.permissions.remove_user_from_team.name",
"authentication.permissions.remove_user_from_team.description",
}
PERMISSION_MANAGE_TEAM = &Permission{
"manage_team",
"authentication.permissions.manage_team.name",
"authentication.permissions.manage_team.description",
}
PERMISSION_IMPORT_TEAM = &Permission{
"import_team",
"authentication.permissions.import_team.name",
"authentication.permissions.import_team.description",
}
}
func InitalizeRoles() {
InitalizePermissions()
BuiltInRoles = make(map[string]*Role)
ROLE_CHANNEL_USER = &Role{
"channel_user",
"authentication.roles.channel_user.name",
"authentication.roles.channel_user.description",
[]string{
PERMISSION_READ_CHANNEL.Id,
PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS.Id,
PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS.Id,
PERMISSION_UPLOAD_FILE.Id,
PERMISSION_GET_PUBLIC_LINK.Id,
PERMISSION_CREATE_POST.Id,
PERMISSION_EDIT_POST.Id,
PERMISSION_USE_SLASH_COMMANDS.Id,
},
}
BuiltInRoles[ROLE_CHANNEL_USER.Id] = ROLE_CHANNEL_USER
ROLE_CHANNEL_ADMIN = &Role{
"channel_admin",
"authentication.roles.channel_admin.name",
"authentication.roles.channel_admin.description",
[]string{},
}
BuiltInRoles[ROLE_CHANNEL_ADMIN.Id] = ROLE_CHANNEL_ADMIN
ROLE_CHANNEL_GUEST = &Role{
"guest",
"authentication.roles.global_guest.name",
"authentication.roles.global_guest.description",
[]string{},
}
BuiltInRoles[ROLE_CHANNEL_GUEST.Id] = ROLE_CHANNEL_GUEST
ROLE_TEAM_USER = &Role{
"team_user",
"authentication.roles.team_user.name",
"authentication.roles.team_user.description",
[]string{
PERMISSION_LIST_TEAM_CHANNELS.Id,
PERMISSION_JOIN_PUBLIC_CHANNELS.Id,
},
}
BuiltInRoles[ROLE_TEAM_USER.Id] = ROLE_TEAM_USER
ROLE_TEAM_ADMIN = &Role{
"team_admin",
"authentication.roles.team_admin.name",
"authentication.roles.team_admin.description",
[]string{
PERMISSION_EDIT_OTHERS_POSTS.Id,
PERMISSION_ADD_USER_TO_TEAM.Id,
PERMISSION_REMOVE_USER_FROM_TEAM.Id,
PERMISSION_MANAGE_TEAM.Id,
PERMISSION_IMPORT_TEAM.Id,
PERMISSION_MANAGE_ROLES.Id,
PERMISSION_MANAGE_OTHERS_WEBHOOKS.Id,
PERMISSION_MANAGE_SLASH_COMMANDS.Id,
PERMISSION_MANAGE_OTHERS_SLASH_COMMANDS.Id,
PERMISSION_MANAGE_WEBHOOKS.Id,
},
}
BuiltInRoles[ROLE_TEAM_ADMIN.Id] = ROLE_TEAM_ADMIN
ROLE_SYSTEM_USER = &Role{
"system_user",
"authentication.roles.global_user.name",
"authentication.roles.global_user.description",
[]string{
PERMISSION_CREATE_DIRECT_CHANNEL.Id,
PERMISSION_PERMANENT_DELETE_USER.Id,
PERMISSION_MANAGE_OAUTH.Id,
},
}
BuiltInRoles[ROLE_SYSTEM_USER.Id] = ROLE_SYSTEM_USER
ROLE_SYSTEM_ADMIN = &Role{
"system_admin",
"authentication.roles.global_admin.name",
"authentication.roles.global_admin.description",
// 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
append(
append(
append(
append(
[]string{
PERMISSION_ASSIGN_SYSTEM_ADMIN_ROLE.Id,
PERMISSION_MANAGE_SYSTEM.Id,
PERMISSION_MANAGE_PUBLIC_CHANNEL_PROPERTIES.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_WEBHOOKS.Id,
PERMISSION_EDIT_OTHER_USERS.Id,
PERMISSION_MANAGE_OAUTH.Id,
PERMISSION_INVITE_USER.Id,
},
ROLE_TEAM_USER.Permissions...,
),
ROLE_CHANNEL_USER.Permissions...,
),
ROLE_TEAM_ADMIN.Permissions...,
),
ROLE_CHANNEL_ADMIN.Permissions...,
),
}
BuiltInRoles[ROLE_SYSTEM_ADMIN.Id] = ROLE_SYSTEM_ADMIN
}
func RoleIdsToString(roles []string) string {
output := ""
for _, role := range roles {
output += role + ", "
}
if output == "" {
return "[<NO ROLES>]"
}
return output[:len(output)-1]
}
func init() {
InitalizeRoles()
}

View File

@ -0,0 +1,58 @@
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package model
import (
"encoding/json"
"io"
)
type UserAutocompleteInChannel struct {
InChannel []*User `json:"in_channel"`
OutOfChannel []*User `json:"out_of_channel"`
}
type UserAutocompleteInTeam struct {
InTeam []*User `json:"in_team"`
}
func (o *UserAutocompleteInChannel) ToJson() string {
b, err := json.Marshal(o)
if err != nil {
return ""
} else {
return string(b)
}
}
func UserAutocompleteInChannelFromJson(data io.Reader) *UserAutocompleteInChannel {
decoder := json.NewDecoder(data)
var o UserAutocompleteInChannel
err := decoder.Decode(&o)
if err == nil {
return &o
} else {
return nil
}
}
func (o *UserAutocompleteInTeam) ToJson() string {
b, err := json.Marshal(o)
if err != nil {
return ""
} else {
return string(b)
}
}
func UserAutocompleteInTeamFromJson(data io.Reader) *UserAutocompleteInTeam {
decoder := json.NewDecoder(data)
var o UserAutocompleteInTeam
err := decoder.Decode(&o)
if err == nil {
return &o
} else {
return nil
}
}

View File

@ -10,10 +10,14 @@ import (
) )
const ( const (
CHANNEL_OPEN = "O" CHANNEL_OPEN = "O"
CHANNEL_PRIVATE = "P" CHANNEL_PRIVATE = "P"
CHANNEL_DIRECT = "D" CHANNEL_DIRECT = "D"
DEFAULT_CHANNEL = "town-square" DEFAULT_CHANNEL = "town-square"
CHANNEL_DISPLAY_NAME_MAX_RUNES = 64
CHANNEL_NAME_MAX_LENGTH = 64
CHANNEL_HEADER_MAX_RUNES = 1024
CHANNEL_PURPOSE_MAX_RUNES = 250
) )
type Channel struct { type Channel struct {
@ -57,8 +61,8 @@ func (o *Channel) Etag() string {
return Etag(o.Id, o.UpdateAt) return Etag(o.Id, o.UpdateAt)
} }
func (o *Channel) ExtraEtag(memberLimit int) string { func (o *Channel) StatsEtag() string {
return Etag(o.Id, o.ExtraUpdateAt, memberLimit) return Etag(o.Id, o.ExtraUpdateAt)
} }
func (o *Channel) IsValid() *AppError { func (o *Channel) IsValid() *AppError {
@ -75,11 +79,11 @@ func (o *Channel) IsValid() *AppError {
return NewLocAppError("Channel.IsValid", "model.channel.is_valid.update_at.app_error", nil, "id="+o.Id) return NewLocAppError("Channel.IsValid", "model.channel.is_valid.update_at.app_error", nil, "id="+o.Id)
} }
if utf8.RuneCountInString(o.DisplayName) > 64 { if utf8.RuneCountInString(o.DisplayName) > CHANNEL_DISPLAY_NAME_MAX_RUNES {
return NewLocAppError("Channel.IsValid", "model.channel.is_valid.display_name.app_error", nil, "id="+o.Id) return NewLocAppError("Channel.IsValid", "model.channel.is_valid.display_name.app_error", nil, "id="+o.Id)
} }
if len(o.Name) > 64 { if len(o.Name) > CHANNEL_NAME_MAX_LENGTH {
return NewLocAppError("Channel.IsValid", "model.channel.is_valid.name.app_error", nil, "id="+o.Id) return NewLocAppError("Channel.IsValid", "model.channel.is_valid.name.app_error", nil, "id="+o.Id)
} }
@ -91,11 +95,11 @@ func (o *Channel) IsValid() *AppError {
return NewLocAppError("Channel.IsValid", "model.channel.is_valid.type.app_error", nil, "id="+o.Id) return NewLocAppError("Channel.IsValid", "model.channel.is_valid.type.app_error", nil, "id="+o.Id)
} }
if utf8.RuneCountInString(o.Header) > 1024 { if utf8.RuneCountInString(o.Header) > CHANNEL_HEADER_MAX_RUNES {
return NewLocAppError("Channel.IsValid", "model.channel.is_valid.header.app_error", nil, "id="+o.Id) return NewLocAppError("Channel.IsValid", "model.channel.is_valid.header.app_error", nil, "id="+o.Id)
} }
if utf8.RuneCountInString(o.Purpose) > 128 { if utf8.RuneCountInString(o.Purpose) > CHANNEL_PURPOSE_MAX_RUNES {
return NewLocAppError("Channel.IsValid", "model.channel.is_valid.purpose.app_error", nil, "id="+o.Id) return NewLocAppError("Channel.IsValid", "model.channel.is_valid.purpose.app_error", nil, "id="+o.Id)
} }

View File

@ -1,49 +0,0 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package model
import (
"encoding/json"
"io"
)
type ExtraMember struct {
Id string `json:"id"`
Nickname string `json:"nickname"`
Email string `json:"email"`
Roles string `json:"roles"`
Username string `json:"username"`
}
func (o *ExtraMember) Sanitize(options map[string]bool) {
if len(options) == 0 || !options["email"] {
o.Email = ""
}
}
type ChannelExtra struct {
Id string `json:"id"`
Members []ExtraMember `json:"members"`
MemberCount int64 `json:"member_count"`
}
func (o *ChannelExtra) ToJson() string {
b, err := json.Marshal(o)
if err != nil {
return ""
} else {
return string(b)
}
}
func ChannelExtraFromJson(data io.Reader) *ChannelExtra {
decoder := json.NewDecoder(data)
var o ChannelExtra
err := decoder.Decode(&o)
if err == nil {
return &o
} else {
return nil
}
}

View File

@ -8,15 +8,11 @@ import (
"io" "io"
) )
type ChannelList struct { type ChannelList []*Channel
Channels []*Channel `json:"channels"`
Members map[string]*ChannelMember `json:"members"`
}
func (o *ChannelList) ToJson() string { func (o *ChannelList) ToJson() string {
b, err := json.Marshal(o) if b, err := json.Marshal(o); err != nil {
if err != nil { return "[]"
return ""
} else { } else {
return string(b) return string(b)
} }
@ -28,7 +24,7 @@ func (o *ChannelList) Etag() string {
var t int64 = 0 var t int64 = 0
var delta int64 = 0 var delta int64 = 0
for _, v := range o.Channels { for _, v := range *o {
if v.LastPostAt > t { if v.LastPostAt > t {
t = v.LastPostAt t = v.LastPostAt
id = v.Id id = v.Id
@ -39,30 +35,9 @@ func (o *ChannelList) Etag() string {
id = v.Id id = v.Id
} }
member := o.Members[v.Id]
if member != nil {
max := v.LastPostAt
if v.UpdateAt > max {
max = v.UpdateAt
}
delta += max - member.LastViewedAt
if member.LastViewedAt > t {
t = member.LastViewedAt
id = v.Id
}
if member.LastUpdateAt > t {
t = member.LastUpdateAt
id = v.Id
}
}
} }
return Etag(id, t, delta, len(o.Channels)) return Etag(id, t, delta, len(*o))
} }
func ChannelListFromJson(data io.Reader) *ChannelList { func ChannelListFromJson(data io.Reader) *ChannelList {

View File

@ -10,7 +10,6 @@ import (
) )
const ( const (
CHANNEL_ROLE_ADMIN = "admin"
CHANNEL_NOTIFY_DEFAULT = "default" CHANNEL_NOTIFY_DEFAULT = "default"
CHANNEL_NOTIFY_ALL = "all" CHANNEL_NOTIFY_ALL = "all"
CHANNEL_NOTIFY_MENTION = "mention" CHANNEL_NOTIFY_MENTION = "mention"
@ -30,6 +29,27 @@ type ChannelMember struct {
LastUpdateAt int64 `json:"last_update_at"` LastUpdateAt int64 `json:"last_update_at"`
} }
type ChannelMembers []ChannelMember
func (o *ChannelMembers) ToJson() string {
if b, err := json.Marshal(o); err != nil {
return "[]"
} else {
return string(b)
}
}
func ChannelMembersFromJson(data io.Reader) *ChannelMembers {
decoder := json.NewDecoder(data)
var o ChannelMembers
err := decoder.Decode(&o)
if err == nil {
return &o
} else {
return nil
}
}
func (o *ChannelMember) ToJson() string { func (o *ChannelMember) ToJson() string {
b, err := json.Marshal(o) b, err := json.Marshal(o)
if err != nil { if err != nil {
@ -60,12 +80,6 @@ func (o *ChannelMember) IsValid() *AppError {
return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.user_id.app_error", nil, "") return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.user_id.app_error", nil, "")
} }
for _, role := range strings.Split(o.Roles, " ") {
if !(role == "" || role == CHANNEL_ROLE_ADMIN) {
return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.role.app_error", nil, "role="+role)
}
}
notifyLevel := o.NotifyProps["desktop"] notifyLevel := o.NotifyProps["desktop"]
if len(notifyLevel) > 20 || !IsChannelNotifyLevelValid(notifyLevel) { if len(notifyLevel) > 20 || !IsChannelNotifyLevelValid(notifyLevel) {
return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.notify_level.app_error", return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.notify_level.app_error",
@ -89,6 +103,10 @@ func (o *ChannelMember) PreUpdate() {
o.LastUpdateAt = GetMillis() o.LastUpdateAt = GetMillis()
} }
func (o *ChannelMember) GetRoles() []string {
return strings.Fields(o.Roles)
}
func IsChannelNotifyLevelValid(notifyLevel string) bool { func IsChannelNotifyLevelValid(notifyLevel string) bool {
return notifyLevel == CHANNEL_NOTIFY_DEFAULT || return notifyLevel == CHANNEL_NOTIFY_DEFAULT ||
notifyLevel == CHANNEL_NOTIFY_ALL || notifyLevel == CHANNEL_NOTIFY_ALL ||

View File

@ -0,0 +1,34 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package model
import (
"encoding/json"
"io"
)
type ChannelStats struct {
ChannelId string `json:"channel_id"`
MemberCount int64 `json:"member_count"`
}
func (o *ChannelStats) ToJson() string {
b, err := json.Marshal(o)
if err != nil {
return ""
} else {
return string(b)
}
}
func ChannelStatsFromJson(data io.Reader) *ChannelStats {
decoder := json.NewDecoder(data)
var o ChannelStats
err := decoder.Decode(&o)
if err == nil {
return &o
} else {
return nil
}
}

View File

@ -108,6 +108,10 @@ func (c *Client) GetChannelRoute(channelId string) string {
return fmt.Sprintf("/teams/%v/channels/%v", c.GetTeamId(), channelId) return fmt.Sprintf("/teams/%v/channels/%v", c.GetTeamId(), channelId)
} }
func (c *Client) GetUserRequiredRoute(userId string) string {
return fmt.Sprintf("/users/%v", userId)
}
func (c *Client) GetChannelNameRoute(channelName string) string { func (c *Client) GetChannelNameRoute(channelName string) string {
return fmt.Sprintf("/teams/%v/channels/name/%v", c.GetTeamId(), channelName) return fmt.Sprintf("/teams/%v/channels/name/%v", c.GetTeamId(), channelName)
} }
@ -120,9 +124,14 @@ func (c *Client) GetGeneralRoute() string {
return "/general" return "/general"
} }
func (c *Client) GetFileRoute(fileId string) string {
return fmt.Sprintf("/files/%v", fileId)
}
func (c *Client) DoPost(url, data, contentType string) (*http.Response, *AppError) { func (c *Client) DoPost(url, data, contentType string) (*http.Response, *AppError) {
rq, _ := http.NewRequest("POST", c.Url+url, strings.NewReader(data)) rq, _ := http.NewRequest("POST", c.Url+url, strings.NewReader(data))
rq.Header.Set("Content-Type", contentType) rq.Header.Set("Content-Type", contentType)
rq.Close = true
if rp, err := c.HttpClient.Do(rq); err != nil { if rp, err := c.HttpClient.Do(rq); err != nil {
return nil, NewLocAppError(url, "model.client.connecting.app_error", nil, err.Error()) return nil, NewLocAppError(url, "model.client.connecting.app_error", nil, err.Error())
@ -136,6 +145,7 @@ func (c *Client) DoPost(url, data, contentType string) (*http.Response, *AppErro
func (c *Client) DoApiPost(url string, data string) (*http.Response, *AppError) { func (c *Client) DoApiPost(url string, data string) (*http.Response, *AppError) {
rq, _ := http.NewRequest("POST", c.ApiUrl+url, strings.NewReader(data)) rq, _ := http.NewRequest("POST", c.ApiUrl+url, strings.NewReader(data))
rq.Close = true
if len(c.AuthToken) > 0 { if len(c.AuthToken) > 0 {
rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken) rq.Header.Set(HEADER_AUTH, c.AuthType+" "+c.AuthToken)
@ -153,6 +163,7 @@ func (c *Client) DoApiPost(url string, data string) (*http.Response, *AppError)
func (c *Client) DoApiGet(url string, data string, etag string) (*http.Response, *AppError) { func (c *Client) DoApiGet(url string, data string, etag string) (*http.Response, *AppError) {
rq, _ := http.NewRequest("GET", c.ApiUrl+url, strings.NewReader(data)) rq, _ := http.NewRequest("GET", c.ApiUrl+url, strings.NewReader(data))
rq.Close = true
if len(etag) > 0 { if len(etag) > 0 {
rq.Header.Set(HEADER_ETAG_CLIENT, etag) rq.Header.Set(HEADER_ETAG_CLIENT, etag)
@ -500,10 +511,9 @@ func (c *Client) GetMe(etag string) (*Result, *AppError) {
} }
} }
// GetProfilesForDirectMessageList returns a map of users for a team that can be direct // GetProfiles returns a map of users using user id as the key. Must be authenticated.
// messaged, using user id as the key. Must be authenticated. func (c *Client) GetProfiles(offset int, limit int, etag string) (*Result, *AppError) {
func (c *Client) GetProfilesForDirectMessageList(teamId string) (*Result, *AppError) { if r, err := c.DoApiGet(fmt.Sprintf("/users/%v/%v", offset, limit), "", etag); err != nil {
if r, err := c.DoApiGet("/users/profiles_for_dm_list/"+teamId, "", ""); err != nil {
return nil, err return nil, err
} else { } else {
defer closeBody(r) defer closeBody(r)
@ -512,10 +522,10 @@ func (c *Client) GetProfilesForDirectMessageList(teamId string) (*Result, *AppEr
} }
} }
// GetProfiles returns a map of users for a team using user id as the key. Must // GetProfilesInTeam returns a map of users for a team using user id as the key. Must
// be authenticated. // be authenticated.
func (c *Client) GetProfiles(teamId string, etag string) (*Result, *AppError) { func (c *Client) GetProfilesInTeam(teamId string, offset int, limit int, etag string) (*Result, *AppError) {
if r, err := c.DoApiGet("/users/profiles/"+teamId, "", etag); err != nil { if r, err := c.DoApiGet(fmt.Sprintf("/teams/%v/users/%v/%v", teamId, offset, limit), "", etag); err != nil {
return nil, err return nil, err
} else { } else {
defer closeBody(r) defer closeBody(r)
@ -524,10 +534,10 @@ func (c *Client) GetProfiles(teamId string, etag string) (*Result, *AppError) {
} }
} }
// GetDirectProfiles gets a map of users that are currently shown in the sidebar, // GetProfilesInChannel returns a map of users for a channel using user id as the key. Must
// using user id as the key. Must be authenticated. // be authenticated.
func (c *Client) GetDirectProfiles(etag string) (*Result, *AppError) { func (c *Client) GetProfilesInChannel(channelId string, offset int, limit int, etag string) (*Result, *AppError) {
if r, err := c.DoApiGet("/users/direct_profiles", "", etag); err != nil { if r, err := c.DoApiGet(fmt.Sprintf(c.GetChannelRoute(channelId)+"/users/%v/%v", offset, limit), "", etag); err != nil {
return nil, err return nil, err
} else { } else {
defer closeBody(r) defer closeBody(r)
@ -536,6 +546,70 @@ func (c *Client) GetDirectProfiles(etag string) (*Result, *AppError) {
} }
} }
// GetProfilesNotInChannel returns a map of users not in a channel but on the team using user id as the key. Must
// be authenticated.
func (c *Client) GetProfilesNotInChannel(channelId string, offset int, limit int, etag string) (*Result, *AppError) {
if r, err := c.DoApiGet(fmt.Sprintf(c.GetChannelRoute(channelId)+"/users/not_in_channel/%v/%v", offset, limit), "", etag); err != nil {
return nil, err
} else {
defer closeBody(r)
return &Result{r.Header.Get(HEADER_REQUEST_ID),
r.Header.Get(HEADER_ETAG_SERVER), UserMapFromJson(r.Body)}, nil
}
}
// GetProfilesByIds returns a map of users based on the user ids provided. Must
// be authenticated.
func (c *Client) GetProfilesByIds(userIds []string) (*Result, *AppError) {
if r, err := c.DoApiPost("/users/ids", ArrayToJson(userIds)); err != nil {
return nil, err
} else {
defer closeBody(r)
return &Result{r.Header.Get(HEADER_REQUEST_ID),
r.Header.Get(HEADER_ETAG_SERVER), UserMapFromJson(r.Body)}, nil
}
}
// SearchUsers returns a list of users that have a username matching or similar to the search term. Must
// be authenticated.
func (c *Client) SearchUsers(params UserSearch) (*Result, *AppError) {
if r, err := c.DoApiPost("/users/search", params.ToJson()); err != nil {
return nil, err
} else {
defer closeBody(r)
return &Result{r.Header.Get(HEADER_REQUEST_ID),
r.Header.Get(HEADER_ETAG_SERVER), UserListFromJson(r.Body)}, nil
}
}
// AutocompleteUsersInChannel returns two lists for autocompletion of users in a channel. The first list "in_channel",
// specifies users in the channel. The second list "out_of_channel" specifies users outside of the
// channel. Term, the string to search against, is required, channel id is also required. Must be authenticated.
func (c *Client) AutocompleteUsersInChannel(term string, channelId string) (*Result, *AppError) {
url := fmt.Sprintf("%s/users/autocomplete?term=%s", c.GetChannelRoute(channelId), url.QueryEscape(term))
if r, err := c.DoApiGet(url, "", ""); err != nil {
return nil, err
} else {
defer closeBody(r)
return &Result{r.Header.Get(HEADER_REQUEST_ID),
r.Header.Get(HEADER_ETAG_SERVER), UserAutocompleteInChannelFromJson(r.Body)}, nil
}
}
// AutocompleteUsersInTeam returns a list for autocompletion of users in a team. The list "in_team" specifies
// the users in the team that match the provided term, matching against username, full name and
// nickname. Must be authenticated.
func (c *Client) AutocompleteUsersInTeam(term string) (*Result, *AppError) {
url := fmt.Sprintf("%s/users/autocomplete?term=%s", c.GetTeamRoute(), url.QueryEscape(term))
if r, err := c.DoApiGet(url, "", ""); err != nil {
return nil, err
} else {
defer closeBody(r)
return &Result{r.Header.Get(HEADER_REQUEST_ID),
r.Header.Get(HEADER_ETAG_SERVER), UserAutocompleteInTeamFromJson(r.Body)}, nil
}
}
// LoginById authenticates a user by user id and password. // LoginById authenticates a user by user id and password.
func (c *Client) LoginById(id string, password string) (*Result, *AppError) { func (c *Client) LoginById(id string, password string) (*Result, *AppError) {
m := make(map[string]string) m := make(map[string]string)
@ -622,15 +696,16 @@ func (c *Client) CheckMfa(loginId string) (*Result, *AppError) {
} }
} }
// GenerateMfaQrCode returns a QR code imagem containing the secret, to be scanned // GenerateMfaSecret returns a QR code image containing the secret, to be scanned
// by a multi-factor authentication mobile application. Must be authenticated. // by a multi-factor authentication mobile application. It also returns the secret
func (c *Client) GenerateMfaQrCode() (*Result, *AppError) { // for manual entry. Must be authenticated.
if r, err := c.DoApiGet("/users/generate_mfa_qr", "", ""); err != nil { func (c *Client) GenerateMfaSecret() (*Result, *AppError) {
if r, err := c.DoApiGet("/users/generate_mfa_secret", "", ""); err != nil {
return nil, err return nil, err
} else { } else {
defer closeBody(r) defer closeBody(r)
return &Result{r.Header.Get(HEADER_REQUEST_ID), return &Result{r.Header.Get(HEADER_REQUEST_ID),
r.Header.Get(HEADER_ETAG_SERVER), r.Body}, nil r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
} }
} }
@ -934,6 +1009,7 @@ func (c *Client) SaveComplianceReport(job *Compliance) (*Result, *AppError) {
func (c *Client) DownloadComplianceReport(id string) (*Result, *AppError) { func (c *Client) DownloadComplianceReport(id string) (*Result, *AppError) {
var rq *http.Request var rq *http.Request
rq, _ = http.NewRequest("GET", c.ApiUrl+"/admin/download_compliance_report/"+id, nil) rq, _ = http.NewRequest("GET", c.ApiUrl+"/admin/download_compliance_report/"+id, nil)
rq.Close = true
if len(c.AuthToken) > 0 { if len(c.AuthToken) > 0 {
rq.Header.Set(HEADER_AUTH, "BEARER "+c.AuthToken) rq.Header.Set(HEADER_AUTH, "BEARER "+c.AuthToken)
@ -1047,13 +1123,13 @@ func (c *Client) UpdateNotifyProps(data map[string]string) (*Result, *AppError)
} }
} }
func (c *Client) GetChannels(etag string) (*Result, *AppError) { func (c *Client) GetMyChannelMembers() (*Result, *AppError) {
if r, err := c.DoApiGet(c.GetTeamRoute()+"/channels/", "", etag); err != nil { if r, err := c.DoApiGet(c.GetTeamRoute()+"/channels/members", "", ""); err != nil {
return nil, err return nil, err
} else { } else {
defer closeBody(r) defer closeBody(r)
return &Result{r.Header.Get(HEADER_REQUEST_ID), return &Result{r.Header.Get(HEADER_REQUEST_ID),
r.Header.Get(HEADER_ETAG_SERVER), ChannelListFromJson(r.Body)}, nil r.Header.Get(HEADER_ETAG_SERVER), ChannelMembersFromJson(r.Body)}, nil
} }
} }
@ -1087,6 +1163,16 @@ func (c *Client) GetChannelCounts(etag string) (*Result, *AppError) {
} }
} }
func (c *Client) GetChannels(etag string) (*Result, *AppError) {
if r, err := c.DoApiGet(c.GetTeamRoute()+"/channels/", "", etag); err != nil {
return nil, err
} else {
defer closeBody(r)
return &Result{r.Header.Get(HEADER_REQUEST_ID),
r.Header.Get(HEADER_ETAG_SERVER), ChannelListFromJson(r.Body)}, nil
}
}
func (c *Client) JoinChannel(id string) (*Result, *AppError) { func (c *Client) JoinChannel(id string) (*Result, *AppError) {
if r, err := c.DoApiPost(c.GetChannelRoute(id)+"/join", ""); err != nil { if r, err := c.DoApiPost(c.GetChannelRoute(id)+"/join", ""); err != nil {
return nil, err return nil, err
@ -1166,13 +1252,23 @@ func (c *Client) UpdateLastViewedAt(channelId string, active bool) (*Result, *Ap
} }
} }
func (c *Client) GetChannelExtraInfo(id string, memberLimit int, etag string) (*Result, *AppError) { func (c *Client) GetChannelStats(id string, etag string) (*Result, *AppError) {
if r, err := c.DoApiGet(c.GetChannelRoute(id)+"/extra_info/"+strconv.FormatInt(int64(memberLimit), 10), "", etag); err != nil { if r, err := c.DoApiGet(c.GetChannelRoute(id)+"/stats", "", etag); err != nil {
return nil, err return nil, err
} else { } else {
defer closeBody(r) defer closeBody(r)
return &Result{r.Header.Get(HEADER_REQUEST_ID), return &Result{r.Header.Get(HEADER_REQUEST_ID),
r.Header.Get(HEADER_ETAG_SERVER), ChannelExtraFromJson(r.Body)}, nil r.Header.Get(HEADER_ETAG_SERVER), ChannelStatsFromJson(r.Body)}, nil
}
}
func (c *Client) GetChannelMember(channelId string, userId string) (*Result, *AppError) {
if r, err := c.DoApiGet(c.GetChannelRoute(channelId)+"/members/"+userId, "", ""); err != nil {
return nil, err
} else {
defer closeBody(r)
return &Result{r.Header.Get(HEADER_REQUEST_ID),
r.Header.Get(HEADER_ETAG_SERVER), ChannelMemberFromJson(r.Body)}, nil
} }
} }
@ -1285,13 +1381,39 @@ func (c *Client) UploadProfileFile(data []byte, contentType string) (*Result, *A
return c.uploadFile(c.ApiUrl+"/users/newimage", data, contentType) return c.uploadFile(c.ApiUrl+"/users/newimage", data, contentType)
} }
func (c *Client) UploadPostAttachment(data []byte, contentType string) (*Result, *AppError) { func (c *Client) UploadPostAttachment(data []byte, channelId string, filename string) (*FileUploadResponse, *AppError) {
return c.uploadFile(c.ApiUrl+c.GetTeamRoute()+"/files/upload", data, contentType) c.clearExtraProperties()
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
if part, err := writer.CreateFormFile("files", filename); err != nil {
return nil, NewLocAppError("UploadPostAttachment", "model.client.upload_post_attachment.file.app_error", nil, err.Error())
} else if _, err = io.Copy(part, bytes.NewBuffer(data)); err != nil {
return nil, NewLocAppError("UploadPostAttachment", "model.client.upload_post_attachment.file.app_error", nil, err.Error())
}
if part, err := writer.CreateFormField("channel_id"); err != nil {
return nil, NewLocAppError("UploadPostAttachment", "model.client.upload_post_attachment.channel_id.app_error", nil, err.Error())
} else if _, err = io.Copy(part, strings.NewReader(channelId)); err != nil {
return nil, NewLocAppError("UploadPostAttachment", "model.client.upload_post_attachment.channel_id.app_error", nil, err.Error())
}
if err := writer.Close(); err != nil {
return nil, NewLocAppError("UploadPostAttachment", "model.client.upload_post_attachment.writer.app_error", nil, err.Error())
}
if result, err := c.uploadFile(c.ApiUrl+c.GetTeamRoute()+"/files/upload", body.Bytes(), writer.FormDataContentType()); err != nil {
return nil, err
} else {
return result.Data.(*FileUploadResponse), nil
}
} }
func (c *Client) uploadFile(url string, data []byte, contentType string) (*Result, *AppError) { func (c *Client) uploadFile(url string, data []byte, contentType string) (*Result, *AppError) {
rq, _ := http.NewRequest("POST", url, bytes.NewReader(data)) rq, _ := http.NewRequest("POST", url, bytes.NewReader(data))
rq.Header.Set("Content-Type", contentType) rq.Header.Set("Content-Type", contentType)
rq.Close = true
if len(c.AuthToken) > 0 { if len(c.AuthToken) > 0 {
rq.Header.Set(HEADER_AUTH, "BEARER "+c.AuthToken) rq.Header.Set(HEADER_AUTH, "BEARER "+c.AuthToken)
@ -1308,55 +1430,51 @@ func (c *Client) uploadFile(url string, data []byte, contentType string) (*Resul
} }
} }
func (c *Client) GetFile(url string, isFullUrl bool) (*Result, *AppError) { func (c *Client) GetFile(fileId string) (io.ReadCloser, *AppError) {
var rq *http.Request if r, err := c.DoApiGet(c.GetFileRoute(fileId)+"/get", "", ""); err != nil {
if isFullUrl { return nil, err
rq, _ = http.NewRequest("GET", url, nil)
} else { } else {
rq, _ = http.NewRequest("GET", c.ApiUrl+c.GetTeamRoute()+"/files/get"+url, nil) c.fillInExtraProperties(r)
} return r.Body, nil
if len(c.AuthToken) > 0 {
rq.Header.Set(HEADER_AUTH, "BEARER "+c.AuthToken)
}
if rp, err := c.HttpClient.Do(rq); err != nil {
return nil, NewLocAppError(url, "model.client.connecting.app_error", nil, err.Error())
} else if rp.StatusCode >= 300 {
return nil, AppErrorFromJson(rp.Body)
} else {
defer closeBody(rp)
return &Result{rp.Header.Get(HEADER_REQUEST_ID),
rp.Header.Get(HEADER_ETAG_SERVER), rp.Body}, nil
} }
} }
func (c *Client) GetFileInfo(url string) (*Result, *AppError) { func (c *Client) GetFileThumbnail(fileId string) (io.ReadCloser, *AppError) {
var rq *http.Request if r, err := c.DoApiGet(c.GetFileRoute(fileId)+"/get_thumbnail", "", ""); err != nil {
rq, _ = http.NewRequest("GET", c.ApiUrl+c.GetTeamRoute()+"/files/get_info"+url, nil) return nil, err
if len(c.AuthToken) > 0 {
rq.Header.Set(HEADER_AUTH, "BEARER "+c.AuthToken)
}
if rp, err := c.HttpClient.Do(rq); err != nil {
return nil, NewLocAppError(url, "model.client.connecting.app_error", nil, err.Error())
} else if rp.StatusCode >= 300 {
return nil, AppErrorFromJson(rp.Body)
} else { } else {
defer closeBody(rp) c.fillInExtraProperties(r)
return &Result{rp.Header.Get(HEADER_REQUEST_ID), return r.Body, nil
rp.Header.Get(HEADER_ETAG_SERVER), FileInfoFromJson(rp.Body)}, nil
} }
} }
func (c *Client) GetPublicLink(filename string) (*Result, *AppError) { func (c *Client) GetFilePreview(fileId string) (io.ReadCloser, *AppError) {
if r, err := c.DoApiPost(c.GetTeamRoute()+"/files/get_public_link", MapToJson(map[string]string{"filename": filename})); err != nil { if r, err := c.DoApiGet(c.GetFileRoute(fileId)+"/get_preview", "", ""); err != nil {
return nil, err return nil, err
} else { } else {
defer closeBody(r) defer closeBody(r)
return &Result{r.Header.Get(HEADER_REQUEST_ID), c.fillInExtraProperties(r)
r.Header.Get(HEADER_ETAG_SERVER), StringFromJson(r.Body)}, nil return r.Body, nil
}
}
func (c *Client) GetFileInfo(fileId string) (*FileInfo, *AppError) {
if r, err := c.DoApiGet(c.GetFileRoute(fileId)+"/get_info", "", ""); err != nil {
return nil, err
} else {
defer closeBody(r)
c.fillInExtraProperties(r)
return FileInfoFromJson(r.Body), nil
}
}
func (c *Client) GetPublicLink(fileId string) (string, *AppError) {
if r, err := c.DoApiGet(c.GetFileRoute(fileId)+"/get_public_link", "", ""); err != nil {
return "", err
} else {
defer closeBody(r)
c.fillInExtraProperties(r)
return StringFromJson(r.Body), nil
} }
} }
@ -1370,8 +1488,25 @@ func (c *Client) UpdateUser(user *User) (*Result, *AppError) {
} }
} }
func (c *Client) UpdateUserRoles(data map[string]string) (*Result, *AppError) { func (c *Client) UpdateUserRoles(userId string, roles string) (*Result, *AppError) {
if r, err := c.DoApiPost("/users/update_roles", MapToJson(data)); err != nil { data := make(map[string]string)
data["new_roles"] = roles
if r, err := c.DoApiPost(c.GetUserRequiredRoute(userId)+"/update_roles", MapToJson(data)); err != nil {
return nil, err
} else {
defer closeBody(r)
return &Result{r.Header.Get(HEADER_REQUEST_ID),
r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
}
}
func (c *Client) UpdateTeamRoles(userId string, roles string) (*Result, *AppError) {
data := make(map[string]string)
data["new_roles"] = roles
data["user_id"] = userId
if r, err := c.DoApiPost(c.GetTeamRoute()+"/update_member_roles", MapToJson(data)); err != nil {
return nil, err return nil, err
} else { } else {
defer closeBody(r) defer closeBody(r)
@ -1479,6 +1614,18 @@ func (c *Client) GetStatuses() (*Result, *AppError) {
} }
} }
// GetStatusesByIds returns a map of string statuses using user id as the key,
// based on the provided user ids
func (c *Client) GetStatusesByIds(userIds []string) (*Result, *AppError) {
if r, err := c.DoApiPost("/users/status/ids", ArrayToJson(userIds)); err != nil {
return nil, err
} else {
defer closeBody(r)
return &Result{r.Header.Get(HEADER_REQUEST_ID),
r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
}
}
// SetActiveChannel sets the the channel id the user is currently viewing. // SetActiveChannel sets the the channel id the user is currently viewing.
// The channelId key is required but the value can be blank. Returns standard // The channelId key is required but the value can be blank. Returns standard
// response. // response.
@ -1504,8 +1651,46 @@ func (c *Client) GetMyTeam(etag string) (*Result, *AppError) {
} }
} }
func (c *Client) GetTeamMembers(teamId string) (*Result, *AppError) { // GetTeamMembers will return a page of team member objects as an array paged based on the
if r, err := c.DoApiGet("/teams/members/"+teamId, "", ""); err != nil { // team id, offset and limit provided. Must be authenticated.
func (c *Client) GetTeamMembers(teamId string, offset int, limit int) (*Result, *AppError) {
if r, err := c.DoApiGet(fmt.Sprintf("/teams/%v/members/%v/%v", teamId, offset, limit), "", ""); err != nil {
return nil, err
} else {
defer closeBody(r)
return &Result{r.Header.Get(HEADER_REQUEST_ID),
r.Header.Get(HEADER_ETAG_SERVER), TeamMembersFromJson(r.Body)}, nil
}
}
// GetTeamMember will return a team member object based on the team id and user id provided.
// Must be authenticated.
func (c *Client) GetTeamMember(teamId string, userId string) (*Result, *AppError) {
if r, err := c.DoApiGet(fmt.Sprintf("/teams/%v/members/%v", teamId, userId), "", ""); err != nil {
return nil, err
} else {
defer closeBody(r)
return &Result{r.Header.Get(HEADER_REQUEST_ID),
r.Header.Get(HEADER_ETAG_SERVER), TeamMemberFromJson(r.Body)}, nil
}
}
// GetTeamStats will return a team stats object containing the number of users on the team
// based on the team id provided. Must be authenticated.
func (c *Client) GetTeamStats(teamId string) (*Result, *AppError) {
if r, err := c.DoApiGet(fmt.Sprintf("/teams/%v/stats", teamId), "", ""); err != nil {
return nil, err
} else {
defer closeBody(r)
return &Result{r.Header.Get(HEADER_REQUEST_ID),
r.Header.Get(HEADER_ETAG_SERVER), TeamStatsFromJson(r.Body)}, nil
}
}
// GetTeamMembersByIds will return team member objects as an array based on the
// team id and a list of user ids provided. Must be authenticated.
func (c *Client) GetTeamMembersByIds(teamId string, userIds []string) (*Result, *AppError) {
if r, err := c.DoApiPost(fmt.Sprintf("/teams/%v/members/ids", teamId), ArrayToJson(userIds)); err != nil {
return nil, err return nil, err
} else { } else {
defer closeBody(r) defer closeBody(r)
@ -1820,6 +2005,7 @@ func (c *Client) CreateEmoji(emoji *Emoji, image []byte, filename string) (*Emoj
rq, _ := http.NewRequest("POST", c.ApiUrl+c.GetEmojiRoute()+"/create", body) rq, _ := http.NewRequest("POST", c.ApiUrl+c.GetEmojiRoute()+"/create", body)
rq.Header.Set("Content-Type", writer.FormDataContentType()) rq.Header.Set("Content-Type", writer.FormDataContentType())
rq.Close = true
if len(c.AuthToken) > 0 { if len(c.AuthToken) > 0 {
rq.Header.Set(HEADER_AUTH, "BEARER "+c.AuthToken) rq.Header.Set(HEADER_AUTH, "BEARER "+c.AuthToken)
@ -1862,6 +2048,7 @@ func (c *Client) UploadCertificateFile(data []byte, contentType string) *AppErro
url := c.ApiUrl + "/admin/add_certificate" url := c.ApiUrl + "/admin/add_certificate"
rq, _ := http.NewRequest("POST", url, bytes.NewReader(data)) rq, _ := http.NewRequest("POST", url, bytes.NewReader(data))
rq.Header.Set("Content-Type", contentType) rq.Header.Set("Content-Type", contentType)
rq.Close = true
if len(c.AuthToken) > 0 { if len(c.AuthToken) > 0 {
rq.Header.Set(HEADER_AUTH, "BEARER "+c.AuthToken) rq.Header.Set(HEADER_AUTH, "BEARER "+c.AuthToken)
@ -1898,3 +2085,28 @@ func (c *Client) SamlCertificateStatus(filename string) (map[string]interface{},
return StringInterfaceFromJson(r.Body), nil return StringInterfaceFromJson(r.Body), nil
} }
} }
// GetWebrtcToken if Successful returns a map with a valid token, stun server and turn server with credentials to use with
// the Mattermost WebRTC service, otherwise returns an AppError. Must be authenticated user.
func (c *Client) GetWebrtcToken() (map[string]string, *AppError) {
if r, err := c.DoApiPost("/webrtc/token", ""); err != nil {
return nil, err
} else {
defer closeBody(r)
return MapFromJson(r.Body), nil
}
}
// GetFileInfosForPost returns a list of FileInfo objects for a given post id, if successful.
// Otherwise, it returns an error.
func (c *Client) GetFileInfosForPost(channelId string, postId string, etag string) ([]*FileInfo, *AppError) {
c.clearExtraProperties()
if r, err := c.DoApiGet(c.GetChannelRoute(channelId)+fmt.Sprintf("/posts/%v/get_file_infos", postId), "", etag); err != nil {
return nil, err
} else {
defer closeBody(r)
c.fillInExtraProperties(r)
return FileInfosFromJson(r.Body), nil
}
}

View File

@ -34,7 +34,7 @@ type CompliancePost struct {
PostType string PostType string
PostProps string PostProps string
PostHashtags string PostHashtags string
PostFilenames string PostFileIds string
} }
func CompliancePostHeader() []string { func CompliancePostHeader() []string {
@ -60,7 +60,7 @@ func CompliancePostHeader() []string {
"PostType", "PostType",
"PostProps", "PostProps",
"PostHashtags", "PostHashtags",
"PostFilenames", "PostFileIds",
} }
} }
@ -99,6 +99,6 @@ func (me *CompliancePost) Row() []string {
me.PostType, me.PostType,
me.PostProps, me.PostProps,
me.PostHashtags, me.PostHashtags,
me.PostFilenames, me.PostFileIds,
} }
} }

View File

@ -57,6 +57,14 @@ const (
type ServiceSettings struct { type ServiceSettings struct {
SiteURL *string SiteURL *string
ListenAddress string ListenAddress string
ConnectionSecurity *string
TLSCertFile *string
TLSKeyFile *string
UseLetsEncrypt *bool
LetsEncryptCertificateCacheFile *string
Forward80To443 *bool
ReadTimeout *int
WriteTimeout *int
MaximumLoginAttempts int MaximumLoginAttempts int
SegmentDeveloperKey string SegmentDeveloperKey string
GoogleDeveloperKey string GoogleDeveloperKey string
@ -130,26 +138,24 @@ type PasswordSettings struct {
} }
type FileSettings struct { type FileSettings struct {
MaxFileSize *int64 MaxFileSize *int64
DriverName string DriverName string
Directory string Directory string
EnablePublicLink bool EnablePublicLink bool
PublicLinkSalt *string PublicLinkSalt *string
ThumbnailWidth int ThumbnailWidth int
ThumbnailHeight int ThumbnailHeight int
PreviewWidth int PreviewWidth int
PreviewHeight int PreviewHeight int
ProfileWidth int ProfileWidth int
ProfileHeight int ProfileHeight int
InitialFont string InitialFont string
AmazonS3AccessKeyId string AmazonS3AccessKeyId string
AmazonS3SecretAccessKey string AmazonS3SecretAccessKey string
AmazonS3Bucket string AmazonS3Bucket string
AmazonS3Region string AmazonS3Region string
AmazonS3Endpoint string AmazonS3Endpoint string
AmazonS3BucketEndpoint string AmazonS3SSL *bool
AmazonS3LocationConstraint *bool
AmazonS3LowercaseBucket *bool
} }
type EmailSettings struct { type EmailSettings struct {
@ -177,11 +183,12 @@ type EmailSettings struct {
} }
type RateLimitSettings struct { type RateLimitSettings struct {
EnableRateLimiter bool Enable *bool
PerSec int PerSec int
MemoryStoreSize int MaxBurst *int
VaryByRemoteAddr bool MemoryStoreSize int
VaryByHeader string VaryByRemoteAddr bool
VaryByHeader string
} }
type PrivacySettings struct { type PrivacySettings struct {
@ -205,7 +212,6 @@ type TeamSettings struct {
EnableUserCreation bool EnableUserCreation bool
EnableOpenServer *bool EnableOpenServer *bool
RestrictCreationToDomains string RestrictCreationToDomains string
RestrictTeamNames *bool
EnableCustomBrand *bool EnableCustomBrand *bool
CustomBrandText *string CustomBrandText *string
CustomDescriptionText *string CustomDescriptionText *string
@ -214,6 +220,7 @@ type TeamSettings struct {
RestrictPublicChannelManagement *string RestrictPublicChannelManagement *string
RestrictPrivateChannelManagement *string RestrictPrivateChannelManagement *string
UserStatusAwayTimeout *int64 UserStatusAwayTimeout *int64
MaxChannelsPerTeam *int64
} }
type LdapSettings struct { type LdapSettings struct {
@ -292,6 +299,17 @@ type NativeAppSettings struct {
IosAppDownloadLink *string IosAppDownloadLink *string
} }
type WebrtcSettings struct {
Enable *bool
GatewayWebsocketUrl *string
GatewayAdminUrl *string
GatewayAdminSecret *string
StunURI *string
TurnURI *string
TurnUsername *string
TurnSharedKey *string
}
type Config struct { type Config struct {
ServiceSettings ServiceSettings ServiceSettings ServiceSettings
TeamSettings TeamSettings TeamSettings TeamSettings
@ -312,6 +330,7 @@ type Config struct {
SamlSettings SamlSettings SamlSettings SamlSettings
NativeAppSettings NativeAppSettings NativeAppSettings NativeAppSettings
ClusterSettings ClusterSettings ClusterSettings ClusterSettings
WebrtcSettings WebrtcSettings
} }
func (o *Config) ToJson() string { func (o *Config) ToJson() string {
@ -353,26 +372,27 @@ func (o *Config) SetDefaults() {
o.SqlSettings.AtRestEncryptKey = NewRandomString(32) o.SqlSettings.AtRestEncryptKey = NewRandomString(32)
} }
if o.FileSettings.AmazonS3Endpoint == "" {
// Defaults to "s3.amazonaws.com"
o.FileSettings.AmazonS3Endpoint = "s3.amazonaws.com"
}
if o.FileSettings.AmazonS3Region == "" {
// Defaults to "us-east-1" region.
o.FileSettings.AmazonS3Region = "us-east-1"
}
if o.FileSettings.AmazonS3SSL == nil {
o.FileSettings.AmazonS3SSL = new(bool)
*o.FileSettings.AmazonS3SSL = true // Secure by default.
}
if o.FileSettings.MaxFileSize == nil { if o.FileSettings.MaxFileSize == nil {
o.FileSettings.MaxFileSize = new(int64) o.FileSettings.MaxFileSize = new(int64)
*o.FileSettings.MaxFileSize = 52428800 // 50 MB *o.FileSettings.MaxFileSize = 52428800 // 50 MB
} }
if len(*o.FileSettings.PublicLinkSalt) == 0 { if len(*o.FileSettings.PublicLinkSalt) == 0 {
o.FileSettings.PublicLinkSalt = new(string) o.FileSettings.PublicLinkSalt = new(string)
*o.FileSettings.PublicLinkSalt = NewRandomString(32) *o.FileSettings.PublicLinkSalt = NewRandomString(32)
} }
if o.FileSettings.AmazonS3LocationConstraint == nil {
o.FileSettings.AmazonS3LocationConstraint = new(bool)
*o.FileSettings.AmazonS3LocationConstraint = false
}
if o.FileSettings.AmazonS3LowercaseBucket == nil {
o.FileSettings.AmazonS3LowercaseBucket = new(bool)
*o.FileSettings.AmazonS3LowercaseBucket = false
}
if len(o.EmailSettings.InviteSalt) == 0 { if len(o.EmailSettings.InviteSalt) == 0 {
o.EmailSettings.InviteSalt = NewRandomString(32) o.EmailSettings.InviteSalt = NewRandomString(32)
} }
@ -431,11 +451,6 @@ func (o *Config) SetDefaults() {
*o.PasswordSettings.Symbol = false *o.PasswordSettings.Symbol = false
} }
if o.TeamSettings.RestrictTeamNames == nil {
o.TeamSettings.RestrictTeamNames = new(bool)
*o.TeamSettings.RestrictTeamNames = true
}
if o.TeamSettings.EnableCustomBrand == nil { if o.TeamSettings.EnableCustomBrand == nil {
o.TeamSettings.EnableCustomBrand = new(bool) o.TeamSettings.EnableCustomBrand = new(bool)
*o.TeamSettings.EnableCustomBrand = false *o.TeamSettings.EnableCustomBrand = false
@ -481,6 +496,11 @@ func (o *Config) SetDefaults() {
*o.TeamSettings.UserStatusAwayTimeout = 300 *o.TeamSettings.UserStatusAwayTimeout = 300
} }
if o.TeamSettings.MaxChannelsPerTeam == nil {
o.TeamSettings.MaxChannelsPerTeam = new(int64)
*o.TeamSettings.MaxChannelsPerTeam = 2000
}
if o.EmailSettings.EnableSignInWithEmail == nil { if o.EmailSettings.EnableSignInWithEmail == nil {
o.EmailSettings.EnableSignInWithEmail = new(bool) o.EmailSettings.EnableSignInWithEmail = new(bool)
@ -881,6 +901,58 @@ func (o *Config) SetDefaults() {
o.NativeAppSettings.IosAppDownloadLink = new(string) o.NativeAppSettings.IosAppDownloadLink = new(string)
*o.NativeAppSettings.IosAppDownloadLink = "https://about.mattermost.com/mattermost-ios-app/" *o.NativeAppSettings.IosAppDownloadLink = "https://about.mattermost.com/mattermost-ios-app/"
} }
if o.RateLimitSettings.Enable == nil {
o.RateLimitSettings.Enable = new(bool)
*o.RateLimitSettings.Enable = false
}
if o.RateLimitSettings.MaxBurst == nil {
o.RateLimitSettings.MaxBurst = new(int)
*o.RateLimitSettings.MaxBurst = 100
}
if o.ServiceSettings.ConnectionSecurity == nil {
o.ServiceSettings.ConnectionSecurity = new(string)
*o.ServiceSettings.ConnectionSecurity = ""
}
if o.ServiceSettings.TLSKeyFile == nil {
o.ServiceSettings.TLSKeyFile = new(string)
*o.ServiceSettings.TLSKeyFile = ""
}
if o.ServiceSettings.TLSCertFile == nil {
o.ServiceSettings.TLSCertFile = new(string)
*o.ServiceSettings.TLSCertFile = ""
}
if o.ServiceSettings.UseLetsEncrypt == nil {
o.ServiceSettings.UseLetsEncrypt = new(bool)
*o.ServiceSettings.UseLetsEncrypt = false
}
if o.ServiceSettings.LetsEncryptCertificateCacheFile == nil {
o.ServiceSettings.LetsEncryptCertificateCacheFile = new(string)
*o.ServiceSettings.LetsEncryptCertificateCacheFile = "./config/letsencrypt.cache"
}
if o.ServiceSettings.ReadTimeout == nil {
o.ServiceSettings.ReadTimeout = new(int)
*o.ServiceSettings.ReadTimeout = 300
}
if o.ServiceSettings.WriteTimeout == nil {
o.ServiceSettings.WriteTimeout = new(int)
*o.ServiceSettings.WriteTimeout = 300
}
if o.ServiceSettings.Forward80To443 == nil {
o.ServiceSettings.Forward80To443 = new(bool)
*o.ServiceSettings.Forward80To443 = false
}
o.defaultWebrtcSettings()
} }
func (o *Config) IsValid() *AppError { func (o *Config) IsValid() *AppError {
@ -911,6 +983,10 @@ func (o *Config) IsValid() *AppError {
return NewLocAppError("Config.IsValid", "model.config.is_valid.max_users.app_error", nil, "") return NewLocAppError("Config.IsValid", "model.config.is_valid.max_users.app_error", nil, "")
} }
if *o.TeamSettings.MaxChannelsPerTeam <= 0 {
return NewLocAppError("Config.IsValid", "model.config.is_valid.max_channels.app_error", nil, "")
}
if !(*o.TeamSettings.RestrictDirectMessage == DIRECT_MESSAGE_ANY || *o.TeamSettings.RestrictDirectMessage == DIRECT_MESSAGE_TEAM) { if !(*o.TeamSettings.RestrictDirectMessage == DIRECT_MESSAGE_ANY || *o.TeamSettings.RestrictDirectMessage == DIRECT_MESSAGE_TEAM) {
return NewLocAppError("Config.IsValid", "model.config.is_valid.restrict_direct_message.app_error", nil, "") return NewLocAppError("Config.IsValid", "model.config.is_valid.restrict_direct_message.app_error", nil, "")
} }
@ -1083,6 +1159,26 @@ func (o *Config) IsValid() *AppError {
return NewLocAppError("Config.IsValid", "model.config.is_valid.sitename_length.app_error", map[string]interface{}{"MaxLength": SITENAME_MAX_LENGTH}, "") return NewLocAppError("Config.IsValid", "model.config.is_valid.sitename_length.app_error", map[string]interface{}{"MaxLength": SITENAME_MAX_LENGTH}, "")
} }
if *o.RateLimitSettings.MaxBurst <= 0 {
return NewLocAppError("Config.IsValid", "model.config.is_valid.max_burst.app_error", nil, "")
}
if err := o.isValidWebrtcSettings(); err != nil {
return err
}
if !(*o.ServiceSettings.ConnectionSecurity == CONN_SECURITY_NONE || *o.ServiceSettings.ConnectionSecurity == CONN_SECURITY_TLS) {
return NewLocAppError("Config.IsValid", "model.config.is_valid.webserver_security.app_error", nil, "")
}
if *o.ServiceSettings.ReadTimeout <= 0 {
return NewLocAppError("Config.IsValid", "model.config.is_valid.read_timeout.app_error", nil, "")
}
if *o.ServiceSettings.WriteTimeout <= 0 {
return NewLocAppError("Config.IsValid", "model.config.is_valid.write_timeout.app_error", nil, "")
}
return nil return nil
} }
@ -1121,3 +1217,71 @@ func (o *Config) Sanitize() {
o.SqlSettings.DataSourceReplicas[i] = FAKE_SETTING o.SqlSettings.DataSourceReplicas[i] = FAKE_SETTING
} }
} }
func (o *Config) defaultWebrtcSettings() {
if o.WebrtcSettings.Enable == nil {
o.WebrtcSettings.Enable = new(bool)
*o.WebrtcSettings.Enable = false
}
if o.WebrtcSettings.GatewayWebsocketUrl == nil {
o.WebrtcSettings.GatewayWebsocketUrl = new(string)
*o.WebrtcSettings.GatewayWebsocketUrl = ""
}
if o.WebrtcSettings.GatewayAdminUrl == nil {
o.WebrtcSettings.GatewayAdminUrl = new(string)
*o.WebrtcSettings.GatewayAdminUrl = ""
}
if o.WebrtcSettings.GatewayAdminSecret == nil {
o.WebrtcSettings.GatewayAdminSecret = new(string)
*o.WebrtcSettings.GatewayAdminSecret = ""
}
if o.WebrtcSettings.StunURI == nil {
o.WebrtcSettings.StunURI = new(string)
*o.WebrtcSettings.StunURI = ""
}
if o.WebrtcSettings.TurnURI == nil {
o.WebrtcSettings.TurnURI = new(string)
*o.WebrtcSettings.TurnURI = ""
}
if o.WebrtcSettings.TurnUsername == nil {
o.WebrtcSettings.TurnUsername = new(string)
*o.WebrtcSettings.TurnUsername = ""
}
if o.WebrtcSettings.TurnSharedKey == nil {
o.WebrtcSettings.TurnSharedKey = new(string)
*o.WebrtcSettings.TurnSharedKey = ""
}
}
func (o *Config) isValidWebrtcSettings() *AppError {
if *o.WebrtcSettings.Enable {
if len(*o.WebrtcSettings.GatewayWebsocketUrl) == 0 || !IsValidWebsocketUrl(*o.WebrtcSettings.GatewayWebsocketUrl) {
return NewLocAppError("Config.IsValid", "model.config.is_valid.webrtc_gateway_ws_url.app_error", nil, "")
} else if len(*o.WebrtcSettings.GatewayAdminUrl) == 0 || !IsValidHttpUrl(*o.WebrtcSettings.GatewayAdminUrl) {
return NewLocAppError("Config.IsValid", "model.config.is_valid.webrtc_gateway_admin_url.app_error", nil, "")
} else if len(*o.WebrtcSettings.GatewayAdminSecret) == 0 {
return NewLocAppError("Config.IsValid", "model.config.is_valid.webrtc_gateway_admin_secret.app_error", nil, "")
} else if len(*o.WebrtcSettings.StunURI) != 0 && !IsValidTurnOrStunServer(*o.WebrtcSettings.StunURI) {
return NewLocAppError("Config.IsValid", "model.config.is_valid.webrtc_stun_uri.app_error", nil, "")
} else if len(*o.WebrtcSettings.TurnURI) != 0 {
if !IsValidTurnOrStunServer(*o.WebrtcSettings.TurnURI) {
return NewLocAppError("Config.IsValid", "model.config.is_valid.webrtc_turn_uri.app_error", nil, "")
}
if len(*o.WebrtcSettings.TurnUsername) == 0 {
return NewLocAppError("Config.IsValid", "model.config.is_valid.webrtc_turn_username.app_error", nil, "")
} else if len(*o.WebrtcSettings.TurnSharedKey) == 0 {
return NewLocAppError("Config.IsValid", "model.config.is_valid.webrtc_turn_shared_key.app_error", nil, "")
}
}
}
return nil
}

View File

@ -14,8 +14,8 @@ var (
) )
type FileUploadResponse struct { type FileUploadResponse struct {
Filenames []string `json:"filenames"` FileInfos []*FileInfo `json:"file_infos"`
ClientIds []string `json:"client_ids"` ClientIds []string `json:"client_ids"`
} }
func FileUploadResponseFromJson(data io.Reader) *FileUploadResponse { func FileUploadResponseFromJson(data io.Reader) *FileUploadResponse {

View File

@ -6,54 +6,31 @@ package model
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"image"
"image/gif" "image/gif"
"io" "io"
"mime" "mime"
"path/filepath" "path/filepath"
"strings"
) )
type FileInfo struct { type FileInfo struct {
Filename string `json:"filename"` Id string `json:"id"`
Size int `json:"size"` CreatorId string `json:"user_id"`
PostId string `json:"post_id,omitempty"`
CreateAt int64 `json:"create_at"`
UpdateAt int64 `json:"update_at"`
DeleteAt int64 `json:"delete_at"`
Path string `json:"-"` // not sent back to the client
ThumbnailPath string `json:"-"` // not sent back to the client
PreviewPath string `json:"-"` // not sent back to the client
Name string `json:"name"`
Extension string `json:"extension"` Extension string `json:"extension"`
Size int64 `json:"size"`
MimeType string `json:"mime_type"` MimeType string `json:"mime_type"`
HasPreviewImage bool `json:"has_preview_image"` Width int `json:"width,omitempty"`
} Height int `json:"height,omitempty"`
HasPreviewImage bool `json:"has_preview_image,omitempty"`
func GetInfoForBytes(filename string, data []byte) (*FileInfo, *AppError) {
size := len(data)
var mimeType string
extension := filepath.Ext(filename)
isImage := IsFileExtImage(extension)
if isImage {
mimeType = GetImageMimeType(extension)
} else {
mimeType = mime.TypeByExtension(extension)
}
if extension != "" && extension[0] == '.' {
// the client expects a file extension without the leading period
extension = extension[1:]
}
hasPreviewImage := isImage
if mimeType == "image/gif" {
// just show the gif itself instead of a preview image for animated gifs
if gifImage, err := gif.DecodeAll(bytes.NewReader(data)); err != nil {
return nil, NewLocAppError("GetInfoForBytes", "model.file_info.get.gif.app_error", nil, "filename="+filename)
} else {
hasPreviewImage = len(gifImage.Image) == 1
}
}
return &FileInfo{
Filename: filename,
Size: size,
Extension: extension,
MimeType: mimeType,
HasPreviewImage: hasPreviewImage,
}, nil
} }
func (info *FileInfo) ToJson() string { func (info *FileInfo) ToJson() string {
@ -75,3 +52,123 @@ func FileInfoFromJson(data io.Reader) *FileInfo {
return &info return &info
} }
} }
func FileInfosToJson(infos []*FileInfo) string {
b, err := json.Marshal(infos)
if err != nil {
return ""
} else {
return string(b)
}
}
func FileInfosFromJson(data io.Reader) []*FileInfo {
decoder := json.NewDecoder(data)
var infos []*FileInfo
if err := decoder.Decode(&infos); err != nil {
return nil
} else {
return infos
}
}
func (o *FileInfo) PreSave() {
if o.Id == "" {
o.Id = NewId()
}
if o.CreateAt == 0 {
o.CreateAt = GetMillis()
o.UpdateAt = o.CreateAt
}
}
func (o *FileInfo) IsValid() *AppError {
if len(o.Id) != 26 {
return NewLocAppError("FileInfo.IsValid", "model.file_info.is_valid.id.app_error", nil, "")
}
if len(o.CreatorId) != 26 {
return NewLocAppError("FileInfo.IsValid", "model.file_info.is_valid.user_id.app_error", nil, "id="+o.Id)
}
if len(o.PostId) != 0 && len(o.PostId) != 26 {
return NewLocAppError("FileInfo.IsValid", "model.file_info.is_valid.post_id.app_error", nil, "id="+o.Id)
}
if o.CreateAt == 0 {
return NewLocAppError("FileInfo.IsValid", "model.file_info.is_valid.create_at.app_error", nil, "id="+o.Id)
}
if o.UpdateAt == 0 {
return NewLocAppError("FileInfo.IsValid", "model.file_info.is_valid.update_at.app_error", nil, "id="+o.Id)
}
if o.Path == "" {
return NewLocAppError("FileInfo.IsValid", "model.file_info.is_valid.path.app_error", nil, "id="+o.Id)
}
return nil
}
func (o *FileInfo) IsImage() bool {
return strings.HasPrefix(o.MimeType, "image")
}
func GetInfoForBytes(name string, data []byte) (*FileInfo, *AppError) {
info := &FileInfo{
Name: name,
Size: int64(len(data)),
}
var err *AppError
extension := strings.ToLower(filepath.Ext(name))
info.MimeType = mime.TypeByExtension(extension)
if extension != "" && extension[0] == '.' {
// The client expects a file extension without the leading period
info.Extension = extension[1:]
} else {
info.Extension = extension
}
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 {
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 {
// Still return the rest of the info even though it doesn't appear to be an actual gif
info.HasPreviewImage = true
err = NewLocAppError("GetInfoForBytes", "model.file_info.get.gif.app_error", nil, "name="+name)
} else {
info.HasPreviewImage = len(gifConfig.Image) == 1
}
} else {
info.HasPreviewImage = true
}
}
}
return info, err
}
func GetEtagForFileInfos(infos []*FileInfo) string {
if len(infos) == 0 {
return Etag()
}
var maxUpdateAt int64
for _, info := range infos {
if info.UpdateAt > maxUpdateAt {
maxUpdateAt = info.UpdateAt
}
}
return Etag(infos[0].PostId, maxUpdateAt)
}

View File

@ -9,14 +9,13 @@ import (
) )
type InitialLoad struct { type InitialLoad struct {
User *User `json:"user"` User *User `json:"user"`
TeamMembers []*TeamMember `json:"team_members"` TeamMembers []*TeamMember `json:"team_members"`
Teams []*Team `json:"teams"` Teams []*Team `json:"teams"`
DirectProfiles map[string]*User `json:"direct_profiles"` Preferences Preferences `json:"preferences"`
Preferences Preferences `json:"preferences"` ClientCfg map[string]string `json:"client_cfg"`
ClientCfg map[string]string `json:"client_cfg"` LicenseCfg map[string]string `json:"license_cfg"`
LicenseCfg map[string]string `json:"license_cfg"` NoAccounts bool `json:"no_accounts"`
NoAccounts bool `json:"no_accounts"`
} }
func (me *InitialLoad) ToJson() string { func (me *InitialLoad) ToJson() string {

View File

@ -43,7 +43,8 @@ type Features struct {
MHPNS *bool `json:"mhpns"` MHPNS *bool `json:"mhpns"`
SAML *bool `json:"saml"` SAML *bool `json:"saml"`
PasswordRequirements *bool `json:"password_requirements"` PasswordRequirements *bool `json:"password_requirements"`
FutureFeatures *bool `json:"future_features"` // after we enabled more features for webrtc we'll need to control them with this
FutureFeatures *bool `json:"future_features"`
} }
func (f *Features) ToMap() map[string]interface{} { func (f *Features) ToMap() map[string]interface{} {

View File

@ -35,7 +35,8 @@ type Post struct {
Type string `json:"type"` Type string `json:"type"`
Props StringInterface `json:"props"` Props StringInterface `json:"props"`
Hashtags string `json:"hashtags"` Hashtags string `json:"hashtags"`
Filenames StringArray `json:"filenames"` Filenames StringArray `json:"filenames,omitempty"` // Deprecated, do not use this field any more
FileIds StringArray `json:"file_ids,omitempty"`
PendingPostId string `json:"pending_post_id" db:"-"` PendingPostId string `json:"pending_post_id" db:"-"`
} }
@ -118,6 +119,10 @@ func (o *Post) IsValid() *AppError {
return NewLocAppError("Post.IsValid", "model.post.is_valid.filenames.app_error", nil, "id="+o.Id) return NewLocAppError("Post.IsValid", "model.post.is_valid.filenames.app_error", nil, "id="+o.Id)
} }
if utf8.RuneCountInString(ArrayToJson(o.FileIds)) > 150 {
return NewLocAppError("Post.IsValid", "model.post.is_valid.file_ids.app_error", nil, "id="+o.Id)
}
if utf8.RuneCountInString(StringInterfaceToJson(o.Props)) > 8000 { if utf8.RuneCountInString(StringInterfaceToJson(o.Props)) > 8000 {
return NewLocAppError("Post.IsValid", "model.post.is_valid.props.app_error", nil, "id="+o.Id) return NewLocAppError("Post.IsValid", "model.post.is_valid.props.app_error", nil, "id="+o.Id)
} }
@ -145,15 +150,16 @@ func (o *Post) PreSave() {
if o.Filenames == nil { if o.Filenames == nil {
o.Filenames = []string{} o.Filenames = []string{}
} }
if o.FileIds == nil {
o.FileIds = []string{}
}
} }
func (o *Post) MakeNonNil() { func (o *Post) MakeNonNil() {
if o.Props == nil { if o.Props == nil {
o.Props = make(map[string]interface{}) o.Props = make(map[string]interface{})
} }
if o.Filenames == nil {
o.Filenames = []string{}
}
} }
func (o *Post) AddProp(key string, value interface{}) { func (o *Post) AddProp(key string, value interface{}) {

View File

@ -11,7 +11,7 @@ import (
const ( const (
SESSION_COOKIE_TOKEN = "MMAUTHTOKEN" SESSION_COOKIE_TOKEN = "MMAUTHTOKEN"
SESSION_CACHE_SIZE = 10000 SESSION_CACHE_SIZE = 25000
SESSION_PROP_PLATFORM = "platform" SESSION_PROP_PLATFORM = "platform"
SESSION_PROP_OS = "os" SESSION_PROP_OS = "os"
SESSION_PROP_BROWSER = "browser" SESSION_PROP_BROWSER = "browser"
@ -115,6 +115,10 @@ func (me *Session) IsMobileApp() bool {
(strings.HasPrefix(me.DeviceId, PUSH_NOTIFY_APPLE+":") || strings.HasPrefix(me.DeviceId, PUSH_NOTIFY_ANDROID+":")) (strings.HasPrefix(me.DeviceId, PUSH_NOTIFY_APPLE+":") || strings.HasPrefix(me.DeviceId, PUSH_NOTIFY_ANDROID+":"))
} }
func (me *Session) GetUserRoles() []string {
return strings.Fields(me.Roles)
}
func SessionsToJson(o []*Session) string { func SessionsToJson(o []*Session) string {
if b, err := json.Marshal(o); err != nil { if b, err := json.Marshal(o); err != nil {
return "[]" return "[]"

View File

@ -12,8 +12,9 @@ const (
STATUS_OFFLINE = "offline" STATUS_OFFLINE = "offline"
STATUS_AWAY = "away" STATUS_AWAY = "away"
STATUS_ONLINE = "online" STATUS_ONLINE = "online"
STATUS_CACHE_SIZE = 10000 STATUS_CACHE_SIZE = 25000
STATUS_CHANNEL_TIMEOUT = 20000 // 20 seconds STATUS_CHANNEL_TIMEOUT = 20000 // 20 seconds
STATUS_MIN_UPDATE_TIME = 120000 // 2 minutes
) )
type Status struct { type Status struct {

View File

@ -100,7 +100,7 @@ func (o *Team) Etag() string {
return Etag(o.Id, o.UpdateAt) return Etag(o.Id, o.UpdateAt)
} }
func (o *Team) IsValid(restrictTeamNames bool) *AppError { func (o *Team) IsValid() *AppError {
if len(o.Id) != 26 { if len(o.Id) != 26 {
return NewLocAppError("Team.IsValid", "model.team.is_valid.id.app_error", nil, "") return NewLocAppError("Team.IsValid", "model.team.is_valid.id.app_error", nil, "")
@ -130,7 +130,7 @@ func (o *Team) IsValid(restrictTeamNames bool) *AppError {
return NewLocAppError("Team.IsValid", "model.team.is_valid.url.app_error", nil, "id="+o.Id) return NewLocAppError("Team.IsValid", "model.team.is_valid.url.app_error", nil, "id="+o.Id)
} }
if restrictTeamNames && IsReservedTeamName(o.Name) { if IsReservedTeamName(o.Name) {
return NewLocAppError("Team.IsValid", "model.team.is_valid.reserved.app_error", nil, "id="+o.Id) return NewLocAppError("Team.IsValid", "model.team.is_valid.reserved.app_error", nil, "id="+o.Id)
} }
@ -188,7 +188,7 @@ func IsValidTeamName(s string) bool {
return false return false
} }
if len(s) <= 3 { if len(s) <= 1 {
return false return false
} }

View File

@ -9,10 +9,6 @@ import (
"strings" "strings"
) )
const (
ROLE_TEAM_ADMIN = "admin"
)
type TeamMember struct { type TeamMember struct {
TeamId string `json:"team_id"` TeamId string `json:"team_id"`
UserId string `json:"user_id"` UserId string `json:"user_id"`
@ -59,48 +55,6 @@ func TeamMembersFromJson(data io.Reader) []*TeamMember {
} }
} }
func IsValidTeamRoles(teamRoles string) bool {
roles := strings.Split(teamRoles, " ")
for _, r := range roles {
if !isValidTeamRole(r) {
return false
}
}
return true
}
func isValidTeamRole(role string) bool {
if role == "" {
return true
}
if role == ROLE_TEAM_ADMIN {
return true
}
return false
}
func IsInTeamRole(teamRoles string, inRole string) bool {
roles := strings.Split(teamRoles, " ")
for _, r := range roles {
if r == inRole {
return true
}
}
return false
}
func (o *TeamMember) IsTeamAdmin() bool {
return IsInTeamRole(o.Roles, ROLE_TEAM_ADMIN)
}
func (o *TeamMember) IsValid() *AppError { func (o *TeamMember) IsValid() *AppError {
if len(o.TeamId) != 26 { if len(o.TeamId) != 26 {
@ -111,11 +65,12 @@ func (o *TeamMember) IsValid() *AppError {
return NewLocAppError("TeamMember.IsValid", "model.team_member.is_valid.user_id.app_error", nil, "") return NewLocAppError("TeamMember.IsValid", "model.team_member.is_valid.user_id.app_error", nil, "")
} }
for _, role := range strings.Split(o.Roles, " ") {
if !(role == "" || role == ROLE_TEAM_ADMIN) {
return NewLocAppError("TeamMember.IsValid", "model.team_member.is_valid.role.app_error", nil, "role="+role)
}
}
return nil return nil
} }
func (o *TeamMember) PreUpdate() {
}
func (o *TeamMember) GetRoles() []string {
return strings.Fields(o.Roles)
}

View File

@ -0,0 +1,35 @@
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package model
import (
"encoding/json"
"io"
)
type TeamStats struct {
TeamId string `json:"team_id"`
TotalMemberCount int64 `json:"total_member_count"`
ActiveMemberCount int64 `json:"active_member_count"`
}
func (o *TeamStats) ToJson() string {
b, err := json.Marshal(o)
if err != nil {
return ""
} else {
return string(b)
}
}
func TeamStatsFromJson(data io.Reader) *TeamStats {
decoder := json.NewDecoder(data)
var o TeamStats
err := decoder.Decode(&o)
if err == nil {
return &o
} else {
return nil
}
}

View File

@ -15,7 +15,6 @@ import (
) )
const ( const (
ROLE_SYSTEM_ADMIN = "system_admin"
USER_NOTIFY_ALL = "all" USER_NOTIFY_ALL = "all"
USER_NOTIFY_MENTION = "mention" USER_NOTIFY_MENTION = "mention"
USER_NOTIFY_NONE = "none" USER_NOTIFY_NONE = "none"
@ -233,14 +232,15 @@ func (u *User) Sanitize(options map[string]bool) {
if len(options) != 0 && !options["passwordupdate"] { if len(options) != 0 && !options["passwordupdate"] {
u.LastPasswordUpdate = 0 u.LastPasswordUpdate = 0
} }
if len(options) != 0 && !options["authservice"] {
u.AuthService = ""
}
} }
func (u *User) ClearNonProfileFields() { func (u *User) ClearNonProfileFields() {
u.Password = "" u.Password = ""
u.AuthData = new(string) u.AuthData = new(string)
*u.AuthData = "" *u.AuthData = ""
u.AuthService = ""
u.MfaActive = false
u.MfaSecret = "" u.MfaSecret = ""
u.EmailVerified = false u.EmailVerified = false
u.AllowMarketing = false u.AllowMarketing = false
@ -319,9 +319,17 @@ func (u *User) GetDisplayNameForPreference(nameFormat string) string {
return displayName return displayName
} }
func (u *User) GetRoles() []string {
return strings.Fields(u.Roles)
}
func (u *User) GetRawRoles() string {
return u.Roles
}
func IsValidUserRoles(userRoles string) bool { func IsValidUserRoles(userRoles string) bool {
roles := strings.Split(userRoles, " ") roles := strings.Fields(userRoles)
for _, r := range roles { for _, r := range roles {
if !isValidRole(r) { if !isValidRole(r) {
@ -329,19 +337,17 @@ func IsValidUserRoles(userRoles string) bool {
} }
} }
// Exclude just the system_admin role explicitly to prevent mistakes
if len(roles) == 1 && roles[0] == "system_admin" {
return false
}
return true return true
} }
func isValidRole(role string) bool { func isValidRole(roleId string) bool {
if role == "" { _, ok := BuiltInRoles[roleId]
return true return ok
}
if role == ROLE_SYSTEM_ADMIN {
return true
}
return false
} }
// Make sure you acually want to use this function. In context.go there are functions to check permissions // Make sure you acually want to use this function. In context.go there are functions to check permissions
@ -411,6 +417,26 @@ func UserMapFromJson(data io.Reader) map[string]*User {
} }
} }
func UserListToJson(u []*User) string {
b, err := json.Marshal(u)
if err != nil {
return ""
} else {
return string(b)
}
}
func UserListFromJson(data io.Reader) []*User {
decoder := json.NewDecoder(data)
var users []*User
err := decoder.Decode(&users)
if err == nil {
return users
} else {
return nil
}
}
// HashPassword generates a hash using the bcrypt.GenerateFromPassword // HashPassword generates a hash using the bcrypt.GenerateFromPassword
func HashPassword(password string) string { func HashPassword(password string) string {
hash, err := bcrypt.GenerateFromPassword([]byte(password), 10) hash, err := bcrypt.GenerateFromPassword([]byte(password), 10)

View File

@ -0,0 +1,39 @@
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package model
import (
"encoding/json"
"io"
)
type UserSearch struct {
Term string `json:"term"`
TeamId string `json:"team_id"`
InChannelId string `json:"in_channel_id"`
NotInChannelId string `json:"not_in_channel_id"`
AllowInactive bool `json:"allow_inactive"`
}
// ToJson convert a User to a json string
func (u *UserSearch) ToJson() string {
b, err := json.Marshal(u)
if err != nil {
return ""
} else {
return string(b)
}
}
// UserSearchFromJson will decode the input and return a User
func UserSearchFromJson(data io.Reader) *UserSearch {
decoder := json.NewDecoder(data)
var us UserSearch
err := decoder.Decode(&us)
if err == nil {
return &us
} else {
return nil
}
}

View File

@ -10,6 +10,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net/mail" "net/mail"
"net/url" "net/url"
"regexp" "regexp"
@ -74,13 +75,21 @@ func (er *AppError) ToJson() string {
// AppErrorFromJson will decode the input and return an AppError // AppErrorFromJson will decode the input and return an AppError
func AppErrorFromJson(data io.Reader) *AppError { func AppErrorFromJson(data io.Reader) *AppError {
decoder := json.NewDecoder(data) str := ""
bytes, rerr := ioutil.ReadAll(data)
if rerr != nil {
str = rerr.Error()
} else {
str = string(bytes)
}
decoder := json.NewDecoder(strings.NewReader(str))
var er AppError var er AppError
err := decoder.Decode(&er) err := decoder.Decode(&er)
if err == nil { if err == nil {
return &er return &er
} else { } else {
return NewLocAppError("AppErrorFromJson", "model.utils.decode_json.app_error", nil, err.Error()) return NewLocAppError("AppErrorFromJson", "model.utils.decode_json.app_error", nil, "body: "+str)
} }
} }
@ -166,6 +175,23 @@ func ArrayFromJson(data io.Reader) []string {
} }
} }
func ArrayFromInterface(data interface{}) []string {
stringArray := []string{}
dataArray, ok := data.([]interface{})
if !ok {
return stringArray
}
for _, v := range dataArray {
if str, ok := v.(string); ok {
stringArray = append(stringArray, str)
}
}
return stringArray
}
func StringInterfaceToJson(objmap map[string]interface{}) string { func StringInterfaceToJson(objmap map[string]interface{}) string {
if b, err := json.Marshal(objmap); err != nil { if b, err := json.Marshal(objmap); err != nil {
return "" return ""
@ -227,58 +253,15 @@ func IsValidEmail(email string) bool {
} }
var reservedName = []string{ var reservedName = []string{
"www", "signup",
"web", "login",
"admin", "admin",
"support",
"notify",
"test",
"demo",
"mail",
"team",
"channel", "channel",
"internal",
"localhost",
"dockerhost",
"stag",
"post", "post",
"cluster",
"api", "api",
"oauth", "oauth",
} }
var wwwStart = regexp.MustCompile(`^www`)
var betaStart = regexp.MustCompile(`^beta`)
var ciStart = regexp.MustCompile(`^ci`)
func GetSubDomain(s string) (string, string) {
s = strings.Replace(s, "http://", "", 1)
s = strings.Replace(s, "https://", "", 1)
match := wwwStart.MatchString(s)
if match {
return "", ""
}
match = betaStart.MatchString(s)
if match {
return "", ""
}
match = ciStart.MatchString(s)
if match {
return "", ""
}
parts := strings.Split(s, ".")
if len(parts) != 3 {
return "", ""
}
return parts[0], parts[1]
}
func IsValidChannelIdentifier(s string) bool { func IsValidChannelIdentifier(s string) bool {
if !IsValidAlphaNum(s, true) { if !IsValidAlphaNum(s, true) {
@ -413,6 +396,18 @@ func IsValidHttpsUrl(rawUrl string) bool {
return true return true
} }
func IsValidTurnOrStunServer(rawUri string) bool {
if strings.Index(rawUri, "turn:") != 0 && strings.Index(rawUri, "stun:") != 0 {
return false
}
if _, err := url.ParseRequestURI(rawUri); err != nil {
return false
}
return true
}
func IsSafeLink(link *string) bool { func IsSafeLink(link *string) bool {
if link != nil { if link != nil {
if IsValidHttpUrl(*link) { if IsValidHttpUrl(*link) {
@ -426,3 +421,15 @@ func IsSafeLink(link *string) bool {
return true return true
} }
func IsValidWebsocketUrl(rawUrl string) bool {
if strings.Index(rawUrl, "ws://") != 0 && strings.Index(rawUrl, "wss://") != 0 {
return false
}
if _, err := url.ParseRequestURI(rawUrl); err != nil {
return false
}
return true
}

View File

@ -13,6 +13,7 @@ import (
// It should be maitained in chronological order with most current // It should be maitained in chronological order with most current
// release at the front of the list. // release at the front of the list.
var versions = []string{ var versions = []string{
"3.5.0",
"3.4.0", "3.4.0",
"3.3.0", "3.3.0",
"3.2.0", "3.2.0",

21
vendor/github.com/mattermost/platform/model/webrtc.go generated vendored Normal file
View File

@ -0,0 +1,21 @@
package model
import (
"encoding/json"
"io"
)
type GatewayResponse struct {
Status string `json:"janus"`
}
func GatewayResponseFromJson(data io.Reader) *GatewayResponse {
decoder := json.NewDecoder(data)
var o GatewayResponse
err := decoder.Decode(&o)
if err == nil {
return &o
} else {
return nil
}
}

View File

@ -6,7 +6,6 @@ package model
import ( import (
"encoding/json" "encoding/json"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"net/http"
) )
type WebSocketClient struct { type WebSocketClient struct {
@ -17,19 +16,18 @@ type WebSocketClient struct {
Sequence int64 // The ever-incrementing sequence attached to each WebSocket action Sequence int64 // The ever-incrementing sequence attached to each WebSocket action
EventChannel chan *WebSocketEvent EventChannel chan *WebSocketEvent
ResponseChannel chan *WebSocketResponse ResponseChannel chan *WebSocketResponse
ListenError *AppError
} }
// NewWebSocketClient constructs a new WebSocket client with convienence // NewWebSocketClient constructs a new WebSocket client with convienence
// methods for talking to the server. // methods for talking to the server.
func NewWebSocketClient(url, authToken string) (*WebSocketClient, *AppError) { func NewWebSocketClient(url, authToken string) (*WebSocketClient, *AppError) {
header := http.Header{} conn, _, err := websocket.DefaultDialer.Dial(url+API_URL_SUFFIX+"/users/websocket", nil)
header.Set(HEADER_AUTH, "BEARER "+authToken)
conn, _, err := websocket.DefaultDialer.Dial(url+API_URL_SUFFIX+"/users/websocket", header)
if err != nil { if err != nil {
return nil, NewLocAppError("NewWebSocketClient", "model.websocket_client.connect_fail.app_error", nil, err.Error()) return nil, NewLocAppError("NewWebSocketClient", "model.websocket_client.connect_fail.app_error", nil, err.Error())
} }
return &WebSocketClient{ client := &WebSocketClient{
url, url,
url + API_URL_SUFFIX, url + API_URL_SUFFIX,
conn, conn,
@ -37,19 +35,26 @@ func NewWebSocketClient(url, authToken string) (*WebSocketClient, *AppError) {
1, 1,
make(chan *WebSocketEvent, 100), make(chan *WebSocketEvent, 100),
make(chan *WebSocketResponse, 100), make(chan *WebSocketResponse, 100),
}, nil nil,
}
client.SendMessage(WEBSOCKET_AUTHENTICATION_CHALLENGE, map[string]interface{}{"token": authToken})
return client, nil
} }
func (wsc *WebSocketClient) Connect() *AppError { func (wsc *WebSocketClient) Connect() *AppError {
header := http.Header{}
header.Set(HEADER_AUTH, "BEARER "+wsc.AuthToken)
var err error var err error
wsc.Conn, _, err = websocket.DefaultDialer.Dial(wsc.ApiUrl+"/users/websocket", header) wsc.Conn, _, err = websocket.DefaultDialer.Dial(wsc.ApiUrl+"/users/websocket", nil)
if err != nil { if err != nil {
return NewLocAppError("NewWebSocketClient", "model.websocket_client.connect_fail.app_error", nil, err.Error()) return NewLocAppError("NewWebSocketClient", "model.websocket_client.connect_fail.app_error", nil, err.Error())
} }
wsc.EventChannel = make(chan *WebSocketEvent, 100)
wsc.ResponseChannel = make(chan *WebSocketResponse, 100)
wsc.SendMessage(WEBSOCKET_AUTHENTICATION_CHALLENGE, map[string]interface{}{"token": wsc.AuthToken})
return nil return nil
} }
@ -59,10 +64,20 @@ func (wsc *WebSocketClient) Close() {
func (wsc *WebSocketClient) Listen() { func (wsc *WebSocketClient) Listen() {
go func() { go func() {
defer func() {
wsc.Conn.Close()
close(wsc.EventChannel)
close(wsc.ResponseChannel)
}()
for { for {
var rawMsg json.RawMessage var rawMsg json.RawMessage
var err error var err error
if _, rawMsg, err = wsc.Conn.ReadMessage(); err != nil { if _, rawMsg, err = wsc.Conn.ReadMessage(); err != nil {
if !websocket.IsCloseError(err, websocket.CloseNormalClosure, websocket.CloseNoStatusReceived) {
wsc.ListenError = NewLocAppError("NewWebSocketClient", "model.websocket_client.connect_fail.app_error", nil, err.Error())
}
return return
} }
@ -77,6 +92,7 @@ func (wsc *WebSocketClient) Listen() {
wsc.ResponseChannel <- &response wsc.ResponseChannel <- &response
continue continue
} }
} }
}() }()
} }
@ -107,3 +123,12 @@ func (wsc *WebSocketClient) UserTyping(channelId, parentId string) {
func (wsc *WebSocketClient) GetStatuses() { func (wsc *WebSocketClient) GetStatuses() {
wsc.SendMessage("get_statuses", nil) wsc.SendMessage("get_statuses", nil)
} }
// GetStatusesByIds will fetch certain user statuses based on ids and return
// a map of string statuses using user id as the key
func (wsc *WebSocketClient) GetStatusesByIds(userIds []string) {
data := map[string]interface{}{
"user_ids": userIds,
}
wsc.SendMessage("get_statuses_by_ids", data)
}

View File

@ -25,33 +25,57 @@ const (
WEBSOCKET_EVENT_EPHEMERAL_MESSAGE = "ephemeral_message" WEBSOCKET_EVENT_EPHEMERAL_MESSAGE = "ephemeral_message"
WEBSOCKET_EVENT_STATUS_CHANGE = "status_change" WEBSOCKET_EVENT_STATUS_CHANGE = "status_change"
WEBSOCKET_EVENT_HELLO = "hello" WEBSOCKET_EVENT_HELLO = "hello"
WEBSOCKET_EVENT_WEBRTC = "webrtc"
WEBSOCKET_AUTHENTICATION_CHALLENGE = "authentication_challenge"
) )
type WebSocketMessage interface { type WebSocketMessage interface {
ToJson() string ToJson() string
IsValid() bool IsValid() bool
DoPreComputeJson()
GetPreComputeJson() []byte
}
type WebsocketBroadcast struct {
OmitUsers map[string]bool `json:"omit_users"` // broadcast is omitted for users listed here
UserId string `json:"user_id"` // broadcast only occurs for this user
ChannelId string `json:"channel_id"` // broadcast only occurs for users in this channel
TeamId string `json:"team_id"` // broadcast only occurs for users in this team
} }
type WebSocketEvent struct { type WebSocketEvent struct {
TeamId string `json:"team_id"` Event string `json:"event"`
ChannelId string `json:"channel_id"` Data map[string]interface{} `json:"data"`
UserId string `json:"user_id"` Broadcast *WebsocketBroadcast `json:"broadcast"`
Event string `json:"event"` PreComputeJson []byte `json:"-"`
Data map[string]interface{} `json:"data"`
} }
func (m *WebSocketEvent) Add(key string, value interface{}) { func (m *WebSocketEvent) Add(key string, value interface{}) {
m.Data[key] = value m.Data[key] = value
} }
func NewWebSocketEvent(teamId string, channelId string, userId string, event string) *WebSocketEvent { func NewWebSocketEvent(event, teamId, channelId, userId string, omitUsers map[string]bool) *WebSocketEvent {
return &WebSocketEvent{TeamId: teamId, ChannelId: channelId, UserId: userId, Event: event, Data: make(map[string]interface{})} return &WebSocketEvent{Event: event, Data: make(map[string]interface{}),
Broadcast: &WebsocketBroadcast{TeamId: teamId, ChannelId: channelId, UserId: userId, OmitUsers: omitUsers}}
} }
func (o *WebSocketEvent) IsValid() bool { func (o *WebSocketEvent) IsValid() bool {
return o.Event != "" return o.Event != ""
} }
func (o *WebSocketEvent) DoPreComputeJson() {
b, err := json.Marshal(o)
if err != nil {
o.PreComputeJson = []byte("")
} else {
o.PreComputeJson = b
}
}
func (o *WebSocketEvent) GetPreComputeJson() []byte {
return o.PreComputeJson
}
func (o *WebSocketEvent) ToJson() string { func (o *WebSocketEvent) ToJson() string {
b, err := json.Marshal(o) b, err := json.Marshal(o)
if err != nil { if err != nil {
@ -73,10 +97,11 @@ func WebSocketEventFromJson(data io.Reader) *WebSocketEvent {
} }
type WebSocketResponse struct { type WebSocketResponse struct {
Status string `json:"status"` Status string `json:"status"`
SeqReply int64 `json:"seq_reply,omitempty"` SeqReply int64 `json:"seq_reply,omitempty"`
Data map[string]interface{} `json:"data,omitempty"` Data map[string]interface{} `json:"data,omitempty"`
Error *AppError `json:"error,omitempty"` Error *AppError `json:"error,omitempty"`
PreComputeJson []byte `json:"-"`
} }
func (m *WebSocketResponse) Add(key string, value interface{}) { func (m *WebSocketResponse) Add(key string, value interface{}) {
@ -104,6 +129,19 @@ func (o *WebSocketResponse) ToJson() string {
} }
} }
func (o *WebSocketResponse) DoPreComputeJson() {
b, err := json.Marshal(o)
if err != nil {
o.PreComputeJson = []byte("")
} else {
o.PreComputeJson = b
}
}
func (o *WebSocketResponse) GetPreComputeJson() []byte {
return o.PreComputeJson
}
func WebSocketResponseFromJson(data io.Reader) *WebSocketResponse { func WebSocketResponseFromJson(data io.Reader) *WebSocketResponse {
decoder := json.NewDecoder(data) decoder := json.NewDecoder(data)
var o WebSocketResponse var o WebSocketResponse

8
vendor/manifest vendored
View File

@ -87,8 +87,8 @@
"importpath": "github.com/mattermost/platform/einterfaces", "importpath": "github.com/mattermost/platform/einterfaces",
"repository": "https://github.com/mattermost/platform", "repository": "https://github.com/mattermost/platform",
"vcs": "git", "vcs": "git",
"revision": "57f25fa59c71821cc38fd220b133aa6a40815e12", "revision": "b55ec6148caa93d54b660afe55408c643d217108",
"branch": "release-3.4", "branch": "release-3.5",
"path": "/einterfaces", "path": "/einterfaces",
"notests": true "notests": true
}, },
@ -96,8 +96,8 @@
"importpath": "github.com/mattermost/platform/model", "importpath": "github.com/mattermost/platform/model",
"repository": "https://github.com/mattermost/platform", "repository": "https://github.com/mattermost/platform",
"vcs": "git", "vcs": "git",
"revision": "57f25fa59c71821cc38fd220b133aa6a40815e12", "revision": "b55ec6148caa93d54b660afe55408c643d217108",
"branch": "release-3.4", "branch": "release-3.5",
"path": "/model", "path": "/model",
"notests": true "notests": true
}, },