Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 107 additions & 8 deletions cmd/aem/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -29,10 +30,21 @@ func (c *CLI) userKeyStore() *cobra.Command {
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())
Expand All @@ -42,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",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just user keystore ? :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

Aliases: []string{"show", "get", "read", "describe", "ls"},
Run: func(cmd *cobra.Command, args []string) {
instance, err := c.aem.InstanceManager().One()
Expand All @@ -54,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)
Expand All @@ -68,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()
Expand All @@ -87,17 +98,17 @@ 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)
return
}

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")
}
},
}
Expand All @@ -110,6 +121,94 @@ func (c *CLI) KeystoreCreate() *cobra.Command {
return cmd
}

func (c *CLI) userKeyAdd() *cobra.Command {
cmd := &cobra.Command{
Use: "add",
Short: "Add user's private key to their keystore",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just user private key

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

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().Keystore().AddKey(
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 key added")
} else {
c.Ok("User 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) userKeyDelete() *cobra.Command {
cmd := &cobra.Command{
Use: "delete",
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()
if err != nil {
c.Error(err)
return
}

changed, err := instance.Auth().UserManager().Keystore().DeleteKey(
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 key deleted")
} else {
c.Ok("User 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",
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -80,3 +81,4 @@ require (
golang.org/x/text v0.14.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
)

4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down
9 changes: 8 additions & 1 deletion pkg/keystore/keystore_status.go
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

keystore/{keystore_status => status}.go

Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ package keystore

import (
"bytes"
"io"

"github.com/samber/lo"
"github.com/wttech/aemc/pkg/common/fmtx"
"io"
)

type Status struct {
Expand Down Expand Up @@ -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
})
}
159 changes: 159 additions & 0 deletions pkg/keystore_manager.go
Original file line number Diff line number Diff line change
@@ -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"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you are appending ".ks.json" here and on line 52 as well ; extract same func or sth


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
}
Loading
Loading