From 546a23e4e7b338ba16735738ceecf291625e41ad Mon Sep 17 00:00:00 2001 From: Piotr Andruszkiewicz Date: Mon, 26 May 2025 21:02:49 +0200 Subject: [PATCH 01/11] Add user private keys management commands: add, delete --- cmd/aem/user.go | 100 ++++++++++++++++++++++ go.mod | 2 + go.sum | 4 + pkg/keystore/keystore_status.go | 9 +- pkg/user_manager.go | 141 ++++++++++++++++++++++++++++++++ 5 files changed, 255 insertions(+), 1 deletion(-) diff --git a/cmd/aem/user.go b/cmd/aem/user.go index 20c5211a..033e6776 100644 --- a/cmd/aem/user.go +++ b/cmd/aem/user.go @@ -26,6 +26,7 @@ func (c *CLI) userKeyStore() *cobra.Command { } cmd.AddCommand(c.KeystoreStatus()) cmd.AddCommand(c.KeystoreCreate()) + cmd.AddCommand(c.KeystoreKey()) return cmd } @@ -110,6 +111,105 @@ func (c *CLI) KeystoreCreate() *cobra.Command { return cmd } +func (c *CLI) KeystoreKey() *cobra.Command { + cmd := &cobra.Command{ + Use: "key", + Short: "Manage user Keystore keys", + Aliases: []string{"keys"}, + } + cmd.AddCommand(c.KeystoreKeyAdd()) + cmd.AddCommand(c.KeystoreKeyDelete()) + return cmd +} + +func (c *CLI) KeystoreKeyAdd() *cobra.Command { + cmd := &cobra.Command{ + Use: "add", + Short: "Create user Keystore key", + Aliases: []string{"create", "new"}, + Run: func(cmd *cobra.Command, args []string) { + instance, err := c.aem.InstanceManager().One() + if err != nil { + c.Error(err) + return + } + + changed, err := instance.Auth().UserManager().AddKeystoreKey( + cmd.Flag("scope").Value.String(), + cmd.Flag("id").Value.String(), + cmd.Flag("keystore-file").Value.String(), + cmd.Flag("keystore-password").Value.String(), + cmd.Flag("key-alias").Value.String(), + cmd.Flag("key-password").Value.String(), + cmd.Flag("new-alias").Value.String(), + ) + + if err != nil { + c.Error(err) + return + } + if changed { + c.Changed("User Keystore key added") + } else { + c.Ok("User Keystore key already exists") + } + }, + } + + cmd.Flags().String("id", "", "user id") + _ = cmd.MarkFlagRequired("id") + cmd.Flags().String("scope", "", "user scope") + cmd.Flags().String("keystore-file", "", "path to keystore file") + _ = cmd.MarkFlagRequired("keystore-file") + cmd.Flags().String("keystore-password", "", "keystore password") + _ = cmd.MarkFlagRequired("keystore-password") + cmd.Flags().String("key-alias", "", "key alias") + _ = cmd.MarkFlagRequired("key-alias") + cmd.Flags().String("key-password", "", "key password") + cmd.Flags().String("new-alias", "", "new key alias (optional)") + + return cmd +} + +func (c *CLI) KeystoreKeyDelete() *cobra.Command { + cmd := &cobra.Command{ + Use: "delete", + Short: "Delete user Keystore key", + Aliases: []string{"remove", "rm"}, + Run: func(cmd *cobra.Command, args []string) { + instance, err := c.aem.InstanceManager().One() + if err != nil { + c.Error(err) + return + } + + changed, err := instance.Auth().UserManager().DeleteKeystoreKey( + cmd.Flag("scope").Value.String(), + cmd.Flag("id").Value.String(), + cmd.Flag("key-alias").Value.String(), + ) + + if err != nil { + c.Error(err) + return + } + if changed { + c.Changed("User Keystore key deleted") + } else { + c.Ok("User Keystore key does not exist") + } + }, + } + + cmd.Flags().String("id", "", "user id") + _ = cmd.MarkFlagRequired("id") + cmd.Flags().String("scope", "", "user scope") + cmd.Flags().String("key-alias", "", "key alias") + _ = cmd.MarkFlagRequired("key-alias") + + return cmd +} + func (c *CLI) UserPasswordSet() *cobra.Command { cmd := &cobra.Command{ Use: "set", diff --git a/go.mod b/go.mod index e1eca5b0..1b3518f9 100644 --- a/go.mod +++ b/go.mod @@ -61,6 +61,7 @@ require ( github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/nwaples/rardecode v1.1.3 // indirect + github.com/pavlo-v-chernykh/keystore-go/v4 v4.5.0 // indirect github.com/pelletier/go-toml/v2 v2.1.1 // indirect github.com/pierrec/lz4/v4 v4.1.19 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect @@ -80,3 +81,4 @@ require ( golang.org/x/text v0.14.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect ) + diff --git a/go.sum b/go.sum index e47b84bf..4bf5ec94 100644 --- a/go.sum +++ b/go.sum @@ -107,6 +107,10 @@ github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6 github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU= github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w= github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks= +github.com/pavlo-v-chernykh/keystore-go v2.1.0+incompatible h1:/g2gGGxrs2aQU7IrnLlFL0h3CrBrhwNFpKYhXh/gKm4= +github.com/pavlo-v-chernykh/keystore-go v2.1.0+incompatible/go.mod h1:FUVGm7LLk1CudKS/gecQcL9T6GpdP2M97mJTuRzA5rw= +github.com/pavlo-v-chernykh/keystore-go/v4 v4.5.0 h1:2nosf3P75OZv2/ZO/9Px5ZgZ5gbKrzA3joN1QMfOGMQ= +github.com/pavlo-v-chernykh/keystore-go/v4 v4.5.0/go.mod h1:lAVhWwbNaveeJmxrxuSTxMgKpF6DjnuVpn6T8WiBwYQ= github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= diff --git a/pkg/keystore/keystore_status.go b/pkg/keystore/keystore_status.go index 90172a14..3ea71b9d 100644 --- a/pkg/keystore/keystore_status.go +++ b/pkg/keystore/keystore_status.go @@ -2,9 +2,10 @@ package keystore import ( "bytes" + "io" + "github.com/samber/lo" "github.com/wttech/aemc/pkg/common/fmtx" - "io" ) type Status struct { @@ -34,3 +35,9 @@ func (s *Status) MarshalText() string { }))) return bs.String() } + +func (s *Status) HasAlias(privateKeyAlias string) bool { + return lo.ContainsBy(s.PrivateKeys, func(c PrivateKey) bool { + return c.Alias == privateKeyAlias + }) +} diff --git a/pkg/user_manager.go b/pkg/user_manager.go index 12f3b7ab..d3acbf56 100644 --- a/pkg/user_manager.go +++ b/pkg/user_manager.go @@ -2,9 +2,13 @@ package pkg import ( "fmt" + "os" + jks "github.com/pavlo-v-chernykh/keystore-go/v4" + "github.com/wttech/aemc/pkg/common/pathx" "github.com/wttech/aemc/pkg/keystore" "github.com/wttech/aemc/pkg/user" + "golang.org/x/exp/slices" ) type UserManager struct { @@ -72,6 +76,127 @@ func (um *UserManager) KeystoreCreate(scope, id, keystorePassword string) (bool, return true, nil } +func (um *UserManager) AddKeystoreKey(scope, id, keystoreFilePath, keystoreFilePassword, privateKeyAlias, privateKeyPassword, privateKeyNewAlias string) (bool, error) { + if !pathx.Exists(keystoreFilePath) { + return false, fmt.Errorf("%s > keystore file does not exist: %s", um.instance.IDColor(), keystoreFilePath) + } + if privateKeyNewAlias == "" { + privateKeyNewAlias = privateKeyAlias + } + if privateKeyPassword == "" { + privateKeyPassword = keystoreFilePassword + } + + readKeystore, err := readKeyStore(keystoreFilePath, []byte(keystoreFilePassword)) + if err != nil { + return false, fmt.Errorf("%s > cannot read keystore file %s: %w", um.instance.IDColor(), keystoreFilePath, err) + } + + aliases := readKeystore.Aliases() + + if aliases == nil { + return false, fmt.Errorf("%s > keystore does not contain any aliases", um.instance.IDColor()) + } + if !slices.Contains(aliases, privateKeyAlias) { + return false, fmt.Errorf("%s > keystore does not contain alias: %s", um.instance.IDColor(), privateKeyAlias) + } + + keystorePath := assembleUserPath(scope, id) + ".ks.html" + keystoreStatusPath := assembleUserPath(scope, id) + ".ks.json" + + statusResponse, err := um.instance.http.Request().Get(keystoreStatusPath) + + if err != nil { + return false, fmt.Errorf("%s > cannot read user Keystore: %w", um.instance.IDColor(), err) + } + + if statusResponse.IsError() { + return false, fmt.Errorf("%s > cannot read user keystore: %s", um.instance.IDColor(), statusResponse.Status()) + } + + status, err := keystore.UnmarshalStatus(statusResponse.RawBody()) + + if err != nil { + return false, fmt.Errorf("%s > cannot parse user Keystore status response: %w", um.instance.IDColor(), err) + } + if status == nil || !status.Created { + return false, fmt.Errorf("%s > cannot delete keystore key: keystore does not exist", um.instance.IDColor()) + } + if status.HasAlias(privateKeyAlias) { + return false, nil + } + + requestFiles := map[string]string{ + "keyStore": keystoreFilePath, + } + + formData := map[string]string{ + "keyStorePass": keystoreFilePassword, + "alias": privateKeyAlias, + "keyPassword": privateKeyPassword, + "newAlias": privateKeyNewAlias, + "keyStoreType": "jks", + } + + response, err := um.instance.http.Request(). + SetFiles(requestFiles). + SetFormData(formData). + Post(keystorePath) + + if err != nil { + return false, fmt.Errorf("%s > cannot add keystore key: %w", um.instance.IDColor(), err) + } + if response.IsError() { + return false, fmt.Errorf("%s > cannot add keystore key: %s", um.instance.IDColor(), response.Status()) + } + return true, nil + +} + +func (um *UserManager) DeleteKeystoreKey(scope, id, privateKeyAlias string) (bool, error) { + userKeystorePath := assembleUserPath(scope, id) + ".ks.html" + userKeystoreStatusPath := assembleUserPath(scope, id) + ".ks.json" + + statusResponse, err := um.instance.http.Request().Get(userKeystoreStatusPath) + + if err != nil { + return false, fmt.Errorf("%s > cannot read user Keystore: %w", um.instance.IDColor(), err) + } + + if statusResponse.IsError() { + return false, fmt.Errorf("%s > cannot read user keystore: %s", um.instance.IDColor(), statusResponse.Status()) + } + + status, err := keystore.UnmarshalStatus(statusResponse.RawBody()) + + if err != nil { + return false, fmt.Errorf("%s > cannot parse user Keystore status response: %w", um.instance.IDColor(), err) + } + if status == nil || !status.Created { + return false, fmt.Errorf("%s > cannot delete keystore key: keystore does not exist", um.instance.IDColor()) + } + if !status.HasAlias(privateKeyAlias) { + return false, nil + } + + formData := map[string]string{ + "removeAlias": privateKeyAlias, + } + + response, err := um.instance.http.Request(). + SetFormData(formData). + Post(userKeystorePath) + + if err != nil { + return false, fmt.Errorf("%s > cannot delete keystore key: %w", um.instance.IDColor(), err) + } + if response.IsError() { + return false, fmt.Errorf("%s > cannot delete keystore key: %s", um.instance.IDColor(), response.Status()) + } + + return true, nil +} + func (um *UserManager) ReadState(scope string, id string) (*user.Status, error) { userPath := assembleUserPath(scope, id) @@ -135,3 +260,19 @@ func assembleUserPath(scope string, id string) string { } return UsersPath + "/" + scope + "/" + id } + +func readKeyStore(filename string, password []byte) (*jks.KeyStore, error) { + f, err := os.Open(filename) + if err != nil { + return nil, err + } + + defer f.Close() + + ks := jks.New() + if err := ks.Load(f, password); err != nil { + return nil, err + } + + return &ks, nil +} From 89283a71ce58fd0d98097abd662b0fe62d3634ae Mon Sep 17 00:00:00 2001 From: Piotr Andruszkiewicz Date: Mon, 26 May 2025 22:24:54 +0200 Subject: [PATCH 02/11] Refactor Keystore methods: remove code repetitions --- pkg/user_manager.go | 46 +++++++-------------------------------------- 1 file changed, 7 insertions(+), 39 deletions(-) diff --git a/pkg/user_manager.go b/pkg/user_manager.go index d3acbf56..de02b614 100644 --- a/pkg/user_manager.go +++ b/pkg/user_manager.go @@ -37,7 +37,6 @@ func (um *UserManager) KeystoreStatus(scope, id string) (*keystore.Status, error } result, err := keystore.UnmarshalStatus(response.RawBody()) - if err != nil { return nil, fmt.Errorf("%s > cannot parse user Keystore status response: %w", um.instance.IDColor(), err) } @@ -47,7 +46,6 @@ func (um *UserManager) KeystoreStatus(scope, id string) (*keystore.Status, error func (um *UserManager) KeystoreCreate(scope, id, keystorePassword string) (bool, error) { statusResponse, statusError := um.KeystoreStatus(scope, id) - if statusError != nil { return false, statusError } @@ -93,7 +91,6 @@ func (um *UserManager) AddKeystoreKey(scope, id, keystoreFilePath, keystoreFileP } aliases := readKeystore.Aliases() - if aliases == nil { return false, fmt.Errorf("%s > keystore does not contain any aliases", um.instance.IDColor()) } @@ -101,26 +98,13 @@ func (um *UserManager) AddKeystoreKey(scope, id, keystoreFilePath, keystoreFileP return false, fmt.Errorf("%s > keystore does not contain alias: %s", um.instance.IDColor(), privateKeyAlias) } - keystorePath := assembleUserPath(scope, id) + ".ks.html" - keystoreStatusPath := assembleUserPath(scope, id) + ".ks.json" - - statusResponse, err := um.instance.http.Request().Get(keystoreStatusPath) - + status, err := um.KeystoreStatus(scope, id) if err != nil { - return false, fmt.Errorf("%s > cannot read user Keystore: %w", um.instance.IDColor(), err) - } - - if statusResponse.IsError() { - return false, fmt.Errorf("%s > cannot read user keystore: %s", um.instance.IDColor(), statusResponse.Status()) + return false, err } - status, err := keystore.UnmarshalStatus(statusResponse.RawBody()) - - if err != nil { - return false, fmt.Errorf("%s > cannot parse user Keystore status response: %w", um.instance.IDColor(), err) - } if status == nil || !status.Created { - return false, fmt.Errorf("%s > cannot delete keystore key: keystore does not exist", um.instance.IDColor()) + return false, fmt.Errorf("%s > cannot add keystore key: keystore does not exist", um.instance.IDColor()) } if status.HasAlias(privateKeyAlias) { return false, nil @@ -130,6 +114,7 @@ func (um *UserManager) AddKeystoreKey(scope, id, keystoreFilePath, keystoreFileP "keyStore": keystoreFilePath, } + keystorePath := assembleUserPath(scope, id) + ".ks.html" formData := map[string]string{ "keyStorePass": keystoreFilePassword, "alias": privateKeyAlias, @@ -150,28 +135,14 @@ func (um *UserManager) AddKeystoreKey(scope, id, keystoreFilePath, keystoreFileP return false, fmt.Errorf("%s > cannot add keystore key: %s", um.instance.IDColor(), response.Status()) } return true, nil - } func (um *UserManager) DeleteKeystoreKey(scope, id, privateKeyAlias string) (bool, error) { - userKeystorePath := assembleUserPath(scope, id) + ".ks.html" - userKeystoreStatusPath := assembleUserPath(scope, id) + ".ks.json" - - statusResponse, err := um.instance.http.Request().Get(userKeystoreStatusPath) - + status, err := um.KeystoreStatus(scope, id) if err != nil { - return false, fmt.Errorf("%s > cannot read user Keystore: %w", um.instance.IDColor(), err) - } - - if statusResponse.IsError() { - return false, fmt.Errorf("%s > cannot read user keystore: %s", um.instance.IDColor(), statusResponse.Status()) + return false, err } - status, err := keystore.UnmarshalStatus(statusResponse.RawBody()) - - if err != nil { - return false, fmt.Errorf("%s > cannot parse user Keystore status response: %w", um.instance.IDColor(), err) - } if status == nil || !status.Created { return false, fmt.Errorf("%s > cannot delete keystore key: keystore does not exist", um.instance.IDColor()) } @@ -183,6 +154,7 @@ func (um *UserManager) DeleteKeystoreKey(scope, id, privateKeyAlias string) (boo "removeAlias": privateKeyAlias, } + userKeystorePath := assembleUserPath(scope, id) + ".ks.html" response, err := um.instance.http.Request(). SetFormData(formData). Post(userKeystorePath) @@ -210,7 +182,6 @@ func (um *UserManager) ReadState(scope string, id string) (*user.Status, error) } result, err := user.UnmarshalStatus(response.RawBody()) - if err != nil { return nil, fmt.Errorf("%s > cannot parse user status response: %w", um.instance.IDColor(), err) } @@ -220,13 +191,11 @@ func (um *UserManager) ReadState(scope string, id string) (*user.Status, error) func (um *UserManager) SetPassword(scope string, id string, password string) (bool, error) { userStatus, err := um.ReadState(scope, id) - if err != nil { return false, err } userPath := assembleUserPath(scope, id) - passwordCheckResponse, err := um.instance.http.Request(). SetBasicAuth(userStatus.AuthorizableID, password). Get(userPath + ".json") @@ -243,7 +212,6 @@ func (um *UserManager) SetPassword(scope string, id string, password string) (bo } postResponse, err := um.instance.http.RequestFormData(props).Post(userPath) - if err != nil { return false, fmt.Errorf("%s > cannot set user password: %w", um.instance.IDColor(), err) } From 2384789c89738074e2021973d819fe09a72746fd Mon Sep 17 00:00:00 2001 From: Piotr Andruszkiewicz Date: Tue, 27 May 2025 11:24:23 +0200 Subject: [PATCH 03/11] Refactor user command structure: separate user key management into its own command --- cmd/aem/user.go | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/cmd/aem/user.go b/cmd/aem/user.go index 033e6776..6f530431 100644 --- a/cmd/aem/user.go +++ b/cmd/aem/user.go @@ -13,6 +13,7 @@ func (c *CLI) userCmd() *cobra.Command { Aliases: []string{"usr"}, } cmd.AddCommand(c.userKeyStore()) + cmd.AddCommand(c.userKey()) cmd.AddCommand(c.userPassword()) return cmd } @@ -26,14 +27,24 @@ func (c *CLI) userKeyStore() *cobra.Command { } cmd.AddCommand(c.KeystoreStatus()) cmd.AddCommand(c.KeystoreCreate()) - cmd.AddCommand(c.KeystoreKey()) + return cmd +} + +func (c *CLI) userKey() *cobra.Command { + cmd := &cobra.Command{ + Use: "key", + Short: "Private keys management", + Aliases: []string{"keys"}, + } + cmd.AddCommand(c.userKeyAdd()) + cmd.AddCommand(c.userKeyDelete()) return cmd } func (c *CLI) userPassword() *cobra.Command { cmd := &cobra.Command{ Use: "password", - Short: "User password management", + Short: "Password management", Aliases: []string{"pwd"}, } cmd.AddCommand(c.UserPasswordSet()) @@ -43,7 +54,7 @@ func (c *CLI) userPassword() *cobra.Command { func (c *CLI) KeystoreStatus() *cobra.Command { cmd := &cobra.Command{ Use: "status", - Short: "Get status of keystore", + Short: "Get status of a user's keystore", Aliases: []string{"show", "get", "read", "describe", "ls"}, Run: func(cmd *cobra.Command, args []string) { instance, err := c.aem.InstanceManager().One() @@ -69,14 +80,13 @@ func (c *CLI) KeystoreStatus() *cobra.Command { cmd.Flags().String("id", "", "user id") _ = cmd.MarkFlagRequired("id") cmd.Flags().String("scope", "", "user scope") - _ = cmd.MarkFlagRequired("scope") return cmd } func (c *CLI) KeystoreCreate() *cobra.Command { cmd := &cobra.Command{ Use: "create", - Short: "Create user Keystore", + Short: "Create user's keystore", Aliases: []string{"make", "new"}, Run: func(cmd *cobra.Command, args []string) { instance, err := c.aem.InstanceManager().One() @@ -111,21 +121,10 @@ func (c *CLI) KeystoreCreate() *cobra.Command { return cmd } -func (c *CLI) KeystoreKey() *cobra.Command { - cmd := &cobra.Command{ - Use: "key", - Short: "Manage user Keystore keys", - Aliases: []string{"keys"}, - } - cmd.AddCommand(c.KeystoreKeyAdd()) - cmd.AddCommand(c.KeystoreKeyDelete()) - return cmd -} - -func (c *CLI) KeystoreKeyAdd() *cobra.Command { +func (c *CLI) userKeyAdd() *cobra.Command { cmd := &cobra.Command{ Use: "add", - Short: "Create user Keystore key", + Short: "Add user's private key to their keystore", Aliases: []string{"create", "new"}, Run: func(cmd *cobra.Command, args []string) { instance, err := c.aem.InstanceManager().One() @@ -171,10 +170,10 @@ func (c *CLI) KeystoreKeyAdd() *cobra.Command { return cmd } -func (c *CLI) KeystoreKeyDelete() *cobra.Command { +func (c *CLI) userKeyDelete() *cobra.Command { cmd := &cobra.Command{ Use: "delete", - Short: "Delete user Keystore key", + Short: "Delete user's private key from their keystore", Aliases: []string{"remove", "rm"}, Run: func(cmd *cobra.Command, args []string) { instance, err := c.aem.InstanceManager().One() From 3afa3a8e4bbee0a652af737126c16a6cef5e3222 Mon Sep 17 00:00:00 2001 From: Piotr Andruszkiewicz Date: Tue, 27 May 2025 11:40:23 +0200 Subject: [PATCH 04/11] Refactor UserManager and KeystoreManager: streamline keystore methods and improve structure --- cmd/aem/user.go | 8 +- pkg/keystore_manager.go | 159 ++++++++++++++++++++++++++++++++++++++++ pkg/user_manager.go | 155 ++------------------------------------- 3 files changed, 168 insertions(+), 154 deletions(-) create mode 100644 pkg/keystore_manager.go diff --git a/cmd/aem/user.go b/cmd/aem/user.go index 6f530431..e93b4230 100644 --- a/cmd/aem/user.go +++ b/cmd/aem/user.go @@ -66,7 +66,7 @@ func (c *CLI) KeystoreStatus() *cobra.Command { id, _ := cmd.Flags().GetString("id") scope, _ := cmd.Flags().GetString("scope") - result, err := instance.Auth().UserManager().KeystoreStatus(scope, id) + result, err := instance.Auth().UserManager().Keystore().Status(scope, id) if err != nil { c.Error(err) @@ -98,7 +98,7 @@ func (c *CLI) KeystoreCreate() *cobra.Command { id, _ := cmd.Flags().GetString("id") scope, _ := cmd.Flags().GetString("scope") password, _ := cmd.Flags().GetString("keystore-password") - changed, err := instance.Auth().UserManager().KeystoreCreate(scope, id, password) + changed, err := instance.Auth().UserManager().Keystore().Create(scope, id, password) if err != nil { c.Error(err) @@ -133,7 +133,7 @@ func (c *CLI) userKeyAdd() *cobra.Command { return } - changed, err := instance.Auth().UserManager().AddKeystoreKey( + changed, err := instance.Auth().UserManager().Keystore().AddKey( cmd.Flag("scope").Value.String(), cmd.Flag("id").Value.String(), cmd.Flag("keystore-file").Value.String(), @@ -182,7 +182,7 @@ func (c *CLI) userKeyDelete() *cobra.Command { return } - changed, err := instance.Auth().UserManager().DeleteKeystoreKey( + changed, err := instance.Auth().UserManager().Keystore().DeleteKey( cmd.Flag("scope").Value.String(), cmd.Flag("id").Value.String(), cmd.Flag("key-alias").Value.String(), diff --git a/pkg/keystore_manager.go b/pkg/keystore_manager.go new file mode 100644 index 00000000..474bcc4b --- /dev/null +++ b/pkg/keystore_manager.go @@ -0,0 +1,159 @@ +package pkg + +import ( + "fmt" + "slices" + + "github.com/wttech/aemc/pkg/common/pathx" + "github.com/wttech/aemc/pkg/keystore" +) + +type KeystoreManager struct { + instance *Instance +} + +func (km *KeystoreManager) Status(scope, id string) (*keystore.Status, error) { + userKeystorePath := composeUserPath(scope, id) + ".ks.json" + + response, err := km.instance.http.Request().Get(userKeystorePath) + + if err != nil { + return nil, fmt.Errorf("%s > cannot read user keystore: %w", km.instance.IDColor(), err) + } + + if response.IsError() { + return nil, fmt.Errorf("%s > cannot read user keystore: %s", km.instance.IDColor(), response.Status()) + } + + result, err := keystore.UnmarshalStatus(response.RawBody()) + if err != nil { + return nil, fmt.Errorf("%s > cannot parse user keystore status response: %w", km.instance.IDColor(), err) + } + + return result, nil +} + +func (km *KeystoreManager) Create(scope, id, keystorePassword string) (bool, error) { + statusResponse, statusError := km.Status(scope, id) + if statusError != nil { + return false, statusError + } + + if statusResponse.Created { + return false, nil + } + + pathParams := map[string]string{ + "newPassword": keystorePassword, + "rePassword": keystorePassword, + ":operation": "createStore", + } + + userKeystoreCreatePath := composeUserPath(scope, id) + ".ks.html" + postResponse, postError := km.instance.http.Request().SetQueryParams(pathParams).Post(userKeystoreCreatePath) + + if postError != nil { + return false, fmt.Errorf("%s > cannot create user keystore: %w", km.instance.IDColor(), postError) + } + + if postResponse.IsError() { + return false, fmt.Errorf("%s > cannot create user keystore: %s", km.instance.IDColor(), postResponse.Status()) + } + + return true, nil +} + +func (km *KeystoreManager) AddKey(scope, id, keystoreFilePath, keystoreFilePassword, privateKeyAlias, privateKeyPassword, privateKeyNewAlias string) (bool, error) { + if !pathx.Exists(keystoreFilePath) { + return false, fmt.Errorf("%s > keystore file does not exist: %s", km.instance.IDColor(), keystoreFilePath) + } + if privateKeyNewAlias == "" { + privateKeyNewAlias = privateKeyAlias + } + if privateKeyPassword == "" { + privateKeyPassword = keystoreFilePassword + } + + readKeystore, err := readKeyStore(keystoreFilePath, []byte(keystoreFilePassword)) + if err != nil { + return false, fmt.Errorf("%s > cannot read keystore file %s: %w", km.instance.IDColor(), keystoreFilePath, err) + } + + aliases := readKeystore.Aliases() + if aliases == nil { + return false, fmt.Errorf("%s > keystore file does not contain any aliases", km.instance.IDColor()) + } + if !slices.Contains(aliases, privateKeyAlias) { + return false, fmt.Errorf("%s > keystore file does not contain alias: %s", km.instance.IDColor(), privateKeyAlias) + } + + status, err := km.Status(scope, id) + if err != nil { + return false, err + } + + if status == nil || !status.Created { + return false, fmt.Errorf("%s > cannot add key as keystore does not exist", km.instance.IDColor()) + } + if status.HasAlias(privateKeyAlias) { + return false, nil + } + + requestFiles := map[string]string{ + "keyStore": keystoreFilePath, + } + + keystorePath := composeUserPath(scope, id) + ".ks.html" + formData := map[string]string{ + "keyStorePass": keystoreFilePassword, + "alias": privateKeyAlias, + "keyPassword": privateKeyPassword, + "newAlias": privateKeyNewAlias, + "keyStoreType": "jks", + } + + response, err := km.instance.http.Request(). + SetFiles(requestFiles). + SetFormData(formData). + Post(keystorePath) + + if err != nil { + return false, fmt.Errorf("%s > cannot add key: %w", km.instance.IDColor(), err) + } + if response.IsError() { + return false, fmt.Errorf("%s > cannot add key: %s", km.instance.IDColor(), response.Status()) + } + return true, nil +} + +func (km *KeystoreManager) DeleteKey(scope, id, privateKeyAlias string) (bool, error) { + status, err := km.Status(scope, id) + if err != nil { + return false, err + } + + if status == nil || !status.Created { + return false, fmt.Errorf("%s > cannot delete key: keystore does not exist", km.instance.IDColor()) + } + if !status.HasAlias(privateKeyAlias) { + return false, nil + } + + formData := map[string]string{ + "removeAlias": privateKeyAlias, + } + + userKeystorePath := composeUserPath(scope, id) + ".ks.html" + response, err := km.instance.http.Request(). + SetFormData(formData). + Post(userKeystorePath) + + if err != nil { + return false, fmt.Errorf("%s > cannot delete key: %w", km.instance.IDColor(), err) + } + if response.IsError() { + return false, fmt.Errorf("%s > cannot delete key: %s", km.instance.IDColor(), response.Status()) + } + + return true, nil +} diff --git a/pkg/user_manager.go b/pkg/user_manager.go index de02b614..1d79aeef 100644 --- a/pkg/user_manager.go +++ b/pkg/user_manager.go @@ -5,10 +5,7 @@ import ( "os" jks "github.com/pavlo-v-chernykh/keystore-go/v4" - "github.com/wttech/aemc/pkg/common/pathx" - "github.com/wttech/aemc/pkg/keystore" "github.com/wttech/aemc/pkg/user" - "golang.org/x/exp/slices" ) type UserManager struct { @@ -23,154 +20,12 @@ const ( UsersPath = "/home/users" ) -func (um *UserManager) KeystoreStatus(scope, id string) (*keystore.Status, error) { - userKeystorePath := assembleUserPath(scope, id) + ".ks.json" - - response, err := um.instance.http.Request().Get(userKeystorePath) - - if err != nil { - return nil, fmt.Errorf("%s > cannot read user Keystore: %w", um.instance.IDColor(), err) - } - - if response.IsError() { - return nil, fmt.Errorf("%s > cannot read user keystore: %s", um.instance.IDColor(), response.Status()) - } - - result, err := keystore.UnmarshalStatus(response.RawBody()) - if err != nil { - return nil, fmt.Errorf("%s > cannot parse user Keystore status response: %w", um.instance.IDColor(), err) - } - - return result, nil -} - -func (um *UserManager) KeystoreCreate(scope, id, keystorePassword string) (bool, error) { - statusResponse, statusError := um.KeystoreStatus(scope, id) - if statusError != nil { - return false, statusError - } - - if statusResponse.Created { - return false, nil - } - - pathParams := map[string]string{ - "newPassword": keystorePassword, - "rePassword": keystorePassword, - ":operation": "createStore", - } - - userKeystoreCreatePath := assembleUserPath(scope, id) + ".ks.html" - postResponse, postError := um.instance.http.Request().SetQueryParams(pathParams).Post(userKeystoreCreatePath) - - if postError != nil { - return false, fmt.Errorf("%s > cannot create user keystore: %w", um.instance.IDColor(), postError) - } - - if postResponse.IsError() { - return false, fmt.Errorf("%s > cannot create user keystore: %s", um.instance.IDColor(), postResponse.Status()) - } - - return true, nil -} - -func (um *UserManager) AddKeystoreKey(scope, id, keystoreFilePath, keystoreFilePassword, privateKeyAlias, privateKeyPassword, privateKeyNewAlias string) (bool, error) { - if !pathx.Exists(keystoreFilePath) { - return false, fmt.Errorf("%s > keystore file does not exist: %s", um.instance.IDColor(), keystoreFilePath) - } - if privateKeyNewAlias == "" { - privateKeyNewAlias = privateKeyAlias - } - if privateKeyPassword == "" { - privateKeyPassword = keystoreFilePassword - } - - readKeystore, err := readKeyStore(keystoreFilePath, []byte(keystoreFilePassword)) - if err != nil { - return false, fmt.Errorf("%s > cannot read keystore file %s: %w", um.instance.IDColor(), keystoreFilePath, err) - } - - aliases := readKeystore.Aliases() - if aliases == nil { - return false, fmt.Errorf("%s > keystore does not contain any aliases", um.instance.IDColor()) - } - if !slices.Contains(aliases, privateKeyAlias) { - return false, fmt.Errorf("%s > keystore does not contain alias: %s", um.instance.IDColor(), privateKeyAlias) - } - - status, err := um.KeystoreStatus(scope, id) - if err != nil { - return false, err - } - - if status == nil || !status.Created { - return false, fmt.Errorf("%s > cannot add keystore key: keystore does not exist", um.instance.IDColor()) - } - if status.HasAlias(privateKeyAlias) { - return false, nil - } - - requestFiles := map[string]string{ - "keyStore": keystoreFilePath, - } - - keystorePath := assembleUserPath(scope, id) + ".ks.html" - formData := map[string]string{ - "keyStorePass": keystoreFilePassword, - "alias": privateKeyAlias, - "keyPassword": privateKeyPassword, - "newAlias": privateKeyNewAlias, - "keyStoreType": "jks", - } - - response, err := um.instance.http.Request(). - SetFiles(requestFiles). - SetFormData(formData). - Post(keystorePath) - - if err != nil { - return false, fmt.Errorf("%s > cannot add keystore key: %w", um.instance.IDColor(), err) - } - if response.IsError() { - return false, fmt.Errorf("%s > cannot add keystore key: %s", um.instance.IDColor(), response.Status()) - } - return true, nil -} - -func (um *UserManager) DeleteKeystoreKey(scope, id, privateKeyAlias string) (bool, error) { - status, err := um.KeystoreStatus(scope, id) - if err != nil { - return false, err - } - - if status == nil || !status.Created { - return false, fmt.Errorf("%s > cannot delete keystore key: keystore does not exist", um.instance.IDColor()) - } - if !status.HasAlias(privateKeyAlias) { - return false, nil - } - - formData := map[string]string{ - "removeAlias": privateKeyAlias, - } - - userKeystorePath := assembleUserPath(scope, id) + ".ks.html" - response, err := um.instance.http.Request(). - SetFormData(formData). - Post(userKeystorePath) - - if err != nil { - return false, fmt.Errorf("%s > cannot delete keystore key: %w", um.instance.IDColor(), err) - } - if response.IsError() { - return false, fmt.Errorf("%s > cannot delete keystore key: %s", um.instance.IDColor(), response.Status()) - } - - return true, nil +func (um *UserManager) Keystore() *KeystoreManager { + return &KeystoreManager{instance: um.instance} } func (um *UserManager) ReadState(scope string, id string) (*user.Status, error) { - userPath := assembleUserPath(scope, id) + userPath := composeUserPath(scope, id) response, err := um.instance.http.Request().Get(userPath + ".json") @@ -195,7 +50,7 @@ func (um *UserManager) SetPassword(scope string, id string, password string) (bo return false, err } - userPath := assembleUserPath(scope, id) + userPath := composeUserPath(scope, id) passwordCheckResponse, err := um.instance.http.Request(). SetBasicAuth(userStatus.AuthorizableID, password). Get(userPath + ".json") @@ -222,7 +77,7 @@ func (um *UserManager) SetPassword(scope string, id string, password string) (bo return true, nil } -func assembleUserPath(scope string, id string) string { +func composeUserPath(scope string, id string) string { if scope == "" { return UsersPath + "/" + id } From 7ede2e494c11a9a0845bc7c3a1a5fd26fe6126d0 Mon Sep 17 00:00:00 2001 From: Piotr Andruszkiewicz Date: Tue, 27 May 2025 11:41:48 +0200 Subject: [PATCH 05/11] Fix capitalization in keystore and key management messages --- cmd/aem/user.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cmd/aem/user.go b/cmd/aem/user.go index e93b4230..22e6dee5 100644 --- a/cmd/aem/user.go +++ b/cmd/aem/user.go @@ -106,9 +106,9 @@ func (c *CLI) KeystoreCreate() *cobra.Command { } if changed { - c.Changed("User Keystore created") + c.Changed("User keystore created") } else { - c.Ok("User Keystore already exists") + c.Ok("User keystore already exists") } }, } @@ -148,9 +148,9 @@ func (c *CLI) userKeyAdd() *cobra.Command { return } if changed { - c.Changed("User Keystore key added") + c.Changed("User key added") } else { - c.Ok("User Keystore key already exists") + c.Ok("User key already exists") } }, } @@ -193,9 +193,9 @@ func (c *CLI) userKeyDelete() *cobra.Command { return } if changed { - c.Changed("User Keystore key deleted") + c.Changed("User key deleted") } else { - c.Ok("User Keystore key does not exist") + c.Ok("User key does not exist") } }, } From 4dabdca1d03a535f0e882a67775ecbceb348986c Mon Sep 17 00:00:00 2001 From: Piotr Andruszkiewicz Date: Tue, 27 May 2025 11:52:44 +0200 Subject: [PATCH 06/11] Fix capitalization in keystore command descriptions --- cmd/aem/user.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/aem/user.go b/cmd/aem/user.go index 22e6dee5..0a9897d8 100644 --- a/cmd/aem/user.go +++ b/cmd/aem/user.go @@ -54,7 +54,7 @@ func (c *CLI) userPassword() *cobra.Command { func (c *CLI) KeystoreStatus() *cobra.Command { cmd := &cobra.Command{ Use: "status", - Short: "Get status of a user's keystore", + Short: "Get status of a user keystore", Aliases: []string{"show", "get", "read", "describe", "ls"}, Run: func(cmd *cobra.Command, args []string) { instance, err := c.aem.InstanceManager().One() @@ -86,7 +86,7 @@ func (c *CLI) KeystoreStatus() *cobra.Command { func (c *CLI) KeystoreCreate() *cobra.Command { cmd := &cobra.Command{ Use: "create", - Short: "Create user's keystore", + Short: "Create user keystore", Aliases: []string{"make", "new"}, Run: func(cmd *cobra.Command, args []string) { instance, err := c.aem.InstanceManager().One() @@ -124,7 +124,7 @@ func (c *CLI) KeystoreCreate() *cobra.Command { func (c *CLI) userKeyAdd() *cobra.Command { cmd := &cobra.Command{ Use: "add", - Short: "Add user's private key to their keystore", + Short: "Add user private key to the keystore", Aliases: []string{"create", "new"}, Run: func(cmd *cobra.Command, args []string) { instance, err := c.aem.InstanceManager().One() @@ -173,7 +173,7 @@ func (c *CLI) userKeyAdd() *cobra.Command { func (c *CLI) userKeyDelete() *cobra.Command { cmd := &cobra.Command{ Use: "delete", - Short: "Delete user's private key from their keystore", + Short: "Delete user private key from the keystore", Aliases: []string{"remove", "rm"}, Run: func(cmd *cobra.Command, args []string) { instance, err := c.aem.InstanceManager().One() From 7f18fed2391e0242d495c2e868362ab2e22b5e8b Mon Sep 17 00:00:00 2001 From: Piotr Andruszkiewicz Date: Tue, 27 May 2025 11:54:06 +0200 Subject: [PATCH 07/11] Rename files for conciseness --- pkg/keystore/{keystore_private_key.go => private_key.go} | 0 pkg/keystore/{keystore_status.go => status.go} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename pkg/keystore/{keystore_private_key.go => private_key.go} (100%) rename pkg/keystore/{keystore_status.go => status.go} (100%) diff --git a/pkg/keystore/keystore_private_key.go b/pkg/keystore/private_key.go similarity index 100% rename from pkg/keystore/keystore_private_key.go rename to pkg/keystore/private_key.go diff --git a/pkg/keystore/keystore_status.go b/pkg/keystore/status.go similarity index 100% rename from pkg/keystore/keystore_status.go rename to pkg/keystore/status.go From 03897216cc96a501c2aebab7b53abce9a29ca599 Mon Sep 17 00:00:00 2001 From: Piotr Andruszkiewicz Date: Tue, 27 May 2025 11:56:31 +0200 Subject: [PATCH 08/11] Update GitHub Actions to use latest versions of cache, setup-go, and checkout actions --- .github/workflows/test.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index dccd950a..ce3d9b2c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,7 +19,7 @@ jobs: steps: - name: Cache Go modules - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: | ~/.cache/go-build @@ -29,13 +29,13 @@ jobs: ${{ runner.os }}-go- - name: Set up Go - uses: actions/setup-go@v2 + uses: actions/setup-go@v5 with: go-version: '1.20' id: go - name: Checkout source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Run build run: make From 967dd39b2592d827ff1680b2ed17546fc8b5a9a5 Mon Sep 17 00:00:00 2001 From: Piotr Andruszkiewicz Date: Tue, 27 May 2025 11:58:15 +0200 Subject: [PATCH 09/11] Update Go version to 1.24 in workflows and go.mod --- .github/workflows/release-perform.yml | 2 +- .github/workflows/test.yml | 2 +- go.mod | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release-perform.yml b/.github/workflows/release-perform.yml index 633a0db7..9840325d 100644 --- a/.github/workflows/release-perform.yml +++ b/.github/workflows/release-perform.yml @@ -31,7 +31,7 @@ jobs: - uses: actions/setup-go@v3 with: - go-version: '1.20' + go-version: '1.24' cache: true - uses: goreleaser/goreleaser-action@v2 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ce3d9b2c..8bcc9fc5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -31,7 +31,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version: '1.20' + go-version: '1.24' id: go - name: Checkout source code diff --git a/go.mod b/go.mod index 1b3518f9..d160dc18 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/wttech/aemc -go 1.20 +go 1.24 require ( github.com/Masterminds/goutils v1.1.1 From edfd649f96db5d4d7465cdf00397202bc117474a Mon Sep 17 00:00:00 2001 From: Piotr Andruszkiewicz Date: Tue, 27 May 2025 12:02:20 +0200 Subject: [PATCH 10/11] Refactor CLI and project package imports; streamline logging messages --- cmd/aem/cli.go | 23 ++++++++++++----------- go.mod | 3 +-- go.sum | 11 +++++++++-- pkg/project.go | 11 ++++++----- 4 files changed, 28 insertions(+), 20 deletions(-) diff --git a/cmd/aem/cli.go b/cmd/aem/cli.go index f1e321ed..4146a643 100644 --- a/cmd/aem/cli.go +++ b/cmd/aem/cli.go @@ -5,6 +5,14 @@ import ( "bytes" "encoding/json" "fmt" + "io" + "os" + "path" + "reflect" + "sort" + "strings" + "time" + "github.com/fatih/color" "github.com/iancoleman/strcase" "github.com/jmespath-community/go-jmespath" @@ -18,13 +26,6 @@ import ( "github.com/wttech/aemc/pkg/common/fmtx" "github.com/wttech/aemc/pkg/common/pathx" "github.com/wttech/aemc/pkg/common/stringsx" - "io" - "os" - "path" - "reflect" - "sort" - "strings" - "time" ) const ( @@ -206,7 +207,7 @@ func (c *CLI) openOutputLogFile() *os.File { } file, err := os.OpenFile(c.outputLogFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) if err != nil { - log.Fatalf(fmt.Sprintf("cannot open/create CLI output file '%s': %s", c.outputLogFile, err)) + log.Fatalf("cannot open/create CLI output file '%s': %s", c.outputLogFile, err) } return file } @@ -231,13 +232,13 @@ func (c *CLI) printCommandResult() { if c.outputNoColor { entry := log.WithField("changed", r.Changed).WithField("elapsed", r.Elapsed) if r.Failed { - entry.Errorf(msg) + entry.Error(msg) } else { - entry.Infof(msg) + entry.Info(msg) } } else { if r.Failed { - log.Errorf(color.RedString(msg)) + log.Error(color.RedString(msg)) } else { if r.Changed { log.Info(color.YellowString(msg)) diff --git a/go.mod b/go.mod index d160dc18..82af4845 100644 --- a/go.mod +++ b/go.mod @@ -23,6 +23,7 @@ require ( github.com/mholt/archiver/v3 v3.5.1 github.com/olekukonko/tablewriter v0.0.5 github.com/otiai10/copy v1.14.0 + github.com/pavlo-v-chernykh/keystore-go/v4 v4.5.0 github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 github.com/samber/lo v1.39.0 github.com/segmentio/textio v1.2.0 @@ -61,7 +62,6 @@ require ( github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/nwaples/rardecode v1.1.3 // indirect - github.com/pavlo-v-chernykh/keystore-go/v4 v4.5.0 // indirect github.com/pelletier/go-toml/v2 v2.1.1 // indirect github.com/pierrec/lz4/v4 v4.1.19 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect @@ -81,4 +81,3 @@ require ( golang.org/x/text v0.14.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect ) - diff --git a/go.sum b/go.sum index 4bf5ec94..6823638b 100644 --- a/go.sum +++ b/go.sum @@ -33,11 +33,13 @@ github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdf github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/essentialkaos/check v1.2.1 h1:avvyFy/1acUNwfxwuOLsHeCjfXtMygtbu0lVDr3nxFs= +github.com/essentialkaos/check v1.2.1/go.mod h1:PhxzfJWlf5L/skuyhzBLIvjMB5Xu9TIyDIsqpY5MvB8= github.com/essentialkaos/go-jar v1.0.6 h1:BAzvYTsXurNd/FcL4nYSH2pWV9DfavVAzlyYqULxcPE= github.com/essentialkaos/go-jar v1.0.6/go.mod h1:HZPc/zrzjoE2FjCRmIXZj7zQ6RN33/qjtf6ZxaEy7Hw= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/go-resty/resty/v2 v2.11.0 h1:i7jMfNOJYMp69lq7qozJP+bjgzfAzeOhuGlyDrqxT/8= @@ -79,7 +81,9 @@ github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQ github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -95,6 +99,7 @@ github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssn github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= @@ -107,8 +112,7 @@ github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6 github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU= github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w= github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks= -github.com/pavlo-v-chernykh/keystore-go v2.1.0+incompatible h1:/g2gGGxrs2aQU7IrnLlFL0h3CrBrhwNFpKYhXh/gKm4= -github.com/pavlo-v-chernykh/keystore-go v2.1.0+incompatible/go.mod h1:FUVGm7LLk1CudKS/gecQcL9T6GpdP2M97mJTuRzA5rw= +github.com/otiai10/mint v1.5.1/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM= github.com/pavlo-v-chernykh/keystore-go/v4 v4.5.0 h1:2nosf3P75OZv2/ZO/9Px5ZgZ5gbKrzA3joN1QMfOGMQ= github.com/pavlo-v-chernykh/keystore-go/v4 v4.5.0/go.mod h1:lAVhWwbNaveeJmxrxuSTxMgKpF6DjnuVpn6T8WiBwYQ= github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= @@ -123,6 +127,7 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 h1:OkMGxebDjyw0ULyrTYWeN0UNCCkmCWfjPnIA2W6oviI= github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06/go.mod h1:+ePHsJ1keEjQtpvf9HHw0f4ZeJ0TLRsxhunSI2hYJSs= @@ -222,6 +227,7 @@ golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= @@ -230,6 +236,7 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/project.go b/pkg/project.go index 9002df8f..d0dec35d 100644 --- a/pkg/project.go +++ b/pkg/project.go @@ -3,6 +3,9 @@ package pkg import ( "embed" "fmt" + "io/fs" + "strings" + "github.com/magiconair/properties" log "github.com/sirupsen/logrus" "github.com/wttech/aemc/pkg/common" @@ -10,8 +13,6 @@ import ( "github.com/wttech/aemc/pkg/common/osx" "github.com/wttech/aemc/pkg/common/pathx" "github.com/wttech/aemc/pkg/project" - "io/fs" - "strings" ) type Project struct { @@ -210,7 +211,7 @@ func (p Project) DirsIgnored() []string { } func (p Project) ScaffoldGettingStarted() string { - text := fmt.Sprintf(strings.Join([]string{ + text := fmt.Sprint(strings.Join([]string{ "AEM Compose project now contains required files.", "", "Consider saving the project to VCS repository.", @@ -222,7 +223,7 @@ func (p Project) ScaffoldGettingStarted() string { } func (p Project) InitGettingStartedError() string { - text := fmt.Sprintf(strings.Join([]string{ + text := fmt.Sprint(strings.Join([]string{ "AEM Compose project is not yet ready to use!", "", "Be sure to provide AEM files (SDK ZIP or Quickstart JAR + License + Service Packs) to directory '" + p.aem.BaseOpts().LibDir + "'.", @@ -231,7 +232,7 @@ func (p Project) InitGettingStartedError() string { } func (p Project) InitGettingStartedSuccess() string { - text := fmt.Sprintf(strings.Join([]string{ + text := fmt.Sprint(strings.Join([]string{ "AEM Compose project is ready to use!", "", fmt.Sprintf("Consider excluding the directories from VCS versioning and IDE indexing: %s", strings.Join(p.DirsIgnored(), ", ")), From 9ad92cf6e49e9f23379e23e6af72e5f43e7b26ac Mon Sep 17 00:00:00 2001 From: Piotr Andruszkiewicz Date: Tue, 27 May 2025 12:09:40 +0200 Subject: [PATCH 11/11] Refactor keystore management: streamline path composition and relocate readKeyStore function --- pkg/keystore_manager.go | 34 ++++++++++++++++++++++++++++++---- pkg/user_manager.go | 18 ------------------ 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/pkg/keystore_manager.go b/pkg/keystore_manager.go index 474bcc4b..2fd12a3c 100644 --- a/pkg/keystore_manager.go +++ b/pkg/keystore_manager.go @@ -2,8 +2,10 @@ package pkg import ( "fmt" + "os" "slices" + jks "github.com/pavlo-v-chernykh/keystore-go/v4" "github.com/wttech/aemc/pkg/common/pathx" "github.com/wttech/aemc/pkg/keystore" ) @@ -13,7 +15,7 @@ type KeystoreManager struct { } func (km *KeystoreManager) Status(scope, id string) (*keystore.Status, error) { - userKeystorePath := composeUserPath(scope, id) + ".ks.json" + userKeystorePath := composeKeystoreStatusPath(scope, id) response, err := km.instance.http.Request().Get(userKeystorePath) @@ -49,7 +51,7 @@ func (km *KeystoreManager) Create(scope, id, keystorePassword string) (bool, err ":operation": "createStore", } - userKeystoreCreatePath := composeUserPath(scope, id) + ".ks.html" + userKeystoreCreatePath := composeKeystoreOperationsPath(scope, id) postResponse, postError := km.instance.http.Request().SetQueryParams(pathParams).Post(userKeystoreCreatePath) if postError != nil { @@ -103,7 +105,7 @@ func (km *KeystoreManager) AddKey(scope, id, keystoreFilePath, keystoreFilePassw "keyStore": keystoreFilePath, } - keystorePath := composeUserPath(scope, id) + ".ks.html" + keystorePath := composeKeystoreOperationsPath(scope, id) formData := map[string]string{ "keyStorePass": keystoreFilePassword, "alias": privateKeyAlias, @@ -143,7 +145,7 @@ func (km *KeystoreManager) DeleteKey(scope, id, privateKeyAlias string) (bool, e "removeAlias": privateKeyAlias, } - userKeystorePath := composeUserPath(scope, id) + ".ks.html" + userKeystorePath := composeKeystoreOperationsPath(scope, id) response, err := km.instance.http.Request(). SetFormData(formData). Post(userKeystorePath) @@ -157,3 +159,27 @@ func (km *KeystoreManager) DeleteKey(scope, id, privateKeyAlias string) (bool, e return true, nil } + +func composeKeystoreStatusPath(scope, id string) string { + return composeUserPath(scope, id) + ".ks.json" +} + +func composeKeystoreOperationsPath(scope, id string) string { + return composeUserPath(scope, id) + ".ks.html" +} + +func readKeyStore(filename string, password []byte) (*jks.KeyStore, error) { + f, err := os.Open(filename) + if err != nil { + return nil, err + } + + defer f.Close() + + ks := jks.New() + if err := ks.Load(f, password); err != nil { + return nil, err + } + + return &ks, nil +} diff --git a/pkg/user_manager.go b/pkg/user_manager.go index 1d79aeef..d2722078 100644 --- a/pkg/user_manager.go +++ b/pkg/user_manager.go @@ -2,9 +2,7 @@ package pkg import ( "fmt" - "os" - jks "github.com/pavlo-v-chernykh/keystore-go/v4" "github.com/wttech/aemc/pkg/user" ) @@ -83,19 +81,3 @@ func composeUserPath(scope string, id string) string { } return UsersPath + "/" + scope + "/" + id } - -func readKeyStore(filename string, password []byte) (*jks.KeyStore, error) { - f, err := os.Open(filename) - if err != nil { - return nil, err - } - - defer f.Close() - - ks := jks.New() - if err := ks.Load(f, password); err != nil { - return nil, err - } - - return &ks, nil -}