cmd/clef: add newaccount command (#20782)

* cmd/clef: add newaccount command

* cmd/clef: document clef_New, update API versioning

* Update cmd/clef/intapi_changelog.md

Co-Authored-By: ligi <ligi@ligi.de>

* Update signer/core/uiapi.go

Co-Authored-By: ligi <ligi@ligi.de>

Co-authored-by: ligi <ligi@ligi.de>
This commit is contained in:
Martin Holst Swende 2020-03-31 12:03:48 +02:00 committed by GitHub
parent 8f05cfa122
commit c56f4fa808
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 78 additions and 5 deletions

@ -10,6 +10,17 @@ TL;DR: Given a version number MAJOR.MINOR.PATCH, increment the:
Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format. Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format.
### 7.0.1
Added `clef_New` to the internal API calleable from a UI.
> `New` creates a new password protected Account. The private key is protected with
> the given password. Users are responsible to backup the private key that is stored
> in the keystore location that was specified when this API was created.
> This method is the same as New on the external API, the difference being that
> this implementation does not ask for confirmation, since it's initiated by
> the user
### 7.0.0 ### 7.0.0
- The `message` field was renamed to `messages` in all data signing request methods to better reflect that it's a list, not a value. - The `message` field was renamed to `messages` in all data signing request methods to better reflect that it's a list, not a value.

@ -187,6 +187,21 @@ The setpw command stores a password for a given address (keyfile).
Description: ` Description: `
The delpw command removes a password for a given address (keyfile). The delpw command removes a password for a given address (keyfile).
`} `}
newAccountCommand = cli.Command{
Action: utils.MigrateFlags(newAccount),
Name: "newaccount",
Usage: "Create a new account",
ArgsUsage: "",
Flags: []cli.Flag{
logLevelFlag,
keystoreFlag,
utils.LightKDFFlag,
},
Description: `
The newaccount command creates a new keystore-backed account. It is a convenience-method
which can be used in lieu of an external UI.`,
}
gendocCommand = cli.Command{ gendocCommand = cli.Command{
Action: GenDoc, Action: GenDoc,
Name: "gendoc", Name: "gendoc",
@ -222,7 +237,12 @@ func init() {
advancedMode, advancedMode,
} }
app.Action = signer app.Action = signer
app.Commands = []cli.Command{initCommand, attestCommand, setCredentialCommand, delCredentialCommand, gendocCommand} app.Commands = []cli.Command{initCommand,
attestCommand,
setCredentialCommand,
delCredentialCommand,
newAccountCommand,
gendocCommand}
cli.CommandHelpTemplate = utils.OriginCommandHelpTemplate cli.CommandHelpTemplate = utils.OriginCommandHelpTemplate
} }
@ -382,6 +402,31 @@ func removeCredential(ctx *cli.Context) error {
return nil return nil
} }
func newAccount(c *cli.Context) error {
if err := initialize(c); err != nil {
return err
}
// The newaccount is meant for users using the CLI, since 'real' external
// UIs can use the UI-api instead. So we'll just use the native CLI UI here.
var (
ui = core.NewCommandlineUI()
pwStorage storage.Storage = &storage.NoStorage{}
ksLoc = c.GlobalString(keystoreFlag.Name)
lightKdf = c.GlobalBool(utils.LightKDFFlag.Name)
)
log.Info("Starting clef", "keystore", ksLoc, "light-kdf", lightKdf)
am := core.StartClefAccountManager(ksLoc, true, lightKdf, "")
// This gives is us access to the external API
apiImpl := core.NewSignerAPI(am, 0, true, ui, nil, false, pwStorage)
// This gives us access to the internal API
internalApi := core.NewUIServerAPI(apiImpl)
addr, err := internalApi.New(context.Background())
if err == nil {
fmt.Printf("Generated account %v\n", addr.String())
}
return err
}
func initialize(c *cli.Context) error { func initialize(c *cli.Context) error {
// Set up the logger to print everything // Set up the logger to print everything
logOutput := os.Stdout logOutput := os.Stdout
@ -457,7 +502,6 @@ func signer(c *cli.Context) error {
api core.ExternalAPI api core.ExternalAPI
pwStorage storage.Storage = &storage.NoStorage{} pwStorage storage.Storage = &storage.NoStorage{}
) )
configDir := c.GlobalString(configdirFlag.Name) configDir := c.GlobalString(configdirFlag.Name)
if stretchedKey, err := readMasterKey(c, ui); err != nil { if stretchedKey, err := readMasterKey(c, ui); err != nil {
log.Warn("Failed to open master, rules disabled", "err", err) log.Warn("Failed to open master, rules disabled", "err", err)

@ -43,7 +43,7 @@ const (
// ExternalAPIVersion -- see extapi_changelog.md // ExternalAPIVersion -- see extapi_changelog.md
ExternalAPIVersion = "6.0.0" ExternalAPIVersion = "6.0.0"
// InternalAPIVersion -- see intapi_changelog.md // InternalAPIVersion -- see intapi_changelog.md
InternalAPIVersion = "7.0.0" InternalAPIVersion = "7.0.1"
) )
// ExternalAPI defines the external API through which signing requests are made. // ExternalAPI defines the external API through which signing requests are made.
@ -395,8 +395,7 @@ func (api *SignerAPI) List(ctx context.Context) ([]common.Address, error) {
// the given password. Users are responsible to backup the private key that is stored // the given password. Users are responsible to backup the private key that is stored
// in the keystore location thas was specified when this API was created. // in the keystore location thas was specified when this API was created.
func (api *SignerAPI) New(ctx context.Context) (common.Address, error) { func (api *SignerAPI) New(ctx context.Context) (common.Address, error) {
be := api.am.Backends(keystore.KeyStoreType) if be := api.am.Backends(keystore.KeyStoreType); len(be) == 0 {
if len(be) == 0 {
return common.Address{}, errors.New("password based accounts not supported") return common.Address{}, errors.New("password based accounts not supported")
} }
if resp, err := api.UI.ApproveNewAccount(&NewAccountRequest{MetadataFromContext(ctx)}); err != nil { if resp, err := api.UI.ApproveNewAccount(&NewAccountRequest{MetadataFromContext(ctx)}); err != nil {
@ -404,7 +403,16 @@ func (api *SignerAPI) New(ctx context.Context) (common.Address, error) {
} else if !resp.Approved { } else if !resp.Approved {
return common.Address{}, ErrRequestDenied return common.Address{}, ErrRequestDenied
} }
return api.newAccount()
}
// newAccount is the internal method to create a new account. It should be used
// _after_ user-approval has been obtained
func (api *SignerAPI) newAccount() (common.Address, error) {
be := api.am.Backends(keystore.KeyStoreType)
if len(be) == 0 {
return common.Address{}, errors.New("password based accounts not supported")
}
// Three retries to get a valid password // Three retries to get a valid password
for i := 0; i < 3; i++ { for i := 0; i < 3; i++ {
resp, err := api.UI.OnInputRequired(UserInputRequest{ resp, err := api.UI.OnInputRequired(UserInputRequest{

@ -195,6 +195,16 @@ func (api *UIServerAPI) Import(ctx context.Context, keyJSON json.RawMessage, old
return be[0].(*keystore.KeyStore).Import(keyJSON, oldPassphrase, newPassphrase) return be[0].(*keystore.KeyStore).Import(keyJSON, oldPassphrase, newPassphrase)
} }
// New creates a new password protected Account. The private key is protected with
// the given password. Users are responsible to backup the private key that is stored
// in the keystore location that was specified when this API was created.
// This method is the same as New on the external API, the difference being that
// this implementation does not ask for confirmation, since it's initiated by
// the user
func (api *UIServerAPI) New(ctx context.Context) (common.Address, error) {
return api.extApi.newAccount()
}
// Other methods to be added, not yet implemented are: // Other methods to be added, not yet implemented are:
// - Ruleset interaction: add rules, attest rulefiles // - Ruleset interaction: add rules, attest rulefiles
// - Store metadata about accounts, e.g. naming of accounts // - Store metadata about accounts, e.g. naming of accounts