From 9d90f7a2cc10ec2bf96114b5c3d07fed9d45cdbf Mon Sep 17 00:00:00 2001 From: Michael Quigley Date: Wed, 25 Jun 2025 14:04:23 -0400 Subject: [PATCH] infrastructure for account delete api endpoint (#993) --- controller/controller.go | 1 + controller/createAccount.go | 7 ++-- controller/deleteAccount.go | 72 +++++++++++++++++++++++++++++++++++++ controller/disable.go | 62 ++++++++++++++++++-------------- controller/store/account.go | 11 ++++++ 5 files changed, 122 insertions(+), 31 deletions(-) create mode 100644 controller/deleteAccount.go diff --git a/controller/controller.go b/controller/controller.go index 7c8daecf..4493374f 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -58,6 +58,7 @@ func Run(inCfg *config.Config) error { api.AdminCreateFrontendHandler = newCreateFrontendHandler() api.AdminCreateIdentityHandler = newCreateIdentityHandler() api.AdminCreateOrganizationHandler = newCreateOrganizationHandler() + api.AdminDeleteAccountHandler = newDeleteAccountHandler() api.AdminDeleteFrontendGrantHandler = newDeleteFrontendGrantHandler() api.AdminDeleteFrontendHandler = newDeleteFrontendHandler() api.AdminDeleteOrganizationHandler = newDeleteOrganizationHandler() diff --git a/controller/createAccount.go b/controller/createAccount.go index 269cff07..d5298964 100644 --- a/controller/createAccount.go +++ b/controller/createAccount.go @@ -16,7 +16,7 @@ func newCreateAccountHandler() *createAccountHandler { func (h *createAccountHandler) Handle(params admin.CreateAccountParams, principal *rest_model_zrok.Principal) middleware.Responder { if !principal.Admin { - logrus.Errorf("invalid admin principal") + logrus.Error("invalid admin principal") return admin.NewCreateAccountUnauthorized() } @@ -36,9 +36,8 @@ func (h *createAccountHandler) Handle(params admin.CreateAccountParams, principa logrus.Errorf("error starting transaction: %v", err) return admin.NewCreateAccountInternalServerError() } - defer func() { - _ = trx.Rollback() - }() + defer trx.Rollback() + a := &store.Account{ Email: params.Body.Email, Salt: hpwd.Salt, diff --git a/controller/deleteAccount.go b/controller/deleteAccount.go new file mode 100644 index 00000000..2b65b09f --- /dev/null +++ b/controller/deleteAccount.go @@ -0,0 +1,72 @@ +package controller + +import ( + "github.com/go-openapi/runtime/middleware" + "github.com/openziti/zrok/controller/zrokEdgeSdk" + "github.com/openziti/zrok/rest_model_zrok" + "github.com/openziti/zrok/rest_server_zrok/operations/admin" + "github.com/sirupsen/logrus" +) + +type deleteAccountHandler struct{} + +func newDeleteAccountHandler() *deleteAccountHandler { + return &deleteAccountHandler{} +} + +func (h *deleteAccountHandler) Handle(params admin.DeleteAccountParams, principal *rest_model_zrok.Principal) middleware.Responder { + if !principal.Admin { + logrus.Error("invalid admin principal") + return admin.NewDeleteAccountUnauthorized() + } + + logrus.Infof("starting deletion of account with email '%s'", params.Body.Email) + + trx, err := str.Begin() + if err != nil { + logrus.Errorf("error starting transaction: %v", err) + return admin.NewDeleteAccountInternalServerError() + } + defer trx.Rollback() + + account, err := str.FindAccountWithEmail(params.Body.Email, trx) + if err != nil { + logrus.Errorf("error finding account with email '%s': %v", params.Body.Email, err) + return admin.NewDeleteAccountNotFound() + } + + envs, err := str.FindEnvironmentsForAccount(account.Id, trx) + if err != nil { + logrus.Errorf("error finding environments for account '%s': %v", params.Body.Email, err) + return admin.NewDeleteAccountInternalServerError() + } + logrus.Infof("found %d environments to clean up for account '%s'", len(envs), params.Body.Email) + + edge, err := zrokEdgeSdk.Client(cfg.Ziti) + if err != nil { + logrus.Errorf("error getting edge client: %v", err) + return admin.NewDeleteAccountInternalServerError() + } + + for _, env := range envs { + logrus.Infof("disabling environment %d (ZId: %s) for account '%s'", env.Id, env.ZId, params.Body.Email) + if err := disableEnvironment(env, trx, edge); err != nil { + logrus.Errorf("error disabling environment %d for account '%s': %v", env.Id, params.Body.Email, err) + return admin.NewDeleteAccountInternalServerError() + } + logrus.Infof("successfully disabled environment %d for account '%s'", env.Id, params.Body.Email) + } + + if err := str.DeleteAccount(account.Id, trx); err != nil { + logrus.Errorf("error deleting account '%s': %v", params.Body.Email, err) + return admin.NewDeleteAccountInternalServerError() + } + + if err := trx.Commit(); err != nil { + logrus.Errorf("error committing transaction: %v", err) + return admin.NewDeleteAccountInternalServerError() + } + + logrus.Infof("successfully deleted account '%s'", params.Body.Email) + return admin.NewDeleteAccountOK() +} diff --git a/controller/disable.go b/controller/disable.go index fceb2c4a..9d47a666 100644 --- a/controller/disable.go +++ b/controller/disable.go @@ -3,6 +3,8 @@ package controller import ( "context" "fmt" + "time" + "github.com/go-openapi/runtime/middleware" "github.com/jmoiron/sqlx" "github.com/openziti/edge-api/rest_management_api_client" @@ -13,7 +15,6 @@ import ( "github.com/openziti/zrok/rest_server_zrok/operations/environment" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "time" ) type disableHandler struct{} @@ -29,48 +30,55 @@ func (h *disableHandler) Handle(params environment.DisableParams, principal *res return environment.NewDisableInternalServerError() } defer func() { _ = trx.Rollback() }() + env, err := str.FindEnvironmentForAccount(params.Body.Identity, int(principal.ID), trx) if err != nil { logrus.Errorf("identity check failed for user '%v': %v", principal.Email, err) return environment.NewDisableUnauthorized() } + edge, err := zrokEdgeSdk.Client(cfg.Ziti) if err != nil { logrus.Errorf("error getting edge client for user '%v': %v", principal.Email, err) return environment.NewDisableInternalServerError() } - if err := h.removeSharesForEnvironment(env, trx, edge); err != nil { - logrus.Errorf("error removing shares for environment for user '%v': %v", principal.Email, err) - return environment.NewDisableInternalServerError() - } - if err := h.removeFrontendsForEnvironment(env, trx, edge); err != nil { - logrus.Errorf("error removing frontends for environment for user '%v': %v", principal.Email, err) - return environment.NewDisableInternalServerError() - } - if err := h.removeAgentRemoteForEnvironment(env, trx, edge); err != nil { - logrus.Errorf("error removing agent remote for '%v' (%v): %v", env.ZId, principal.Email, err) - return environment.NewDisableInternalServerError() - } - if err := zrokEdgeSdk.DeleteEdgeRouterPolicy(env.ZId, edge); err != nil { - logrus.Errorf("error deleting edge router policy for user '%v': %v", principal.Email, err) - return environment.NewDisableInternalServerError() - } - if err := zrokEdgeSdk.DeleteIdentity(env.ZId, edge); err != nil { - logrus.Errorf("error deleting identity for user '%v': %v", principal.Email, err) - return environment.NewDisableInternalServerError() - } - if err := h.removeEnvironmentFromStore(env, trx); err != nil { - logrus.Errorf("error removing environment for user '%v': %v", principal.Email, err) + + if err := disableEnvironment(env, trx, edge); err != nil { + logrus.Errorf("error disabling environment for user '%v': %v", principal.Email, err) return environment.NewDisableInternalServerError() } + if err := trx.Commit(); err != nil { logrus.Errorf("error committing for user '%v': %v", principal.Email, err) return environment.NewDisableInternalServerError() } + return environment.NewDisableOK() } -func (h *disableHandler) removeSharesForEnvironment(env *store.Environment, trx *sqlx.Tx, edge *rest_management_api_client.ZitiEdgeManagement) error { +func disableEnvironment(env *store.Environment, trx *sqlx.Tx, edge *rest_management_api_client.ZitiEdgeManagement) error { + if err := removeSharesForEnvironment(env, trx, edge); err != nil { + return errors.Wrapf(err, "error removing shares for environment '%v'", env.ZId) + } + if err := removeFrontendsForEnvironment(env, trx, edge); err != nil { + return errors.Wrapf(err, "error removing frontends for environment '%v'", env.ZId) + } + if err := removeAgentRemoteForEnvironment(env, trx, edge); err != nil { + return errors.Wrapf(err, "error removing agent remote for '%v'", env.ZId) + } + if err := zrokEdgeSdk.DeleteEdgeRouterPolicy(env.ZId, edge); err != nil { + return errors.Wrapf(err, "error deleting edge router policy for environment '%v'", env.ZId) + } + if err := zrokEdgeSdk.DeleteIdentity(env.ZId, edge); err != nil { + return errors.Wrapf(err, "error deleting identity for environment '%v'", env.ZId) + } + if err := removeEnvironmentFromStore(env, trx); err != nil { + return errors.Wrapf(err, "error removing environment '%v' from store", env.ZId) + } + return nil +} + +func removeSharesForEnvironment(env *store.Environment, trx *sqlx.Tx, edge *rest_management_api_client.ZitiEdgeManagement) error { shrs, err := str.FindSharesForEnvironment(env.Id, trx) if err != nil { return err @@ -98,7 +106,7 @@ func (h *disableHandler) removeSharesForEnvironment(env *store.Environment, trx return nil } -func (h *disableHandler) removeFrontendsForEnvironment(env *store.Environment, trx *sqlx.Tx, edge *rest_management_api_client.ZitiEdgeManagement) error { +func removeFrontendsForEnvironment(env *store.Environment, trx *sqlx.Tx, edge *rest_management_api_client.ZitiEdgeManagement) error { fes, err := str.FindFrontendsForEnvironment(env.Id, trx) if err != nil { return err @@ -111,7 +119,7 @@ func (h *disableHandler) removeFrontendsForEnvironment(env *store.Environment, t return nil } -func (h *disableHandler) removeAgentRemoteForEnvironment(env *store.Environment, trx *sqlx.Tx, edge *rest_management_api_client.ZitiEdgeManagement) error { +func removeAgentRemoteForEnvironment(env *store.Environment, trx *sqlx.Tx, edge *rest_management_api_client.ZitiEdgeManagement) error { enrolled, err := str.IsAgentEnrolledForEnvironment(env.Id, trx) if err != nil { return err @@ -160,7 +168,7 @@ func (h *disableHandler) removeAgentRemoteForEnvironment(env *store.Environment, return nil } -func (h *disableHandler) removeEnvironmentFromStore(env *store.Environment, trx *sqlx.Tx) error { +func removeEnvironmentFromStore(env *store.Environment, trx *sqlx.Tx) error { shrs, err := str.FindSharesForEnvironment(env.Id, trx) if err != nil { return errors.Wrapf(err, "error finding shares for environment '%d'", env.Id) diff --git a/controller/store/account.go b/controller/store/account.go index 7f1d683d..8e1adc1c 100644 --- a/controller/store/account.go +++ b/controller/store/account.go @@ -70,3 +70,14 @@ func (str *Store) UpdateAccount(a *Account, tx *sqlx.Tx) (int, error) { } return id, nil } + +func (str *Store) DeleteAccount(id int, trx *sqlx.Tx) error { + stmt, err := trx.Prepare("update accounts set deleted = true where id = $1") + if err != nil { + return errors.Wrap(err, "error preparing accounts delete statement") + } + if _, err := stmt.Exec(id); err != nil { + return errors.Wrap(err, "error executing accounts delete statement") + } + return nil +}