2017-08-02 01:36:54 -07:00
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
import (
"encoding/json"
2017-09-01 09:00:27 -04:00
"io"
"io/ioutil"
2017-08-02 01:36:54 -07:00
"net/http"
2017-09-01 09:00:27 -04:00
"os"
"path/filepath"
"strings"
2017-08-02 01:36:54 -07:00
l4g "github.com/alecthomas/log4go"
"github.com/gorilla/mux"
2017-09-06 23:05:10 -07:00
"github.com/mattermost/mattermost-server/einterfaces"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/utils"
2017-08-02 01:36:54 -07:00
2017-09-06 23:05:10 -07:00
"github.com/mattermost/mattermost-server/app/plugin"
"github.com/mattermost/mattermost-server/app/plugin/jira"
"github.com/mattermost/mattermost-server/app/plugin/ldapextras"
2017-08-02 01:36:54 -07:00
)
type PluginAPI struct {
id string
router * mux . Router
}
func ( api * PluginAPI ) LoadPluginConfiguration ( dest interface { } ) error {
if b , err := json . Marshal ( utils . Cfg . PluginSettings . Plugins [ api . id ] ) ; err != nil {
return err
} else {
return json . Unmarshal ( b , dest )
}
}
func ( api * PluginAPI ) PluginRouter ( ) * mux . Router {
return api . router
}
func ( api * PluginAPI ) GetTeamByName ( name string ) ( * model . Team , * model . AppError ) {
2017-09-06 17:12:54 -05:00
return Global ( ) . GetTeamByName ( name )
2017-08-02 01:36:54 -07:00
}
func ( api * PluginAPI ) GetUserByName ( name string ) ( * model . User , * model . AppError ) {
2017-09-06 17:12:54 -05:00
return Global ( ) . GetUserByUsername ( name )
2017-08-02 01:36:54 -07:00
}
func ( api * PluginAPI ) GetChannelByName ( teamId , name string ) ( * model . Channel , * model . AppError ) {
2017-09-06 17:12:54 -05:00
return Global ( ) . GetChannelByName ( name , teamId )
2017-08-02 01:36:54 -07:00
}
func ( api * PluginAPI ) GetDirectChannel ( userId1 , userId2 string ) ( * model . Channel , * model . AppError ) {
2017-09-06 17:12:54 -05:00
return Global ( ) . GetDirectChannel ( userId1 , userId2 )
2017-08-02 01:36:54 -07:00
}
2017-08-28 07:08:37 -07:00
func ( api * PluginAPI ) CreatePost ( post * model . Post ) ( * model . Post , * model . AppError ) {
2017-09-06 17:12:54 -05:00
return Global ( ) . CreatePostMissingChannel ( post , true )
2017-08-02 01:36:54 -07:00
}
2017-09-01 14:28:15 -04:00
func ( api * PluginAPI ) GetLdapUserAttributes ( userId string , attributes [ ] string ) ( map [ string ] string , * model . AppError ) {
ldapInterface := einterfaces . GetLdapInterface ( )
if ldapInterface == nil {
return nil , model . NewAppError ( "GetLdapUserAttributes" , "ent.ldap.disabled.app_error" , nil , "" , http . StatusNotImplemented )
}
2017-09-06 17:12:54 -05:00
user , err := Global ( ) . GetUser ( userId )
2017-09-01 14:28:15 -04:00
if err != nil {
return nil , err
}
return ldapInterface . GetUserAttributes ( * user . AuthData , attributes )
}
func ( api * PluginAPI ) 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 )
}
2017-09-06 17:12:54 -05:00
session , err := Global ( ) . GetSession ( token )
2017-09-01 14:28:15 -04:00
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
}
2017-08-02 01:36:54 -07:00
func ( api * PluginAPI ) I18n ( id string , r * http . Request ) string {
if r != nil {
f , _ := utils . GetTranslationsAndLocale ( nil , r )
return f ( id )
}
f , _ := utils . GetTranslationsBySystemLocale ( )
return f ( id )
}
2017-09-06 17:12:54 -05:00
func ( a * App ) InitPlugins ( ) {
2017-08-02 01:36:54 -07:00
plugins := map [ string ] plugin . Plugin {
2017-09-01 14:28:15 -04:00
"jira" : & jira . Plugin { } ,
"ldapextras" : & ldapextras . Plugin { } ,
2017-08-02 01:36:54 -07:00
}
for id , p := range plugins {
l4g . Info ( "Initializing plugin: " + id )
api := & PluginAPI {
id : id ,
2017-09-06 17:12:54 -05:00
router : a . Srv . Router . PathPrefix ( "/plugins/" + id ) . Subrouter ( ) ,
2017-08-02 01:36:54 -07:00
}
p . Initialize ( api )
}
utils . AddConfigListener ( func ( before , after * model . Config ) {
for _ , p := range plugins {
p . OnConfigurationChange ( )
}
} )
for _ , p := range plugins {
p . OnConfigurationChange ( )
}
}
2017-09-01 09:00:27 -04:00
2017-09-06 17:12:54 -05:00
func ( a * App ) ActivatePlugins ( ) {
if a . Srv . PluginEnv == nil {
2017-09-01 09:00:27 -04:00
l4g . Error ( "plugin env not initialized" )
return
}
2017-09-06 17:12:54 -05:00
plugins , err := a . Srv . PluginEnv . Plugins ( )
2017-09-01 09:00:27 -04:00
if err != nil {
l4g . Error ( "failed to start up plugins: " + err . Error ( ) )
return
}
for _ , plugin := range plugins {
2017-09-06 17:12:54 -05:00
err := a . Srv . PluginEnv . ActivatePlugin ( plugin . Manifest . Id )
2017-09-01 09:00:27 -04:00
if err != nil {
l4g . Error ( err . Error ( ) )
}
l4g . Info ( "Activated %v plugin" , plugin . Manifest . Id )
}
}
2017-09-06 17:12:54 -05:00
func ( a * App ) UnpackAndActivatePlugin ( pluginFile io . Reader ) ( * model . Manifest , * model . AppError ) {
if a . Srv . PluginEnv == nil || ! * utils . Cfg . PluginSettings . Enable {
2017-09-01 09:00:27 -04:00
return nil , model . NewAppError ( "UnpackAndActivatePlugin" , "app.plugin.disabled.app_error" , nil , "" , http . StatusNotImplemented )
}
tmpDir , err := ioutil . TempDir ( "" , "plugintmp" )
if err != nil {
return nil , model . NewAppError ( "UnpackAndActivatePlugin" , "app.plugin.temp_dir.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
}
defer func ( ) {
os . RemoveAll ( tmpDir )
} ( )
filenames , err := utils . ExtractTarGz ( pluginFile , tmpDir )
if err != nil {
return nil , model . NewAppError ( "UnpackAndActivatePlugin" , "app.plugin.extract.app_error" , nil , err . Error ( ) , http . StatusBadRequest )
}
if len ( filenames ) == 0 {
return nil , model . NewAppError ( "UnpackAndActivatePlugin" , "app.plugin.no_files.app_error" , nil , err . Error ( ) , http . StatusBadRequest )
}
splitPath := strings . Split ( filenames [ 0 ] , string ( os . PathSeparator ) )
if len ( splitPath ) == 0 {
return nil , model . NewAppError ( "UnpackAndActivatePlugin" , "app.plugin.bad_path.app_error" , nil , err . Error ( ) , http . StatusBadRequest )
}
manifestDir := filepath . Join ( tmpDir , splitPath [ 0 ] )
manifest , _ , err := model . FindManifest ( manifestDir )
if err != nil {
return nil , model . NewAppError ( "UnpackAndActivatePlugin" , "app.plugin.manifest.app_error" , nil , err . Error ( ) , http . StatusBadRequest )
}
2017-09-06 17:12:54 -05:00
os . Rename ( manifestDir , filepath . Join ( a . Srv . PluginEnv . SearchPath ( ) , manifest . Id ) )
2017-09-01 09:00:27 -04:00
if err != nil {
return nil , model . NewAppError ( "UnpackAndActivatePlugin" , "app.plugin.mvdir.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
}
// Should add manifest validation and error handling here
2017-09-06 17:12:54 -05:00
err = a . Srv . PluginEnv . ActivatePlugin ( manifest . Id )
2017-09-01 09:00:27 -04:00
if err != nil {
return nil , model . NewAppError ( "UnpackAndActivatePlugin" , "app.plugin.activate.app_error" , nil , err . Error ( ) , http . StatusBadRequest )
}
return manifest , nil
}
2017-09-06 17:12:54 -05:00
func ( a * App ) GetActivePluginManifests ( ) ( [ ] * model . Manifest , * model . AppError ) {
if a . Srv . PluginEnv == nil || ! * utils . Cfg . PluginSettings . Enable {
2017-09-01 09:00:27 -04:00
return nil , model . NewAppError ( "GetActivePluginManifests" , "app.plugin.disabled.app_error" , nil , "" , http . StatusNotImplemented )
}
2017-09-06 17:12:54 -05:00
plugins , err := a . Srv . PluginEnv . ActivePlugins ( )
2017-09-01 09:00:27 -04:00
if err != nil {
return nil , model . NewAppError ( "GetActivePluginManifests" , "app.plugin.get_plugins.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
}
manifests := make ( [ ] * model . Manifest , len ( plugins ) )
for i , plugin := range plugins {
manifests [ i ] = plugin . Manifest
}
return manifests , nil
}
2017-09-06 17:12:54 -05:00
func ( a * App ) RemovePlugin ( id string ) * model . AppError {
if a . Srv . PluginEnv == nil || ! * utils . Cfg . PluginSettings . Enable {
2017-09-01 09:00:27 -04:00
return model . NewAppError ( "RemovePlugin" , "app.plugin.disabled.app_error" , nil , "" , http . StatusNotImplemented )
}
2017-09-06 17:12:54 -05:00
err := a . Srv . PluginEnv . DeactivatePlugin ( id )
2017-09-01 09:00:27 -04:00
if err != nil {
return model . NewAppError ( "RemovePlugin" , "app.plugin.deactivate.app_error" , nil , err . Error ( ) , http . StatusBadRequest )
}
2017-09-06 17:12:54 -05:00
err = os . RemoveAll ( filepath . Join ( a . Srv . PluginEnv . SearchPath ( ) , id ) )
2017-09-01 09:00:27 -04:00
if err != nil {
return model . NewAppError ( "RemovePlugin" , "app.plugin.remove.app_error" , nil , err . Error ( ) , http . StatusInternalServerError )
}
return nil
}
// Temporary WIP function/type for experimental webapp plugins
type ClientConfigPlugin struct {
Id string ` json:"id" `
BundlePath string ` json:"bundle_path" `
}
2017-09-06 17:12:54 -05:00
func ( a * App ) GetPluginsForClientConfig ( ) string {
if a . Srv . PluginEnv == nil || ! * utils . Cfg . PluginSettings . Enable {
2017-09-01 09:00:27 -04:00
return ""
}
2017-09-06 17:12:54 -05:00
plugins , err := a . Srv . PluginEnv . ActivePlugins ( )
2017-09-01 09:00:27 -04:00
if err != nil {
return ""
}
pluginsConfig := [ ] ClientConfigPlugin { }
for _ , plugin := range plugins {
if plugin . Manifest . Webapp == nil {
continue
}
pluginsConfig = append ( pluginsConfig , ClientConfigPlugin { Id : plugin . Manifest . Id , BundlePath : plugin . Manifest . Webapp . BundlePath } )
}
b , err := json . Marshal ( pluginsConfig )
if err != nil {
return ""
}
return string ( b )
}