Delete data part 1 (#360)

This commit is contained in:
Dominic Wong
2022-02-01 12:09:33 +00:00
committed by GitHub
parent 1f494e6638
commit 9b8d144dfd
9 changed files with 177 additions and 35 deletions

View File

@@ -9,7 +9,10 @@ import (
"github.com/micro/micro/v3/service/errors"
log "github.com/micro/micro/v3/service/logger"
pb "github.com/micro/services/cache/proto"
pauth "github.com/micro/services/pkg/auth"
"github.com/micro/services/pkg/cache"
adminpb "github.com/micro/services/pkg/service/proto"
"github.com/micro/services/pkg/tenant"
)
type Cache struct{}
@@ -133,3 +136,29 @@ func (c *Cache) ListKeys(ctx context.Context, req *pb.ListKeysRequest, rsp *pb.L
return nil
}
func (c *Cache) DeleteData(ctx context.Context, request *adminpb.DeleteDataRequest, response *adminpb.DeleteDataResponse) error {
method := "admin.DeleteData"
_, err := pauth.VerifyMicroAdmin(ctx, method)
if err != nil {
return err
}
if len(request.TenantId) == 0 {
return errors.BadRequest(method, "Missing tenant ID")
}
split := strings.Split(request.TenantId, "/")
tctx := tenant.NewContext(split[1], split[0], split[1])
keys, err := cache.Context(tctx).ListKeys()
if err != nil {
return err
}
for _, k := range keys {
if err := cache.Context(tctx).Delete(k); err != nil {
return err
}
}
log.Infof("Deleted %d keys for %s", len(keys), request.TenantId)
return nil
}

5
cache/main.go vendored
View File

@@ -3,6 +3,7 @@ package main
import (
"github.com/micro/services/cache/handler"
pb "github.com/micro/services/cache/proto"
adminpb "github.com/micro/services/pkg/service/proto"
"github.com/micro/services/pkg/tracing"
"github.com/micro/micro/v3/service"
@@ -17,7 +18,9 @@ func main() {
)
// Register handler
pb.RegisterCacheHandler(srv.Server(), new(handler.Cache))
c := new(handler.Cache)
pb.RegisterCacheHandler(srv.Server(), c)
adminpb.RegisterAdminHandler(srv.Server(), c)
traceCloser := tracing.SetupOpentracing("cache")
defer traceCloser.Close()

View File

@@ -6,6 +6,10 @@ import (
"github.com/google/uuid"
"github.com/micro/micro/v3/service/errors"
"github.com/micro/micro/v3/service/logger"
pauth "github.com/micro/services/pkg/auth"
adminpb "github.com/micro/services/pkg/service/proto"
"github.com/micro/services/pkg/tenant"
"github.com/micro/services/contact/domain"
pb "github.com/micro/services/contact/proto"
@@ -125,3 +129,42 @@ func (c *contact) List(ctx context.Context, req *pb.ListRequest, rsp *pb.ListRes
return nil
}
func (c *contact) DeleteData(ctx context.Context, request *adminpb.DeleteDataRequest, response *adminpb.DeleteDataResponse) error {
method := "admin.DeleteData"
_, err := pauth.VerifyMicroAdmin(ctx, method)
if err != nil {
return err
}
if len(request.TenantId) == 0 {
return errors.BadRequest(method, "Missing tenant ID")
}
split := strings.Split(request.TenantId, "/")
tctx := tenant.NewContext(split[1], split[0], split[1])
// load all keys
keys := []string{}
offset := uint(0)
for {
res, err := c.contact.List(tctx, offset, 100)
if err != nil && !strings.Contains(err.Error(), "not found") {
return err
}
for _, r := range res {
keys = append(keys, r.Id)
}
if len(res) < 100 {
break
}
offset += 100
}
for _, k := range keys {
if err := c.contact.Delete(tctx, k); err != nil {
return err
}
}
logger.Infof("Deleted %d keys for %s", len(keys), request.TenantId)
return nil
}

View File

@@ -2,6 +2,7 @@ package main
import (
"github.com/micro/micro/v3/service/store"
admin "github.com/micro/services/pkg/service/proto"
"github.com/micro/services/contact/domain"
"github.com/micro/services/contact/handler"
@@ -20,9 +21,10 @@ func main() {
contactDomain := domain.NewContactDomain(store.DefaultStore)
h := handler.NewContact(contactDomain)
// Register handler
pb.RegisterContactHandler(srv.Server(), handler.NewContact(contactDomain))
pb.RegisterContactHandler(srv.Server(), h)
admin.RegisterAdminHandler(srv.Server(), h)
// Run service
if err := srv.Run(); err != nil {
logger.Fatal(err)

View File

@@ -12,7 +12,9 @@ import (
"github.com/micro/micro/v3/service/errors"
"github.com/micro/micro/v3/service/logger"
db "github.com/micro/services/db/proto"
pauth "github.com/micro/services/pkg/auth"
gorm2 "github.com/micro/services/pkg/gorm"
adminpb "github.com/micro/services/pkg/service/proto"
"github.com/micro/services/pkg/tenant"
"github.com/patrickmn/go-cache"
"google.golang.org/protobuf/types/known/structpb"
@@ -441,3 +443,44 @@ func (e *Db) ListTables(ctx context.Context, req *db.ListTablesRequest, rsp *db.
}
return nil
}
func (e *Db) DeleteData(ctx context.Context, request *adminpb.DeleteDataRequest, response *adminpb.DeleteDataResponse) error {
method := "admin.DeleteData"
_, err := pauth.VerifyMicroAdmin(ctx, method)
if err != nil {
return err
}
if len(request.TenantId) == 0 {
return errors.BadRequest(method, "Missing tenant ID")
}
split := strings.Split(request.TenantId, "/")
tctx := tenant.NewContext(split[1], split[0], split[1])
tenantId := request.TenantId
tenantId = strings.Replace(strings.Replace(tenantId, "/", "_", -1), "-", "_", -1)
db, err := e.GetDBConn(tctx)
if err != nil {
return err
}
var tables []string
if err := db.Table("information_schema.tables").Select("table_name").Where("table_schema = ?", "public").Find(&tables).Error; err != nil {
return err
}
dropCount := 0
for _, v := range tables {
if !strings.HasPrefix(v, tenantId) {
continue
}
if err := db.Exec(fmt.Sprintf(dropTableStmt, v)).Error; err != nil {
return err
}
dropCount++
}
logger.Infof("Deleted %d tables for %s", dropCount, request.TenantId)
return nil
}

View File

@@ -2,6 +2,7 @@ package main
import (
pb "github.com/micro/services/db/proto"
admin "github.com/micro/services/pkg/service/proto"
"github.com/micro/services/pkg/tracing"
"github.com/micro/services/db/handler"
@@ -40,9 +41,7 @@ func main() {
// Register handler
pb.RegisterDbHandler(srv.Server(), h)
// Register handler
pb.RegisterDbHandler(srv.Server(), &handler.Db{})
admin.RegisterAdminHandler(srv.Server(), h)
traceCloser := tracing.SetupOpentracing("db")
defer traceCloser.Close()

34
pkg/auth/auth.go Normal file
View File

@@ -0,0 +1,34 @@
package auth
import (
"context"
"github.com/micro/micro/v3/service/auth"
"github.com/micro/micro/v3/service/errors"
)
func VerifyMicroAdmin(ctx context.Context, method string) (*auth.Account, error) {
acc, ok := auth.AccountFromContext(ctx)
if !ok {
return nil, errors.Unauthorized(method, "Unauthorized")
}
if err := doVerifyMicroAdmin(acc, method); err != nil {
return nil, err
}
return acc, nil
}
func doVerifyMicroAdmin(acc *auth.Account, method string) error {
errForbid := errors.Forbidden(method, "Forbidden")
if acc.Issuer != "micro" {
return errForbid
}
for _, s := range acc.Scopes {
if (s == "admin" && acc.Type == "user") || (s == "service" && acc.Type == "service") {
return nil
}
}
return errForbid
}

View File

@@ -8,6 +8,10 @@ import (
"github.com/micro/micro/v3/service/auth"
)
const (
metaOwner = "apikey_owner"
)
// FromContext returns a tenant from the context
func FromContext(ctx context.Context) (string, bool) {
acc, ok := auth.AccountFromContext(ctx)
@@ -21,7 +25,7 @@ func FromContext(ctx context.Context) (string, bool) {
func FromAccount(acc *auth.Account) string {
id := acc.ID
issuer := acc.Issuer
owner := acc.Metadata["apikey_owner"]
owner := acc.Metadata[metaOwner]
if len(id) == 0 {
id = "micro"
@@ -47,3 +51,14 @@ func CreateKey(ctx context.Context, key string) string {
// return a tenant prefixed key e.g micro/asim/foobar
return fmt.Sprintf("%s/%s", t, key)
}
// NewContext returns a context that will encapsulate the given tenant
func NewContext(id, issuer, owner string) context.Context {
return auth.ContextWithAccount(context.Background(), &auth.Account{
ID: id,
Issuer: issuer,
Metadata: map[string]string{
metaOwner: owner,
},
})
}

View File

@@ -10,10 +10,10 @@ import (
"time"
"github.com/google/uuid"
"github.com/micro/micro/v3/service/auth"
"github.com/micro/micro/v3/service/errors"
"github.com/micro/micro/v3/service/logger"
"github.com/micro/micro/v3/service/store"
pauth "github.com/micro/services/pkg/auth"
adminpb "github.com/micro/services/pkg/service/proto"
"golang.org/x/crypto/bcrypt"
@@ -510,7 +510,7 @@ func (s *User) VerifyToken(ctx context.Context, req *pb.VerifyTokenRequest, rsp
}
func (s *User) DeleteData(ctx context.Context, request *adminpb.DeleteDataRequest, response *adminpb.DeleteDataResponse) error {
if _, err := verifyMicroAdmin(ctx, "user.DeleteData"); err != nil {
if _, err := pauth.VerifyMicroAdmin(ctx, "user.DeleteData"); err != nil {
return err
}
@@ -519,29 +519,3 @@ func (s *User) DeleteData(ctx context.Context, request *adminpb.DeleteDataReques
}
return s.domain.DeleteTenantData(request.TenantId)
}
func verifyMicroAdmin(ctx context.Context, method string) (*auth.Account, error) {
acc, ok := auth.AccountFromContext(ctx)
if !ok {
return nil, errors.Unauthorized(method, "Unauthorized")
}
if err := doVerifyMicroAdmin(acc, method); err != nil {
return nil, err
}
return acc, nil
}
func doVerifyMicroAdmin(acc *auth.Account, method string) error {
errForbid := errors.Forbidden(method, "Forbidden")
if acc.Issuer != "micro" {
return errForbid
}
for _, s := range acc.Scopes {
if (s == "admin" && acc.Type == "user") || (s == "service" && acc.Type == "service") {
return nil
}
}
return errForbid
}