diff --git a/CHANGELOG.md b/CHANGELOG.md index 448b2a83e..9e1ed2199 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # CHANGELOG +## v0.4.24 + +FEATURE: New `zrok admin create account` command that allows populating accounts directly into the underlying controller database (https://github.com/openziti/zrok/issues/551) + +CHANGE: The `zrok test loopback public` utility to report non-`200` errors and also ensure that the listening side of the test is fully established before starting loopback testing. + ## v0.4.23 FEATURE: New CLI commands have been implemented for working with the `drive` share backend mode (part of the "zrok Drives" functionality). These commands include `zrok cp`, `zrok mkdir` `zrok mv`, `zrok ls`, and `zrok rm`. These are initial, minimal versions of these commands and very likely contain bugs and ergonomic annoyances. There is a guide available at (`docs/guides/drives/cli.md`) that explains how to work with these tools in detail (https://github.com/openziti/zrok/issues/438) diff --git a/cmd/zrok/adminCreateAccount.go b/cmd/zrok/adminCreateAccount.go new file mode 100644 index 000000000..d6b5bc2c6 --- /dev/null +++ b/cmd/zrok/adminCreateAccount.go @@ -0,0 +1,66 @@ +package main + +import ( + "fmt" + "github.com/openziti/zrok/controller" + "github.com/openziti/zrok/controller/config" + "github.com/openziti/zrok/controller/store" + "github.com/spf13/cobra" +) + +func init() { + adminCreateCmd.AddCommand(newAdminCreateAccount().cmd) +} + +type adminCreateAccount struct { + cmd *cobra.Command +} + +func newAdminCreateAccount() *adminCreateAccount { + cmd := &cobra.Command{ + Use: "account ", + Short: "Pre-populate an account in the database; returns an enable token for the account", + Args: cobra.ExactArgs(3), + } + command := &adminCreateAccount{cmd: cmd} + cmd.Run = command.run + return command +} + +func (cmd *adminCreateAccount) run(_ *cobra.Command, args []string) { + cfg, err := config.LoadConfig(args[0]) + if err != nil { + panic(err) + } + str, err := store.Open(cfg.Store) + if err != nil { + panic(err) + } + token, err := controller.CreateToken() + if err != nil { + panic(err) + } + hpwd, err := controller.HashPassword(args[2]) + if err != nil { + panic(err) + } + trx, err := str.Begin() + if err != nil { + panic(err) + } + defer func() { + if err := trx.Commit(); err != nil { + panic(err) + } + }() + a := &store.Account{ + Email: args[1], + Salt: hpwd.Salt, + Password: hpwd.Password, + Token: token, + } + if _, err := str.CreateAccount(a, trx); err != nil { + panic(err) + } + fmt.Println(token) +} diff --git a/cmd/zrok/testLoopPublic.go b/cmd/zrok/testLoopPublic.go index 0b05c9bbb..a47ef7210 100644 --- a/cmd/zrok/testLoopPublic.go +++ b/cmd/zrok/testLoopPublic.go @@ -136,18 +136,19 @@ func (l *looper) run() { l.startup() logrus.Infof("looper #%d, shrToken: %v, frontend: %v", l.id, l.shrToken, l.proxyEndpoint) - go l.serviceListener() - l.dwell() - l.iterate() + if l.serviceListener() { + l.dwell() + l.iterate() + } logrus.Infof("looper #%d: complete", l.id) l.shutdown() } -func (l *looper) serviceListener() { +func (l *looper) serviceListener() bool { zcfg, err := ziti.NewConfigFromFile(l.zif) if err != nil { logrus.Errorf("error opening ziti config '%v': %v", l.zif, err) - return + return false } options := ziti.ListenOptions{ ConnectTimeout: 5 * time.Minute, @@ -156,15 +157,21 @@ func (l *looper) serviceListener() { zctx, err := ziti.NewContext(zcfg) if err != nil { logrus.Errorf("error loading ziti context: %v", err) - return + return false + } + + if l.listener, err = zctx.ListenWithOptions(l.shrToken, &options); err != nil { + logrus.Errorf("looper #%d, error listening: %v", l.id, err) + return false } - if l.listener, err = zctx.ListenWithOptions(l.shrToken, &options); err == nil { + + go func() { if err := http.Serve(l.listener, l); err != nil { logrus.Errorf("looper #%d, error serving: %v", l.id, err) } - } else { - logrus.Errorf("looper #%d, error listening: %v", l.id, err) - } + }() + + return true } func (l *looper) ServeHTTP(w http.ResponseWriter, r *http.Request) { @@ -239,6 +246,9 @@ func (l *looper) iterate() { if req, err := http.NewRequest("POST", l.proxyEndpoint, bytes.NewBufferString(outbase64)); err == nil { client := &http.Client{Timeout: time.Second * time.Duration(l.cmd.timeoutSeconds)} if resp, err := client.Do(req); err == nil { + if resp.StatusCode != 200 { + logrus.Errorf("looper #%d unexpected response status code %v!", l.id, resp.StatusCode) + } inpayload := new(bytes.Buffer) io.Copy(inpayload, resp.Body) inbase64 := inpayload.String() diff --git a/controller/access.go b/controller/access.go index 56f9c8c5d..3d84323a6 100644 --- a/controller/access.go +++ b/controller/access.go @@ -62,7 +62,7 @@ func (h *accessHandler) Handle(params share.AccessParams, principal *rest_model_ return share.NewAccessNotFound() } - feToken, err := createToken() + feToken, err := CreateToken() if err != nil { logrus.Error(err) return share.NewAccessInternalServerError() diff --git a/controller/createFrontend.go b/controller/createFrontend.go index 7f0371178..457673870 100644 --- a/controller/createFrontend.go +++ b/controller/createFrontend.go @@ -50,7 +50,7 @@ func (h *createFrontendHandler) Handle(params admin.CreateFrontendParams, princi } defer func() { _ = tx.Rollback() }() - feToken, err := createToken() + feToken, err := CreateToken() if err != nil { logrus.Errorf("error creating frontend token: %v", err) return admin.NewCreateFrontendInternalServerError() diff --git a/controller/invite.go b/controller/invite.go index dabd02c16..b9835cd6f 100644 --- a/controller/invite.go +++ b/controller/invite.go @@ -55,7 +55,7 @@ func (h *inviteHandler) Handle(params account.InviteParams) middleware.Responder logrus.Infof("using invite token '%v' to process invite request for '%v'", inviteToken.Token, params.Body.Email) } - token, err = createToken() + token, err = CreateToken() if err != nil { logrus.Error(err) return account.NewInviteInternalServerError() diff --git a/controller/passwords.go b/controller/passwords.go index 9e87e92cf..7feac74e5 100644 --- a/controller/passwords.go +++ b/controller/passwords.go @@ -24,7 +24,7 @@ func salt() string { return base64.StdEncoding.EncodeToString(buf) } -func hashPassword(password string) (*hashedPassword, error) { +func HashPassword(password string) (*hashedPassword, error) { return rehashPassword(password, salt()) } diff --git a/controller/register.go b/controller/register.go index 8bfb1fe93..7067c3561 100644 --- a/controller/register.go +++ b/controller/register.go @@ -38,7 +38,7 @@ func (h *registerHandler) Handle(params account.RegisterParams) middleware.Respo return account.NewRegisterNotFound() } - token, err := createToken() + token, err := CreateToken() if err != nil { logrus.Errorf("error creating token for request '%v' (%v): %v", params.Body.Token, ar.Email, err) return account.NewRegisterInternalServerError() @@ -49,7 +49,7 @@ func (h *registerHandler) Handle(params account.RegisterParams) middleware.Respo return account.NewRegisterUnprocessableEntity().WithPayload(rest_model_zrok.ErrorMessage(err.Error())) } - hpwd, err := hashPassword(params.Body.Password) + hpwd, err := HashPassword(params.Body.Password) if err != nil { logrus.Errorf("error hashing password for request '%v' (%v): %v", params.Body.Token, ar.Email, err) return account.NewRegisterInternalServerError() diff --git a/controller/resetPassword.go b/controller/resetPassword.go index 5327e3c72..fb66ed778 100644 --- a/controller/resetPassword.go +++ b/controller/resetPassword.go @@ -53,7 +53,7 @@ func (handler *resetPasswordHandler) Handle(params account.ResetPasswordParams) return account.NewResetPasswordUnprocessableEntity().WithPayload(rest_model_zrok.ErrorMessage(err.Error())) } - hpwd, err := hashPassword(params.Body.Password) + hpwd, err := HashPassword(params.Body.Password) if err != nil { logrus.Errorf("error hashing password for '%v' (%v): %v", params.Body.Token, a.Email, err) return account.NewResetPasswordRequestInternalServerError() diff --git a/controller/resetPasswordRequest.go b/controller/resetPasswordRequest.go index 7913d359b..c73c860af 100644 --- a/controller/resetPasswordRequest.go +++ b/controller/resetPasswordRequest.go @@ -34,7 +34,7 @@ func (handler *resetPasswordRequestHandler) Handle(params account.ResetPasswordR } defer func() { _ = tx.Rollback() }() - token, err = createToken() + token, err = CreateToken() if err != nil { logrus.Errorf("error creating token for '%v': %v", params.Body.EmailAddress, err) return account.NewResetPasswordRequestInternalServerError() diff --git a/controller/util.go b/controller/util.go index c490fde2c..cf70f9829 100644 --- a/controller/util.go +++ b/controller/util.go @@ -65,7 +65,7 @@ func createShareToken() (string, error) { return gen(), nil } -func createToken() (string, error) { +func CreateToken() (string, error) { gen, err := nanoid.CustomASCII("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", 12) if err != nil { return "", err