HTTP API закрыт за вход через OIDC у Authelia
- шаг схемы закрывает поверхность, которую хранилище приносит открытой: собственную регистрацию, вход по паролю и одноразовый код — без этого закрытие приёма обходилось двумя запросами - продление сессии выключено, срок семь суток: иначе отзыв доступа у провайдера до сервиса не доходит никогда - файл записи отдаётся вошедшему по токену файла — пересмотр ADR-2026-08-12-file-link-open-but-not-logged
This commit is contained in:
@@ -70,6 +70,50 @@ type testEnv struct {
|
||||
handler *TranscribeHandler
|
||||
app core.App
|
||||
journal *journalBuffer
|
||||
// session — значение сессии вошедшего. Приём и опрос закрыты за
|
||||
// аутентификацией, и проверка, судящая их по существу, обязана предъявить
|
||||
// сессию ровно так же, как это делает браузер.
|
||||
session string
|
||||
// account — учётная запись, которой выдана сессия. Нужна проверкам выхода.
|
||||
account *core.Record
|
||||
}
|
||||
|
||||
// serve шлёт запрос от имени вошедшего: сессия предъявляется кукой — тем же
|
||||
// способом, каким её предъявляет браузер. Заголовок проверки не ставят: куку в
|
||||
// него перекладывает слой предъявления, и подмена его здесь означала бы проверку
|
||||
// не той цепочки.
|
||||
//
|
||||
// Проверки, судящие отказ без сессии, зовут `mux` напрямую.
|
||||
func (e *testEnv) serve(w http.ResponseWriter, req *http.Request) {
|
||||
req.AddCookie(&http.Cookie{Name: SessionCookieName, Value: e.session})
|
||||
e.mux.ServeHTTP(w, req)
|
||||
}
|
||||
|
||||
// newTestAccount заводит учётную запись и выдаёт ей сессию.
|
||||
//
|
||||
// Запись создаётся прямым сохранением, а не запросом к API: заводить её
|
||||
// запросом больше нельзя — создание закрыто шагом схемы, и в этом весь смысл
|
||||
// изменения. Прямое сохранение идёт мимо правил доступа так же, как идёт вход,
|
||||
// когда учётную запись заводит само хранилище.
|
||||
func newTestAccount(t *testing.T, app core.App) (*core.Record, string) {
|
||||
t.Helper()
|
||||
|
||||
users, err := app.FindCollectionByNameOrId("users")
|
||||
require.NoError(t, err)
|
||||
|
||||
record := core.NewRecord(users)
|
||||
record.Set("email", "person@example.com")
|
||||
record.Set("verified", true)
|
||||
// Случайный пароль ставит и само хранилище, когда заводит запись по входу у
|
||||
// провайдера: запись auth-коллекции без пароля не сохраняется, а войти по
|
||||
// нему всё равно нельзя — парольный вход выключен шагом схемы.
|
||||
record.SetRandomPassword()
|
||||
require.NoError(t, app.Save(record))
|
||||
|
||||
token, err := record.NewAuthToken()
|
||||
require.NoError(t, err)
|
||||
|
||||
return record, token
|
||||
}
|
||||
|
||||
// journalBuffer — перехваченный журнал одной проверки. Свой на случай: общий на
|
||||
@@ -147,7 +191,16 @@ func setupTestEnv(t *testing.T, metaviewer contract.AudioMetaViewer) *testEnv {
|
||||
mux, err := r.BuildMux()
|
||||
require.NoError(t, err)
|
||||
|
||||
return &testEnv{mux: mux, handler: handler, app: app, journal: journal}
|
||||
account, session := newTestAccount(t, app)
|
||||
|
||||
return &testEnv{
|
||||
mux: mux,
|
||||
handler: handler,
|
||||
app: app,
|
||||
journal: journal,
|
||||
session: session,
|
||||
account: account,
|
||||
}
|
||||
}
|
||||
|
||||
// createMultipartRequest собирает запрос из имени и содержимого. Файла на диске
|
||||
@@ -241,7 +294,7 @@ func TestCreateTranscribeJob_Success(t *testing.T) {
|
||||
req := createMultipartRequest(t, "sample.m4a", content)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
env.mux.ServeHTTP(w, req)
|
||||
env.serve(w, req)
|
||||
|
||||
require.Equal(t, http.StatusCreated, w.Code)
|
||||
|
||||
@@ -304,7 +357,7 @@ func TestCreateTranscribeJob_NoFile(t *testing.T) {
|
||||
env := setupTestEnv(t, readableMetaViewer())
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
env.mux.ServeHTTP(w, tc.req(t))
|
||||
env.serve(w, tc.req(t))
|
||||
|
||||
require.Equal(t, http.StatusBadRequest, w.Code)
|
||||
|
||||
@@ -327,7 +380,7 @@ func TestCreateTranscribeJob_EmptyFile(t *testing.T) {
|
||||
req := createMultipartRequest(t, "empty.m4a", nil)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
env.mux.ServeHTTP(w, req)
|
||||
env.serve(w, req)
|
||||
|
||||
require.Equal(t, http.StatusCreated, w.Code)
|
||||
|
||||
@@ -374,7 +427,7 @@ func TestCreateTranscribeJob_DifferentFileExtensions(t *testing.T) {
|
||||
req := createMultipartRequest(t, tc.fileName, []byte("запись"))
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
env.mux.ServeHTTP(w, req)
|
||||
env.serve(w, req)
|
||||
|
||||
require.Equal(t, http.StatusCreated, w.Code)
|
||||
|
||||
@@ -400,7 +453,7 @@ func TestCreateTranscribeJob_SenderFileNameNotStored(t *testing.T) {
|
||||
req := createMultipartRequest(t, "секретное-слово.mp3", []byte("запись"))
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
env.mux.ServeHTTP(w, req)
|
||||
env.serve(w, req)
|
||||
|
||||
require.Equal(t, http.StatusCreated, w.Code)
|
||||
|
||||
@@ -419,7 +472,7 @@ func TestCreateTranscribeJob_MetaViewerFailure(t *testing.T) {
|
||||
req := createMultipartRequest(t, "broken.m4a", []byte("не запись вовсе"))
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
env.mux.ServeHTTP(w, req)
|
||||
env.serve(w, req)
|
||||
|
||||
require.Equal(t, http.StatusInternalServerError, w.Code)
|
||||
|
||||
@@ -459,7 +512,7 @@ func TestCreateTranscribeJob_SenderFileNameNotLogged(t *testing.T) {
|
||||
req := createMultipartRequest(t, senderNameMarker+".mp3", []byte("запись"))
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
env.mux.ServeHTTP(w, req)
|
||||
env.serve(w, req)
|
||||
|
||||
require.Equal(t, http.StatusCreated, w.Code)
|
||||
|
||||
@@ -482,7 +535,7 @@ func TestCreateTranscribeJob_SenderFileNameNotLoggedOnFailure(t *testing.T) {
|
||||
req := createMultipartRequest(t, senderNameMarker+".mp3", []byte("не запись вовсе"))
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
env.mux.ServeHTTP(w, req)
|
||||
env.serve(w, req)
|
||||
|
||||
require.Equal(t, http.StatusInternalServerError, w.Code)
|
||||
|
||||
@@ -507,7 +560,7 @@ func TestCreateTranscribeJob_StorageFileNameNotLogged(t *testing.T) {
|
||||
req := createMultipartRequest(t, "sample.mp3", []byte("запись"))
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
env.mux.ServeHTTP(w, req)
|
||||
env.serve(w, req)
|
||||
|
||||
require.Equal(t, http.StatusCreated, w.Code)
|
||||
|
||||
@@ -528,7 +581,7 @@ func TestCreateTranscribeJob_JournalTracesRecord(t *testing.T) {
|
||||
req := createMultipartRequest(t, "sample.mp3", content)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
env.mux.ServeHTTP(w, req)
|
||||
env.serve(w, req)
|
||||
|
||||
require.Equal(t, http.StatusCreated, w.Code)
|
||||
|
||||
@@ -593,7 +646,7 @@ func TestCreateTranscribeJob_MetricLabelCarriesNoSenderName(t *testing.T) {
|
||||
req := createMultipartRequest(t, "sample."+senderNameMarker, []byte("запись"))
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
env.mux.ServeHTTP(w, req)
|
||||
env.serve(w, req)
|
||||
|
||||
require.Equal(t, http.StatusCreated, w.Code)
|
||||
|
||||
@@ -622,7 +675,7 @@ func TestGetTranscribeJobStatus_Success(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/api/status/"+job.Id, http.NoBody)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
env.mux.ServeHTTP(w, req)
|
||||
env.serve(w, req)
|
||||
|
||||
require.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
@@ -642,7 +695,7 @@ func TestGetTranscribeJobStatus_NoTranscriptionText(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/api/status/"+job.Id, http.NoBody)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
env.mux.ServeHTTP(w, req)
|
||||
env.serve(w, req)
|
||||
|
||||
require.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
@@ -664,7 +717,7 @@ func TestGetTranscribeJobStatus_NotFound(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/api/status/non-existent-id", http.NoBody)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
env.mux.ServeHTTP(w, req)
|
||||
env.serve(w, req)
|
||||
|
||||
require.Equal(t, http.StatusNotFound, w.Code)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user