refactor: убрать вход через внешние провайдеры (OAuth) полностью
Решение владельца 27.09.2026 (D-084): сервис ставится на сервер конкретного человека, и ему всё равно нужен свой OAuth у провайдера — поддержка общего входа только создаёт трение (регистрация приложений, redirect URI, модерация, чужие ключи в конфиге). Способы входа остаются: пароль + 2FA и ключи доступа (WebAuthn). Удалено: - сервер: internal/auth/oauth.go, internal/server/oauth.go, internal/store/oauth.go и их тесты; поля и методы конфига OAuth*; oauthLimiter и регистрация ручек; отображение ошибок oauth.*; features.oauth_enabled/oauth_providers в /meta; - клиент: web/src/api/oauth.ts, раздел «Вход через внешние сервисы», кнопки провайдеров на странице входа, ключи i18n (ru/en), тесты и фикстуры; - установщик: переменные OAUTH_* из .env, .env.example и шаблона (хелпер чтения существующих значений переименован в existing_value — он остался нужен для VAPID_SUBJECT); - зависимость golang.org/x/oauth2 (go mod tidy). Схема: миграция 00027 удаляет таблицу oauth_accounts (00019 не переписываем — она применена на стендах). Откат миграции возвращает структуру; тест TestOAuthRemovalMigration проверяет накат, откат и повторный накат. AGENT.md (локальный) помечает пункты про OAuth как отменённые.
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
-- +goose Up
|
||||
-- Вход через внешние провайдеры (OAuth) удалён по решению владельца
|
||||
-- 27.09.2026: самодостаточному инстансу «на своём сервере» он не нужен, а
|
||||
-- оператору пришлось бы заводить отдельное приложение у каждого провайдера
|
||||
-- (свой client id/secret, свои redirect URI, модерация) — слишком много
|
||||
-- трения ради способа входа, который дублирует пароль и ключи доступа.
|
||||
-- Решение и разбор — D-084.
|
||||
--
|
||||
-- Таблица осталась от Фазы 7 (миграция 00019, её не переписываем: она уже
|
||||
-- применена на стендах), кода, который её читает, больше нет. Данные в ней
|
||||
-- могли быть только у инсталляций, которые реально включали OAuth; на наших
|
||||
-- стендах провайдеры ни разу не настраивались, поэтому удаление безопасно.
|
||||
DROP TABLE IF EXISTS oauth_accounts;
|
||||
|
||||
-- +goose Down
|
||||
-- Откат возвращает структуру таблицы (пустую): сам код входа лежит в истории
|
||||
-- git, повторное включение потребует и его, и новые ключи провайдеров.
|
||||
CREATE TABLE IF NOT EXISTS oauth_accounts (
|
||||
id INTEGER PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL REFERENCES users (id) ON DELETE CASCADE,
|
||||
provider TEXT NOT NULL,
|
||||
subject_index TEXT NOT NULL,
|
||||
email_index TEXT NOT NULL DEFAULT '',
|
||||
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
||||
last_login_at TEXT,
|
||||
UNIQUE (provider, subject_index)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS oauth_accounts_user_idx ON oauth_accounts (user_id);
|
||||
@@ -0,0 +1,54 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/pressly/goose/v3"
|
||||
)
|
||||
|
||||
// tableExists сообщает, есть ли таблица в схеме.
|
||||
func tableExists(ctx context.Context, t *testing.T, db *DB, name string) bool {
|
||||
t.Helper()
|
||||
var count int
|
||||
if err := db.Reader.QueryRowContext(ctx,
|
||||
`SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name = ?`, name).Scan(&count); err != nil {
|
||||
t.Fatalf("sqlite_master(%s): %v", name, err)
|
||||
}
|
||||
return count > 0
|
||||
}
|
||||
|
||||
// TestOAuthRemovalMigration проверяет миграцию 00027 (D-084): вход через
|
||||
// внешние провайдеры удалён, таблица связи внешних аккаунтов уходит из схемы.
|
||||
// Откат возвращает структуру (пустую) — код входа лежит в истории git.
|
||||
func TestOAuthRemovalMigration(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
db, err := Open(ctx, Options{
|
||||
Path: filepath.Join(t.TempDir(), "glchat.db"),
|
||||
ReadPool: 2,
|
||||
Migrate: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("open: %v", err)
|
||||
}
|
||||
defer func() { _ = db.Close() }()
|
||||
|
||||
if tableExists(ctx, t, db, "oauth_accounts") {
|
||||
t.Fatal("после миграций таблицы oauth_accounts быть не должно")
|
||||
}
|
||||
|
||||
// Откат одной миграции возвращает таблицу, повторный накат снова убирает.
|
||||
if err := goose.DownToContext(ctx, db.Writer, "migrations", 26); err != nil {
|
||||
t.Fatalf("down to 26: %v", err)
|
||||
}
|
||||
if !tableExists(ctx, t, db, "oauth_accounts") {
|
||||
t.Fatal("откат 00027 должен вернуть структуру oauth_accounts")
|
||||
}
|
||||
if err := goose.UpContext(ctx, db.Writer, "migrations"); err != nil {
|
||||
t.Fatalf("up: %v", err)
|
||||
}
|
||||
if tableExists(ctx, t, db, "oauth_accounts") {
|
||||
t.Fatal("повторный накат 00027 должен снова удалить oauth_accounts")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user