瀏覽代碼

Merge pull request #202 from writeas/T319-delete-account

add account deletion

Ref T319
pull/260/head
Matt Baer 4 年之前
committed by GitHub
父節點
當前提交
f8a40fac4b
沒有發現已知的金鑰在資料庫的簽署中 GPG 金鑰 ID: 4AEE18F83AFDEB23
共有 4 個檔案被更改,包括 154 行新增49 行删除
  1. +5
    -1
      admin.go
  2. +47
    -1
      app.go
  3. +11
    -2
      cmd/writefreely/main.go
  4. +91
    -45
      database.go

+ 5
- 1
admin.go 查看文件

@@ -187,7 +187,11 @@ func handleViewAdminUser(app *App, u *User, w http.ResponseWriter, r *http.Reque
var err error var err error
p.User, err = app.db.GetUserForAuth(username) p.User, err = app.db.GetUserForAuth(username)
if err != nil { if err != nil {
return impart.HTTPError{http.StatusInternalServerError, fmt.Sprintf("Could not get user: %v", err)}
if err == ErrUserNotFound {
return err
}
log.Error("Could not get user: %v", err)
return impart.HTTPError{http.StatusInternalServerError, err.Error()}
} }


flashes, _ := getSessionFlashes(app, w, r, nil) flashes, _ := getSessionFlashes(app, w, r, nil)


+ 47
- 1
app.go 查看文件

@@ -30,7 +30,7 @@ import (
"github.com/gorilla/schema" "github.com/gorilla/schema"
"github.com/gorilla/sessions" "github.com/gorilla/sessions"
"github.com/manifoldco/promptui" "github.com/manifoldco/promptui"
"github.com/writeas/go-strip-markdown"
stripmd "github.com/writeas/go-strip-markdown"
"github.com/writeas/impart" "github.com/writeas/impart"
"github.com/writeas/web-core/auth" "github.com/writeas/web-core/auth"
"github.com/writeas/web-core/converter" "github.com/writeas/web-core/converter"
@@ -689,6 +689,52 @@ func ResetPassword(apper Apper, username string) error {
return nil return nil
} }


// DoDeleteAccount runs the confirmation and account delete process.
func DoDeleteAccount(apper Apper, username string) error {
// Connect to the database
apper.LoadConfig()
connectToDatabase(apper.App())
defer shutdown(apper.App())

// check user exists
u, err := apper.App().db.GetUserForAuth(username)
if err != nil {
log.Error("%s", err)
os.Exit(1)
}
userID := u.ID

// do not delete the admin account
// TODO: check for other admins and skip?
if u.IsAdmin() {
log.Error("Can not delete admin account")
os.Exit(1)
}

// confirm deletion, w/ w/out posts
prompt := promptui.Prompt{
Templates: &promptui.PromptTemplates{
Success: "{{ . | bold | faint }}: ",
},
Label: fmt.Sprintf("Really delete user : %s", username),
IsConfirm: true,
}
_, err = prompt.Run()
if err != nil {
log.Info("Aborted...")
os.Exit(0)
}

log.Info("Deleting...")
err = apper.App().db.DeleteAccount(userID)
if err != nil {
log.Error("%s", err)
os.Exit(1)
}
log.Info("Success.")
return nil
}

func connectToDatabase(app *App) { func connectToDatabase(app *App) {
log.Info("Connecting to %s database...", app.cfg.Database.Type) log.Info("Connecting to %s database...", app.cfg.Database.Type)




+ 11
- 2
cmd/writefreely/main.go 查看文件

@@ -13,11 +13,12 @@ package main
import ( import (
"flag" "flag"
"fmt" "fmt"
"os"
"strings"

"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/writeas/web-core/log" "github.com/writeas/web-core/log"
"github.com/writeas/writefreely" "github.com/writeas/writefreely"
"os"
"strings"
) )


func main() { func main() {
@@ -38,6 +39,7 @@ func main() {
// Admin actions // Admin actions
createAdmin := flag.String("create-admin", "", "Create an admin with the given username:password") createAdmin := flag.String("create-admin", "", "Create an admin with the given username:password")
createUser := flag.String("create-user", "", "Create a regular user with the given username:password") createUser := flag.String("create-user", "", "Create a regular user with the given username:password")
deleteUsername := flag.String("delete-user", "", "Delete a user with the given username")
resetPassUser := flag.String("reset-pass", "", "Reset the given user's password") resetPassUser := flag.String("reset-pass", "", "Reset the given user's password")
outputVersion := flag.Bool("v", false, "Output the current version") outputVersion := flag.Bool("v", false, "Output the current version")
flag.Parse() flag.Parse()
@@ -102,6 +104,13 @@ func main() {
os.Exit(1) os.Exit(1)
} }
os.Exit(0) os.Exit(0)
} else if *deleteUsername != "" {
err := writefreely.DoDeleteAccount(app, *deleteUsername)
if err != nil {
log.Error(err.Error())
os.Exit(1)
}
os.Exit(0)
} else if *migrate { } else if *migrate {
err := writefreely.Migrate(app) err := writefreely.Migrate(app)
if err != nil { if err != nil {


+ 91
- 45
database.go 查看文件

@@ -64,7 +64,7 @@ type writestore interface {
GetAccessToken(userID int64) (string, error) GetAccessToken(userID int64) (string, error)
GetTemporaryAccessToken(userID int64, validSecs int) (string, error) GetTemporaryAccessToken(userID int64, validSecs int) (string, error)
GetTemporaryOneTimeAccessToken(userID int64, validSecs int, oneTime bool) (string, error) GetTemporaryOneTimeAccessToken(userID int64, validSecs int, oneTime bool) (string, error)
DeleteAccount(userID int64) (l *string, err error)
DeleteAccount(userID int64) error
ChangeSettings(app *App, u *User, s *userSettings) error ChangeSettings(app *App, u *User, s *userSettings) error
ChangePassphrase(userID int64, sudo bool, curPass string, hashedPass []byte) error ChangePassphrase(userID int64, sudo bool, curPass string, hashedPass []byte) error


@@ -2114,22 +2114,13 @@ func (db *datastore) CollectionHasAttribute(id int64, attr string) bool {
return true return true
} }


func (db *datastore) DeleteAccount(userID int64) (l *string, err error) {
debug := ""
l = &debug

t, err := db.Begin()
if err != nil {
stringLogln(l, "Unable to begin: %v", err)
return
}

// DeleteAccount will delete the entire account for userID
func (db *datastore) DeleteAccount(userID int64) error {
// Get all collections // Get all collections
rows, err := db.Query("SELECT id, alias FROM collections WHERE owner_id = ?", userID) rows, err := db.Query("SELECT id, alias FROM collections WHERE owner_id = ?", userID)
if err != nil { if err != nil {
t.Rollback()
stringLogln(l, "Unable to get collections: %v", err)
return
log.Error("Unable to get collections: %v", err)
return err
} }
defer rows.Close() defer rows.Close()
colls := []Collection{} colls := []Collection{}
@@ -2137,103 +2128,158 @@ func (db *datastore) DeleteAccount(userID int64) (l *string, err error) {
for rows.Next() { for rows.Next() {
err = rows.Scan(&c.ID, &c.Alias) err = rows.Scan(&c.ID, &c.Alias)
if err != nil { if err != nil {
t.Rollback()
stringLogln(l, "Unable to scan collection cols: %v", err)
return
log.Error("Unable to scan collection cols: %v", err)
return err
} }
colls = append(colls, c) colls = append(colls, c)
} }


// Start transaction
t, err := db.Begin()
if err != nil {
log.Error("Unable to begin: %v", err)
return err
}

// Clean up all collection related information
var res sql.Result var res sql.Result
for _, c := range colls { for _, c := range colls {
// TODO: user deleteCollection() func
// Delete tokens // Delete tokens
res, err = t.Exec("DELETE FROM collectionattributes WHERE collection_id = ?", c.ID) res, err = t.Exec("DELETE FROM collectionattributes WHERE collection_id = ?", c.ID)
if err != nil { if err != nil {
t.Rollback() t.Rollback()
stringLogln(l, "Unable to delete attributes on %s: %v", c.Alias, err)
return
log.Error("Unable to delete attributes on %s: %v", c.Alias, err)
return err
} }
rs, _ := res.RowsAffected() rs, _ := res.RowsAffected()
stringLogln(l, "Deleted %d for %s from collectionattributes", rs, c.Alias)
log.Info("Deleted %d for %s from collectionattributes", rs, c.Alias)


// Remove any optional collection password // Remove any optional collection password
res, err = t.Exec("DELETE FROM collectionpasswords WHERE collection_id = ?", c.ID) res, err = t.Exec("DELETE FROM collectionpasswords WHERE collection_id = ?", c.ID)
if err != nil { if err != nil {
t.Rollback() t.Rollback()
stringLogln(l, "Unable to delete passwords on %s: %v", c.Alias, err)
return
log.Error("Unable to delete passwords on %s: %v", c.Alias, err)
return err
} }
rs, _ = res.RowsAffected() rs, _ = res.RowsAffected()
stringLogln(l, "Deleted %d for %s from collectionpasswords", rs, c.Alias)
log.Info("Deleted %d for %s from collectionpasswords", rs, c.Alias)


// Remove redirects to this collection // Remove redirects to this collection
res, err = t.Exec("DELETE FROM collectionredirects WHERE new_alias = ?", c.Alias) res, err = t.Exec("DELETE FROM collectionredirects WHERE new_alias = ?", c.Alias)
if err != nil { if err != nil {
t.Rollback() t.Rollback()
stringLogln(l, "Unable to delete redirects on %s: %v", c.Alias, err)
return
log.Error("Unable to delete redirects on %s: %v", c.Alias, err)
return err
} }
rs, _ = res.RowsAffected() rs, _ = res.RowsAffected()
stringLogln(l, "Deleted %d for %s from collectionredirects", rs, c.Alias)
log.Info("Deleted %d for %s from collectionredirects", rs, c.Alias)

// Remove any collection keys
res, err = t.Exec("DELETE FROM collectionkeys WHERE collection_id = ?", c.ID)
if err != nil {
t.Rollback()
log.Error("Unable to delete keys on %s: %v", c.Alias, err)
return err
}
rs, _ = res.RowsAffected()
log.Info("Deleted %d for %s from collectionkeys", rs, c.Alias)

// TODO: federate delete collection

// Remove remote follows
res, err = t.Exec("DELETE FROM remotefollows WHERE collection_id = ?", c.ID)
if err != nil {
t.Rollback()
log.Error("Unable to delete remote follows on %s: %v", c.Alias, err)
return err
}
rs, _ = res.RowsAffected()
log.Info("Deleted %d for %s from remotefollows", rs, c.Alias)
} }


// Delete collections // Delete collections
res, err = t.Exec("DELETE FROM collections WHERE owner_id = ?", userID) res, err = t.Exec("DELETE FROM collections WHERE owner_id = ?", userID)
if err != nil { if err != nil {
t.Rollback() t.Rollback()
stringLogln(l, "Unable to delete collections: %v", err)
return
log.Error("Unable to delete collections: %v", err)
return err
} }
rs, _ := res.RowsAffected() rs, _ := res.RowsAffected()
stringLogln(l, "Deleted %d from collections", rs)
log.Info("Deleted %d from collections", rs)


// Delete tokens // Delete tokens
res, err = t.Exec("DELETE FROM accesstokens WHERE user_id = ?", userID) res, err = t.Exec("DELETE FROM accesstokens WHERE user_id = ?", userID)
if err != nil { if err != nil {
t.Rollback() t.Rollback()
stringLogln(l, "Unable to delete access tokens: %v", err)
return
log.Error("Unable to delete access tokens: %v", err)
return err
} }
rs, _ = res.RowsAffected() rs, _ = res.RowsAffected()
stringLogln(l, "Deleted %d from accesstokens", rs)
log.Info("Deleted %d from accesstokens", rs)

// Delete user attributes
res, err = t.Exec("DELETE FROM oauth_users WHERE user_id = ?", userID)
if err != nil {
t.Rollback()
log.Error("Unable to delete oauth_users: %v", err)
return err
}
rs, _ = res.RowsAffected()
log.Info("Deleted %d from oauth_users", rs)


// Delete posts // Delete posts
// TODO: should maybe get each row so we can federate a delete
// if so needs to be outside of transaction like collections
res, err = t.Exec("DELETE FROM posts WHERE owner_id = ?", userID) res, err = t.Exec("DELETE FROM posts WHERE owner_id = ?", userID)
if err != nil { if err != nil {
t.Rollback() t.Rollback()
stringLogln(l, "Unable to delete posts: %v", err)
return
log.Error("Unable to delete posts: %v", err)
return err
} }
rs, _ = res.RowsAffected() rs, _ = res.RowsAffected()
stringLogln(l, "Deleted %d from posts", rs)
log.Info("Deleted %d from posts", rs)


// Delete user attributes
res, err = t.Exec("DELETE FROM userattributes WHERE user_id = ?", userID) res, err = t.Exec("DELETE FROM userattributes WHERE user_id = ?", userID)
if err != nil { if err != nil {
t.Rollback() t.Rollback()
stringLogln(l, "Unable to delete attributes: %v", err)
return
log.Error("Unable to delete attributes: %v", err)
return err
}
rs, _ = res.RowsAffected()
log.Info("Deleted %d from userattributes", rs)

// Delete user invites
res, err = t.Exec("DELETE FROM userinvites WHERE owner_id = ?", userID)
if err != nil {
t.Rollback()
log.Error("Unable to delete invites: %v", err)
return err
} }
rs, _ = res.RowsAffected() rs, _ = res.RowsAffected()
stringLogln(l, "Deleted %d from userattributes", rs)
log.Info("Deleted %d from userinvites", rs)


// Delete the user
res, err = t.Exec("DELETE FROM users WHERE id = ?", userID) res, err = t.Exec("DELETE FROM users WHERE id = ?", userID)
if err != nil { if err != nil {
t.Rollback() t.Rollback()
stringLogln(l, "Unable to delete user: %v", err)
return
log.Error("Unable to delete user: %v", err)
return err
} }
rs, _ = res.RowsAffected() rs, _ = res.RowsAffected()
stringLogln(l, "Deleted %d from users", rs)
log.Info("Deleted %d from users", rs)


// Commit all changes to the database
err = t.Commit() err = t.Commit()
if err != nil { if err != nil {
t.Rollback() t.Rollback()
stringLogln(l, "Unable to commit: %v", err)
return
log.Error("Unable to commit: %v", err)
return err
} }


return
// TODO: federate delete actor

return nil
} }


func (db *datastore) GetAPActorKeys(collectionID int64) ([]byte, []byte) { func (db *datastore) GetAPActorKeys(collectionID int64) ([]byte, []byte) {


Loading…
取消
儲存