aboutsummaryrefslogtreecommitdiff
path: root/userapi/storage
diff options
context:
space:
mode:
authorLoïck Bonniot <git@lesterpig.com>2020-10-02 18:18:20 +0200
committerGitHub <noreply@github.com>2020-10-02 17:18:20 +0100
commit4e8c484618f5bfd5b7349cc2afd102cd5feb3861 (patch)
tree915618d3127f7ead8c5cce5a5814ae861841498a /userapi/storage
parent279044cd90722ba98b018b0aa277285113454822 (diff)
Implement account deactivation (#1455)
* Implement account deactivation See #610 Signed-off-by: Loïck Bonniot <git@lesterpig.com> * Rename 'is_active' to 'is_deactivated' Signed-off-by: Loïck Bonniot <git@lesterpig.com> Co-authored-by: Kegsay <kegan@matrix.org>
Diffstat (limited to 'userapi/storage')
-rw-r--r--userapi/storage/accounts/interface.go1
-rw-r--r--userapi/storage/accounts/postgres/accounts_table.go20
-rw-r--r--userapi/storage/accounts/postgres/deltas/20200929203058_is_active.sql9
-rw-r--r--userapi/storage/accounts/postgres/storage.go5
-rw-r--r--userapi/storage/accounts/sqlite3/accounts_table.go20
-rw-r--r--userapi/storage/accounts/sqlite3/deltas/20200929203058_is_active.sql38
-rw-r--r--userapi/storage/accounts/sqlite3/storage.go5
7 files changed, 94 insertions, 4 deletions
diff --git a/userapi/storage/accounts/interface.go b/userapi/storage/accounts/interface.go
index 49446f11..c86b2c39 100644
--- a/userapi/storage/accounts/interface.go
+++ b/userapi/storage/accounts/interface.go
@@ -51,6 +51,7 @@ type Database interface {
CheckAccountAvailability(ctx context.Context, localpart string) (bool, error)
GetAccountByLocalpart(ctx context.Context, localpart string) (*api.Account, error)
SearchProfiles(ctx context.Context, searchString string, limit int) ([]authtypes.Profile, error)
+ DeactivateAccount(ctx context.Context, localpart string) (err error)
}
// Err3PIDInUse is the error returned when trying to save an association involving
diff --git a/userapi/storage/accounts/postgres/accounts_table.go b/userapi/storage/accounts/postgres/accounts_table.go
index 7500e1e8..254da84c 100644
--- a/userapi/storage/accounts/postgres/accounts_table.go
+++ b/userapi/storage/accounts/postgres/accounts_table.go
@@ -37,7 +37,9 @@ CREATE TABLE IF NOT EXISTS account_accounts (
-- The password hash for this account. Can be NULL if this is a passwordless account.
password_hash TEXT,
-- Identifies which application service this account belongs to, if any.
- appservice_id TEXT
+ appservice_id TEXT,
+ -- If the account is currently active
+ is_deactivated BOOLEAN DEFAULT FALSE
-- TODO:
-- is_guest, is_admin, upgraded_ts, devices, any email reset stuff?
);
@@ -51,11 +53,14 @@ const insertAccountSQL = "" +
const updatePasswordSQL = "" +
"UPDATE account_accounts SET password_hash = $1 WHERE localpart = $2"
+const deactivateAccountSQL = "" +
+ "UPDATE account_accounts SET is_deactivated = TRUE WHERE localpart = $1"
+
const selectAccountByLocalpartSQL = "" +
"SELECT localpart, appservice_id FROM account_accounts WHERE localpart = $1"
const selectPasswordHashSQL = "" +
- "SELECT password_hash FROM account_accounts WHERE localpart = $1"
+ "SELECT password_hash FROM account_accounts WHERE localpart = $1 AND is_deactivated = FALSE"
const selectNewNumericLocalpartSQL = "" +
"SELECT nextval('numeric_username_seq')"
@@ -63,6 +68,7 @@ const selectNewNumericLocalpartSQL = "" +
type accountsStatements struct {
insertAccountStmt *sql.Stmt
updatePasswordStmt *sql.Stmt
+ deactivateAccountStmt *sql.Stmt
selectAccountByLocalpartStmt *sql.Stmt
selectPasswordHashStmt *sql.Stmt
selectNewNumericLocalpartStmt *sql.Stmt
@@ -80,6 +86,9 @@ func (s *accountsStatements) prepare(db *sql.DB, server gomatrixserverlib.Server
if s.updatePasswordStmt, err = db.Prepare(updatePasswordSQL); err != nil {
return
}
+ if s.deactivateAccountStmt, err = db.Prepare(deactivateAccountSQL); err != nil {
+ return
+ }
if s.selectAccountByLocalpartStmt, err = db.Prepare(selectAccountByLocalpartSQL); err != nil {
return
}
@@ -127,6 +136,13 @@ func (s *accountsStatements) updatePassword(
return
}
+func (s *accountsStatements) deactivateAccount(
+ ctx context.Context, localpart string,
+) (err error) {
+ _, err = s.deactivateAccountStmt.ExecContext(ctx, localpart)
+ return
+}
+
func (s *accountsStatements) selectPasswordHash(
ctx context.Context, localpart string,
) (hash string, err error) {
diff --git a/userapi/storage/accounts/postgres/deltas/20200929203058_is_active.sql b/userapi/storage/accounts/postgres/deltas/20200929203058_is_active.sql
new file mode 100644
index 00000000..32e6e166
--- /dev/null
+++ b/userapi/storage/accounts/postgres/deltas/20200929203058_is_active.sql
@@ -0,0 +1,9 @@
+-- +goose Up
+-- +goose StatementBegin
+ALTER TABLE account_accounts ADD COLUMN IF NOT EXISTS is_deactivated BOOLEAN DEFAULT FALSE;
+-- +goose StatementEnd
+
+-- +goose Down
+-- +goose StatementBegin
+ALTER TABLE account_accounts DROP COLUMN is_deactivated;
+-- +goose StatementEnd
diff --git a/userapi/storage/accounts/postgres/storage.go b/userapi/storage/accounts/postgres/storage.go
index 8b9ebef8..2230f7e7 100644
--- a/userapi/storage/accounts/postgres/storage.go
+++ b/userapi/storage/accounts/postgres/storage.go
@@ -317,3 +317,8 @@ func (d *Database) SearchProfiles(ctx context.Context, searchString string, limi
) ([]authtypes.Profile, error) {
return d.profiles.selectProfilesBySearch(ctx, searchString, limit)
}
+
+// DeactivateAccount deactivates the user's account, removing all ability for the user to login again.
+func (d *Database) DeactivateAccount(ctx context.Context, localpart string) (err error) {
+ return d.accounts.deactivateAccount(ctx, localpart)
+}
diff --git a/userapi/storage/accounts/sqlite3/accounts_table.go b/userapi/storage/accounts/sqlite3/accounts_table.go
index 2d935fb6..d0ea8a8b 100644
--- a/userapi/storage/accounts/sqlite3/accounts_table.go
+++ b/userapi/storage/accounts/sqlite3/accounts_table.go
@@ -37,7 +37,9 @@ CREATE TABLE IF NOT EXISTS account_accounts (
-- The password hash for this account. Can be NULL if this is a passwordless account.
password_hash TEXT,
-- Identifies which application service this account belongs to, if any.
- appservice_id TEXT
+ appservice_id TEXT,
+ -- If the account is currently active
+ is_deactivated BOOLEAN DEFAULT 0
-- TODO:
-- is_guest, is_admin, upgraded_ts, devices, any email reset stuff?
);
@@ -49,11 +51,14 @@ const insertAccountSQL = "" +
const updatePasswordSQL = "" +
"UPDATE account_accounts SET password_hash = $1 WHERE localpart = $2"
+const deactivateAccountSQL = "" +
+ "UPDATE account_accounts SET is_deactivated = 1 WHERE localpart = $1"
+
const selectAccountByLocalpartSQL = "" +
"SELECT localpart, appservice_id FROM account_accounts WHERE localpart = $1"
const selectPasswordHashSQL = "" +
- "SELECT password_hash FROM account_accounts WHERE localpart = $1"
+ "SELECT password_hash FROM account_accounts WHERE localpart = $1 AND is_deactivated = 0"
const selectNewNumericLocalpartSQL = "" +
"SELECT COUNT(localpart) FROM account_accounts"
@@ -62,6 +67,7 @@ type accountsStatements struct {
db *sql.DB
insertAccountStmt *sql.Stmt
updatePasswordStmt *sql.Stmt
+ deactivateAccountStmt *sql.Stmt
selectAccountByLocalpartStmt *sql.Stmt
selectPasswordHashStmt *sql.Stmt
selectNewNumericLocalpartStmt *sql.Stmt
@@ -81,6 +87,9 @@ func (s *accountsStatements) prepare(db *sql.DB, server gomatrixserverlib.Server
if s.updatePasswordStmt, err = db.Prepare(updatePasswordSQL); err != nil {
return
}
+ if s.deactivateAccountStmt, err = db.Prepare(deactivateAccountSQL); err != nil {
+ return
+ }
if s.selectAccountByLocalpartStmt, err = db.Prepare(selectAccountByLocalpartSQL); err != nil {
return
}
@@ -128,6 +137,13 @@ func (s *accountsStatements) updatePassword(
return
}
+func (s *accountsStatements) deactivateAccount(
+ ctx context.Context, localpart string,
+) (err error) {
+ _, err = s.deactivateAccountStmt.ExecContext(ctx, localpart)
+ return
+}
+
func (s *accountsStatements) selectPasswordHash(
ctx context.Context, localpart string,
) (hash string, err error) {
diff --git a/userapi/storage/accounts/sqlite3/deltas/20200929203058_is_active.sql b/userapi/storage/accounts/sqlite3/deltas/20200929203058_is_active.sql
new file mode 100644
index 00000000..51e9bae3
--- /dev/null
+++ b/userapi/storage/accounts/sqlite3/deltas/20200929203058_is_active.sql
@@ -0,0 +1,38 @@
+-- +goose Up
+-- +goose StatementBegin
+ALTER TABLE account_accounts RENAME TO account_accounts_tmp;
+CREATE TABLE account_accounts (
+ localpart TEXT NOT NULL PRIMARY KEY,
+ created_ts BIGINT NOT NULL,
+ password_hash TEXT,
+ appservice_id TEXT,
+ is_deactivated BOOLEAN DEFAULT 0
+);
+INSERT
+ INTO account_accounts (
+ localpart, created_ts, password_hash, appservice_id
+ ) SELECT
+ localpart, created_ts, password_hash, appservice_id
+ FROM account_accounts_tmp
+;
+DROP TABLE account_accounts_tmp;
+-- +goose StatementEnd
+
+-- +goose Down
+-- +goose StatementBegin
+ALTER TABLE account_accounts RENAME TO account_accounts_tmp;
+CREATE TABLE account_accounts (
+ localpart TEXT NOT NULL PRIMARY KEY,
+ created_ts BIGINT NOT NULL,
+ password_hash TEXT,
+ appservice_id TEXT
+);
+INSERT
+ INTO account_accounts (
+ localpart, created_ts, password_hash, appservice_id
+ ) SELECT
+ localpart, created_ts, password_hash, appservice_id
+ FROM account_accounts_tmp
+;
+DROP TABLE account_accounts_tmp;
+-- +goose StatementEnd
diff --git a/userapi/storage/accounts/sqlite3/storage.go b/userapi/storage/accounts/sqlite3/storage.go
index 4b66304c..7a2830a9 100644
--- a/userapi/storage/accounts/sqlite3/storage.go
+++ b/userapi/storage/accounts/sqlite3/storage.go
@@ -359,3 +359,8 @@ func (d *Database) SearchProfiles(ctx context.Context, searchString string, limi
) ([]authtypes.Profile, error) {
return d.profiles.selectProfilesBySearch(ctx, searchString, limit)
}
+
+// DeactivateAccount deactivates the user's account, removing all ability for the user to login again.
+func (d *Database) DeactivateAccount(ctx context.Context, localpart string) (err error) {
+ return d.accounts.deactivateAccount(ctx, localpart)
+}