diff options
Diffstat (limited to 'app')
-rw-r--r-- | app/plugin.go (renamed from app/plugins.go) | 269 | ||||
-rw-r--r-- | app/plugin_api.go | 260 | ||||
-rw-r--r-- | app/plugin_test.go | 35 |
3 files changed, 341 insertions, 223 deletions
diff --git a/app/plugins.go b/app/plugin.go index 8f81086df..4645eb9fb 100644 --- a/app/plugins.go +++ b/app/plugin.go @@ -5,7 +5,6 @@ package app import ( "context" - "encoding/json" "io" "io/ioutil" "net/http" @@ -27,228 +26,6 @@ import ( "github.com/mattermost/mattermost-server/plugin/pluginenv" ) -type PluginAPI struct { - id string - app *App -} - -func (api *PluginAPI) LoadPluginConfiguration(dest interface{}) error { - if b, err := json.Marshal(api.app.Config().PluginSettings.Plugins[api.id]); err != nil { - return err - } else { - return json.Unmarshal(b, dest) - } -} - -func (api *PluginAPI) CreateTeam(team *model.Team) (*model.Team, *model.AppError) { - return api.app.CreateTeam(team) -} - -func (api *PluginAPI) DeleteTeam(teamId string) *model.AppError { - return api.app.SoftDeleteTeam(teamId) -} - -func (api *PluginAPI) GetTeam(teamId string) (*model.Team, *model.AppError) { - return api.app.GetTeam(teamId) -} - -func (api *PluginAPI) GetTeamByName(name string) (*model.Team, *model.AppError) { - return api.app.GetTeamByName(name) -} - -func (api *PluginAPI) UpdateTeam(team *model.Team) (*model.Team, *model.AppError) { - return api.app.UpdateTeam(team) -} - -func (api *PluginAPI) CreateUser(user *model.User) (*model.User, *model.AppError) { - return api.app.CreateUser(user) -} - -func (api *PluginAPI) DeleteUser(userId string) *model.AppError { - user, err := api.app.GetUser(userId) - if err != nil { - return err - } - _, err = api.app.UpdateActive(user, false) - return err -} - -func (api *PluginAPI) GetUser(userId string) (*model.User, *model.AppError) { - return api.app.GetUser(userId) -} - -func (api *PluginAPI) GetUserByEmail(email string) (*model.User, *model.AppError) { - return api.app.GetUserByEmail(email) -} - -func (api *PluginAPI) GetUserByUsername(name string) (*model.User, *model.AppError) { - return api.app.GetUserByUsername(name) -} - -func (api *PluginAPI) UpdateUser(user *model.User) (*model.User, *model.AppError) { - return api.app.UpdateUser(user, true) -} - -func (api *PluginAPI) CreateChannel(channel *model.Channel) (*model.Channel, *model.AppError) { - return api.app.CreateChannel(channel, false) -} - -func (api *PluginAPI) DeleteChannel(channelId string) *model.AppError { - channel, err := api.app.GetChannel(channelId) - if err != nil { - return err - } - return api.app.DeleteChannel(channel, "") -} - -func (api *PluginAPI) GetChannel(channelId string) (*model.Channel, *model.AppError) { - return api.app.GetChannel(channelId) -} - -func (api *PluginAPI) GetChannelByName(name, teamId string) (*model.Channel, *model.AppError) { - return api.app.GetChannelByName(name, teamId) -} - -func (api *PluginAPI) GetDirectChannel(userId1, userId2 string) (*model.Channel, *model.AppError) { - return api.app.GetDirectChannel(userId1, userId2) -} - -func (api *PluginAPI) GetGroupChannel(userIds []string) (*model.Channel, *model.AppError) { - return api.app.CreateGroupChannel(userIds, "") -} - -func (api *PluginAPI) UpdateChannel(channel *model.Channel) (*model.Channel, *model.AppError) { - return api.app.UpdateChannel(channel) -} - -func (api *PluginAPI) CreatePost(post *model.Post) (*model.Post, *model.AppError) { - return api.app.CreatePostMissingChannel(post, true) -} - -func (api *PluginAPI) DeletePost(postId string) *model.AppError { - _, err := api.app.DeletePost(postId) - return err -} - -func (api *PluginAPI) GetPost(postId string) (*model.Post, *model.AppError) { - return api.app.GetSinglePost(postId) -} - -func (api *PluginAPI) UpdatePost(post *model.Post) (*model.Post, *model.AppError) { - return api.app.UpdatePost(post, false) -} - -type BuiltInPluginAPI struct { - id string - router *mux.Router - app *App -} - -func (api *BuiltInPluginAPI) LoadPluginConfiguration(dest interface{}) error { - if b, err := json.Marshal(api.app.Config().PluginSettings.Plugins[api.id]); err != nil { - return err - } else { - return json.Unmarshal(b, dest) - } -} - -func (api *BuiltInPluginAPI) PluginRouter() *mux.Router { - return api.router -} - -func (api *BuiltInPluginAPI) GetTeamByName(name string) (*model.Team, *model.AppError) { - return api.app.GetTeamByName(name) -} - -func (api *BuiltInPluginAPI) GetUserByName(name string) (*model.User, *model.AppError) { - return api.app.GetUserByUsername(name) -} - -func (api *BuiltInPluginAPI) GetChannelByName(teamId, name string) (*model.Channel, *model.AppError) { - return api.app.GetChannelByName(name, teamId) -} - -func (api *BuiltInPluginAPI) GetDirectChannel(userId1, userId2 string) (*model.Channel, *model.AppError) { - return api.app.GetDirectChannel(userId1, userId2) -} - -func (api *BuiltInPluginAPI) CreatePost(post *model.Post) (*model.Post, *model.AppError) { - return api.app.CreatePostMissingChannel(post, true) -} - -func (api *BuiltInPluginAPI) GetLdapUserAttributes(userId string, attributes []string) (map[string]string, *model.AppError) { - if api.app.Ldap == nil { - return nil, model.NewAppError("GetLdapUserAttributes", "ent.ldap.disabled.app_error", nil, "", http.StatusNotImplemented) - } - - user, err := api.app.GetUser(userId) - if err != nil { - return nil, err - } - - if user.AuthData == nil { - return map[string]string{}, nil - } - - return api.app.Ldap.GetUserAttributes(*user.AuthData, attributes) -} - -func (api *BuiltInPluginAPI) GetSessionFromRequest(r *http.Request) (*model.Session, *model.AppError) { - token := "" - isTokenFromQueryString := false - - // Attempt to parse token out of the header - authHeader := r.Header.Get(model.HEADER_AUTH) - if len(authHeader) > 6 && strings.ToUpper(authHeader[0:6]) == model.HEADER_BEARER { - // Default session token - token = authHeader[7:] - - } else if len(authHeader) > 5 && strings.ToLower(authHeader[0:5]) == model.HEADER_TOKEN { - // OAuth token - token = authHeader[6:] - } - - // Attempt to parse the token from the cookie - if len(token) == 0 { - if cookie, err := r.Cookie(model.SESSION_COOKIE_TOKEN); err == nil { - token = cookie.Value - - if r.Header.Get(model.HEADER_REQUESTED_WITH) != model.HEADER_REQUESTED_WITH_XML { - return nil, model.NewAppError("ServeHTTP", "api.context.session_expired.app_error", nil, "token="+token+" Appears to be a CSRF attempt", http.StatusUnauthorized) - } - } - } - - // Attempt to parse token out of the query string - if len(token) == 0 { - token = r.URL.Query().Get("access_token") - isTokenFromQueryString = true - } - - if len(token) == 0 { - return nil, model.NewAppError("ServeHTTP", "api.context.session_expired.app_error", nil, "token="+token, http.StatusUnauthorized) - } - - session, err := api.app.GetSession(token) - - if err != nil { - return nil, model.NewAppError("ServeHTTP", "api.context.session_expired.app_error", nil, "token="+token, http.StatusUnauthorized) - } else if !session.IsOAuth && isTokenFromQueryString { - return nil, model.NewAppError("ServeHTTP", "api.context.token_provided.app_error", nil, "token="+token, http.StatusUnauthorized) - } - - return session, nil -} - -func (api *BuiltInPluginAPI) I18n(id string, r *http.Request) string { - if r != nil { - f, _ := utils.GetTranslationsAndLocale(nil, r) - return f(id) - } - f, _ := utils.GetTranslationsBySystemLocale() - return f(id) -} - func (a *App) initBuiltInPlugins() { plugins := map[string]builtinplugin.Plugin{ "jira": &jira.Plugin{}, @@ -557,6 +334,10 @@ func (a *App) InitPlugins(pluginPath, webappPath string) { return &PluginAPI{ id: m.Id, app: a, + keyValueStore: &PluginKeyValueStore{ + id: m.Id, + app: a, + }, }, nil }), ) @@ -647,3 +428,45 @@ func (a *App) ShutDownPlugins() { a.PluginConfigListenerId = "" a.PluginEnv = nil } + +func (a *App) SetPluginKey(pluginId string, key string, value []byte) *model.AppError { + kv := &model.PluginKeyValue{ + PluginId: pluginId, + Key: key, + Value: value, + } + + result := <-a.Srv.Store.Plugin().SaveOrUpdate(kv) + + if result.Err != nil { + l4g.Error(result.Err.Error()) + } + + return result.Err +} + +func (a *App) GetPluginKey(pluginId string, key string) ([]byte, *model.AppError) { + result := <-a.Srv.Store.Plugin().Get(pluginId, key) + + if result.Err != nil { + if result.Err.StatusCode == http.StatusNotFound { + return nil, nil + } + l4g.Error(result.Err.Error()) + return nil, result.Err + } + + kv := result.Data.(*model.PluginKeyValue) + + return kv.Value, nil +} + +func (a *App) DeletePluginKey(pluginId string, key string) *model.AppError { + result := <-a.Srv.Store.Plugin().Delete(pluginId, key) + + if result.Err != nil { + l4g.Error(result.Err.Error()) + } + + return result.Err +} diff --git a/app/plugin_api.go b/app/plugin_api.go new file mode 100644 index 000000000..bfe86453f --- /dev/null +++ b/app/plugin_api.go @@ -0,0 +1,260 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package app + +import ( + "encoding/json" + "net/http" + "strings" + + "github.com/gorilla/mux" + "github.com/mattermost/mattermost-server/model" + "github.com/mattermost/mattermost-server/utils" + + "github.com/mattermost/mattermost-server/plugin" +) + +type PluginAPI struct { + id string + app *App + keyValueStore *PluginKeyValueStore +} + +type PluginKeyValueStore struct { + id string + app *App +} + +func (api *PluginAPI) LoadPluginConfiguration(dest interface{}) error { + if b, err := json.Marshal(api.app.Config().PluginSettings.Plugins[api.id]); err != nil { + return err + } else { + return json.Unmarshal(b, dest) + } +} + +func (api *PluginAPI) CreateTeam(team *model.Team) (*model.Team, *model.AppError) { + return api.app.CreateTeam(team) +} + +func (api *PluginAPI) DeleteTeam(teamId string) *model.AppError { + return api.app.SoftDeleteTeam(teamId) +} + +func (api *PluginAPI) GetTeam(teamId string) (*model.Team, *model.AppError) { + return api.app.GetTeam(teamId) +} + +func (api *PluginAPI) GetTeamByName(name string) (*model.Team, *model.AppError) { + return api.app.GetTeamByName(name) +} + +func (api *PluginAPI) UpdateTeam(team *model.Team) (*model.Team, *model.AppError) { + return api.app.UpdateTeam(team) +} + +func (api *PluginAPI) CreateUser(user *model.User) (*model.User, *model.AppError) { + return api.app.CreateUser(user) +} + +func (api *PluginAPI) DeleteUser(userId string) *model.AppError { + user, err := api.app.GetUser(userId) + if err != nil { + return err + } + _, err = api.app.UpdateActive(user, false) + return err +} + +func (api *PluginAPI) GetUser(userId string) (*model.User, *model.AppError) { + return api.app.GetUser(userId) +} + +func (api *PluginAPI) GetUserByEmail(email string) (*model.User, *model.AppError) { + return api.app.GetUserByEmail(email) +} + +func (api *PluginAPI) GetUserByUsername(name string) (*model.User, *model.AppError) { + return api.app.GetUserByUsername(name) +} + +func (api *PluginAPI) UpdateUser(user *model.User) (*model.User, *model.AppError) { + return api.app.UpdateUser(user, true) +} + +func (api *PluginAPI) CreateChannel(channel *model.Channel) (*model.Channel, *model.AppError) { + return api.app.CreateChannel(channel, false) +} + +func (api *PluginAPI) DeleteChannel(channelId string) *model.AppError { + channel, err := api.app.GetChannel(channelId) + if err != nil { + return err + } + return api.app.DeleteChannel(channel, "") +} + +func (api *PluginAPI) GetChannel(channelId string) (*model.Channel, *model.AppError) { + return api.app.GetChannel(channelId) +} + +func (api *PluginAPI) GetChannelByName(name, teamId string) (*model.Channel, *model.AppError) { + return api.app.GetChannelByName(name, teamId) +} + +func (api *PluginAPI) GetDirectChannel(userId1, userId2 string) (*model.Channel, *model.AppError) { + return api.app.GetDirectChannel(userId1, userId2) +} + +func (api *PluginAPI) GetGroupChannel(userIds []string) (*model.Channel, *model.AppError) { + return api.app.CreateGroupChannel(userIds, "") +} + +func (api *PluginAPI) UpdateChannel(channel *model.Channel) (*model.Channel, *model.AppError) { + return api.app.UpdateChannel(channel) +} + +func (api *PluginAPI) CreatePost(post *model.Post) (*model.Post, *model.AppError) { + return api.app.CreatePostMissingChannel(post, true) +} + +func (api *PluginAPI) DeletePost(postId string) *model.AppError { + _, err := api.app.DeletePost(postId) + return err +} + +func (api *PluginAPI) GetPost(postId string) (*model.Post, *model.AppError) { + return api.app.GetSinglePost(postId) +} + +func (api *PluginAPI) UpdatePost(post *model.Post) (*model.Post, *model.AppError) { + return api.app.UpdatePost(post, false) +} + +func (api *PluginAPI) KeyValueStore() plugin.KeyValueStore { + return api.keyValueStore +} + +func (s *PluginKeyValueStore) Set(key string, value []byte) *model.AppError { + return s.app.SetPluginKey(s.id, key, value) +} + +func (s *PluginKeyValueStore) Get(key string) ([]byte, *model.AppError) { + return s.app.GetPluginKey(s.id, key) +} + +func (s *PluginKeyValueStore) Delete(key string) *model.AppError { + return s.app.DeletePluginKey(s.id, key) +} + +type BuiltInPluginAPI struct { + id string + router *mux.Router + app *App +} + +func (api *BuiltInPluginAPI) LoadPluginConfiguration(dest interface{}) error { + if b, err := json.Marshal(api.app.Config().PluginSettings.Plugins[api.id]); err != nil { + return err + } else { + return json.Unmarshal(b, dest) + } +} + +func (api *BuiltInPluginAPI) PluginRouter() *mux.Router { + return api.router +} + +func (api *BuiltInPluginAPI) GetTeamByName(name string) (*model.Team, *model.AppError) { + return api.app.GetTeamByName(name) +} + +func (api *BuiltInPluginAPI) GetUserByName(name string) (*model.User, *model.AppError) { + return api.app.GetUserByUsername(name) +} + +func (api *BuiltInPluginAPI) GetChannelByName(teamId, name string) (*model.Channel, *model.AppError) { + return api.app.GetChannelByName(name, teamId) +} + +func (api *BuiltInPluginAPI) GetDirectChannel(userId1, userId2 string) (*model.Channel, *model.AppError) { + return api.app.GetDirectChannel(userId1, userId2) +} + +func (api *BuiltInPluginAPI) CreatePost(post *model.Post) (*model.Post, *model.AppError) { + return api.app.CreatePostMissingChannel(post, true) +} + +func (api *BuiltInPluginAPI) GetLdapUserAttributes(userId string, attributes []string) (map[string]string, *model.AppError) { + if api.app.Ldap == nil { + return nil, model.NewAppError("GetLdapUserAttributes", "ent.ldap.disabled.app_error", nil, "", http.StatusNotImplemented) + } + + user, err := api.app.GetUser(userId) + if err != nil { + return nil, err + } + + if user.AuthData == nil { + return map[string]string{}, nil + } + + return api.app.Ldap.GetUserAttributes(*user.AuthData, attributes) +} + +func (api *BuiltInPluginAPI) GetSessionFromRequest(r *http.Request) (*model.Session, *model.AppError) { + token := "" + isTokenFromQueryString := false + + // Attempt to parse token out of the header + authHeader := r.Header.Get(model.HEADER_AUTH) + if len(authHeader) > 6 && strings.ToUpper(authHeader[0:6]) == model.HEADER_BEARER { + // Default session token + token = authHeader[7:] + + } else if len(authHeader) > 5 && strings.ToLower(authHeader[0:5]) == model.HEADER_TOKEN { + // OAuth token + token = authHeader[6:] + } + + // Attempt to parse the token from the cookie + if len(token) == 0 { + if cookie, err := r.Cookie(model.SESSION_COOKIE_TOKEN); err == nil { + token = cookie.Value + + if r.Header.Get(model.HEADER_REQUESTED_WITH) != model.HEADER_REQUESTED_WITH_XML { + return nil, model.NewAppError("ServeHTTP", "api.context.session_expired.app_error", nil, "token="+token+" Appears to be a CSRF attempt", http.StatusUnauthorized) + } + } + } + + // Attempt to parse token out of the query string + if len(token) == 0 { + token = r.URL.Query().Get("access_token") + isTokenFromQueryString = true + } + + if len(token) == 0 { + return nil, model.NewAppError("ServeHTTP", "api.context.session_expired.app_error", nil, "token="+token, http.StatusUnauthorized) + } + + session, err := api.app.GetSession(token) + + if err != nil { + return nil, model.NewAppError("ServeHTTP", "api.context.session_expired.app_error", nil, "token="+token, http.StatusUnauthorized) + } else if !session.IsOAuth && isTokenFromQueryString { + return nil, model.NewAppError("ServeHTTP", "api.context.token_provided.app_error", nil, "token="+token, http.StatusUnauthorized) + } + + return session, nil +} + +func (api *BuiltInPluginAPI) I18n(id string, r *http.Request) string { + if r != nil { + f, _ := utils.GetTranslationsAndLocale(nil, r) + return f(id) + } + f, _ := utils.GetTranslationsBySystemLocale() + return f(id) +} diff --git a/app/plugin_test.go b/app/plugin_test.go new file mode 100644 index 000000000..a9d872401 --- /dev/null +++ b/app/plugin_test.go @@ -0,0 +1,35 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package app + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestPluginKeyValueStore(t *testing.T) { + th := Setup().InitBasic() + defer th.TearDown() + + pluginId := "testpluginid" + + assert.Nil(t, th.App.SetPluginKey(pluginId, "key", []byte("test"))) + ret, err := th.App.GetPluginKey(pluginId, "key") + assert.Nil(t, err) + assert.Equal(t, []byte("test"), ret) + + // Test inserting over existing entries + assert.Nil(t, th.App.SetPluginKey(pluginId, "key", []byte("test2"))) + + // Test getting non-existent key + ret, err = th.App.GetPluginKey(pluginId, "notakey") + assert.Nil(t, err) + assert.Nil(t, ret) + + assert.Nil(t, th.App.DeletePluginKey(pluginId, "stringkey")) + assert.Nil(t, th.App.DeletePluginKey(pluginId, "intkey")) + assert.Nil(t, th.App.DeletePluginKey(pluginId, "postkey")) + assert.Nil(t, th.App.DeletePluginKey(pluginId, "notrealkey")) +} |