github.com/mattermosttest/mattermost-server/v5@v5.0.0-20200917143240-9dfa12e121f9/app/bot.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package app
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"io"
    10  	"mime/multipart"
    11  	"net/http"
    12  
    13  	"github.com/mattermost/mattermost-server/v5/mlog"
    14  	"github.com/mattermost/mattermost-server/v5/model"
    15  	"github.com/mattermost/mattermost-server/v5/store"
    16  	"github.com/mattermost/mattermost-server/v5/utils"
    17  )
    18  
    19  // CreateBot creates the given bot and corresponding user.
    20  func (a *App) CreateBot(bot *model.Bot) (*model.Bot, *model.AppError) {
    21  	user, err := a.Srv().Store.User().Save(model.UserFromBot(bot))
    22  	if err != nil {
    23  		return nil, err
    24  	}
    25  	bot.UserId = user.Id
    26  
    27  	savedBot, nErr := a.Srv().Store.Bot().Save(bot)
    28  	if nErr != nil {
    29  		a.Srv().Store.User().PermanentDelete(bot.UserId)
    30  		var appErr *model.AppError
    31  		switch {
    32  		case errors.As(nErr, &appErr): // in case we haven't converted to plain error.
    33  			return nil, appErr
    34  		default: // last fallback in case it doesn't map to an existing app error.
    35  			return nil, model.NewAppError("CreateBot", "app.bot.createbot.internal_error", nil, nErr.Error(), http.StatusInternalServerError)
    36  		}
    37  	}
    38  
    39  	// Get the owner of the bot, if one exists. If not, don't send a message
    40  	ownerUser, err := a.Srv().Store.User().Get(bot.OwnerId)
    41  	if err != nil && err.Id != store.MISSING_ACCOUNT_ERROR {
    42  		mlog.Error(err.Error())
    43  		return nil, err
    44  	} else if ownerUser != nil {
    45  		// Send a message to the bot's creator to inform them that the bot needs to be added
    46  		// to a team and channel after it's created
    47  		channel, err := a.GetOrCreateDirectChannel(savedBot.UserId, bot.OwnerId)
    48  		if err != nil {
    49  			return nil, err
    50  		}
    51  
    52  		T := utils.GetUserTranslations(ownerUser.Locale)
    53  		botAddPost := &model.Post{
    54  			Type:      model.POST_ADD_BOT_TEAMS_CHANNELS,
    55  			UserId:    savedBot.UserId,
    56  			ChannelId: channel.Id,
    57  			Message:   T("api.bot.teams_channels.add_message_mobile"),
    58  		}
    59  
    60  		if _, err := a.CreatePostAsUser(botAddPost, a.Session().Id, true); err != nil {
    61  			return nil, err
    62  		}
    63  	}
    64  
    65  	return savedBot, nil
    66  }
    67  
    68  func (a *App) getOrCreateWarnMetricsBot(botDef *model.Bot) (*model.Bot, *model.AppError) {
    69  	botUser, appErr := a.GetUserByUsername(botDef.Username)
    70  	if appErr != nil {
    71  		if appErr.StatusCode != http.StatusNotFound {
    72  			mlog.Error(appErr.Error())
    73  			return nil, appErr
    74  		}
    75  
    76  		// cannot find this bot user, save the user
    77  		user, err := a.Srv().Store.User().Save(model.UserFromBot(botDef))
    78  		if err != nil {
    79  			mlog.Error(err.Error())
    80  			return nil, err
    81  		}
    82  		botDef.UserId = user.Id
    83  
    84  		//save the bot
    85  		savedBot, nErr := a.Srv().Store.Bot().Save(botDef)
    86  		if nErr != nil {
    87  			a.Srv().Store.User().PermanentDelete(savedBot.UserId)
    88  			var nAppErr *model.AppError
    89  			switch {
    90  			case errors.As(nErr, &nAppErr): // in case we haven't converted to plain error.
    91  				return nil, nAppErr
    92  			default: // last fallback in case it doesn't map to an existing app error.
    93  				return nil, model.NewAppError("getOrCreateWarnMetricsBot", "app.bot.createbot.internal_error", nil, nErr.Error(), http.StatusInternalServerError)
    94  			}
    95  		}
    96  		return savedBot, nil
    97  	}
    98  
    99  	if botUser == nil {
   100  		return nil, model.NewAppError("getOrCreateWarnMetricsBot", "app.bot.createbot.internal_error", nil, "", http.StatusInternalServerError)
   101  	}
   102  
   103  	//return the bot for this user
   104  	savedBot, appErr := a.GetBot(botUser.Id, false)
   105  	if appErr != nil {
   106  		mlog.Error(appErr.Error())
   107  		return nil, appErr
   108  	}
   109  
   110  	return savedBot, nil
   111  }
   112  
   113  // PatchBot applies the given patch to the bot and corresponding user.
   114  func (a *App) PatchBot(botUserId string, botPatch *model.BotPatch) (*model.Bot, *model.AppError) {
   115  	bot, err := a.GetBot(botUserId, true)
   116  	if err != nil {
   117  		return nil, err
   118  	}
   119  
   120  	bot.Patch(botPatch)
   121  
   122  	user, err := a.Srv().Store.User().Get(botUserId)
   123  	if err != nil {
   124  		return nil, err
   125  	}
   126  
   127  	patchedUser := model.UserFromBot(bot)
   128  	user.Id = patchedUser.Id
   129  	user.Username = patchedUser.Username
   130  	user.Email = patchedUser.Email
   131  	user.FirstName = patchedUser.FirstName
   132  
   133  	userUpdate, err := a.Srv().Store.User().Update(user, true)
   134  	if err != nil {
   135  		return nil, err
   136  	}
   137  	a.InvalidateCacheForUser(user.Id)
   138  
   139  	ruser := userUpdate.New
   140  	a.sendUpdatedUserEvent(*ruser)
   141  
   142  	bot, nErr := a.Srv().Store.Bot().Update(bot)
   143  	if nErr != nil {
   144  		var nfErr *store.ErrNotFound
   145  		var appErr *model.AppError
   146  		switch {
   147  		case errors.As(nErr, &nfErr):
   148  			return nil, model.MakeBotNotFoundError(nfErr.Id)
   149  		case errors.As(nErr, &appErr): // in case we haven't converted to plain error.
   150  			return nil, appErr
   151  		default: // last fallback in case it doesn't map to an existing app error.
   152  			return nil, model.NewAppError("PatchBot", "app.bot.patchbot.internal_error", nil, nErr.Error(), http.StatusInternalServerError)
   153  		}
   154  	}
   155  	return bot, nil
   156  }
   157  
   158  // GetBot returns the given bot.
   159  func (a *App) GetBot(botUserId string, includeDeleted bool) (*model.Bot, *model.AppError) {
   160  	bot, err := a.Srv().Store.Bot().Get(botUserId, includeDeleted)
   161  	if err != nil {
   162  		var nfErr *store.ErrNotFound
   163  		switch {
   164  		case errors.As(err, &nfErr):
   165  			return nil, model.MakeBotNotFoundError(nfErr.Id)
   166  		default: // last fallback in case it doesn't map to an existing app error.
   167  			return nil, model.NewAppError("GetBot", "app.bot.getbot.internal_error", nil, err.Error(), http.StatusInternalServerError)
   168  		}
   169  	}
   170  	return bot, nil
   171  }
   172  
   173  // GetBots returns the requested page of bots.
   174  func (a *App) GetBots(options *model.BotGetOptions) (model.BotList, *model.AppError) {
   175  	bots, err := a.Srv().Store.Bot().GetAll(options)
   176  	if err != nil {
   177  		return nil, model.NewAppError("GetBots", "app.bot.getbots.internal_error", nil, err.Error(), http.StatusInternalServerError)
   178  	}
   179  	return bots, nil
   180  }
   181  
   182  // UpdateBotActive marks a bot as active or inactive, along with its corresponding user.
   183  func (a *App) UpdateBotActive(botUserId string, active bool) (*model.Bot, *model.AppError) {
   184  	user, err := a.Srv().Store.User().Get(botUserId)
   185  	if err != nil {
   186  		return nil, err
   187  	}
   188  
   189  	if _, err = a.UpdateActive(user, active); err != nil {
   190  		return nil, err
   191  	}
   192  
   193  	bot, nErr := a.Srv().Store.Bot().Get(botUserId, true)
   194  	if nErr != nil {
   195  		var nfErr *store.ErrNotFound
   196  		switch {
   197  		case errors.As(nErr, &nfErr):
   198  			return nil, model.MakeBotNotFoundError(nfErr.Id)
   199  		default: // last fallback in case it doesn't map to an existing app error.
   200  			return nil, model.NewAppError("UpdateBotActive", "app.bot.getbot.internal_error", nil, nErr.Error(), http.StatusInternalServerError)
   201  		}
   202  	}
   203  
   204  	changed := true
   205  	if active && bot.DeleteAt != 0 {
   206  		bot.DeleteAt = 0
   207  	} else if !active && bot.DeleteAt == 0 {
   208  		bot.DeleteAt = model.GetMillis()
   209  	} else {
   210  		changed = false
   211  	}
   212  
   213  	if changed {
   214  		bot, nErr = a.Srv().Store.Bot().Update(bot)
   215  		if nErr != nil {
   216  			var nfErr *store.ErrNotFound
   217  			var appErr *model.AppError
   218  			switch {
   219  			case errors.As(nErr, &nfErr):
   220  				return nil, model.MakeBotNotFoundError(nfErr.Id)
   221  			case errors.As(nErr, &appErr): // in case we haven't converted to plain error.
   222  				return nil, appErr
   223  			default: // last fallback in case it doesn't map to an existing app error.
   224  				return nil, model.NewAppError("PatchBot", "app.bot.patchbot.internal_error", nil, nErr.Error(), http.StatusInternalServerError)
   225  			}
   226  		}
   227  	}
   228  
   229  	return bot, nil
   230  }
   231  
   232  // PermanentDeleteBot permanently deletes a bot and its corresponding user.
   233  func (a *App) PermanentDeleteBot(botUserId string) *model.AppError {
   234  	if err := a.Srv().Store.Bot().PermanentDelete(botUserId); err != nil {
   235  		var invErr *store.ErrInvalidInput
   236  		switch {
   237  		case errors.As(err, &invErr):
   238  			return model.NewAppError("PermanentDeleteBot", "app.bot.permenent_delete.bad_id", map[string]interface{}{"user_id": invErr.Value}, invErr.Error(), http.StatusBadRequest)
   239  		default: // last fallback in case it doesn't map to an existing app error.
   240  			return model.NewAppError("PatchBot", "app.bot.permanent_delete.internal_error", nil, err.Error(), http.StatusInternalServerError)
   241  		}
   242  	}
   243  
   244  	if err := a.Srv().Store.User().PermanentDelete(botUserId); err != nil {
   245  		return err
   246  	}
   247  
   248  	return nil
   249  }
   250  
   251  // UpdateBotOwner changes a bot's owner to the given value.
   252  func (a *App) UpdateBotOwner(botUserId, newOwnerId string) (*model.Bot, *model.AppError) {
   253  	bot, err := a.Srv().Store.Bot().Get(botUserId, true)
   254  	if err != nil {
   255  		var nfErr *store.ErrNotFound
   256  		switch {
   257  		case errors.As(err, &nfErr):
   258  			return nil, model.MakeBotNotFoundError(nfErr.Id)
   259  		default: // last fallback in case it doesn't map to an existing app error.
   260  			return nil, model.NewAppError("UpdateBotOwner", "app.bot.getbot.internal_error", nil, err.Error(), http.StatusInternalServerError)
   261  		}
   262  	}
   263  
   264  	bot.OwnerId = newOwnerId
   265  
   266  	bot, err = a.Srv().Store.Bot().Update(bot)
   267  	if err != nil {
   268  		var nfErr *store.ErrNotFound
   269  		var appErr *model.AppError
   270  		switch {
   271  		case errors.As(err, &nfErr):
   272  			return nil, model.MakeBotNotFoundError(nfErr.Id)
   273  		case errors.As(err, &appErr): // in case we haven't converted to plain error.
   274  			return nil, appErr
   275  		default: // last fallback in case it doesn't map to an existing app error.
   276  			return nil, model.NewAppError("PatchBot", "app.bot.patchbot.internal_error", nil, err.Error(), http.StatusInternalServerError)
   277  		}
   278  	}
   279  
   280  	return bot, nil
   281  }
   282  
   283  // disableUserBots disables all bots owned by the given user.
   284  func (a *App) disableUserBots(userId string) *model.AppError {
   285  	perPage := 20
   286  	for {
   287  		options := &model.BotGetOptions{
   288  			OwnerId:        userId,
   289  			IncludeDeleted: false,
   290  			OnlyOrphaned:   false,
   291  			Page:           0,
   292  			PerPage:        perPage,
   293  		}
   294  		userBots, err := a.GetBots(options)
   295  		if err != nil {
   296  			return err
   297  		}
   298  
   299  		for _, bot := range userBots {
   300  			_, err := a.UpdateBotActive(bot.UserId, false)
   301  			if err != nil {
   302  				mlog.Error("Unable to deactivate bot.", mlog.String("bot_user_id", bot.UserId), mlog.Err(err))
   303  			}
   304  		}
   305  
   306  		// Get next set of bots if we got the max number of bots
   307  		if len(userBots) == perPage {
   308  			options.Page += 1
   309  			continue
   310  		}
   311  		break
   312  	}
   313  
   314  	return nil
   315  }
   316  
   317  func (a *App) notifySysadminsBotOwnerDeactivated(userId string) *model.AppError {
   318  	perPage := 25
   319  	botOptions := &model.BotGetOptions{
   320  		OwnerId:        userId,
   321  		IncludeDeleted: false,
   322  		OnlyOrphaned:   false,
   323  		Page:           0,
   324  		PerPage:        perPage,
   325  	}
   326  	// get owner bots
   327  	var userBots []*model.Bot
   328  	for {
   329  		bots, err := a.GetBots(botOptions)
   330  		if err != nil {
   331  			return err
   332  		}
   333  
   334  		userBots = append(userBots, bots...)
   335  
   336  		if len(bots) < perPage {
   337  			break
   338  		}
   339  
   340  		botOptions.Page += 1
   341  	}
   342  
   343  	// user does not own bots
   344  	if len(userBots) == 0 {
   345  		return nil
   346  	}
   347  
   348  	userOptions := &model.UserGetOptions{
   349  		Page:     0,
   350  		PerPage:  perPage,
   351  		Role:     model.SYSTEM_ADMIN_ROLE_ID,
   352  		Inactive: false,
   353  	}
   354  	// get sysadmins
   355  	var sysAdmins []*model.User
   356  	for {
   357  		sysAdminsList, err := a.GetUsers(userOptions)
   358  		if err != nil {
   359  			return err
   360  		}
   361  
   362  		sysAdmins = append(sysAdmins, sysAdminsList...)
   363  
   364  		if len(sysAdminsList) < perPage {
   365  			break
   366  		}
   367  
   368  		userOptions.Page += 1
   369  	}
   370  
   371  	// user being disabled
   372  	user, err := a.GetUser(userId)
   373  	if err != nil {
   374  		return err
   375  	}
   376  
   377  	// for each sysadmin, notify user that owns bots was disabled
   378  	for _, sysAdmin := range sysAdmins {
   379  		channel, appErr := a.GetOrCreateDirectChannel(sysAdmin.Id, sysAdmin.Id)
   380  		if appErr != nil {
   381  			return appErr
   382  		}
   383  
   384  		post := &model.Post{
   385  			UserId:    sysAdmin.Id,
   386  			ChannelId: channel.Id,
   387  			Message:   a.getDisableBotSysadminMessage(user, userBots),
   388  			Type:      model.POST_SYSTEM_GENERIC,
   389  		}
   390  
   391  		_, appErr = a.CreatePost(post, channel, false, true)
   392  		if appErr != nil {
   393  			return appErr
   394  		}
   395  	}
   396  	return nil
   397  }
   398  
   399  func (a *App) getDisableBotSysadminMessage(user *model.User, userBots model.BotList) string {
   400  	disableBotsSetting := *a.Config().ServiceSettings.DisableBotsWhenOwnerIsDeactivated
   401  
   402  	var printAllBots = true
   403  	numBotsToPrint := len(userBots)
   404  
   405  	if numBotsToPrint > 10 {
   406  		numBotsToPrint = 10
   407  		printAllBots = false
   408  	}
   409  
   410  	var message, botList string
   411  	for _, bot := range userBots[:numBotsToPrint] {
   412  		botList += fmt.Sprintf("* %v\n", bot.Username)
   413  	}
   414  
   415  	T := utils.GetUserTranslations(user.Locale)
   416  	message = T("app.bot.get_disable_bot_sysadmin_message",
   417  		map[string]interface{}{
   418  			"UserName":           user.Username,
   419  			"NumBots":            len(userBots),
   420  			"BotNames":           botList,
   421  			"disableBotsSetting": disableBotsSetting,
   422  			"printAllBots":       printAllBots,
   423  		})
   424  
   425  	return message
   426  }
   427  
   428  // ConvertUserToBot converts a user to bot.
   429  func (a *App) ConvertUserToBot(user *model.User) (*model.Bot, *model.AppError) {
   430  	bot, err := a.Srv().Store.Bot().Save(model.BotFromUser(user))
   431  	if err != nil {
   432  		var appErr *model.AppError
   433  		switch {
   434  		case errors.As(err, &appErr): // in case we haven't converted to plain error.
   435  			return nil, appErr
   436  		default: // last fallback in case it doesn't map to an existing app error.
   437  			return nil, model.NewAppError("CreateBot", "app.bot.createbot.internal_error", nil, err.Error(), http.StatusInternalServerError)
   438  		}
   439  	}
   440  	return bot, nil
   441  }
   442  
   443  // SetBotIconImageFromMultiPartFile sets LHS icon for a bot.
   444  func (a *App) SetBotIconImageFromMultiPartFile(botUserId string, imageData *multipart.FileHeader) *model.AppError {
   445  	file, err := imageData.Open()
   446  	if err != nil {
   447  		return model.NewAppError("SetBotIconImage", "api.bot.set_bot_icon_image.open.app_error", nil, err.Error(), http.StatusBadRequest)
   448  	}
   449  	defer file.Close()
   450  
   451  	file.Seek(0, 0)
   452  	return a.SetBotIconImage(botUserId, file)
   453  }
   454  
   455  // SetBotIconImage sets LHS icon for a bot.
   456  func (a *App) SetBotIconImage(botUserId string, file io.ReadSeeker) *model.AppError {
   457  	bot, err := a.GetBot(botUserId, true)
   458  	if err != nil {
   459  		return err
   460  	}
   461  
   462  	if _, err := parseSVG(file); err != nil {
   463  		return model.NewAppError("SetBotIconImage", "api.bot.set_bot_icon_image.parse.app_error", nil, err.Error(), http.StatusBadRequest)
   464  	}
   465  
   466  	// Set icon
   467  	file.Seek(0, 0)
   468  	if _, err = a.WriteFile(file, getBotIconPath(botUserId)); err != nil {
   469  		return model.NewAppError("SetBotIconImage", "api.bot.set_bot_icon_image.app_error", nil, err.Error(), http.StatusInternalServerError)
   470  	}
   471  
   472  	bot.LastIconUpdate = model.GetMillis()
   473  	if _, err := a.Srv().Store.Bot().Update(bot); err != nil {
   474  		var nfErr *store.ErrNotFound
   475  		var appErr *model.AppError
   476  		switch {
   477  		case errors.As(err, &nfErr):
   478  			return model.MakeBotNotFoundError(nfErr.Id)
   479  		case errors.As(err, &appErr): // in case we haven't converted to plain error.
   480  			return appErr
   481  		default: // last fallback in case it doesn't map to an existing app error.
   482  			return model.NewAppError("SetBotIconImage", "app.bot.patchbot.internal_error", nil, err.Error(), http.StatusInternalServerError)
   483  		}
   484  	}
   485  	a.invalidateUserCacheAndPublish(botUserId)
   486  
   487  	return nil
   488  }
   489  
   490  // DeleteBotIconImage deletes LHS icon for a bot.
   491  func (a *App) DeleteBotIconImage(botUserId string) *model.AppError {
   492  	bot, err := a.GetBot(botUserId, true)
   493  	if err != nil {
   494  		return err
   495  	}
   496  
   497  	// Delete icon
   498  	if err = a.RemoveFile(getBotIconPath(botUserId)); err != nil {
   499  		return model.NewAppError("DeleteBotIconImage", "api.bot.delete_bot_icon_image.app_error", nil, err.Error(), http.StatusInternalServerError)
   500  	}
   501  
   502  	if err = a.Srv().Store.User().UpdateLastPictureUpdate(botUserId); err != nil {
   503  		mlog.Error(err.Error())
   504  	}
   505  
   506  	bot.LastIconUpdate = int64(0)
   507  	if _, err := a.Srv().Store.Bot().Update(bot); err != nil {
   508  		var nfErr *store.ErrNotFound
   509  		var appErr *model.AppError
   510  		switch {
   511  		case errors.As(err, &nfErr):
   512  			return model.MakeBotNotFoundError(nfErr.Id)
   513  		case errors.As(err, &appErr): // in case we haven't converted to plain error.
   514  			return appErr
   515  		default: // last fallback in case it doesn't map to an existing app error.
   516  			return model.NewAppError("DeleteBotIconImage", "app.bot.patchbot.internal_error", nil, err.Error(), http.StatusInternalServerError)
   517  		}
   518  	}
   519  
   520  	a.invalidateUserCacheAndPublish(botUserId)
   521  
   522  	return nil
   523  }
   524  
   525  // GetBotIconImage retrieves LHS icon for a bot.
   526  func (a *App) GetBotIconImage(botUserId string) ([]byte, *model.AppError) {
   527  	if _, err := a.GetBot(botUserId, true); err != nil {
   528  		return nil, err
   529  	}
   530  
   531  	data, err := a.ReadFile(getBotIconPath(botUserId))
   532  	if err != nil {
   533  		return nil, model.NewAppError("GetBotIconImage", "api.bot.get_bot_icon_image.read.app_error", nil, err.Error(), http.StatusNotFound)
   534  	}
   535  
   536  	return data, nil
   537  }
   538  
   539  func getBotIconPath(botUserId string) string {
   540  	return fmt.Sprintf("bots/%v/icon.svg", botUserId)
   541  }