Plugins: Simplify plugin file removal (#66115)

* make explicit class check when attempting to remove plugin

* simplify plugin file tracking

* fix test

* apply feedback

* fix linter
This commit is contained in:
Will Browne
2023-04-20 10:52:59 +01:00
committed by GitHub
parent 6e8b17efd8
commit 739c7f1c68
15 changed files with 193 additions and 281 deletions

View File

@ -12,36 +12,27 @@ import (
"path/filepath"
"regexp"
"strings"
"sync"
"github.com/grafana/grafana/pkg/plugins/log"
)
var _ Manager = (*FS)(nil)
var _ ZipExtractor = (*FS)(nil)
var reGitBuild = regexp.MustCompile("^[a-zA-Z0-9_.-]*/")
var (
ErrUninstallOutsideOfPluginDir = errors.New("cannot uninstall a plugin outside of the plugins directory")
ErrUninstallInvalidPluginDir = errors.New("cannot recognize as plugin folder")
)
type FS struct {
store map[string]string
mu sync.RWMutex
pluginsDir string
log log.PrettyLogger
}
func FileSystem(logger log.PrettyLogger, pluginsDir string) *FS {
return &FS{
store: make(map[string]string),
pluginsDir: pluginsDir,
log: logger,
}
}
func (fs *FS) Add(ctx context.Context, pluginID string, pluginArchive *zip.ReadCloser) (
func (fs *FS) Extract(ctx context.Context, pluginID string, pluginArchive *zip.ReadCloser) (
*ExtractedPluginArchive, error) {
pluginDir, err := fs.extractFiles(ctx, pluginArchive, pluginID)
if err != nil {
@ -71,38 +62,6 @@ func (fs *FS) Add(ctx context.Context, pluginID string, pluginArchive *zip.ReadC
}, nil
}
func (fs *FS) Register(_ context.Context, pluginID, pluginDir string) error {
fs.mu.Lock()
fs.store[pluginID] = pluginDir
fs.mu.Unlock()
return nil
}
func (fs *FS) Remove(_ context.Context, pluginID string) error {
fs.mu.RLock()
pluginDir, exists := fs.store[pluginID]
fs.mu.RUnlock()
if !exists {
return fmt.Errorf("%s does not exist", pluginID)
}
// extra security check to ensure we only remove plugins that are located in the configured plugins directory
path, err := filepath.Rel(fs.pluginsDir, pluginDir)
if err != nil || strings.HasPrefix(path, ".."+string(filepath.Separator)) {
return ErrUninstallOutsideOfPluginDir
}
if _, err = os.Stat(filepath.Join(pluginDir, "plugin.json")); os.IsNotExist(err) {
if _, err = os.Stat(filepath.Join(pluginDir, "dist/plugin.json")); os.IsNotExist(err) {
return ErrUninstallInvalidPluginDir
}
}
fs.log.Infof("Uninstalling plugin %v", pluginDir)
return os.RemoveAll(pluginDir)
}
func (fs *FS) extractFiles(_ context.Context, pluginArchive *zip.ReadCloser, pluginID string) (string, error) {
installDir := filepath.Join(fs.pluginsDir, pluginID)
if _, err := os.Stat(installDir); !os.IsNotExist(err) {
@ -261,7 +220,7 @@ func removeGitBuildFromName(filename, pluginID string) string {
return reGitBuild.ReplaceAllString(filename, pluginID+"/")
}
func toPluginDTO(pluginID, pluginDir string) (InstalledPlugin, error) {
func toPluginDTO(pluginID, pluginDir string) (installedPlugin, error) {
distPluginDataPath := filepath.Join(pluginDir, "dist", "plugin.json")
// It's safe to ignore gosec warning G304 since the file path suffix is hardcoded
@ -273,17 +232,17 @@ func toPluginDTO(pluginID, pluginDir string) (InstalledPlugin, error) {
// nolint:gosec
data, err = os.ReadFile(pluginDataPath)
if err != nil {
return InstalledPlugin{}, fmt.Errorf("could not find dist/plugin.json or plugin.json for %s in %s", pluginID, pluginDir)
return installedPlugin{}, fmt.Errorf("could not find dist/plugin.json or plugin.json for %s in %s", pluginID, pluginDir)
}
}
res := InstalledPlugin{}
res := installedPlugin{}
if err = json.Unmarshal(data, &res); err != nil {
return res, err
}
if res.ID == "" {
return InstalledPlugin{}, fmt.Errorf("could not find valid plugin %s in %s", pluginID, pluginDir)
return installedPlugin{}, fmt.Errorf("could not find valid plugin %s in %s", pluginID, pluginDir)
}
if res.Info.Version == "" {