MM-16368 - Plugin Signing (#13017)
* [MM-18757] POST handler for `/plugins/marketplace` (#12372) * Implement installMarketplacePlugin * Add InstallMarketplacePlugin endpoint * Fix go.mod * merge with master * Fix go.mod * Fix plugin tests * Move get plugin to marketplace client * Fix stylistic concerns * Add trailing newline to the go.mod * [MM-16586] Add plugin signature settings (#12390) * MM-17149 - Extend config.json for marketplace settings (#11933) * MM-17149 - Extend config.json for marketplace settings * Renamed MarketplaceUrl, tracking default marketplace url * Added EnableMarketplace to the client config * Revert "Added EnableMarketplace to the client config" This reverts commit 0f982c4c661c2cd9bb96264e9a01a2363c40d9c5. * MM-17149 - Added EnableMarketplace to the client config (#11958) * Added EnableMarketplace to the client config * Moved EnableMarketplace setting out of limited client configuration * Add public key settings to the config.json * Rename PublicKeys to SignaturePublicKeyFiles * Change filepath.Split to Base * Remove additional prints * Force extention of a public key file * Remove config validation * Remove error on delete * Remove config cloning * Add error messages * Add plugin public key tests * Rename extension to PluginSignaturePublicKeyFileExtention * Remove EnforceVerification * Change []*PublicKeyDescription to []string * Change .asc extension to .plugin.asc * Change ordering of public methods * Change plugin key commands * Update examples in the plugin key commands * Remove forcing extention * Add verify signature in settings * Fix tabbing * Fix naming * Remove unused text * Remove unused text * Update command examples * Fix unit tests * Change errors.New to errors.Wrap * Fix verbose flag * Change .asc to .gpg * Fix } * Change AddPublicKey signature * Change public.key extension * Add plugin public key command tests * Update en.json * Bootstrap the public keys * Update en.json * Fix en.json * Fix en.json * Bootstrap hard-coded public key * Remove unused texts in en.json * Change file to name * Add license header * Update development public key * Remove writeFile method * Remove .plugin.asc extension * Rename publiKey to mattermostPublicKey * Remove init_public_keys string * GolangCI * Closing file handlers * Fixed test that was installing nps plugin * [MM-19798] Implement plugin signature verification (#12768) * MM-17149 - Extend config.json for marketplace settings (#11933) * MM-17149 - Extend config.json for marketplace settings * Renamed MarketplaceUrl, tracking default marketplace url * Added EnableMarketplace to the client config * Revert "Added EnableMarketplace to the client config" This reverts commit 0f982c4c661c2cd9bb96264e9a01a2363c40d9c5. * MM-17149 - Added EnableMarketplace to the client config (#11958) * Added EnableMarketplace to the client config * Moved EnableMarketplace setting out of limited client configuration * Add public key settings to the config.json * Rename PublicKeys to SignaturePublicKeyFiles * Change filepath.Split to Base * Remove additional prints * Force extention of a public key file * Remove config validation * Remove error on delete * Remove config cloning * Add error messages * Add plugin public key tests * Rename extension to PluginSignaturePublicKeyFileExtention * Remove EnforceVerification * Change []*PublicKeyDescription to []string * Change .asc extension to .plugin.asc * Change ordering of public methods * Change plugin key commands * Update examples in the plugin key commands * Remove forcing extention * Add verify signature in settings * Fix tabbing * Fix naming * Remove unused text * Remove unused text * Update command examples * Fix unit tests * Change errors.New to errors.Wrap * Fix verbose flag * Change .asc to .gpg * Fix } * Change AddPublicKey signature * Change public.key extension * Add plugin public key command tests * Update en.json * Bootstrap the public keys * Update en.json * Fix en.json * Fix en.json * Bootstrap hard-coded public key * Remove unused texts in en.json * Change file to name * Add license header * Implement plugin signature verification * Remove benburker openpgp * Update en.json * Update development public key * Add support of multiple signatures in filestore * Update en.json * Run go mod vendor * Fix style * Remove writeFile method * Remove .plugin.asc extension * Rename publiKey to mattermostPublicKey * Verify plugin with mattermost public key * Remove init_public_keys string * Add InstallPluginWithSignature method and Refactor * Add signature verification on claster notification * Remove armored signature headers * Add error strings * Fix en.json * Change signatureStorePath * Implement minor fixes * Refactor plugin install methods * Add installPlugin method to uploadPlugin * Update en.json * Refactor installPlugin * Limit number of signatures * Close signatures * Fix helper function * Fix fromReadCloseSeekerToReadSeeker * Cleaned up ReadCloseSeeker for signatures * Remove signature truncation on FS * GolangCI * Add tests for armored signatures and plugin uploads * Fix nil slice issue * Fix TestPluginSync * Fixed tests * Return io.ReadSeeker from downloadFromUrl * Add log for the found plugins in the file store * Remove logging plugin detection info * [MM-20134] Consume and store single-signature for each plugin (#13081) * Consume and store single-signature for each plugin * Fix en.json * Remove saveSignature method * Remove public key hash * PR Feedback * refactored config * PR feedback
Этот коммит содержится в:
@@ -4,9 +4,12 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -55,14 +58,46 @@ var PluginListCmd = &cobra.Command{
|
||||
RunE: pluginListCmdF,
|
||||
}
|
||||
|
||||
var PluginPublicKeysCmd = &cobra.Command{
|
||||
Use: "keys",
|
||||
Short: "List public keys",
|
||||
Long: "List names of all public keys installed on your Mattermost server.",
|
||||
Example: ` plugin keys
|
||||
plugin keys --verbose`,
|
||||
RunE: pluginPublicKeysCmdF,
|
||||
}
|
||||
|
||||
var PluginAddPublicKeyCmd = &cobra.Command{
|
||||
Use: "add [keys]",
|
||||
Short: "Adds public key(s)",
|
||||
Long: "Adds public key(s) for plugins on your Mattermost server.",
|
||||
Example: ` plugin keys add my-pk-file1 my-pk-file2`,
|
||||
RunE: pluginAddPublicKeyCmdF,
|
||||
}
|
||||
|
||||
var PluginDeletePublicKeyCmd = &cobra.Command{
|
||||
Use: "delete [keys]",
|
||||
Short: "Deletes public key(s)",
|
||||
Long: "Deletes public key(s) for plugins on your Mattermost server.",
|
||||
Example: ` plugin keys delete my-pk-file1 my-pk-file2`,
|
||||
RunE: pluginDeletePublicKeyCmdF,
|
||||
}
|
||||
|
||||
func init() {
|
||||
PluginPublicKeysCmd.Flags().Bool("verbose", false, "List names and details of all public keys installed on your Mattermost server.")
|
||||
PluginPublicKeysCmd.AddCommand(
|
||||
PluginAddPublicKeyCmd,
|
||||
PluginDeletePublicKeyCmd,
|
||||
)
|
||||
PluginCmd.AddCommand(
|
||||
PluginAddCmd,
|
||||
PluginDeleteCmd,
|
||||
PluginEnableCmd,
|
||||
PluginDisableCmd,
|
||||
PluginListCmd,
|
||||
PluginPublicKeysCmd,
|
||||
)
|
||||
|
||||
RootCmd.AddCommand(PluginCmd)
|
||||
}
|
||||
|
||||
@@ -169,7 +204,7 @@ func pluginListCmdF(command *cobra.Command, args []string) error {
|
||||
|
||||
pluginsResp, appErr := a.GetPlugins()
|
||||
if appErr != nil {
|
||||
return errors.New("Unable to list plugins. Error: " + appErr.Error())
|
||||
return errors.Wrap(appErr, "Unable to list plugins.")
|
||||
}
|
||||
|
||||
CommandPrettyPrintln("Listing active plugins")
|
||||
@@ -184,3 +219,88 @@ func pluginListCmdF(command *cobra.Command, args []string) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func pluginPublicKeysCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer a.Shutdown()
|
||||
|
||||
verbose, err := command.Flags().GetBool("verbose")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Failed reading verbose flag.")
|
||||
}
|
||||
|
||||
pluginPublicKeysResp, appErr := a.GetPluginPublicKeyFiles()
|
||||
if appErr != nil {
|
||||
return errors.Wrap(appErr, "Unable to list public keys.")
|
||||
}
|
||||
|
||||
if verbose {
|
||||
for _, publicKey := range pluginPublicKeysResp {
|
||||
key, err := a.GetPublicKey(publicKey)
|
||||
if err != nil {
|
||||
CommandPrintErrorln("Unable to get plugin public key: " + publicKey + ". Error: " + err.Error())
|
||||
}
|
||||
CommandPrettyPrintln("Plugin name: " + publicKey + ". \nPublic key: \n" + string(key) + "\n")
|
||||
}
|
||||
} else {
|
||||
for _, publicKey := range pluginPublicKeysResp {
|
||||
CommandPrettyPrintln(publicKey)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func pluginAddPublicKeyCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer a.Shutdown()
|
||||
|
||||
if len(args) < 1 {
|
||||
return errors.New("Expected at least one argument. See help text for details.")
|
||||
}
|
||||
|
||||
for _, pkFile := range args {
|
||||
filename := filepath.Base(pkFile)
|
||||
fileReader, err := os.Open(pkFile)
|
||||
if err != nil {
|
||||
return model.NewAppError("AddPublicKey", "api.plugin.add_public_key.open.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
defer fileReader.Close()
|
||||
|
||||
if err := a.AddPublicKey(filename, fileReader); err != nil {
|
||||
CommandPrintErrorln("Unable to add public key: " + pkFile + ". Error: " + err.Error())
|
||||
} else {
|
||||
CommandPrettyPrintln("Added public key: " + pkFile)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func pluginDeletePublicKeyCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer a.Shutdown()
|
||||
|
||||
if len(args) < 1 {
|
||||
return errors.New("Expected at least one argument. See help text for details.")
|
||||
}
|
||||
|
||||
for _, pkFile := range args {
|
||||
if err := a.DeletePublicKey(pkFile); err != nil {
|
||||
CommandPrintErrorln("Unable to delete public key: " + pkFile + ". Error: " + err.Error())
|
||||
} else {
|
||||
CommandPrettyPrintln("Deleted public key: " + pkFile)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
package commands
|
||||
|
||||
import (
|
||||
@@ -44,3 +46,75 @@ func TestPlugin(t *testing.T) {
|
||||
|
||||
th.CheckCommand(t, "plugin", "delete", "testplugin")
|
||||
}
|
||||
|
||||
func TestPluginPublicKeys(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
cfg := th.Config()
|
||||
cfg.PluginSettings.SignaturePublicKeyFiles = []string{"public-key"}
|
||||
th.SetConfig(cfg)
|
||||
|
||||
output := th.CheckCommand(t, "plugin", "keys")
|
||||
assert.Contains(t, output, "public-key")
|
||||
assert.NotContains(t, output, "Plugin name:")
|
||||
}
|
||||
|
||||
func TestPluginPublicKeyDetails(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
cfg := th.Config()
|
||||
cfg.PluginSettings.SignaturePublicKeyFiles = []string{"public-key"}
|
||||
|
||||
th.SetConfig(cfg)
|
||||
|
||||
output := th.CheckCommand(t, "plugin", "keys", "--verbose", "true")
|
||||
assert.Contains(t, output, "Plugin name: public-key")
|
||||
output = th.CheckCommand(t, "plugin", "keys", "--verbose")
|
||||
assert.Contains(t, output, "Plugin name: public-key")
|
||||
}
|
||||
|
||||
func TestAddPluginPublicKeys(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
cfg := th.Config()
|
||||
cfg.PluginSettings.SignaturePublicKeyFiles = []string{"public-key"}
|
||||
th.SetConfig(cfg)
|
||||
|
||||
err := th.RunCommand(t, "plugin", "keys", "add", "pk1")
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestDeletePluginPublicKeys(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
cfg := th.Config()
|
||||
cfg.PluginSettings.SignaturePublicKeyFiles = []string{"pk1"}
|
||||
th.SetConfig(cfg)
|
||||
|
||||
output := th.CheckCommand(t, "plugin", "keys", "delete", "pk1")
|
||||
assert.Contains(t, output, "Deleted public key: pk1")
|
||||
}
|
||||
|
||||
func TestPluginPublicKeysFlow(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
path, _ := fileutils.FindDir("tests")
|
||||
name := "test-public-key.plugin.gpg"
|
||||
output := th.CheckCommand(t, "plugin", "keys", "add", filepath.Join(path, name))
|
||||
assert.Contains(t, output, "Added public key: "+filepath.Join(path, name))
|
||||
|
||||
output = th.CheckCommand(t, "plugin", "keys")
|
||||
assert.Contains(t, output, name)
|
||||
assert.NotContains(t, output, "Plugin name:")
|
||||
|
||||
output = th.CheckCommand(t, "plugin", "keys", "--verbose")
|
||||
assert.Contains(t, output, "Plugin name: "+name)
|
||||
|
||||
output = th.CheckCommand(t, "plugin", "keys", "delete", name)
|
||||
assert.Contains(t, output, "Deleted public key: "+name)
|
||||
}
|
||||
|
||||
Ссылка в новой задаче
Block a user