Compare commits

..

96 Commits

Author SHA1 Message Date
648922a1ce Merge branch 'main-readme' 2026-06-02 18:36:41 +03:00
77fa01c9b0 Readme 2026-06-02 18:36:16 +03:00
7d3b213144 Пережаты картинки 2026-05-31 22:34:26 +03:00
3e4a6e8112 отзывы 2026-05-31 16:41:11 +03:00
60352652c2 Отключен счётчик страниц на аннотациях 2026-05-31 14:27:40 +03:00
58fb2afcd7 Мелкие правки 2026-05-31 01:49:11 +03:00
7bbd646a96 убран невалидный источник 2026-05-31 00:53:18 +03:00
71ad7fcc2e Скорректированы подписи скриншотов 2026-05-31 00:50:15 +03:00
ee2c3a198c Гостовый puml 2026-05-30 18:25:54 +03:00
7bb62d0152 Добавлены книги 2026-05-30 18:17:04 +03:00
229c0629f8 Исправлено тире 2026-05-30 16:47:52 +03:00
af9cd2dead Исправлены страницы 2026-05-30 16:41:00 +03:00
48d13342bf Готовая ПЗ (обновить первые 3 стр) 2026-05-30 16:26:28 +03:00
0c171fc406 Диаграмма классов Gaphor 2026-05-28 19:35:13 +03:00
31f119454a Вставлено реальное ТЗ в ПЗ 2026-05-28 19:24:04 +03:00
cce399a9e7 Правильные файлы о тестировании 2026-05-28 19:09:52 +03:00
0c0dda8cc6 Переименованы документы о тестировании 2026-05-28 18:57:47 +03:00
21221ef37b Вырезана экономическая часть 2026-05-28 18:53:26 +03:00
e380b3241a Поправлен заголовок таблиц в тестировании 2026-05-28 18:43:38 +03:00
f7023380b3 Отдельные документы о тестировании 2026-05-28 18:07:46 +03:00
32c579e0c0 Заменены \n на реальный символ переноса 2026-05-28 17:30:20 +03:00
0c15c7b786 Улучшение соответсвия 2026-05-28 17:23:54 +03:00
5c40687011 Добавлены разделы о ВПК, алгоритме, ИИ 2026-05-28 16:25:59 +03:00
3673c4aa8d Все картинки в отчёте 2026-05-26 23:16:40 +03:00
ce70c13f86 Более правильная структура Vault,Storage 2026-05-26 21:12:05 +03:00
04f7e998ff Красивая вставка картинок 2026-05-26 20:49:06 +03:00
e997f9ec84 Правильная тема ВКР 2026-05-26 19:35:01 +03:00
99230f7ed3 Gitea -> GitLab 2026-05-26 18:52:01 +03:00
de571ed41d Аннотации 2026-05-26 18:42:00 +03:00
c117ecd9b2 Красивый перенос таблиц 2026-05-26 15:26:18 +03:00
e3a615cb50 Отличное форматирование 2026-05-26 14:59:25 +03:00
2b139a18b3 Черновик ПЗ 2026-05-25 19:34:22 +03:00
adc3730b8d Улучшена фоновая синхронизация, обработана ошибка 2026-05-23 23:36:35 +03:00
6e719e7f52 Исправлена ошибка компиляции 2026-05-22 13:44:17 +03:00
442b9d0442 Исправлена ошибка навигации 2026-05-22 13:43:00 +03:00
96e9de49c3 Более аккуратная обработка сетвой ошибки на экране Vault 2026-05-22 13:33:14 +03:00
6ab402da51 perf(yandex): сузил инвалидацию кэша Disk API и добавил счётчик запросов
Инвалидирую list/get по префиксу пути вместо полной очистки, учитываю вызовы
в cloudApiCallCount для замеров.
2026-05-22 13:22:17 +03:00
2618df41e3 feat(sync): добавил cooperative-отмену sync и pipeline-задач
ensureActive в StorageSyncEngine, flush журнала перед чтением, Cancelled
в StorageSyncRunOutcome и TaskContext.ensureNotCancelled.
2026-05-22 13:22:15 +03:00
bc2b354820 fix(sync): исправил журнал при DELETE/TRASH и безопасный flush
Добавил recordSyncJournal для delete/moveToTrash, StorageSyncJournalBuffer
с восстановлением pending при ошибке записи и немедленным flush без debounce.
2026-05-22 13:22:05 +03:00
b00eed901b foreground task для фоновой синхронизации 2026-05-22 00:51:29 +03:00
35ba6dd377 Костыль для подавления цикла синхронизации 2026-05-22 00:37:00 +03:00
07d54b5996 Заголовок задачи синхронизации 2026-05-22 00:20:49 +03:00
233a716e47 Замена mlkit на свободную библиотеку 2026-05-21 23:21:21 +03:00
763334c488 Фикс TOTP 2fa 2026-05-21 23:06:43 +03:00
99cb410919 Native Яндекс 2026-05-21 22:57:53 +03:00
d3eac81660 Причина синхронизации и временная метка в логах 2026-05-21 22:30:40 +03:00
d0f490a3fd refactor(sync): перевёл журнал на map по пути и убрал цикл debounce-sync
Журнал хранится как словарь path→entry, служебные пути исключены из sync.
Apply пишет файлы без записи в журнал; bootstrap не триггерит sync во время работы.
2026-05-21 22:05:57 +03:00
51e6f40587 fix(sync): стабилизировал синхронизацию, Yandex I/O и вёрстку карточки storage
Добавил TRASH вместо DELETE для moveToTrash, компакцию журналов и отчёт об ошибках apply.
Исправил проброс ошибок upload Yandex при close, CAS lock и загрузку OAuth-токена.
Упростил совместимость sync-групп (только encInfo), поправил растягивание StorageTree при недоступных meta.
2026-05-21 18:46:03 +03:00
ef40aa9e73 Исправлен ворнинг 2026-05-21 13:14:00 +03:00
08caf08fad Обновлён gradle 2026-05-21 11:40:31 +03:00
7dd4a43c3d Плавный прогрессбар 2fa 2026-05-21 11:12:39 +03:00
671f1f1c2a fix(ui): улучшил vault/sync UX и подписи прогресса
Rescan в заголовке vault, sync-кнопка только при скане релевантных vault,
блокировка UI при недоступных meta, remember/open после encrypt,
убрал … из task_progress (точки остаются в foreground-сервисе).
2026-05-21 11:05:25 +03:00
467ed64426 fix(vault): исправил шифрование, meta Yandex и enc-meta при первом открытии
Remember key после encrypt, мягкий auto-open в UnlockManager,
StorageMetaLoadState без затирания meta на сетевых ошибках,
фильтр storages в YandexVault и создание .enc-meta при FileNotFound.
2026-05-21 11:05:14 +03:00
da8b970078 fix(sync): обработал отсутствие journal и lock при синхронизации
Добавил readSystemFileBytesOrEmpty и подключил в Local/Yandex accessors,
чтобы фоновый sync не падал с FileNotFound на пустых journal/lock.
2026-05-21 11:05:04 +03:00
c58bcdc35b Сильно улучшен UX при работе с Yandex vault 2026-05-21 01:40:30 +03:00
9c38da76d2 Красивый UI 2026-05-21 01:10:55 +03:00
184edc0b67 Иконка приложения 2026-05-21 00:37:18 +03:00
ffdab4563d Исправлено множество предупреждений 2026-05-19 01:42:22 +03:00
eecaf44b72 Первые тесты 2026-05-19 00:48:07 +03:00
fd6f2e5879 Наведён порядок в DI 2026-05-18 18:29:32 +03:00
03709d910b Исправлено несколько ворнингов 2026-05-18 18:12:14 +03:00
9ea88855f2 Улучшена фоновая синхронизация 2026-05-18 17:48:33 +03:00
f99d79fece Переключение языка 2026-05-18 15:35:06 +03:00
f3f99aed5a refactor(errors): унифицировал доменные ошибки и добавил failed-статус задач 2026-05-18 14:52:33 +03:00
a1226a8803 Progress label для всех задач 2026-05-18 11:14:50 +03:00
c7a08342b2 Переименован пакет 2026-05-18 11:14:43 +03:00
a138da2a40 Немного улучшен UI/UX экрана секретов 2026-05-17 19:42:58 +03:00
fe0c0e3f8f Исправлено ещё несколько deprecated предупреждений 2026-05-17 19:39:21 +03:00
2e23d0da0a Отключен экран настроек 2026-05-17 19:29:57 +03:00
7d5fd1b634 Исправлено много варнингов 2026-05-17 19:27:15 +03:00
3820a60d2c Работающий TOTP 2fa 2026-05-17 18:59:54 +03:00
845b3a1d76 Галочка сохранения пароля при включении шифрования 2026-05-17 18:06:23 +03:00
8fd10084f7 fix(sync): запретил зашифрованные storage в группах и перевёл резолв storages на FindStorageUseCase 2026-05-17 18:04:20 +03:00
e562e4d9e9 feat(sync): перевёл группы синхронизации на Room и добавил контроль совместимости 2026-05-17 18:03:14 +03:00
15f13577c8 Возможность переподключения к remote vault 2026-05-17 12:11:53 +03:00
f8d4407eb0 Работа с 2fa и секретами перенесена в tasks 2026-05-17 11:54:11 +03:00
555448d998 Улучшение UI/UX 2026-05-17 11:54:02 +03:00
5777f8e459 feat(storage): добавлены маршруты и экраны для управления текстовыми секретами и 2FA токенами 2026-05-13 20:39:55 +03:00
c6df089668 Улучшение UI/UX 2026-05-13 18:11:48 +03:00
abac9d93eb Улучшение кода 2026-05-13 17:43:37 +03:00
f551efe4a6 feat(ui): добавлены новые состояния и компоненты для отображения статуса работы 2026-05-13 17:22:31 +03:00
6c18a1d741 feat(sync): добавлен механизм снятия блокировки синхронизации для хранилищ 2026-05-13 14:43:27 +03:00
f38b3dfbb4 feat(sync): добавил механизм синхронизации хранилищ и управление группами
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-12 23:46:31 +03:00
d6bfdff077 perf(storage): оптимизация Яндекс.Диска и индикатора загрузки
Яндекс: параметр fields, getOrNull, инкрементальная статистика с манифестом yandex-vault-stats.json, PATCH custom_properties без предварительного GET, touchDir с проверкой существования, дебаунс записи статистики.

UI: CircularProgressIndicator на экране хранилищ — Modifier.size вместо width, чтобы вращение было по центру кольца.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-11 23:24:54 +03:00
60627f11d6 feat(app): deep links, notification launch, and night splash
Add wallenc:// URI patterns, manifest VIEW intent-filters, and NavDeepLink
with handleDeepLink from MainActivity. Move FGS notification PendingIntent
constants to WallencExternalLaunch. Theme splash/window background via
splash_screen_background in values and values-night.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-11 22:43:22 +03:00
88a13080e5 Открытие экрана Tasks при нажатии на уведомление 2026-05-11 22:28:07 +03:00
61bcaa95d8 Исправлено много варнингов 2026-05-11 22:18:15 +03:00
d176f2a464 Переименован domain-vault 2026-05-11 20:56:26 +03:00
9ceb8bd934 Перенос localVault в domain-storage 2026-05-11 20:54:15 +03:00
3928ac5409 Большая реструктуризация проекта 2026-05-11 19:33:32 +03:00
ad985679ee Yandex штуки 2026-05-03 22:03:47 +03:00
be1ba29f4d feat(yandex-disk): REST client, repository, and app-folder storages
Add Yandex Disk API (Retrofit + OkHttp), per-vault repository with DB-backed
OAuth token, YandexStorage/YandexStorageAccessor under app:/<uuid>, and a
real YandexVault bootstrap (disk quota, list storages, create/remove).

Expose vault and storage availability: vault is reachable after diskInfo;
each storage combines vault reachability with local init readiness. Map
401 to YandexDiskAuthException and mark the vault unavailable.

Add YandexAccountDao.getByVaultUuid for fresh tokens on each request.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-03 21:11:53 +03:00
d60cd9053a refactor(vault): extract BaseStorage and align storage interfaces
Consolidate duplicated meta-info and clear logic into BaseStorage. Promote
system file accessors and DataPage-based flows into IStorageAccessor. Use Long
for vault disk space to support cloud byte counts. Combine local and remote
storages in VaultsManager so UnlockManager sees all backends.

Yandex Disk REST integration (phase B) is deferred to a follow-up change.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-03 20:25:59 +03:00
78aa776adc Общий VaultScreen 2026-05-03 19:47:18 +03:00
1034e134c2 Большой рефакторинг
Из domain выкинуты типы vault, теперь он ничего не знает о Yandex. Объявления провайдеров вынесены в vault-api, а реализации в data
2026-04-27 02:47:02 +03:00
2b1be58a8e Использования менеджера Tasks 2026-04-27 00:54:37 +03:00
477 changed files with 215089 additions and 3165 deletions

53
README.md Normal file
View File

@@ -0,0 +1,53 @@
# Wallenc
Android-приложение для **защищённого хранения** файлов и секретов: шифрование выполняется на устройстве до выгрузки во внешнее хранилище. Собственного backend нет — клиент и API провайдеров (Яндекс.Диск и др.).
## Задача
Иерархия **VaultsManager → vault → storage → файлы**:
| Уровень | Смысл |
|---------|--------|
| Локальный vault | Один на устройстве, список storage |
| Удалённый vault | Подключение по OAuth (Яндекс) |
| Storage | Папка/контейнер с файлами, метаданные в Room |
| Шифрование | AES на storage; ключ не передаётся провайдеру |
| Sync | Журнал изменений, группы storage, фоновые задачи |
| Секреты / 2FA | Текстовые записи и TOTP внутри открытого storage |
Провайдер хранилища и сеть считаются недоверенными. Room хранит только служебные данные (метаданные, токены, журнал sync), не пользовательский контент в открытом виде.
## Модули
```
app/ — точка входа, Hilt, навигация
domain/ — интерфейсы и доменные модели
usecases/ — сценарии (шифрование, sync, CRUD storage)
domain-vault/ — storage, Яндекс.Диск, Encryptor
ui/ — Jetpack Compose
infrastructure-android/ — Room, DAO, платформенные реализации
vault-contracts/ — контракты для новых провайдеров
task-runtime/ — очередь длительных операций
```
**Архитектура:** MVVM + Clean Architecture (`ui``usecases` / `domain``domain-vault` / `infrastructure-android`).
## Стек
Kotlin · Jetpack Compose · Coroutines / Flow · Hilt · Room · WorkManager · клиентское AES
## Сборка и тесты
```bash
./gradlew assembleDebug
./gradlew test
./gradlew connectedDebugAndroidTest # устройство или эмулятор
```
JDK 21 рекомендуется (см. toolchain в Gradle).
## Требования
- Android с поддержкой Compose
- Для OAuth и удалённых vault — сеть
- Для фонового шифрования и sync — достаточно RAM под Worker и Room

View File

@@ -0,0 +1,81 @@
#import "../includes/common.typ": pz-fig
== Обзор программного продукта Wallenc
Wallenc мобильное приложение для Android: `VaultsManager` объединяет vault (один локальный и удалённые по OAuth), внутри каждого vault пользователь управляет storage с файлами и клиентским шифрованием. Продукт не использует собственный сервер; взаимодействие с облаком выполняется через API внешних провайдеров после OAuth.
== Техническое задание
Полный текст технического задания на разрабатываемое ПО (производственная практика, ООО НМФ «Нейротех»):
#import "tz-pdf.typ": pz-embed-tz-pdf
#pz-embed-tz-pdf()
== Программа и методика испытаний
Испытания проводятся на устройстве или эмуляторе Android. Матрица сценариев табл. @tbl-testplan. Критерий приёмки: отсутствие блокирующих дефектов по сценариям T-1T-6.
== Отчёт о результатах испытаний
Результаты приведены в табл. @tbl-testres (глава 5). Модульные тесты криптографии пройдены.
== Руководство пользователя
=== Установка
Установите APK сборки debug/release, полученной от разработчика, или соберите проект из репозитория GitLab ЮФУ. Разрешите доступ к файловой системе при запросе системы.
=== Первый запуск и storage в локальном vault
1. Откройте приложение Wallenc.
2. На экране «локальные vault» (список storage в единственном LocalVault) нажмите «+» для создания storage.
3. Укажите имя storage и подтвердите создание.
#pz-fig("fig_05_local_vaults.jpg", [Экран списка storage (локальный vault)], "fig-05-rp")
=== Шифрование storage
1. Выберите storage в списке.
2. Выберите действие «Включить шифрование».
3. Введите пароль (мастер-ключ) и подтвердите. *Важно:* без пароля восстановление невозможно.
#pz-fig("fig_06_encrypt_dialog.jpg", [Диалог включения шифрования], "fig-06-rp")
=== Открытие и закрытие зашифрованного storage
1. Для зашифрованного storage выберите «Открыть».
2. Введите пароль. При успехе содержимое storage доступно для просмотра (`IUnlockManager`).
3. Используйте «Закрыть» для блокировки storage.
#pz-fig("fig_07_open_close_dialog.jpg", [Диалог открытия зашифрованного storage], "fig-07-rp")
=== Переименование и удаление
Нажмите на значок «три точки» (опции) у storage и выберите «Переименовать» или «Удалить» (рис. @fig-08-rp). Подтвердите действие в диалоге.
#pz-fig("fig_08_rename_delete_dialog.jpg", [Меню storage: переименование и удаление], "fig-08-rp")
=== Удалённые vault и Яндекс
1. Перейдите на экран удалённых vault (рис. @fig-09-rp).
2. Нажмите «+» и в диалоге выберите провайдера «Яндекс» (рис. @fig-10-rp).
3. Пройдите OAuth в браузере или встроенном окне (последовательность рис. @fig-20, гл. 1).
4. После успеха аккаунт отображается в списке удалённых vault.
#pz-fig("fig_09_remote_vaults.jpg", [Экран удалённых vault: подключённый аккаунт], "fig-09-rp")
#pz-fig("fig_10_yandex_oauth.jpg", [Диалог «Добавить хранилище»: выбор провайдера Яндекс], "fig-10-rp")
=== Секреты и 2FA внутри storage
1. Откройте storage (после создания или из списка).
2. На экране storage перейдите в разделы «Секреты» и «2FA».
#pz-fig("fig_33_storage_secrets_2fa.jpg", [Экран storage: секреты и 2FA], "fig-33-rp")
3. В разделе 2FA добавьте TOTP-токен; на экране отображается сгенерированный код.
#pz-fig("fig_34_2fa_single_token.jpg", [Экран 2FA с одним токеном], "fig-34-rp")
=== Фоновые задачи
На экране задач отображаются операции шифрования и синхронизации (рис. @fig-12, гл. 5). Уведомления информируют о прогрессе и завершении (рис. @fig-13, гл. 5).

View File

@@ -0,0 +1,18 @@
// Вставка полного ТЗ (PDF практики) в приложение Б.
#let tz-pdf-file = "ПытковРЕ ТЗ на ПО.pdf"
#let tz-pdf-pages = 14
#let pz-embed-tz-pdf() = {
for i in range(tz-pdf-pages) {
let p = i + 1
if p > 1 {
pagebreak(weak: true)
}
image(
tz-pdf-file,
page: p,
width: 100%,
fit: "contain",
)
}
}

Binary file not shown.

View File

@@ -0,0 +1,23 @@
# Первые страницы ПЗ
`ВКР_первы_3_страницы_Пытков.pdf` — титул, задание на ВКР и резерв (3 страницы). При сборке ПЗ файл **склеивается в начало** через `qpdf` (оригинальные страницы без полей Typst).
## Сборка пояснительной записки
```bash
cd Report/scripts
./build.sh # полная (с PlantUML)
./build-pz.sh # быстрая (без PUML)
```
Нужен `qpdf`. Результат: `Report/Пояснительная_записка_ПытковРЕ.pdf`.
## Черновик задания на ВКР (Typst, по желанию)
```bash
cd Report
typst compile --root .. front-matter-export/tz-vkr-assignment.typ \
front-matter-export/ТЗа_ВКР_ПытковРЕ.pdf
```
Исходник: `includes/technical-assignment.typ`. В итоговый комплект входит ваша трёхстраничная PDF, не этот черновик.

View File

@@ -0,0 +1,9 @@
// Автономный PDF: задание на ВКР (бланк п. 17) для ручной вставки в начало ПЗ.
#set page(
paper: "a4",
margin: (left: 3cm, right: 1.5cm, top: 2cm, bottom: 2cm),
numbering: none,
)
#set text(font: "Times New Roman", size: 14pt, lang: "ru")
#include "../includes/technical-assignment.typ"

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,79 @@
# Реестр иллюстраций ПЗ Wallenc
Правила:
- Файл в `Report/images/`**только** имя из таблицы.
- Статус `ready` — файл на месте и подключён в `.typ`.
- Статус `placeholder` — файл ещё не подготовлен (сейчас все иллюстрации `ready`).
- Диаграммы PlantUML: `cd Report && bash scripts/render_puml.sh`.
## Соответствие «new images» → `images/`
| Исходник в `new images/` | Целевой файл |
|--------------------------|--------------|
| `05.jpg``10.jpg`, `12.jpg`, `13.jpg` | `fig_05``fig_10`, `fig_12`, `fig_13` |
| `27-domain-test.png``31-gradle-connected.png` | `fig_27``fig_31` |
| `33-storage-screen.jpg` | `fig_33_storage_secrets_2fa.jpg` |
| `34-2fa-screen.jpg` | `fig_34_2fa_single_token.jpg` |
| `Domain_Диаграмма_классов.png` | `fig_24_domain_class_manual.png` |
---
## Полный реестр
| № | Имя файла | Тип | Статус | Где в ПЗ | Примечание (содержание) | label |
|---|-----------|-----|--------|----------|-------------------------|-------|
| 01 | `fig_01_start_sync.png` | PlantUML | ready | гл. 3.3.3, 4.1.4 | Старт → Room → SyncWorker. **PUML** | fig-01 |
| 02 | `fig_02_vault_lifecycle.png` | PlantUML | ready | гл. 3.3.3, 4.1.4 | Жизненный цикл **IStorage** (не vault). **PUML** | fig-02 |
| 03 | `fig_03_navigation_hub.png` | PlantUML | ready | гл. 3.3.3, 4.1.4 | Main: LocalVault → storage; удалённые vault → VaultBrowser → storage. **PUML** | fig-03 |
| 04 | `fig_04_domain_class.png` | PlantUML | ready | гл. 2.3.2, 4.2.1 | Классы `:domain`. **PUML** | fig-04 |
| 05 | `fig_05_local_vaults.jpg` | Скриншот UI | ready | гл. 3.4, 5, прил. Б, В | Список storage (локальный vault) | fig-05 |
| 06 | `fig_06_encrypt_dialog.jpg` | Скриншот UI | ready | гл. 3.4, 4.1.1, 5, прил. Б, В | Диалог включения шифрования | fig-06 |
| 07 | `fig_07_open_close_dialog.jpg` | Скриншот UI | ready | гл. 3.4, 4.1.2, прил. Б, В | Диалог открытия зашифрованного storage | fig-07 |
| 08 | `fig_08_rename_delete_dialog.jpg` | Скриншот UI | ready | гл. 3.4, прил. Б, В | Меню storage: переименование и удаление | fig-08 |
| 09 | `fig_09_remote_vaults.jpg` | Скриншот UI | ready | гл. 1, 3.4, 4.2.3, 5, прил. Б, В | Удалённые vault: подключённый аккаунт Яндекс | fig-09 |
| 10 | `fig_10_yandex_oauth.jpg` | Скриншот UI | ready | гл. 1, 3.4, 4.2.3, 5, прил. Б, В | Диалог «Добавить хранилище»: выбор провайдера Яндекс (не OAuth) | fig-10 |
| 11 | `fig_11_room_schema.png` | PlantUML | ready | гл. 2.3.2, 4.2.2, прил. В | Схема Room. **PUML** | fig-11 |
| 12 | `fig_12_tasks_screen.jpg` | Скриншот UI | ready | гл. 5, прил. Б, В | Экран «Очередь задач» | fig-12 |
| 13 | `fig_13_tasks_notification.jpg` | Скриншот UI | ready | гл. 5, прил. Б, В | Уведомление Android о синхронизации хранилищ | fig-13 |
| 14 | `fig_14_context_system.png` | PlantUML | ready | гл. 1.2.1, 2.1.4 | Контекстная диаграмма. **PUML** | fig-14 |
| 15 | `fig_15_bpmn_vault.png` | PlantUML | ready | гл. 2.1.3 | BPMN жизненный цикл **storage** (шифрование storage, не vault). **PUML** | fig-15 |
| 16 | `fig_16_dfd_level0.png` | PlantUML | ready | гл. 2.2 | DFD-0. **PUML** | fig-16 |
| 17 | `fig_17_use_case.png` | PlantUML | ready | гл. 2.3.1 | Прецеденты. **PUML** | fig-17 |
| 18 | `fig_18_deployment.png` | PlantUML | ready | гл. 2.3.3 | Развёртывание. **PUML** | fig-18 |
| 19 | `fig_19_clean_architecture.png` | PlantUML | ready | гл. 2.3, 4.3 | Clean Architecture. **PUML** | fig-19 |
| 20 | `fig_20_oauth_sequence.png` | PlantUML | ready | гл. 1.5.2, 4.2.3 | OAuth sequence. **PUML** | fig-20 |
| 21 | `fig_21_encrypt_flow.png` | PlantUML | ready | гл. 4.1.1, 5.2.1 | Блок-схема шифрования. **PUML** | fig-21 |
| 23 | `fig_23_module_deps.png` | PlantUML | ready | гл. 4.3 | Зависимости Gradle. **PUML** | fig-23 |
| 24 | `fig_24_domain_class_manual.png` | Ручная схема | ready | гл. 4 (ИИ) | Диаграмма классов `:domain` до Cursor; исх. `Domain_Диаграмма_классов.png` | fig-24 |
| 27 | `fig_27_gradle_domain_test.png` | Gradle | ready | гл. 5.2.1 | `:domain:test` SUCCESS | fig-27 |
| 28 | `fig_28_gradle_usecases_test.png` | Gradle | ready | гл. 5.2.2 | `:usecases:test` SUCCESS | fig-28 |
| 29 | `fig_29_gradle_ui_test.png` | Gradle | ready | гл. 5.2.3 | `:ui:test` SUCCESS | fig-29 |
| 30 | `fig_30_gradle_test_summary.png` | Gradle | ready | гл. 5.4 | Сводка `test` | fig-30 |
| 31 | `fig_31_gradle_connected_test.png` | Gradle | ready | гл. 5.3 | `connectedDebugAndroidTest` SUCCESS | fig-31 |
| 32 | `fig_32_manual_test_checklist.png` | PlantUML Salt | ready | гл. 5.3 | Таблица-чек-лист T-7…T-12 (как @tbl-testres). **PUML** `fig_32_manual_test_checklist.puml` | fig-32 |
| 33 | `fig_33_storage_secrets_2fa.jpg` | Скриншот UI | ready | гл. 1, 3.4, 5, прил. Б, В | Экран storage: вкладки/разделы секретов и 2FA | fig-33 |
| 34 | `fig_34_2fa_single_token.jpg` | Скриншот UI | ready | гл. 1, 3.4, 5, прил. Б, В | Экран 2FA с одним TOTP-токеном | fig-34 |
| 35 | `fig_35_sync_merge_algorithm.png` | PlantUML | ready | гл. 4 | Алгоритм согласования журналов синхронизации. **PUML** `fig_35_sync_merge_algorithm.puml` | fig-35 |
| 36 | `fig_36_ml_on_device.png` | PlantUML | ready | гл. 6 | Контур on-device ML без выгрузки plaintext. **PUML** `fig_36_ml_on_device.puml` | fig-36 |
## Нумерация в PDF
Номер «Рисунок N» — по порядку появления в тексте, не по номеру в имени файла.
Пример: **Рисунок 3** в ПЗ — это `fig_15_bpmn_vault.png` (гл. 2), а не `fig_03_navigation_hub.png` (гл. 3, обычно ~рис. 6).
## fig_31
Один скрин `./gradlew connectedDebugAndroidTest` из корня репозитория, `BUILD SUCCESSFUL` (см. прежнее описание в истории реестра).
## Команды
```bash
cd Report && bash scripts/render_puml.sh # PlantUML → PNG (dpi 72 + pngquant 2845)
cd Report && python3 scripts/compress_screenshots.py # скриншоты: JPEG q28, pngquant 2540
cd Report && python3 scripts/check_images.py
typst compile --root .. ояснительная_записка_ПытковРЕ.typ"
```
Сначала `render_puml.sh`, затем `compress_screenshots.py` — диаграммы не пережимаются повторно.

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -0,0 +1,25 @@
#import "common.typ": pz-front-heading, pz-table
#pz-front-heading[Обозначения и сокращения]
#set par(first-line-indent: 1.25cm, justify: true)
В настоящем документе применяются следующие обозначения и сокращения:
#pz-table(
[Обозначения и сокращения],
2,
table.header([Обозначение], [Расшифровка]),
[API], [Application Programming Interface программный интерфейс приложения],
[DAO], [Data Access Object объект доступа к данным],
[E2E], [End-to-end сквозное шифрование],
[MVVM], [Model View ViewModel],
[OAuth], [протокол авторизации открытого типа],
[UI], [User Interface пользовательский интерфейс],
[CRUD], [Create, Read, Update, Delete создание, чтение, изменение, удаление],
[ПЗ], [Пояснительная записка],
[ТЗ], [Техническое задание],
[ВКР], [Выпускная квалификационная работа],
[JUnit], [Фреймворк модульного тестирования Java/Kotlin],
[IT], [Instrumented Test инструментальный тест Android],
) <tbl-abbr>

View File

@@ -0,0 +1,22 @@
#import "common.typ": pz-biblio-strip-en, pz-front-heading, pz-thesis-topic-en
#pagebreak(weak: true)
#pz-biblio-strip-en(
udk: "004.738.5",
author: "Pytkov Roman Evgenievich",
title: pz-thesis-topic-en,
year: 2026,
)
#pz-front-heading(page-break: false)[Annotation]
#{
set par(first-line-indent: 1.25cm, justify: true)
[This explanatory note is devoted to the development of a mobile application for secure storage of user data (the Wallenc software product). It describes analysis of the subject area and analogues, requirements formulation, architecture and user interface design, software implementation in Kotlin (Android, Jetpack Compose, Room, Hilt), and testing.]
parbreak()
[Implemented: the VaultsManager vault storage files hierarchy (one LocalVault on the device, remote vaults via OAuth), client-side AES encryption, service metadata in Room, and a project-level synchronization contour without passing encryption keys to the provider. The source code is hosted in a private GitLab repository at Southern Federal University. Full source listings are in Appendix A; software documentation is in Appendix B.]
}

View File

@@ -0,0 +1,20 @@
#import "common.typ": pz-biblio-strip-ru, pz-front-heading, pz-thesis-topic
#pz-biblio-strip-ru(
udk: "004.738.5",
author: "Пытков Роман Евгеньевич",
title: pz-thesis-topic,
year: 2026,
)
#pz-front-heading(page-break: false)[Аннотация]
#{
set par(first-line-indent: 1.25cm, justify: true)
[Пояснительная записка посвящена разработке мобильного приложения для защищённого хранения пользовательских данных (программный продукт Wallenc). Описаны анализ предметной области и аналогов, формирование требований, проектирование архитектуры и пользовательского интерфейса, программная реализация на Kotlin (Android, Jetpack Compose, Room, Hilt) и тестирование.]
parbreak()
[Реализованы иерархия VaultsManager vault storage файлы (один LocalVault на устройстве, удалённые vault по OAuth), клиентское AES-шифрование, служебные метаданные в Room и проектный контур синхронизации без передачи ключей провайдеру. Исходный код размещён в приватном репозитории GitLab ЮФУ. Полный листинг исходных файлов приложение А; программная документация приложение Б.]
}

131
Report/includes/ch01.typ Normal file
View File

@@ -0,0 +1,131 @@
#import "common.typ": pz-fig, pz-table
= Анализ требований и предметной области
== Анализ предметной области
В рамках анализа предметной области рассмотрены подходы к хранению чувствительных данных в мобильных приложениях и облачных хранилищах. Выделены требования: конфиденциальность при хранении и передаче; отсутствие необходимости доверять инфраструктуре хранилища; устойчивость к компрометации удалённого провайдера; разделение логики хранения и криптографической защиты; удобные сценарии создания хранилища, шифрования, открытия и работы с содержимым.
Сформирован вывод о приоритете клиентских криптографических механизмов, унифицированного доступа к разным типам хранилищ и архитектуры с чётким разделением слоёв.
Рассмотрены угрозы STRIDE в применении к клиентскому хранилищу: подмена файлов на диске провайдера (tampering) нейтрализуется шифрованием; утечка метаданных минимизируется за счёт шифрования имён путей (опция `encryptPath`); отказ в обслуживании облака не блокирует локальный доступ при наличии копии vault. Модель доверия: пользователь доверяет только собственному устройству и корректности реализации криптомодуля; облако и сеть считаются активными противниками для ciphertext.
Контекст взаимодействия участников показан на рисунке @fig-14.
#pz-fig("fig_14_context_system.png", [Контекстная диаграмма: пользователь, приложение Wallenc и внешний провайдер], "fig-14")
== Разработка требований к программному продукту
=== Назначение и цели создания системы
*Назначение* системы Wallenc предоставление пользователю мобильного клиента для работы с иерархией *VaultsManager → vault → storage → файлы*: один `LocalVault` на устройстве, удалённые vault по OAuth; внутри vault пользователь создаёт и управляет *storage*; шифрование (`StorageEncryptionInfo`, `Encryptor`) применяется к storage, а не к vault.
*Цели создания*: обеспечить единую модель vault и storage; минимизировать доверие к провайдеру; поддержать расширение списка провайдеров через адаптеры; сохранить служебные метаданные в локальной БД без хранения пользовательского контента в открытом виде.
=== Функциональные требования
Функциональные требования сведены в таблице @tbl-req.
#pz-table(
[Свод функциональных требований],
2,
table.header([Код], [Требование]),
[ФР-1], [Создание, просмотр, переименование и удаление storage в локальном vault (LocalVault один на устройстве)],
[ФР-2], [Включение шифрования storage, проверка ключа, открытие и закрытие зашифрованного представления],
[ФР-3], [Просмотр и операции с файлами внутри storage; текстовые секреты и 2FA],
[ФР-4], [OAuth-авторизация (Яндекс), регистрация удалённых vault и листинг их storage],
[ФР-5], [Синхронизация: группы хранилищ, журнал коммитов, фоновый Worker без передачи ключей],
[ФР-6], [Очередь фоновых задач: шифрование, синхронизация, отображение прогресса],
) <tbl-req>
==== Управление storage в локальном vault
Пользователь создаёт storage, просматривает список, переименовывает и удаляет их в единственном `LocalVault`. Служебные каталоги и системные пути не отображаются в пользовательском представлении.
==== Шифрование и открытие storage
При включении шифрования формируются параметры `StorageEncryptionInfo`; открытие выполняется только после успешной проверки ключа. Повторное шифрование одного storage до завершения предыдущей операции блокируется.
==== Работа с содержимым storage
Операции чтения и записи выполняются через единый интерфейс файлового доступа независимо от типа хранилища. Внутри открытого storage доступны текстовые секреты и генерация TOTP для 2FA (рис. @fig-33, @fig-34).
==== Удалённые хранилища и авторизация во внешних провайдерах
Подключение удалённого vault выполняется через экран удалённых vault (рис. @fig-09, @fig-10) и поток OAuth 2.0 для Яндекса @oauth-rfc6749 @yandex-oauth (рис. @fig-20).
#pz-fig("fig_20_oauth_sequence.png", [Диаграмма последовательности OAuth Яндекс], "fig-20")
==== Синхронизация зашифрованных данных
Спроектирован механизм фоновой синхронизации: в Room хранятся записи с UUID хранилищ; по таймеру запускается сервис, сравнивающий истории коммитов локального и удалённого представления без передачи ключей на сервер провайдера (рис. @fig-01@fig-03).
=== Нефункциональные требования
К системе предъявляются требования по производительности (асинхронные операции на Coroutines), безопасности (AES на клиенте, минимизация утечек через имена путей), расширяемости (модульная структура Gradle) и устойчивости к гонкам при длительных операциях шифрования.
=== Требования к программно-аппаратной платформе
Минимальная платформа Android с поддержкой Jetpack Compose; для OAuth и удалённых операций требуется сетевое подключение. Объём оперативной памяти должен быть достаточен для фоновых задач шифрования и Room.
== Аналоги
=== Аналог Google Files Secure Folder
Google Files Secure Folder локально прячет и защищает файлы в отдельной папке по PIN или графическому ключу внутри Android. Пользователь получает понятный «сейф» без настройки облака, однако сценарий ограничен устройством: отсутствует полноценная кроссплатформенная синхронизация зашифрованного vault между провайдерами, а модель хранилища не универсальна для подключения внешних API.
=== Аналог Proton Pass / Proton Drive
Экосистема Proton обеспечивает end-to-end шифрование для паролей, заметок и файлов с облачной синхронизацией. Для Wallenc релевантен опыт прозрачной для пользователя защиты и синхронизации, однако продукт жёстко привязан к инфраструктуре Proton, часть функций и объёмов зависит от тарифа, а роль «универсального клиента» к произвольному внешнему хранилищу ограничена.
=== Аналог Bitwarden
Менеджер секретов с шифрованием и синхронизацией. Ограничения: ориентация на учётные данные, а не файловый vault @bitwarden-help.
=== Аналог Cryptomator
Клиентское шифрование файловых vault в облаке (zero-knowledge). Ограничения: фокус на файлах, ограничения интеграций по платформам @cryptomator-docs.
=== Сводная оценка
#pz-table(
[Сравнительная оценка аналогов],
6,
table.header([Критерий], [Secure Folder], [Proton], [Bitwarden], [Cryptomator], [Wallenc]),
[Собственный backend приложения], [], [+], [+/], [], [],
[E2E / клиентское шифрование], [+/], [+], [+], [+], [+],
[Файловый vault], [+], [+], [], [+], [+],
[OAuth внешнего провайдера], [], [+/], [+/], [+/], [+],
[Переносимость провайдеров], [], [], [+/], [+], [+],
[Unit-тесты без сервера], [н/д], [н/д], [+/], [+/], [+ (68)],
) <tbl-analog>
Обзор подтверждает актуальность концепции Wallenc: безопасность без собственного сервера и переносимая архитектура хранилищ.
== Стек технологий
Для реализации выбраны Kotlin, Android SDK, Jetpack Compose, Coroutines/Flow, Hilt, Room, AES на клиенте; модульная структура: `:app`, `:domain`, `:usecases`, `:ui`, `:domain-vault`, `:infrastructure-android`, `:vault-contracts`, `:task-runtime` @kotlin-docs @smith-jetpack-compose @room-docs @hilt-docs @android-arch.
*Kotlin* обеспечивает выразительную доменную модель и безопасность типов. *Jetpack Compose* декларативно описывает UI и состояние экранов vault. *Coroutines/Flow* используются для асинхронного шифрования, обращения к DAO и отображения прогресса без блокировки главного потока. *Hilt* связывает реализации интерфейсов domain с Android-инфраструктурой. *Room* персистентно хранит метаданные. Криптографические операции выполняются в доменном слое с опорой на стандартные API Java/Android и рекомендации NIST по AES @nist-aes.
Выбор стека согласован с целями практики: все компоненты поддерживаются Google и сообществом, документированы на русском и английском языках, применимы в промышленной разработке мобильных приложений.
== Обзор методов и подходов к защите данных в мобильных приложениях
=== Клиентское шифрование и модель zero-knowledge
Данные шифруются до отправки во внешнее хранилище; провайдер не получает ключ расшифрования. Модель угроз мобильной платформы и практики защиты приложений рассмотрены в @zobnin-android-security. Используется AES @nist-aes @losev-crypto; проверка ключа выполняется через `Encryptor.checkKey` без расшифровки всего содержимого.
=== Авторизация и взаимодействие с внешними провайдерами без собственного сервера
Применяется OAuth 2.0: токены доступа хранятся локально в Room (`DbYandexAccount`); ключи шифрования не передаются на сторону провайдера @oauth-rfc6749.
== Обзор рынка и обоснование выбора решения Wallenc
Рынок мобильных хранилищ демонстрирует поляризацию: закрытые экосистемы с удобным UX (Proton, Google) и открытые криптоклиенты с ручной настройкой (Cryptomator). Wallenc занимает промежуточную нишу *мобильный zero-knowledge vault* с OAuth к популярному провайдеру (Яндекс) и расширяемой регистрацией типов хранилищ. Для корпоративного заказчика (Нейротех) важны: отсутствие затрат на сервер приложения, возможность аудита кода, формализованное тестирование (68 unit-тестов, см. гл. 5).
Перспективы коммерциализации связаны не с продажей облака, а с лицензированием клиента или внутренним внедрением. Барьер входа ответственность пользователя за ключ; в ПЗ это отражено в руководстве пользователя (прил. Б) и предупреждениях в UI.
== Формирование технического задания
Структура ТЗ оформлена по ГОСТ 7.322017: цели, этапы, функциональные и нефункциональные требования, порядок приёмки. Приоритет отдан ядру хранения и шифрования; расширения (2FA, текстовые секреты, синхронизация) зафиксированы как дополнительные функции второго этапа практики. Полный текст ТЗ в приложении Б.

102
Report/includes/ch02.typ Normal file
View File

@@ -0,0 +1,102 @@
#import "common.typ": pz-fig, pz-table
= Проектирование архитектуры системы
== Схема бизнес-процессов предметной области
=== Организационная структура
Участники процесса: *пользователь*, *мобильное приложение Wallenc*, *внешний провайдер хранения* (облачный API). Сервер приложения отсутствует.
=== Карта процессов
Основные процессы: создание storage в vault опциональное шифрование storage открытие storage работа с файлами закрытие; для удалённых vault OAuth привязка учётной записи листинг storage провайдера проектная синхронизация.
=== Диаграмма BPMN
На рисунке @fig-15 представлена диаграмма BPMN основного процесса работы со storage внутри vault.
#pz-fig("fig_15_bpmn_vault.png", [BPMN: storage создание, шифрование, открытие, содержимое], "fig-15")
=== Карта систем
Wallenc выступает посредником между пользователем и файловыми API провайдера, дополняя взаимодействие локальной БД Room и криптографическим модулем.
== Диаграмма DFD
DFD уровня 0 (рис. @fig-16) отражает потоки между UI, доменной логикой, криптографией, адаптерами хранилищ, Room и внешним API.
#pz-fig("fig_16_dfd_level0.png", [DFD уровень 0: потоки данных в Wallenc], "fig-16")
== Объектно-ориентированный анализ и проектирование (UML)
=== Диаграмма прецедентов
Прецеденты включают управление storage, подключение удалённых vault (OAuth), шифрование и открытие storage, работу с файлами и синхронизацию групп storage (рис. @fig-17).
#pz-fig("fig_17_use_case.png", [Диаграмма прецедентов Wallenc], "fig-17")
=== Диаграмма классов
Доменная модель модуля `:domain` (интерфейсы хранилищ, use case, сущности шифрования) приведена на рисунке @fig-04.
#pz-fig("fig_04_domain_class.png", [Диаграмма классов модуля domain], "fig-04")
=== Доменная иерархия хранения данных
Модель данных приложения строится на трёх уровнях (см. `IVaultsManager`, `IVault`, `IStorage` на рис. @fig-04):
- *VaultsManager* единая точка доступа: реактивный список всех vault (`vaults`), агрегированный список storage (`allStorages`) и `IUnlockManager` для открытых storage;
- *IVault* контейнер, объединяющий набор storage. *LocalVault* в приложении один (корень на устройстве); удалённые vault (например, `YandexVault`) добавляются по одному на привязанную учётную запись OAuth;
- *IStorage* хранилище, которое пользователь создаёт, переименовывает и удаляет в интерфейсе; внутри файлы и каталоги, доступные через `IStorageAccessor` (с опциональным клиентским шифрованием).
Пользовательские сценарии «создать локальный vault» в UI соответствуют созданию нового *storage* внутри единственного `LocalVault`, а не появлению второго локального vault.
Служебные сущности Room показаны на рисунке @fig-11.
#pz-fig("fig_11_room_schema.png", [Схема служебных сущностей Room], "fig-11")
=== Диаграмма развёртывания
Компоненты развёртывания: устройство Android (приложение, Room), облачный API Яндекса (рис. @fig-18).
#pz-fig("fig_18_deployment.png", [Диаграмма развёртывания], "fig-18")
Архитектурные слои MVVM + Clean Architecture @martin-clean-architecture и соответствие модулям Gradle на рисунке @fig-19.
#pz-fig("fig_19_clean_architecture.png", [Слои Clean Architecture и модули проекта], "fig-19")
== Проектирование локальной базы данных
Служебная БД Room хранит ключи шифрования для пар «исходный storage зашифрованное представление» (`DbStorageKeyMap`), сериализованные метаданные storage (`DbStorageMetaInfo`), учётные записи Яндекс (`DbYandexAccount`) и группы синхронизации (`DbStorageSyncGroup`). Пользовательский контент в открытом виде в БД не сохраняется только параметры, необходимые для восстановления состояния приложения при следующем запуске. Доступ инкапсулирован в DAO и репозиториях модуля `:infrastructure-android`.
#pz-table(
[Сущности Room и назначение],
3,
table.header([Сущность], [Назначение], [Связь с тестами]),
[`DbStorageKeyMap`], [Ключ для `sourceUuid` storage (связь с зашифрованной копией)], [Интеграция],
[`DbStorageMetaInfo`], [Сериализованные метаданные `IStorage` (имя, путь, шифрование)], [Интеграция],
[`DbYandexAccount`], [OAuth access token, идентификатор аккаунта], [`YandexAccountRepositoryTest`],
[`DbStorageSyncGroup`], [Группа UUID для синхронизации], [`StorageSyncEngineTest`],
) <tbl-room-entities>
== Проектирование подсистемы синхронизации
Подсистема синхронизации спроектирована как набор независимых операций над журналом изменений каждого `Storage`. Алгоритм выбирает «победителя» по ревизии, копирует или удаляет файлы на целевом хранилище, не расшифровывая данные на сервере. Блокировки предотвращают одновременную синхронизацию одной группы; при отмене задачи блокировки снимаются (покрыто unit-тестами `syncGroupCooperativeCancellationReleasesLocks` и др., гл. 5). Подробное описание реализации и выбора источника для каждого пути в гл. 4 алгоритм согласования журналов, рис. @fig-35).
== Нефункциональные архитектурные решения
#pz-table(
[Нефункциональные решения],
2,
table.header([Атрибут], [Решение]),
[Производительность], [Потоковое шифрование, фоновые задачи в `:task-runtime`],
[Безопасность], [AES, проверка ключа без полного decrypt, скрытие служебных путей],
[Расширяемость], [`vault-contracts`, регистрация провайдеров],
[Сопровождаемость], [Модульные тесты 68 + листинги в приложении А],
[Надёжность], [Room-транзакции, восстановление журнала после сбоя записи],
) <tbl-nfr-arch>
== Вывод
Спроектирована клиентская архитектура с разделением domain, infrastructure и presentation, единым интерфейсом vault и заделом под синхронизацию без передачи ключей. Диаграммы зафиксированы в приложении Г.

68
Report/includes/ch03.typ Normal file
View File

@@ -0,0 +1,68 @@
#import "common.typ": pz-fig, pz-table
= Проектирование пользовательского интерфейса мобильного приложения
== Подготовка проекта и аналитика
=== Анализ конкурентов и определение сильных и слабых сторон
По результатам сравнения аналогов (табл. @tbl-analog) для Wallenc выделены сильные стороны: единый UI для списков storage LocalVault и удалённых vault), явное отображение состояния шифрования storage, диалоги подтверждения деструктивных операций. Слабые стороны конкурентов (привязка к экосистеме, узкая предметная область) учтены при проектировании навигации.
=== Ограничения и допущения проектирования UI
Полевые интервью пользователей не проводились; сценарии восстановлены по аналогам и отчётам этапов практики. Допущение: пользователь понимает риск потери ключа шифрования.
== Исследования пользовательских сценариев
=== Потребности и барьеры пользователя
Потребности: хранить файлы в «сейфе» без доверия к облаку; синхронизировать между устройствами. Барьеры: сложность OAuth, страх потери пароля, неочевидность состояния «storage открыт» (разблокирован).
=== Полезные сценарии из аналогов
Заимствованы: список хранилищ с индикацией статуса (Cryptomator, Bitwarden); локальная защита папки (Secure Folder); пошаговый мастер шифрования.
== Проектирование пользовательских потоков
=== User Story
#pz-table(
[User Story Wallenc],
3,
table.header([ID], [Формулировка], [Критерий приёмки]),
[US-1], [Создаю storage для файлов в локальном vault на устройстве], [Storage в списке (рис. 5)],
[US-2], [Включаю шифрование storage паролем], [Статус encrypted, тест T-8],
[US-3], [Открываю зашифрованный storage ключом], [Доступ к контенту, тест T-9],
[US-4], [Подключаю Яндекс и удалённый vault], [OAuth OK, тест T-10],
[US-5], [Вижу прогресс фоновых задач], [Экран задач, тест T-11],
[US-6], [Храню TOTP и текстовые секреты в storage], [`TwoFaTotpTest`, UI IT],
) <tbl-userstory>
=== Пользовательский сценарий
Диаграммы потоков: старт приложения и фоновая синхронизация (@fig-01), жизненный цикл storage (@fig-02), навигация Main: LocalVault / удалённые vault storage (@fig-03).
#pz-fig("fig_01_start_sync.png", [Старт приложения и фоновая синхронизация], "fig-01")
#pz-fig("fig_02_vault_lifecycle.png", [Жизненный цикл storage и журнал синхронизации], "fig-02")
#pz-fig("fig_03_navigation_hub.png", [Навигация Main: список storage, VaultBrowser, StorageHome], "fig-03")
== Проработка прототипа и особенности дизайна
Интерфейс реализован на Jetpack Compose @smith-jetpack-compose @compose-docs. Экраны локального и удалённого vault, диалоги работы со storage, а также разделы текстовых секретов и 2FA показаны на рис. @fig-05@fig-10 и @fig-33@fig-34 (подробно приложение В и руководство пользователя в приложении Б). Поток OAuth 2.0 для Яндекса на рис. @fig-20 (гл. 1).
#pz-fig("fig_05_local_vaults.jpg", [Список storage в локальном vault (экран «локальные vault»)], "fig-05")
#pz-fig("fig_06_encrypt_dialog.jpg", [Диалог включения шифрования], "fig-06")
#pz-fig("fig_07_open_close_dialog.jpg", [Диалог открытия зашифрованного storage], "fig-07")
#pz-fig("fig_08_rename_delete_dialog.jpg", [Меню storage: переименование и удаление], "fig-08")
#pz-fig("fig_09_remote_vaults.jpg", [Экран удалённых vault: подключённый аккаунт Яндекс], "fig-09")
#pz-fig("fig_10_yandex_oauth.jpg", [Диалог «Добавить хранилище»: выбор провайдера Яндекс], "fig-10")
#pz-fig("fig_33_storage_secrets_2fa.jpg", [Экран storage: разделы «Секреты» и «2FA»], "fig-33")
#pz-fig("fig_34_2fa_single_token.jpg", [Экран 2FA: список с одним TOTP-токеном], "fig-34")
== Требования к эргономике и доступности
Интерфейс должен отображать состояние каждого storage без перехода в технические экраны: признаки «зашифровано», «открыто» (`IUnlockManager.openedStorages`), «выполняется операция» (`isBusy`). Диалоги и меню деструктивных действий (удаление storage, отключение шифрования; см. @fig-08) требуют явного подтверждения. Тексты сообщений об ошибках OAuth и неверном ключе формулируются нейтрально, без раскрытия внутренних путей и имён файлов.
== Вывод
Спроектированы пользовательские потоки и экранная структура, согласованные с архитектурой и требованиями безопасности. Иллюстрации интерфейса приведены в приложении В.

View File

@@ -0,0 +1,21 @@
// Additional implementation detail (included from ch04)
=== Модуль :vault-contracts
Определяет дескрипторы vault (`VaultDescriptor`, `DescribedVault`) и контракты регистрации удалённых vault (`VaultRegistrar`, `VaultRegistration`). Реализация агрегатора `VaultsManager` в `:domain-vault`.
=== Модуль :domain-vault
Содержит реализацию доступа к Yandex Disk API, маппинг сетевых исключений в доменные коды (`VaultThrowableMappingTest`), буфер журнала синхронизации. Unit-тесты репозитория используют подмену HTTP-клиента.
=== Модуль :task-runtime
`TaskOrchestrator` управляет очередью долгих операций: единая точка для прогресса, отмены и логов, что используется UI экрана задач (гл. 5, рис. 12).
=== Модуль :infrastructure-android
Реализует Room (`AppDb` v5), DAO, репозитории, OAuth-хранилище токенов, файловые адаптеры Android. Модуль единственная точка зависимости от Android SDK в слое данных.
=== Сборка и зависимости
Корневой `settings.gradle.kts` фиксирует восемь включаемых модулей. Версии библиотек централизованы в `gradle/libs.versions.toml`. Задача `test` каждого модуля входит в обязательный прогон перед релизом прототипа (см. гл. 5, рис. 30).

View File

@@ -0,0 +1,34 @@
== Детализация реализации по модулям Gradle
=== Модуль :domain
Содержит чистую бизнес-логику: `Encryptor`, типы ключей, интерфейсы use case для хранилищ. Не зависит от Android SDK, что позволяет выполнять 12 unit-тестов на JVM без эмулятора. Класс `Encryptor` реализует симметричное шифрование для строк, массивов байт и потоков; метод `generateEncryptionInfo` формирует соль и параметры для проверки пароля без хранения пароля в открытом виде.
=== Модуль :usecases
Координирует сценарии приложения: `ManageStoragesEncryptionUseCase`, движок `StorageSyncEngine`, операции с 2FA и текстовыми секретами. Здесь сосредоточена наибольшая доля автоматических тестов (25), так как поведение синхронизации задаётся правилами журнала и блокировок, удобными для изоляции на моках файловой системы.
=== Модуль :domain-vault
Инкапсулирует сетевой доступ к Yandex Disk и преобразование исключений Retrofit/OkHttp в доменные коды. Тесты репозитория не обращаются к реальной сети подставляется фейковый HTTP-слой, что обеспечивает детерминированность CI.
=== Модуль :ui
Предоставляет Compose-экраны, ViewModel, навигацию (`WallencDeepLinks`), строковые ресурсы для прогресса задач. Unit-тесты проверяют парсеры и маппинг без поднятия Activity; инструментальные тесты (`androidTest`) валидируют композицию экранов 2FA и секретов.
=== Модуль :infrastructure-android
Связывает Room, DataStore, OAuth Activity Result API, реализации репозиториев. Версия схемы БД 5; миграции отключены (`exportSchema = false`) на этапе прототипа. Инструментальный `YandexAccountRepositoryTest` подтверждает CRUD учётной записи в in-memory БД.
=== Модуль :task-runtime
Очередь задач с состояниями `pending`, `running`, `completed`, `failed`, `cancelled`. Используется при длительном шифровании каталогов и будущей синхронизации; UI подписывается на Flow прогресса.
=== Модуль :vault-contracts и :app
`:vault-contracts` задаёт точку расширения для новых провайдеров. `:app` Hilt-модули, `Application`, навигационный граф, сборка APK. Точка входа не содержит бизнес-правил; они делегируются use case.
=== Журнал разработки и контроль версий
Исходный код размещён в приватном репозитории GitLab ЮФУ; в процессе разработки использовалось зеркало на личном сервере Gitea (https://git.nullptr.top/nullptr/Wallenc).

160
Report/includes/ch04.typ Normal file
View File

@@ -0,0 +1,160 @@
#import "common.typ": pz-fig, pz-table
= Программная реализация
== Разработка программных модулей
Архитектура реализации следует принятой на этапе проектирования схеме MVVM + Clean Architecture. Модули Gradle разделены по ответственности: контракты vault, доменная логика, сценарии use case, Android-инфраструктура (Room, OAuth, файловые адаптеры), UI и точка входа `:app`. Такое разбиение позволило параллельно развивать локальный и удалённый контуры и изолировать криптографию от представления данных.
=== Модуль криптографической защиты данных
Класс `Encryptor` формирует `StorageEncryptionInfo`, проверяет ключ и выполняет шифрование/дешифрование на клиенте. Unit-тесты подтверждают корректность для верного и неверного ключа (гл. 5).
=== Модуль управления storage и шифрованием
Use case `ManageStoragesEncryptionUseCase` инкапсулирует проверку `canEncrypt`, включение шифрования и открытие хранилища. ViewModel предотвращает повторный запуск шифрования для занятого storage.
Фрагмент логики включения шифрования:
```kotlin
fun enableEncryption(storage: IStorageInfo, password: String, encryptPath: Boolean) {
val key = EncryptKey(password)
viewModelScope.launch {
when (manageStoragesEncryptionUseCase.canEncrypt(storage)) {
ManageStoragesEncryptionUseCase.CanEncryptResult.Allowed -> {
manageStoragesEncryptionUseCase.enableEncryption(storage, key, encryptPath)
manageStoragesEncryptionUseCase.openStorage(storage, key, true)
}
ManageStoragesEncryptionUseCase.CanEncryptResult.AlreadyEncrypted -> { /* сообщение */ }
else -> { /* неподдерживаемая операция */ }
}
}
}
```
Блок-схема сценария рис. @fig-21.
#pz-fig("fig_21_encrypt_flow.png", [Блок-схема: enableEncryption checkKey openStorage], "fig-21")
=== Модуль адаптеров хранилищ
`VaultsManager` агрегирует один `LocalVault` и удалённые vault; адаптеры реализуют доступ к файлам внутри каждого `IStorage`. Регистрация удалённых vault через модуль `:vault-contracts`.
=== Модуль синхронизации хранилищ
В Room хранится сущность `DbStorageSyncGroup` набор UUID `Storage`, которые должны иметь согласованное состояние. Запуск синхронизации выполняется через `RunStorageSyncUseCase` / `WorkManager` и debounce при изменении файлов (рис. @fig-01@fig-03). Движок `StorageSyncEngine` (модуль `:usecases`) реализует согласование журналов изменений; доработка касается в основном политики фонового расписания и UX отображения прогресса.
Для каждого `Storage` ведётся журнал изменений по относительным путям пользовательских файлов. Запись журнала (`StorageSyncJournalEntry`) содержит операцию (`UPSERT`, `TRASH`, `DELETE`) и ревизию (`sequence`, `actorId`, `createdAt`). Ключи шифрования в обмен не включаются провайдер видит только зашифрованные объекты. Модуль `:task-runtime` обслуживает длительные задачи шифрования и синхронизации без блокировки UI.
=== Алгоритм согласования журналов синхронизации
Синхронизация одной группы выполняется в несколько этапов (рис. @fig-35).
#pz-fig("fig_35_sync_merge_algorithm.png", [Алгоритм согласования журналов (StorageSyncEngine)], "fig-35")
*Подготовка.* По UUID из `DbStorageSyncGroup` загружаются объекты `IStorage`. Если в группе меньше двух хранилищ или несовместимы параметры шифрования, синхронизация пропускается. На каждом accessor запрашивается блокировка sync (lease на удалённом диске best-effort; внутри процесса группа сериализуется `Mutex`). Параллельно для каждого storage вызываются `flushPendingSyncJournal()` и `readSyncJournal()`; служебные пути отфильтровываются (`StorageSyncPaths.isSyncableUserPath`).
*Слияние журналов.* Объект `StorageSyncJournalMerge` объединяет журналы в отображение «путь победитель». Для каждого пути остаётся запись с наибольшей ревизией. Сравнение реализовано функцией `compareEntries`: сначала `revision.sequence`, при равенстве `actorId`, затем `createdAt`. Такой порядок обеспечивает детерминированный выбор одной записи при конкурентных изменениях на разных устройствах.
*Выбор источника для пути.* После слияния для каждой пары (путь, `winnerEntry`) вызывается `findSourceStorage`:
- при `UPSERT` первый `Storage`, у которого запись по этому пути *совпадает* с победителем (`compareEntries == 0`): оттуда читаются байты для копирования;
- при `DELETE` или `TRASH` первый `Storage`, где путь ещё присутствует в журнале (или любой storage группы, если записей нет): оттуда инициируется удаление на целях.
Если для `UPSERT` источник не найден, путь пропускается (файл уже отсутствует на всех носителях).
*Распространение на цели.* Для каждого `target` в группе, отличного от источника, сравнивается ревизия записи на цели с `winnerEntry`. Если цель уже не слабее победителя (`compareEntries(target, winner) >= 0`), шаг пропускается. Иначе вызывается `applyEntry`: для `UPSERT` потоковое копирование `openRead` `openWrite`; для `DELETE` / `TRASH` `delete` или `moveToTrash`. Все операции выполняются с `recordSyncJournal = false`, чтобы не порождать цикл повторной синхронизации. Ошибки отдельных путей учитываются счётчиком `applyFailures`, отмена кооперативная (проверка `syncGeneration`, снятие блокировок в `finally`).
Фрагмент сравнения ревизий и выбора источника:
```kotlin
private fun compareEntries(a: StorageSyncJournalEntry, b: StorageSyncJournalEntry): Int {
val seqCmp = a.revision.sequence.compareTo(b.revision.sequence)
if (seqCmp != 0) return seqCmp
val actorCmp = a.revision.actorId.compareTo(b.revision.actorId)
if (actorCmp != 0) return actorCmp
return a.revision.createdAt.compareTo(b.revision.createdAt)
}
private fun findSourceStorage(..., winnerEntry: StorageSyncJournalEntry): IStorage? {
if (winnerEntry.operation == DELETE || winnerEntry.operation == TRASH) {
return storages.firstOrNull { entriesByStorage[it.uuid]?.get(path) != null }
?: storages.firstOrNull()
}
return storages.firstOrNull { storage ->
val entry = entriesByStorage[storage.uuid]?.get(path) ?: return@firstOrNull false
compareEntries(entry, winnerEntry) == 0
}
}
```
*Ограничения модели.* Механизм не является полноценным CRDT: конфликты снимаются фиксированным порядком ревизий, а не автоматическим слиянием содержимого. Содержимое файла при расхождении версий без роста `sequence` на одном пути не анализируется побайтно. Шифротекст передаётся как есть; расшифровка на стороне провайдера не предполагается. Корректность алгоритма проверена unit-тестами `StorageSyncEngineTest` (гл. 5): слияние одной записи на путь, пропуск цели с актуальной ревизией, копирование и удаление, cooperative cancellation.
=== Использование средств ИИ при разработке
Разработка Wallenc велась в два этапа. На первом этапе исполнитель самостоятельно спроектировал доменную модель (иерархия vault storage файлы, единый `VaultsManager`), навигацию между экранами, визуальный стиль UI на Jetpack Compose @smith-jetpack-compose, границы Gradle-модулей и принципы сопровождаемого кода @martin-clean-code и каркас use case-слоя. Криптографический контур (`Encryptor`, привязка ключей к storage), журнал синхронизации и сценарии OAuth проектировались и проверялись вручную. При проектировании модуля `:domain` была составлена диаграмма классов (рис. @fig-24), фиксирующая первоначальные интерфейсы и связи сущностей до этапа разработки с ИИ-ассистентом.
#pz-fig("fig_24_domain_class_manual.png", [Диаграмма классов модуля domain (самостоятельное проектирование)], "fig-24")
На втором этапе, после готовности архитектурного каркаса, наращивание функционала выполнялось с помощью среды Cursor (модели семейства Composer): адаптеры Yandex Disk и локального storage, движок `StorageSyncEngine`, экраны 2FA и текстовых секретов, unit-тесты. После каждой генерации код просматривался в diff, запускались модульные тесты (`./gradlew :usecases:test` и смежные модули), критичные сценарии проверялись на устройстве.
#pz-table(
[Роли при разработке (фрагмент)],
3,
table.header([Этап], [Исполнитель], [Инструмент / метод]),
[Домен, навигация, UI-концепция], [Исполнитель ВКР], [Ручное проектирование, Compose],
[Адаптеры, sync, тесты, доработка UI], [Исполнитель ВКР + ревью], [Cursor, Gradle test],
[Шифрование, OAuth, ревизии журнала], [Исполнитель ВКР], [Ручная ревизия, без автогенерации «вслепую»],
) <tbl-ai-dev>
ИИ использовался как ускоритель шаблонного и повторяющегося кода, а не как замена проектных решений. Риски (неверные сигнатуры API, лишние зависимости, утечки в логи) снижались обязательной проверкой сборки, отсутствием секретов в репозитории и правилами `.gitignore` для локальных конфигураций.
== Разработка мобильного приложения на Kotlin (Android)
=== Слой domain
Модуль `:domain` содержит интерфейсы хранилищ и use case. Модуль `:usecases` связывает сценарии приложения.
=== Слой data
Модуль `:infrastructure-android` реализует Room `AppDb` (версия 5) с сущностями `DbStorageKeyMap`, `DbStorageMetaInfo`, `DbYandexAccount`, `DbStorageSyncGroup`:
```kotlin
@Database(
entities = [
DbStorageKeyMap::class,
DbStorageMetaInfo::class,
DbYandexAccount::class,
DbStorageSyncGroup::class,
],
version = 5,
exportSchema = false,
)
abstract class AppDb : IAppDb, RoomDatabase()
```
=== Слой presentation
Модуль `:ui` и `:app` содержат Compose-экраны, ViewModel и навигацию. OAuth Яндекс запускается из UI удалённых vault:
```kotlin
remoteVaultAuthenticator.beginLink(CloudBrand.YANDEX) { outcome ->
when (outcome) {
is VaultLinkOutcome.Success ->
viewModel.onVaultLinked(outcome.registration)
is VaultLinkOutcome.Failed -> { /* ошибка */ }
VaultLinkOutcome.Cancelled -> { }
}
}
```
== Взаимодействие подсистем и итоговая архитектура
Зависимости модулей Gradle показаны на рисунке @fig-23. Полный исходный код модулей сборки приведён в приложении А.
#pz-fig("fig_23_module_deps.png", [Зависимости модулей Gradle], "fig-23")
В основном тексте приведены показательные фрагменты; полные листинги в приложении А.
#include "ch04-expand.typ"
#include "ch04-modules.typ"

View File

@@ -0,0 +1,82 @@
// AUTO-GENERATED by gen_test_tables.py — include from ch05.typ
#import "common.typ": pz-test-table
#pz-test-table(
[Реестр модульных unit-тестов],
4,
table.header(
[],
[Модуль],
[Метод],
[Проверяемое поведение],
),
[1], [domain], [mapsFileNotFoundException], [исключение преобразуется в типизированную ошибку Wallenc],
[2], [domain], [mapsGenericExceptionToUnknown], [исключение преобразуется в типизированную ошибку Wallenc],
[3], [domain], [mapsIOExceptionToIoFailed], [исключение преобразуется в типизированную ошибку Wallenc],
[4], [domain], [preservesWallencException], [сохранение уже типизированного WallencException],
[5], [domain], [test bytes encryption with the same key], [симметрия шифрования и дешифрования при верном ключе],
[6], [domain], [test bytes encryption with the wrong key], [дешифрование с неверным ключом завершается ошибкой],
[7], [domain], [test correct key for StorageEncryptionInfo], [верный ключ проходит проверку checkKey],
[8], [domain], [test incorrect key for StorageEncryptionInfo], [верный ключ проходит проверку checkKey],
[9], [domain], [test stream encryption with the same key], [симметрия шифрования и дешифрования при верном ключе],
[10], [domain], [test stream encryption with the wrong key], [дешифрование с неверным ключом завершается ошибкой],
[11], [domain], [test string encryption with the same key], [симметрия шифрования и дешифрования при верном ключе],
[12], [domain], [test string encryption with the wrong key], [дешифрование с неверным ключом завершается ошибкой],
[13], [domain-vault], [diskInfoParsesResponse], [разбор ответа API diskInfo],
[14], [domain-vault], [diskInfoThrowsAuthExceptionOn401], [AuthException при HTTP 401],
[15], [domain-vault], [flushRestoresPendingOnWriteFailure], [откат буфера журнала при сбое записи],
[16], [domain-vault], [listReturnsEmptyEmbeddedOn404], [пустой список при HTTP 404],
[17], [domain-vault], [mapsFileNotFoundToStorageFileNotFound], [исключение преобразуется в типизированную ошибку Wallenc],
[18], [domain-vault], [mapsHttpExceptionToNetworkHttpFailed], [исключение преобразуется в типизированную ошибку Wallenc],
[19], [domain-vault], [mapsIllegalStateNotAFile], [исключение преобразуется в типизированную ошибку Wallenc],
[20], [domain-vault], [mapsMissingOAuthTokenIoToTokenMissing], [исключение преобразуется в типизированную ошибку Wallenc],
[21], [domain-vault], [mapsSocketTimeoutToOperationTimedOut], [исключение преобразуется в типизированную ошибку Wallenc],
[22], [domain-vault], [mapsYandexDiskAuthToAuthFailed], [исключение преобразуется в типизированную ошибку Wallenc],
[23], [task-runtime], [cancelAllMarksRunningTaskCancelled], [жизненный цикл фоновой задачи],
[24], [task-runtime], [cancelMarksTaskCancelled], [жизненный цикл фоновой задачи],
[25], [task-runtime], [enqueueCompletesTask], [жизненный цикл фоновой задачи],
[26], [task-runtime], [failRecordsFailedState], [жизненный цикл фоновой задачи],
[27], [task-runtime], [logAppendsLine], [log appends line],
[28], [task-runtime], [progressUpdatesRunningState], [жизненный цикл фоновой задачи],
[29], [ui], [clearContentProgress_mapsToStringRes], [маршрутизация, deep link или подписи UI],
[30], [ui], [mapsFeatureStorageNotFound], [исключение преобразуется в типизированную ошибку Wallenc],
[31], [ui], [mapsStorageIncorrectKey], [исключение преобразуется в типизированную ошибку Wallenc],
[32], [ui], [mapsUnknown], [исключение преобразуется в типизированную ошибку Wallenc],
[33], [ui], [matchesTasksAndSettingsHosts], [matches tasks and settings hosts],
[34], [ui], [matchesWallencViewIntent], [маршрутизация, deep link или подписи UI],
[35], [ui], [parsesStandardTotpUri], [корректность TOTP/OTP: parses standard totp uri],
[36], [ui], [rejectsMissingSecret], [разбор и валидация входных данных],
[37], [ui], [rejectsNonOtpauthScheme], [корректность TOTP/OTP: rejects non otpauth scheme],
[38], [ui], [rejectsUnrelatedIntent], [разбор и валидация входных данных],
[39], [ui], [startTestTaskEnqueuesWork], [постановка тестовой задачи в очередь orchestrator],
[40], [ui], [storageHomeRouteCarriesVaultAndStorageIds], [маршрутизация, deep link или подписи UI],
[41], [ui], [syncNoGroups_mapsToStringRes], [сценарий синхронизации: no groups_maps to string res],
[42], [ui], [textSecretsRoutesCarryRequiredArguments], [маршрутизация, deep link или подписи UI],
[43], [ui], [vaultTask_mapsToStringRes], [маршрутизация, deep link или подписи UI],
[44], [usecases], [buildTwoFaCodeStateMatchesJavaOtpForKnownSecret], [корректность TOTP/OTP: build two fa code state matches java otp for known secret],
[45], [usecases], [buildTwoFaCodeStateReturnsNullForInvalidSecret], [build two fa code state returns null for invalid secret],
[46], [usecases], [deleteWithRecordSyncJournalFalseDoesNotBumpSequence], [удаление без записи в журнал не увеличивает sequence],
[47], [usecases], [isSyncableUserPathExcludesEncDirAndJournal], [пользовательский путь исключает служебные каталоги],
[48], [usecases], [mergeKeepsSingleEntryPerPath], [слияние журнала оставляет одну запись на путь],
[49], [usecases], [openReadDoesNotChangeJournal], [чтение без записи не изменяет журнал синхронизации],
[50], [usecases], [storageWithEncInfoIsIncompatible], [хранилище с шифрованием несовместимо в одной группе sync],
[51], [usecases], [storageWithoutEncInfoIsCompatible], [хранилище без метаданных шифрования совместимо с синхронизацией],
[52], [usecases], [syncAllGroupsReportsNoGroupsWhenEmpty], [сценарий синхронизации: all groups reports no groups when empty],
[53], [usecases], [syncGroupCooperativeCancellationReleasesLocks], [снятие блокировок при отмене задачи пользователем],
[54], [usecases], [syncGroupCopiesFileFromSourceToTarget], [копирование файла с источника на целевое хранилище в группе],
[55], [usecases], [syncGroupDeleteRemovesFileOnTarget], [удаление файла на целевом хранилище при синхронизации],
[56], [usecases], [syncGroupReleasesLocksAfterSuccessfulSync], [снятие блокировок после успешной синхронизации],
[57], [usecases], [syncGroupReleasesLocksWhenJournalEmpty], [снятие блокировок при пустом журнале],
[58], [usecases], [syncGroupReleasesLocksWhenJournalReadFails], [снятие блокировок при ошибке чтения журнала],
[59], [usecases], [syncGroupSkippedWhenFewerThanTwoStorages], [синхронизация пропускается, если в группе меньше двух хранилищ],
[60], [usecases], [syncGroupStopsWhenLockCannotBeAcquired], [остановка при невозможности захватить блокировку группы],
[61], [usecases], [syncGroupTrashSoftDeletesOnTarget], [мягкое удаление (trash) на целевом хранилище],
[62], [usecases], [syncSkipsWhenTargetRevisionAlreadyWinner], [пропуск синхронизации, если ревизия цели уже новее],
[63], [usecases], [textSecretsCrudWorksWithOptionalLabels], [CRUD-операции и сохранение данных],
[64], [usecases], [textSecretsInvalidJsonFallsBackToEmptyList], [text secrets invalid json falls back to empty list],
[65], [usecases], [totpPeriodProgressIsContinuousWithinPeriod], [корректность TOTP/OTP: totp period progress is continuous within period],
[66], [usecases], [totpSecondsUntilRefreshCountsDownWithinPeriod], [корректность TOTP/OTP: totp seconds until refresh counts down within period],
[67], [usecases], [twoFaCrudWorksAndPersists], [CRUD-операции и сохранение данных],
[68], [usecases], [twoFaInvalidJsonFallsBackToEmptyList], [two fa invalid json falls back to empty list],
) <tbl-unit-all>

104
Report/includes/ch05.typ Normal file
View File

@@ -0,0 +1,104 @@
#import "common.typ": pz-fig, pz-table
= Тестирование программного обеспечения
#include "testing/_intro.typ"
#include "testing/01-plan.typ"
== Модульные тесты (JUnit)
В проекте реализовано 68 автоматических unit-тестов в пяти модулях (`:domain` 12, `:domain-vault` 10, `:usecases` 25, `:ui` 15, `:task-runtime` 6). Тесты выполняются на JVM при сборке.
#include "ch05-tests-generated.typ"
=== Криптография и доменные ошибки
Класс `EncryptorTest` проверяет сценарии AES: `checkKey`, шифрование строк, байтовых массивов и потоков с верным и неверным ключом (строки 514 табл. @tbl-unit-all). `WallencExceptionMappingTest` покрывает преобразование файловых и сетевых исключений.
Прогон `./gradlew :domain:test` на рис. @fig-27.
#pz-fig("fig_27_gradle_domain_test.png", [Отчёт Gradle: модуль :domain, задача test], "fig-27")
=== Синхронизация, 2FA и use cases
`StorageSyncEngineTest` моделирует группы синхронизации, копирование и удаление файлов, soft-delete, отмену и блокировки (строки 5264 табл. @tbl-unit-all); отдельно проверяются слияние журнала (`mergeKeepsSingleEntryPerPath`) и пропуск цели с актуальной ревизией (`syncSkipsWhenTargetRevisionAlreadyWinner`) см. алгоритм в гл. 4. `TwoFaTotpTest` сверяет TOTP с эталоном Java OTP. `StorageDomainUseCasesTest` проверяет CRUD текстовых секретов и 2FA.
#pz-fig("fig_28_gradle_usecases_test.png", [Отчёт Gradle: модуль :usecases], "fig-28")
=== Модуль :domain-vault
`YandexDiskRepositoryTest` использует мок HTTP: разбор `diskInfo`, пустой список при 404, `AuthException` при 401. `VaultThrowableMappingTest` покрывает сетевые и файловые ошибки vault.
=== Модуль :ui
Проверены чистые функции навигации, deep link, подписи уведомлений, парсинг OTP URI и постановка задачи в очередь (`TaskPipelineViewModelTest`).
#pz-fig("fig_29_gradle_ui_test.png", [Отчёт Gradle: модуль :ui], "fig-29")
=== Модуль :task-runtime
`TaskOrchestratorTest` проверяет enqueue, progress, fail, cancel и cancelAll.
== Инструментальные тесты (androidTest)
#pz-table(
[Инструментальные тесты androidTest],
4,
table.header([Модуль], [Класс], [Назначение], [Методов]),
[:ui], [TwoFaTokensScreenContentTest], [Compose: экран 2FA токенов], [2],
[:ui], [TextSecretsScreenContentTest], [Compose: текстовые секреты], [2],
[:infra], [YandexAccountRepositoryTest], [Room in-memory: аккаунт Яндекс], [3],
[:app], [YandexDiskLiveIntegrationTest], [Живой API (при наличии токена)], [3],
) <tbl-androidtest>
Запуск: `./gradlew connectedDebugAndroidTest`. Результат рис. @fig-31. Отрисовка экранов секретов и 2FA подтверждена скриншотами @fig-33@fig-34.
#pz-fig("fig_31_gradle_connected_test.png", [Gradle connectedDebugAndroidTest], "fig-31")
=== Экран задач и уведомления
Очередь фоновых операций (шифрование, синхронизация, тестовые задачи) и системное уведомление о прогрессе показаны на рис. @fig-12 и @fig-13.
#pz-fig("fig_12_tasks_screen.jpg", [Экран «Очередь задач»], "fig-12")
#pz-fig("fig_13_tasks_notification.jpg", [Уведомление Android о синхронизации хранилищ], "fig-13")
== Ручное и UI-тестирование
Ручные прогоны выполнялись по чек-листу T-7…T-12 на эмуляторе и физическом устройстве.
#pz-table(
[Протокол ручного тестирования],
5,
table.header([ID], [Шаг], [Статус], [Фактический результат], [Иллюстрация]),
[T-7], [Создать storage в LocalVault], [OK], [Storage в списке], [@fig-05],
[T-8], [Включить шифрование], [OK], [Статус encrypted], [@fig-06],
[T-9], [Открыть/закрыть storage], [OK], [Контент только при открытом storage], [@fig-07],
[T-10], [OAuth Яндекс], [OK], [Аккаунт на @fig-09, выбор провайдера @fig-10, OAuth @fig-20], [@fig-09],
[T-11], [Фоновая задача шифрования], [OK], [Прогресс на экране задач], [@fig-12],
[T-12], [Уведомление о завершении], [OK], [Notification отображён], [@fig-13],
) <tbl-testres>
#pz-fig("fig_32_manual_test_checklist.png", [Чек-лист ручного UI-тестирования], "fig-32")
== Отчёт о результатах тестирования
По итогам `./gradlew test` все 68 unit-тестов завершились со статусом PASSED. Инструментальные тесты `:ui` подтвердили отрисовку экранов секретов и 2FA; тесты Room персистентность учётной записи Яндекс.
#pz-fig("fig_30_gradle_test_summary.png", [Сводка Gradle test по модулям], "fig-30")
#pz-table(
[Трассировка требований тесты],
3,
table.header([ФР], [Тесты], [Комментарий]),
[ФР-1], [T-7, StorageDomainUseCasesTest], [Storage в LocalVault и CRUD секретов],
[ФР-2], [EncryptorTest, T-8, T-9], [Покрытие AES],
[ФР-3], [TextSecretsScreenContentTest], [UI + domain],
[ФР-4], [YandexDiskRepositoryTest, T-10], [HTTP-мок и ручной OAuth],
[ФР-5], [StorageSyncEngineTest], [Синхронизация групп],
[ФР-6], [TaskOrchestratorTest, T-11], [Очередь и экран задач],
) <tbl-trace>
== Вывод
План тестирования выполнен: автоматизированное покрытие охватывает криптографию, синхронизацию, задачи, парсинг OTP и обработку ошибок; ручные сценарии подтвердили пригодность UI для vault и OAuth. Результаты обосновывают готовность прототипа Wallenc к демонстрации и развитию в рамках ВКР.

View File

@@ -0,0 +1,44 @@
#import "common.typ": pz-table
= Оценка результатов и экономические показатели
== Обзор рынка программного обеспечения и аналогов
Рынок клиентских решений для защищённого хранения данных представлен продуктами классов «локальный сейф», менеджеры секретов и zero-knowledge файловые клиенты (см. табл. @tbl-analog, гл. 1). Ниша Wallenc мобильный универсальный vault без собственного backend приложения с возможностью подключения внешних провайдеров через OAuth.
== Оценка экономических затрат на разработку проекта
Затраты на разработку в рамках практики (09.02.202606.05.2026) оцениваются по трудозатратам обучающегося и использованию открытых инструментов (Android Studio, Kotlin, Typst).
#pz-table(
[Структура затрат на разработку (оценка)],
3,
table.header([Статья затрат], [Ед.], [Сумма, руб.]),
[Трудозатраты (≈480 ч практики)], [н/ч], [0 (учётная работа в рамках учебного плана)],
[Лицензии ПО (IDE, SDK)], [комплект], [0],
[Устройство для тестирования], [1], [имеется у исполнителя],
[Итого прямых денежных затрат], [], [ 0],
) <tbl-cost>
#pz-table(
[Календарный план-график практики (фрагмент)],
2,
table.header([Период], [Результат]),
[09.0228.02.2026], [Анализ, аналоги, ТЗ, архитектура],
[01.0328.03.2026], [Проектирование БД, стек, OAuth-исследования],
[29.0319.04.2026], [Реализация ядра, UI, Room, Яндекс],
[20.0405.05.2026], [Тестирование, иллюстрации, отчётность],
[06.05.2026], [Защита практики, итоговая сборка ПЗ],
) <tbl-calendar>
Календарный график работ совпадает с дневником практики: этап 1 аналитика и проектирование (февраль–март 2026); этап 2 реализация (29.0319.04.2026); оформление документации апрель–май 2026.
== Модель применения и окупаемости
*Сценарий применения* внедрение в ООО НМФ «Нейротех» как внутренний инструмент защищённого хранения файлов сотрудников на корпоративных или личных облачных аккаунтах при сохранении zero-knowledge модели.
*Эффект*: снижение риска утечки при компрометации провайдера; отсутствие затрат на собственный сервер приложения. Окупаемость для учебно-промышленного прототипа выражается не в прямой прибыли, а в сокращении рисков и возможности доработки продукта без смены архитектуры.
== Вывод
Экономическая оценка показывает низкие прямые затраты на создание прототипа и потенциальную практическую ценность для организации-партнёра. Детальный рыночный анализ приведён в п. 1.3.5; полноценный раздел ТЭО в объёме отраслевых ВКР не требуется согласно рекомендациям кафедры.

View File

@@ -0,0 +1,45 @@
#import "common.typ": pz-fig, pz-table
= Вариативная профессиональная компетенция (ВПК-2)
В рамках образовательной программы по направлению 09.03.04 «Программная инженерия» формируется вариативная профессиональная компетенция (ВПК-2):
#emph[Способен решать прикладные задачи анализа данных и принятия решений с использованием искусственного интеллекта.]
В текущей версии Wallenc модели машинного обучения в runtime не внедрены: продукт строится на zero-knowledge модели, отсутствует доверенный сервер приложения, а выгрузка расшифрованного содержимого storage на облако для обучения противоречила бы заявленным требованиям безопасности. Ниже рассмотрено, какие прикладные задачи анализа данных и автоматизированных решений *могли бы* быть добавлены в перспективе без нарушения клиентской модели угроз.
== Связь компетенции с предметом ВКР
Wallenc уже оперирует структурированными данными, пригодными для анализа: журнал синхронизации (пути, операции, ревизии, метки времени), метаданные файлов (имя, размер, тип по расширению), события фоновых задач. Это не «сырой» пользовательский контент, а агрегаты, которые можно обрабатывать локально. Компетенция ВПК-2 в контексте ВКР раскрывается постановкой задач, выбором данных и моделей, метриками качества и правилами принятия решений даже если в MVP остаётся только проектный анализ.
== Прикладные задачи анализа данных и принимаемые решения
#pz-table(
[Гипотетические задачи ИИ в экосистеме Wallenc],
(1.05fr, 1.15fr, 1.05fr, 0.82fr, 1.08fr),
table.header([Задача], [Данные для анализа], [Тип модели], [Где inference], [Решение для пользователя]),
[Классификация и теги файлов], [Имя, расширение, размер; опционально эмбеддинг после локального decrypt], [#text(hyphenate: true, lang: "ru")[Лёгкий класси#sym.zws фикатор / k-NN]], [On-device (TFLite)], [Предложить теги, группировку в vault],
[Аномалии в журнале sync], [Частота операций по путям, время, actorId], [Isolation Forest, пороговые правила], [On-device], [Предупреждение о подозрительной активности],
[Похожие имена / дубликаты], [Нормализованные имена, размер], [Эмбеддинги строк + косинусная близость], [On-device], [Подсказка при синхронизации или импорте],
[Импорт секрета со скриншота], [Изображение экрана (только по действию пользователя)], [OCR (ML Kit)], [On-device], [Заполнить поле текстового секрета],
) <tbl-vpk-ml-tasks>
Каждая строка таблицы связывает *анализ данных* (входные признаки) с *решением* (действие системы или рекомендация), что соответствует формулировке компетенции.
== Модели, обучение и развёртывание
*Классификация файлов.* Обучающая выборка может формироваться из синтетических имён и публичных датасетов типов файлов без доступа к реальным vault пользователей. Пайплайн: разметка классов (документ, изображение, архив) обучение компактной сети (MobileNet-подобный backbone) квантизация конвертация в TensorFlow Lite @tflite-docs. Метрики: accuracy, F1 по классам на hold-out.
*Детектор аномалий sync.* Признаки: число UPSERT/DELETE за окно времени, доля новых путей, смена `actorId`. Обучение на журналах, сгенерированных unit-тестами и симуляциями `StorageSyncEngineTest`, плюс размеченные «нормальные» и «атакующие» сценарии. Метрики: precision/recall для класса «аномалия». Решение: показать уведомление, не блокировать sync автоматически без подтверждения.
*OCR.* Google ML Kit Text Recognition @mlkit-text позволяет извлечь текст без собственного обучения; дообучение нужно только для специфичных шрифтов. Решение: предзаполнить поле секрета с возможностью редактирования.
Общая схема on-device inference приведена на рис. @fig-36: метаданные и признаки не покидают устройство в открытом виде; обучение на расшифрованном содержимом всех пользователей в облаке для Wallenc неприемлемо. Альтернатива *федеративное обучение* только на агрегированных градиентах по opt-in, без централизованного хранения файлов.
#pz-fig("fig_36_ml_on_device.png", [Гипотетический контур on-device ML], "fig-36")
== Этика, ограничения и вывод по ВПК-2
Любой модуль ИИ должен быть отключаемым (opt-in), объяснять пользователю основание рекомендации (например, «похожее имя файла») и не подменять явное действие при удалении или синхронизации. Конфликт с E2E возникает, если отправлять ciphertext или ключи на внешний API inference в проекте такой путь не рассматривается.
*Вывод.* Компетенция «решать прикладные задачи анализа данных и принятия решений с использованием искусственного интеллекта» в пояснительной записке раскрыта анализом применимости ML к Wallenc: сформулированы задачи, источники данных, типы моделей, метрики обучения и границы развёртывания on-device. Внедрение в текущий MVP не выполнялось осознанно в соответствии с целями ВКР по защищённому хранению без доверенного backend.

219
Report/includes/common.typ Normal file
View File

@@ -0,0 +1,219 @@
// Таблицы как в «Пример работы с Typst.typ» и gost: figure + table + table.header.
// ГОСТ 7.32 / чек-лист: в тексте и подписях среднее тире «–» (U+2013), не длинное «—».
#let pz-figure-caption-separator = [#sym.space.nobreak#sym.dash.en#sym.space.nobreak]
// Разрыв длинных таблиц и подпись сверху задаёт шаблон modern-g7-32 (style.typ).
#show table: set text(hyphenate: true, lang: "ru")
#show table.cell: set block(inset: (x: 5pt, y: 3pt))
// Таблица внутри figure по умолчанию один неразрывный block (обрезка длинных реестров).
#show figure.where(kind: table): set block(breakable: true)
#let pz-appendix-title(body) = heading(level: 1)[#body]
#let pz-sig-line(label) = box(
width: 5.5cm,
stroke: (bottom: 0.5pt),
inset: (bottom: 3pt),
)[#label]
// Без heading(level: 1): в gost перед ним всегда pagebreak (add-pagebreaks).
#let pz-front-heading(body, page-break: true) = {
if page-break {
pagebreak(weak: true)
}
align(center)[
#text(weight: "bold")[#upper(body)]
]
v(0.75em)
}
// Официальная тема ВКР (приказ, титул, аннотация, ТЗ).
#let pz-thesis-topic = "Мобильное приложение для защищённого хранения пользовательских данных"
#let pz-thesis-topic-en = "Mobile application for secure storage of user data"
#let pz-thesis-subject = "мобильном приложении для защищённого хранения пользовательских данных"
// Бланк аннотации: 2×2 без рамок; левый столбец отступ, текст во 2-м (2 ячейки).
#let pz-dept-mop = "кафедра МОП ЭВМ"
#let pz-dept-sait = "кафедра системного анализа и телекоммуникаций"
#let pz-biblio-strip(head, tail) = {
table(
columns: (0.82fr, 1fr),
stroke: none,
inset: (x: 0pt, y: 5pt),
align: (left, left),
[], head,
[], tail,
)
v(0.65em)
}
#let pz-biblio-strip-ru(
udk: "",
author: "",
title: "",
direction: "09.03.04",
institute: "Южный федеральный университет",
faculty: "ИКТИБ",
department: pz-dept-mop,
year: 2026,
) = {
let pages = context counter(page).final().first()
pz-biblio-strip(
[
УДК #udk
#linebreak()
#upper(author)
#linebreak()
#quote[#title]
],
[
Квалификационная работа на степень
#linebreak()
«БАКАЛАВР» по направлению #direction
#linebreak()
#institute,
#linebreak()
#faculty, #department #year#sym.space.nobreak г.,
#linebreak()
#pages#sym.space.nobreak с.
],
)
}
#let pz-biblio-strip-en(
udk: "",
author: "",
title: "",
direction: "09.03.04",
institute: "Southern Federal University",
faculty: "ICTIS",
department: "the Department of Mathematical Support and Application of Computers (MOP EVM)",
year: 2026,
) = {
let pages = context counter(page).final().first()
pz-biblio-strip(
[
UDC #udk
#linebreak()
#upper(author)
#linebreak()
#quote[#title]
],
[
Qualification work for the degree of
#linebreak()
BACHELOR in the direction of #direction
#linebreak()
#institute,
#linebreak()
#faculty, #department #year,
#linebreak()
#pages#sym.space.nobreak p.
],
)
}
#let pz-column-count(columns) = if type(columns) == int {
columns
} else if type(columns) == array {
columns.len()
} else {
1
}
// Строка из ..range().map(i => ([a], [b])) в body.pos() один аргумент-массив.
#let pz-flatten-cells(cells) = cells.fold((), (acc, cell) => {
if type(cell) == array { acc + cell } else { acc + (cell,) }
})
#let pz-table-inner(columns, header-cells, tab-num, start-page, ..rows) = {
let col-n = pz-column-count(columns)
let cont-cell = table.cell(
colspan: col-n,
stroke: none,
inset: (x: 0pt, y: 2pt),
align: left,
)[
#context {
if start-page.get() == none {
start-page.update(here().page())
}
let first-page = start-page.get()
if first-page != none and here().page() > first-page {
[Продолжение таблицы #tab-num]
}
}
]
let header-row = if header-cells.len() > 0 {
(cont-cell, ..header-cells)
} else {
(cont-cell,)
}
table(
columns: columns,
table.header(..header-row, repeat: true),
..rows,
)
}
#let pz-table(caption, columns, ..body) = {
let pos = body.pos()
let has-header = pos.len() > 0 and pos.first().func() == table.header
let header-cells = if has-header { pos.first().children } else { () }
let tail = pz-flatten-cells(if has-header { pos.slice(1) } else { pos })
let start-page = state("pz-table-start-page", none)
figure(
kind: table,
context {
let fig-loc = here()
let tab-num = counter(figure.where(kind: table)).at(fig-loc).first()
start-page.update(none)
pz-table-inner(columns, header-cells, tab-num, start-page, ..arguments(..tail))
},
caption: caption,
)
}
// Реестры unit-тестов: перенос длинных идентификаторов (camelCase, _); сетка как в примере Typst.
#let pz-test-table(caption, columns, ..body) = {
let pos = body.pos()
let has-header = pos.len() > 0 and pos.first().func() == table.header
let header-cells = if has-header { pos.first().children } else { () }
let tail = pz-flatten-cells(if has-header { pos.slice(1) } else { pos })
let start-page = state("pz-table-start-page", none)
figure(
kind: table,
{
show table: set text(hyphenate: true, size: 9pt)
show table.cell: set block(inset: (x: 5pt, y: 4pt))
show table.cell: cell => {
show regex("[a-z0-9][A-Z]"): m => {
m.text.first() + sym.zws + m.text.last()
}
show regex("[_]"): it => it + sym.zws
cell
}
context {
let fig-loc = here()
let tab-num = counter(figure.where(kind: table)).at(fig-loc).first()
start-page.update(none)
pz-table-inner(columns, header-cells, tab-num, start-page, ..arguments(..tail))
}
},
caption: caption,
)
}
#let pz-fig(path, caption, lbl) = [
#figure(
image(
"../images/" + path,
width: 100%,
fit: "contain",
),
caption: caption,
)
#label(lbl)
]

View File

@@ -0,0 +1,13 @@
#heading(numbering: none, outlined: true)[Заключение]
В пояснительной записке рассмотрены анализ предметной области, проектирование и реализация мобильного приложения для защищённого хранения пользовательских данных (Wallenc).
По главе 1 сформированы требования и выполнен сравнительный анализ аналогов; обоснован выбор стека Kotlin/Compose/Room/Hilt. По главе 2 спроектированы бизнес-процессы, DFD, UML-диаграммы и модель данных Room. Глава 3 описывает пользовательские сценарии и интерфейсные решения. Глава 4 представляет реализованные модули, алгоритм `StorageSyncEngine` и методологию разработки с применением ИИ-ассистента Cursor; полный исходный код приведён в приложении А. Глава 5 документирует план и результаты тестирования. Глава 6 раскрывает ВПК-2: прикладные задачи анализа данных и принятия решений с использованием ИИ в перспективном развитии Wallenc.
*Цель работы достигнута*: разработан и протестирован прототип Android-приложения с иерархией vault storage файлы, клиентским шифрованием, OAuth Яндекс и реализованным движком синхронизации по журналам ревизий.
*Перспективы развития*: расширение фонового расписания sync; поддержка дополнительных провайдеров; опциональные on-device модели по сценариям гл. 6; расширение автоматизированных UI-тестов.
Программная документация приведена в приложении Б; иллюстрации интерфейса в приложении В.
По тестированию подтверждено: 68 модульных unit-тестов, инструментальные тесты Compose и Room, ручной протокол из двенадцати сценариев (гл. 5).

25
Report/includes/intro.typ Normal file
View File

@@ -0,0 +1,25 @@
#heading(numbering: none, outlined: true)[Введение]
Современные пользователи хранят личные и рабочие данные в облачных сервисах и на съёмных носителях, однако инфраструктура провайдера не всегда может считаться доверенной. Утечки, компрометация учётных записей и юрисдикционные риски делают актуальным подход, при котором конфиденциальность обеспечивается на стороне клиента до размещения данных во внешнем хранилище @nist-aes @martin-clean-architecture.
*Актуальность* темы обусловлена распространением мобильных приложений для хранения файлов и секретов, а также ограниченностью готовых решений: многие продукты привязаны к собственному backend, закрытой экосистеме или узкой предметной области @bitwarden-help @cryptomator-docs.
*Цель работы* повысить конфиденциальность пользовательских данных при работе с недоверенными хранилищами за счёт разработки мобильного приложения для защищённого хранения пользовательских данных (Wallenc) без собственного сервера приложения, с иерархией vault storage файлы и клиентским шифрованием.
Для достижения цели были поставлены следующие *задачи*:
- выполнить анализ предметной области и сравнительный обзор аналогов, сформировать требования к программному продукту;
- спроектировать архитектуру системы, модель данных и пользовательские сценарии;
- реализовать программные модули приложения Wallenc на платформе Android (Kotlin);
- провести тестирование программного обеспечения и оформить программную документацию.
*Объект исследования* методы и средства клиентской защиты данных в мобильных приложениях. *Предмет исследования* проектные и программные решения приложения Wallenc.
*Методы исследования*: анализ нормативной и технической документации, сравнительный анализ программных аналогов, объектно-ориентированное проектирование (UML, BPMN, DFD), прототипирование пользовательского интерфейса, программная реализация и тестирование @gost7322017 @martin-clean-code @kotlin-docs.
*Практическая база.* Работа выполнена в рамках производственной практики в ООО НМФ «Нейротех» (09.02.202606.05.2026) по направлению 09.03.04 «Программная инженерия» на кафедре математического обеспечения и применения ЭВМ (МОП ЭВМ). Научный руководитель Беликов А. Н. (кафедра системного анализа и телекоммуникаций, САИТ); руководитель от организации Алексеев Д. М.
*Научная новизна* заключается в сочетании единого `VaultsManager`, модели vault/storage, клиентского шифрования и адаптерного доступа к провайдерам без собственного сервера приложения, с проектным контуром синхронизации зашифрованных данных без передачи ключей провайдеру.
*Практическая значимость* использование результатов при дальнейшей разработке продукта и в учебных проектах по мобильной разработке и информационной безопасности.
Пояснительная записка состоит из введения, шести глав, заключения, списка использованных источников и трёх приложений (листинги исходного кода, программная документация, скриншоты интерфейса). В главе 1 обоснована актуальность и приведено сравнение аналогов; глава 2 описывает архитектуру и модель Room; глава 3 UX и пользовательские сценарии; глава 4 реализацию по модулям, алгоритм синхронизации журналов и применение ИИ-ассистента при разработке; глава 5 тестирование; глава 6 вариативную профессиональную компетенцию по анализу данных и решениям с использованием ИИ.

View File

@@ -0,0 +1,32 @@
// Show-правила листингов приложения А (вставляются в appendix-a.typ при генерации).
#show raw: set text(font: "DejaVu Sans Mono")
#let pz-listing-num-outset = 30mm
#let pz-listing-code-indent = 1.25cm
#show figure.where(supplement: [Листинг]): set block(breakable: true)
#show raw.where(block: true): it => block(
breakable: true,
width: 100% + pz-listing-num-outset,
outset: (left: pz-listing-num-outset),
)[
#set block(spacing: 0pt)
#it
]
#show raw.line: it => grid(
columns: (1.5em, 1fr),
column-gutter: pz-listing-code-indent,
align: (right + horizon, left + horizon),
inset: (y: 0.35pt),
text(size: 8.5pt, fill: luma(120))[#str(it.number)],
box(width: 100%)[
#set text(size: 9pt)
#set par(leading: 0.45em)
#it.body
],
)
#set figure(gap: 0.35em)

View File

@@ -0,0 +1,5 @@
// Сколько страниц вставляется перед телом ПЗ (front-matter-export/ВКР_первы_3_страницы_Пытков.pdf).
// Должно совпадать с front_pages в scripts/merge_front_matter.sh.
#let pz-vkr-front-page-count = 3
#counter(page).update(pz-vkr-front-page-count + 1)

View File

@@ -0,0 +1,15 @@
// Номера внизу: скрыты до первой страницы после «Содержания»; счётчик page не сбрасывается.
#let pz_pagination_from_page = state("pz-pagination-from-page", 0)
#let pz_enable_pagination() = context {
pz_pagination_from_page.update(here().page())
}
#let pz_page_footer = context {
let from-page = pz_pagination_from_page.at(here())
if from-page == 0 or here().page() < from-page {
[]
} else {
align(center)[#counter(page).display()]
}
}

View File

@@ -0,0 +1,157 @@
#import "common.typ": pz-sig-line, pz-thesis-topic
// Строка ТЗ с линией на всю ширину (как бланк ВКР).
// Не входит в Пояснительная_записка_ПытковРЕ.typ черновик: front-matter-export/tz-vkr-assignment.typ
#let tz-row(body) = {
v(0.35em)
table(
columns: 1,
stroke: none,
inset: (x: 0pt, top: 1pt, bottom: 6pt),
align: left,
table.hline(stroke: 0.5pt + black),
[#body],
)
}
#let tz-row-split(left, right) = {
v(0.35em)
table(
columns: (1fr, auto),
stroke: none,
inset: (x: 0pt, top: 1pt, bottom: 6pt),
table.hline(stroke: 0.5pt + black),
[#left],
align(end)[#right],
)
}
#align(center)[
#set par(first-line-indent: 0pt, justify: false, leading: 1.15em)
#text(weight: "bold")[ТЕХНИЧЕСКОЕ ЗАДАНИЕ]
#text(weight: "bold")[НА ВЫПУСКНУЮ КВАЛИФИКАЦИОННУЮ РАБОТУ]
#v(0.5em)
БАКАЛАВРА по образовательной программе\
#quote[Методы и средства разработки программного обеспечения]\
направления 09.03.04 Программная инженерия
#v(0.35em)
студенту группы КТбо4-9\
Пыткову Роману Евгеньевичу
]
#v(0.5em)
#set par(first-line-indent: 0pt, justify: true, leading: 0.95em)
#tz-row[
1. Тема выпускной квалификационной работы: #quote[#pz-thesis-topic] (программный продукт Wallenc) утверждена приказом по ВУЗу.
]
#tz-row[2. Требования и исходные данные к работе:]
#tz-row[2.1 Среда разработки: Android Studio, Visual Studio Code, Gradle, Git.]
#tz-row[2.2 ОС разработки: GNU/Linux / Windows 11.]
#tz-row[2.3 Язык программирования: Kotlin; целевая платформа Android (API 26+).]
#tz-row[2.4 Стек и инструменты: Jetpack Compose, Kotlin Coroutines/Flow, Hilt, Room, JUnit, PlantUML.]
#tz-row[3. Перечень подлежащих разработке вопросов (содержание работы):]
#tz-row[3.1 Введение]
#tz-row[3.2 Анализ требований и предметной области]
#tz-row[3.3 Проектирование архитектуры системы]
#tz-row[3.4 Проектирование пользовательского интерфейса мобильного приложения]
#tz-row[3.5 Программная реализация]
#tz-row[3.6 Тестирование программного обеспечения]
#tz-row[3.7 Вариативная профессиональная компетенция (ВПК-2)]
#tz-row[3.8 Заключение]
#tz-row[3.9 Список использованных источников]
#tz-row[3.10 Приложение А листинги исходного кода проекта Wallenc]
#tz-row[3.11 Приложение Б программная документация (ТЗ, руководство пользователя)]
#tz-row[3.12 Приложение В скриншоты пользовательского интерфейса]
#tz-row[4. Перечень графического материала:]
#tz-row-split(
[4.1 Цель, задачи и актуальность работы],
[ 1 пл.],
)
#tz-row-split(
[4.2 Контекстная диаграмма и анализ предметной области],
[ 1 пл.],
)
#tz-row-split(
[4.3 Обзор аналогов],
[ 1 пл.],
)
#tz-row-split(
[4.4 Требования к системе (функциональные и нефункциональные)],
[ 1 пл.],
)
#tz-row-split(
[4.5 Архитектура системы (BPMN, DFD, прецеденты, Clean Architecture, развёртывание, Room)],
[ 6 пл.],
)
#tz-row-split(
[4.6 Алгоритмы и диаграммы реализации (OAuth, шифрование, синхронизация журналов, зависимости модулей)],
[ 4 пл.],
)
#tz-row-split(
[4.7 Пользовательские сценарии и экраны UI],
[ 12 пл.],
)
#tz-row-split(
[4.8 Тестирование ПО],
[ 6 пл.],
)
#tz-row-split(
[4.9 Апробация результатов (производственная практика)],
[ 1 пл.],
)
#tz-row-split(
[4.10 Выводы по работе],
[ 1 пл.],
)
#tz-row[
5. Консультанты по выпускной квалификационной работе (с указанием разделов): не предусмотрены.
]
#tz-row[6. Срок сдачи законченной ВКР руководителю: «…» г.]
#tz-row[7. Дата выдачи задания: «…» г.]
#v(1.25em)
#align(left)[
#set par(first-line-indent: 0pt, justify: false, leading: 1.1em)
Руководитель образовательной программы\
заведующий кафедрой математического обеспечения и применения ЭВМ\
#v(0.5em)
#pz-sig-line[Беликов А. Н.]\
(подпись, дата)\
#v(0.75em)
Руководитель ВКР:\
старший преподаватель кафедры системного анализа и телекоммуникаций\
#v(0.5em)
#pz-sig-line[Беликов А. Н.]\
(подпись, дата)\
#v(0.75em)
Исполнитель:\
студент группы КТбо4-9\
#v(0.5em)
#pz-sig-line[Пытков Р. Е.]\
(подпись, дата)
]

View File

@@ -0,0 +1,46 @@
// Метки приложения В: микро-figure в нулевом блоке (без разрыва страницы) + фиксированный текст ссылок.
#box(width: 0pt, height: 0pt, clip: true)[
#figure(
box(width: 0pt, height: 0pt)[
#image("../../images/fig_05_local_vaults.jpg", width: 1pt, height: 1pt)
],
caption: none,
) <fig-05>
#figure(
box(width: 0pt, height: 0pt)[
#image("../../images/fig_06_encrypt_dialog.jpg", width: 1pt, height: 1pt)
],
caption: none,
) <fig-06>
#figure(
box(width: 0pt, height: 0pt)[
#image("../../images/fig_07_open_close_dialog.jpg", width: 1pt, height: 1pt)
],
caption: none,
) <fig-07>
#figure(
box(width: 0pt, height: 0pt)[
#image("../../images/fig_10_yandex_oauth.jpg", width: 1pt, height: 1pt)
],
caption: none,
) <fig-10>
#figure(
box(width: 0pt, height: 0pt)[
#image("../../images/fig_33_storage_secrets_2fa.jpg", width: 1pt, height: 1pt)
],
caption: none,
) <fig-33>
#figure(
box(width: 0pt, height: 0pt)[
#image("../../images/fig_34_2fa_single_token.jpg", width: 1pt, height: 1pt)
],
caption: none,
) <fig-34>
]
#show ref.where(target: label("fig-05")): [рис. 5]
#show ref.where(target: label("fig-06")): [рис. 6]
#show ref.where(target: label("fig-07")): [рис. 7]
#show ref.where(target: label("fig-10")): [рис. 10]
#show ref.where(target: label("fig-33")): [рис. 33]
#show ref.where(target: label("fig-34")): [рис. 34]

View File

@@ -0,0 +1,78 @@
// Файл 3 для загрузки: требования (предметная область + сравнение аналогов → ФР/НФР).
#import "../common.typ": pz-fig, pz-table
= Требования к приложению Wallenc
== Анализ предметной области (выдержка)
В рамках анализа предметной области рассмотрены подходы к хранению чувствительных данных в мобильных приложениях и облачных хранилищах. Выделены требования: конфиденциальность при хранении и передаче; отсутствие необходимости доверять инфраструктуре хранилища; устойчивость к компрометации удалённого провайдера; разделение логики хранения и криптографической защиты; удобные сценарии создания хранилища, шифрования, открытия и работы с содержимым.
Сформирован вывод о приоритете клиентских криптографических механизмов, унифицированного доступа к разным типам хранилищ и архитектуры с чётким разделением слоёв.
== Разработка требований к программному продукту
=== Назначение и цели создания системы
*Назначение* системы Wallenc предоставление пользователю мобильного клиента для работы с иерархией *VaultsManager → vault → storage → файлы*: один `LocalVault` на устройстве, удалённые vault по OAuth; внутри vault пользователь создаёт и управляет *storage*; шифрование (`StorageEncryptionInfo`, `Encryptor`) применяется к storage, а не к vault.
*Цели создания*: обеспечить единую модель vault и storage; минимизировать доверие к провайдеру; поддержать расширение списка провайдеров через адаптеры; сохранить служебные метаданные в локальной БД без хранения пользовательского контента в открытом виде.
=== Функциональные требования
Функциональные требования сведены в таблице @tbl-req-export.
#pz-table(
[Свод функциональных требований],
2,
table.header([Код], [Требование]),
[ФР-1], [Создание, просмотр, переименование и удаление storage в локальном vault (LocalVault один на устройстве)],
[ФР-2], [Включение шифрования storage, проверка ключа, открытие и закрытие зашифрованного представления],
[ФР-3], [Просмотр и операции с файлами внутри storage; текстовые секреты и 2FA],
[ФР-4], [OAuth-авторизация (Яндекс), регистрация удалённых vault и листинг их storage],
[ФР-5], [Синхронизация: группы хранилищ, журнал коммитов, фоновый Worker без передачи ключей],
[ФР-6], [Очередь фоновых задач: шифрование, синхронизация, отображение прогресса],
) <tbl-req-export>
==== Управление storage в локальном vault
Пользователь создаёт storage, просматривает список, переименовывает и удаляет их в единственном `LocalVault`. Служебные каталоги и системные пути не отображаются в пользовательском представлении.
==== Шифрование и открытие storage
При включении шифрования формируются параметры `StorageEncryptionInfo`; открытие выполняется только после успешной проверки ключа. Повторное шифрование одного storage до завершения предыдущей операции блокируется.
==== Работа с содержимым storage
Операции чтения и записи выполняются через единый интерфейс файлового доступа независимо от типа хранилища. Внутри открытого storage доступны текстовые секреты и генерация TOTP для 2FA.
==== Удалённые хранилища и авторизация во внешних провайдерах
Реализован поток OAuth 2.0 для Яндекса.
==== Синхронизация зашифрованных данных
Спроектирован механизм фоновой синхронизации: в Room хранятся записи с UUID хранилищ; по таймеру запускается сервис, сравнивающий истории коммитов локального и удалённого представления без передачи ключей на сервер провайдера.
=== Нефункциональные требования
К системе предъявляются требования по производительности (асинхронные операции на Coroutines), безопасности (AES на клиенте, минимизация утечек через имена путей), расширяемости (модульная структура Gradle) и устойчивости к гонкам при длительных операциях шифрования.
=== Требования к программно-аппаратной платформе
Минимальная платформа Android с поддержкой Jetpack Compose; для OAuth и удалённых операций требуется сетевое подключение. Объём оперативной памяти должен быть достаточен для фоновых задач шифрования и Room.
== Сравнение аналогов (обоснование требований)
#pz-table(
[Сравнительная оценка аналогов],
6,
table.header([Критерий], [Secure Folder], [Proton], [Bitwarden], [Cryptomator], [Wallenc]),
[Собственный backend приложения], [], [+], [+/], [], [],
[E2E / клиентское шифрование], [+/], [+], [+], [+], [+],
[Файловый vault], [+], [+], [], [+], [+],
[OAuth внешнего провайдера], [], [+/], [+/], [+/], [+],
[Переносимость провайдеров], [], [], [+/], [+], [+],
[Unit-тесты без сервера], [н/д], [н/д], [+/], [+/], [+ (68)],
) <tbl-analog-export>
По итогам сравнения аналогов сформированы функциональные требования ФР-1…ФР-6 и нефункциональные ограничения, отражённые в таблице @tbl-req-export.

View File

@@ -0,0 +1,8 @@
// Файл 4 для загрузки: наборы тестов, реестр и листинги src/test, src/androidTest.
#import "../common.typ": pz-fig, pz-table
= Код модулей автоматизированного тестирования и наборы тестов
#include "../testing/02-test-sets.typ"
#include "../testing/03-automation.typ"

View File

@@ -0,0 +1,64 @@
== План тестирования
=== Цели и задачи испытаний
Основная цель подтвердить корректность криптографического ядра, доменной логики синхронизации и сценариев UI. Были поставлены следующие задачи:
- проверить `Encryptor` и проверку ключа для строк, байтов и потоков;
- убедиться в корректном маппинге исключений в коды ошибок;
- протестировать движок синхронизации (`StorageSyncEngine`, журнал, блокировки);
- проверить оркестратор фоновых задач;
- выполнить smoke-тесты навигации, deep link и 2FA/TOTP;
- зафиксировать результаты ручных сценариев vault, OAuth и экрана задач.
=== Объект и уровни тестирования
#import "../common.typ": pz-table
#pz-table(
[Объекты и уровни тестирования Wallenc],
4,
table.header([Уровень], [Объект], [Инструмент], [Критерий успеха]),
[Unit], [Классы domain, usecases, ui, task-runtime, domain-vault], [JUnit 4, JVM], [Все тесты модуля успешны],
[Инструм.], [Room, Compose UI, OAuth], [AndroidJUnit, эмулятор], [Нет падений на целевом API],
[Ручной], [Сборка app, пользовательские цепочки], [Чек-лист], [Сценарии T-1…T-12 пройдены],
[Регресс.], [Синхронизация, шифрование], [Повтор unit + выборочный ручной], [Нет блокирующих дефектов],
) <tbl-test-levels>
=== Матрица тестовых сценариев
#pz-table(
[Матрица тестовых сценариев],
5,
table.header([ID], [Сценарий], [Тип], [Авто], [Ожидаемый результат]),
[T-1], [Проверка ключа шифрования], [Unit], [Да], [`Encryptor.checkKey` true/false],
[T-2], [Шифрование/дешифрование строки и байтов], [Unit], [Да], [Симметрия данных],
[T-3], [Потоковое шифрование файла], [Unit], [Да], [Данные после decrypt равны исходным],
[T-4], [Синхронизация группы хранилищ], [Unit], [Да], [Копирование, удаление, trash, блокировки],
[T-5], [2FA TOTP генерация], [Unit], [Да], [Совпадение с эталоном Java OTP],
[T-6], [Маппинг ошибок сети/диска], [Unit], [Да], [Типизированные `WallencException`],
[T-7], [CRUD storage в LocalVault], [Ручной], [Нет], [Список обновлён (прил. В, рис. 5)],
[T-8], [Включение шифрования storage], [Ручной], [Нет], [Статус «зашифровано» (прил. В, рис. 6)],
[T-9], [Открытие/закрытие storage], [Ручной], [Нет], [Доступ только с ключом (прил. В, рис. 7)],
[T-10], [OAuth Яндекс], [Ручной / IT], [Частично], [Токен в Room (прил. В, рис. 10)],
[T-11], [Экран задач и уведомления], [Ручной], [Частично], [Прогресс и завершение (прил. В, рис. 1213)],
[T-12], [Compose: секреты и 2FA], [IT], [Да], [Отображение без падений (прил. В, рис. 3334)],
) <tbl-testplan>
=== Критерии начала и окончания
*Начало:* собраны модули проекта; выполняется `./gradlew test`; для инструментальных тестов доступен эмулятор API 26+.
*Окончание:* все 68 unit-тестов в `src/test` завершились успешно; инструментальные тесты пройдены на эмуляторе; ручной чек-лист T-7…T-12 выполнен; критические дефекты отсутствуют.
=== Среда и инструменты
#pz-table(
[Тестовая среда],
2,
table.header([Параметр], [Значение]),
[ОС разработки], [GNU/Linux, Android Studio],
[JDK], [OpenJDK 17 / 21],
[Сборка], [`./gradlew test`, `./gradlew connectedDebugAndroidTest`],
[Устройство], [Эмулятор Pixel 6 API 34; физическое устройство для OAuth],
) <tbl-test-env>

View File

@@ -0,0 +1,59 @@
== Наборы тестов и покрытие требований
Наборы тестов сформированы по функциональным требованиям к Wallenc (ФР-1…ФР-6). Матрица сценариев T-1…T-12 связывает испытания с ожидаемым поведением; реестр unit-тестов фиксирует автоматизированные проверки по модулям Gradle.
=== Функциональные требования
#include "tbl-functional-req.typ"
=== Матрица тестовых сценариев
#import "../common.typ": pz-table
#pz-table(
[Матрица тестовых сценариев],
5,
table.header([ID], [Сценарий], [Тип], [Авто], [Ожидаемый результат]),
[T-1], [Проверка ключа шифрования], [Unit], [Да], [`Encryptor.checkKey` true/false],
[T-2], [Шифрование/дешифрование строки и байтов], [Unit], [Да], [Симметрия данных],
[T-3], [Потоковое шифрование файла], [Unit], [Да], [Данные после decrypt равны исходным],
[T-4], [Синхронизация группы хранилищ], [Unit], [Да], [Копирование, удаление, trash, блокировки],
[T-5], [2FA TOTP генерация], [Unit], [Да], [Совпадение с эталоном Java OTP],
[T-6], [Маппинг ошибок сети/диска], [Unit], [Да], [Типизированные `WallencException`],
[T-7], [CRUD storage в LocalVault], [Ручной], [Нет], [Список обновлён],
[T-8], [Включение шифрования storage], [Ручной], [Нет], [Статус encrypted],
[T-9], [Открытие/закрытие storage], [Ручной], [Нет], [Доступ только с ключом],
[T-10], [OAuth Яндекс], [Ручной / IT], [Частично], [Токен в `DbYandexAccount`],
[T-11], [Экран задач и уведомления], [Ручной], [Частично], [Прогресс и завершение],
[T-12], [Compose: секреты и 2FA], [IT], [Да], [Отображение без падений],
) <tbl-testplan-export>
=== Трассировка требований → тесты
#pz-table(
[Трассировка требований тесты],
3,
table.header([ФР], [Тесты], [Комментарий]),
[ФР-1], [T-7, StorageDomainUseCasesTest], [Storage в LocalVault и CRUD секретов],
[ФР-2], [EncryptorTest, T-8, T-9], [Покрытие AES],
[ФР-3], [TextSecretsScreenContentTest], [UI + domain],
[ФР-4], [YandexDiskRepositoryTest, T-10], [HTTP-мок и ручной OAuth],
[ФР-5], [StorageSyncEngineTest], [Синхронизация групп],
[ФР-6], [TaskOrchestratorTest, T-11], [Очередь и экран задач],
) <tbl-trace-export>
=== Реестр модульных unit-тестов
#include "../ch05-tests-generated.typ"
=== Инструментальные тесты (androidTest)
#pz-table(
[Инструментальные тесты androidTest],
4,
table.header([Модуль], [Класс], [Назначение], [Методов]),
[:ui], [TwoFaTokensScreenContentTest], [Compose: экран 2FA токенов], [2],
[:ui], [TextSecretsScreenContentTest], [Compose: текстовые секреты], [2],
[:infra], [YandexAccountRepositoryTest], [Room in-memory: аккаунт Яндекс], [3],
[:app], [YandexDiskLiveIntegrationTest], [Живой API (при наличии токена)], [3],
) <tbl-androidtest-export>

View File

@@ -0,0 +1,43 @@
== Автоматизированное тестирование (код модулей)
В проекте реализовано 68 автоматических unit-тестов в пяти модулях (`:domain` 12, `:domain-vault` 10, `:usecases` 25, `:ui` 15, `:task-runtime` 6). Тесты выполняются на JVM при сборке (`./gradlew test`). Инструментальные тесты размещены в `src/androidTest` соответствующих модулей.
=== Листинги исходного кода автотестов
Ниже листинги файлов `src/test` и `src/androidTest` (при сборке PDF читаются из дерева проекта Wallenc, по тому же принципу, что приложение А пояснительной записки).
#include "../../listings/generated-tests/appendix-tests.typ"
=== Криптография и доменные ошибки
Класс `EncryptorTest` проверяет сценарии AES: `checkKey`, шифрование строк, байтовых массивов и потоков с верным и неверным ключом. `WallencExceptionMappingTest` покрывает преобразование файловых и сетевых исключений.
#import "../common.typ": pz-fig
#pz-fig("fig_27_gradle_domain_test.png", [Отчёт Gradle: модуль :domain, задача test], "fig-27-export")
=== Синхронизация, 2FA и use cases
`StorageSyncEngineTest` моделирует группы синхронизации, копирование и удаление файлов, soft-delete, отмену и блокировки; отдельно проверяются слияние журнала и пропуск цели с актуальной ревизией. `TwoFaTotpTest` сверяет TOTP с эталоном Java OTP. `StorageDomainUseCasesTest` проверяет CRUD текстовых секретов и 2FA.
#pz-fig("fig_28_gradle_usecases_test.png", [Отчёт Gradle: модуль :usecases], "fig-28-export")
=== Модуль :domain-vault
`YandexDiskRepositoryTest` использует мок HTTP: разбор `diskInfo`, пустой список при 404, `AuthException` при 401. `VaultThrowableMappingTest` покрывает сетевые и файловые ошибки vault.
=== Модуль :ui
Проверены чистые функции навигации, deep link, подписи уведомлений, парсинг OTP URI и постановка задачи в очередь (`TaskPipelineViewModelTest`).
#pz-fig("fig_29_gradle_ui_test.png", [Отчёт Gradle: модуль :ui], "fig-29-export")
=== Модуль :task-runtime
`TaskOrchestratorTest` проверяет enqueue, progress, fail, cancel и cancelAll.
== Инструментальные тесты (androidTest)
Запуск: `./gradlew connectedDebugAndroidTest`. Результат на рис. @fig-31-export.
#pz-fig("fig_31_gradle_connected_test.png", [Gradle connectedDebugAndroidTest], "fig-31-export")

View File

@@ -0,0 +1,43 @@
== Отчёт о проведении тестирования
По итогам `./gradlew test` все 68 unit-тестов завершились со статусом PASSED. Инструментальные тесты `:ui` подтвердили отрисовку экранов секретов и 2FA; тесты Room персистентность учётной записи Яндекс.
#import "../common.typ": pz-fig, pz-table
#pz-fig("fig_30_gradle_test_summary.png", [Сводка Gradle test по модулям], "fig-30-export")
== Ручное и UI-тестирование
Ручные прогоны выполнялись по чек-листу T-7…T-12 на эмуляторе и физическом устройстве.
#pz-table(
[Протокол ручного тестирования],
5,
table.header([ID], [Шаг], [Статус], [Фактический результат], [Иллюстрация]),
[T-7], [Создать storage в LocalVault], [OK], [Storage в списке], [рис. 5],
[T-8], [Включить шифрование], [OK], [Статус encrypted], [рис. 6],
[T-9], [Открыть/закрыть storage], [OK], [Контент только при открытом storage], [рис. 7],
[T-10], [OAuth Яндекс], [OK], [Запись в `DbYandexAccount`], [рис. 10],
[T-11], [Фоновая задача шифрования], [OK], [Прогресс на экране задач], [рис. 12],
[T-12], [Уведомление о завершении], [OK], [Notification отображён], [рис. 13],
) <tbl-testres-export>
#pz-fig("fig_32_manual_test_checklist.png", [Чек-лист ручного UI-тестирования], "fig-32-export")
=== Трассировка требований → тесты (итог)
#pz-table(
[Трассировка требований тесты],
3,
table.header([ФР], [Тесты], [Комментарий]),
[ФР-1], [T-7, StorageDomainUseCasesTest], [Storage в LocalVault и CRUD секретов],
[ФР-2], [EncryptorTest, T-8, T-9], [Покрытие AES],
[ФР-3], [TextSecretsScreenContentTest], [UI + domain],
[ФР-4], [YandexDiskRepositoryTest, T-10], [HTTP-мок и ручной OAuth],
[ФР-5], [StorageSyncEngineTest], [Синхронизация групп],
[ФР-6], [TaskOrchestratorTest, T-11], [Очередь и экран задач],
) <tbl-trace-report>
=== Вывод
План тестирования выполнен: автоматизированное покрытие охватывает криптографию, синхронизацию, задачи, парсинг OTP и обработку ошибок; ручные сценарии подтвердили пригодность UI для vault и OAuth. Критические дефекты не выявлены. Результаты обосновывают готовность прототипа Wallenc к демонстрации и защите ВКР.

View File

@@ -0,0 +1 @@
В ходе работы было организовано тестирование Wallenc на нескольких уровнях: модульные автоматические тесты (JUnit, каталог `src/test` каждого Gradle-модуля), инструментальные тесты (`src/androidTest`), а также ручные функциональные и UI-прогоны. Программа и методика испытаний приведены в приложении Б пояснительной записки.

View File

@@ -0,0 +1,13 @@
#import "../common.typ": pz-table
#pz-table(
[Свод функциональных требований (фрагмент ПЗ, гл. 1)],
2,
table.header([Код], [Требование]),
[ФР-1], [Создание, просмотр, переименование и удаление storage в локальном vault (LocalVault один на устройстве)],
[ФР-2], [Включение шифрования storage, проверка ключа, открытие и закрытие зашифрованного представления],
[ФР-3], [Просмотр и операции с файлами внутри storage; текстовые секреты и 2FA],
[ФР-4], [OAuth-авторизация (Яндекс), регистрация удалённых vault и листинг их storage],
[ФР-5], [Синхронизация: группы хранилищ, журнал коммитов, фоновый Worker без передачи ключей],
[ФР-6], [Очередь фоновых задач: шифрование, синхронизация, отображение прогресса],
) <tbl-req-export>

View File

@@ -0,0 +1,120 @@
#import "common.typ": pz-sig-line, pz-thesis-topic
#let pz-title-page(
ministry: [],
university-full: [],
university-short: [],
institute: [],
department: [],
topic: [],
program: [],
direction: [],
student-name: [],
student-position: [],
supervisor-name: [],
supervisor-position: [],
norm-name: [],
norm-position: [],
dept-head-name: [],
dept-head-position: [],
city: [],
year: [],
) = {
set par(first-line-indent: 0pt, justify: false)
set align(center)
ministry
v(0.35em)
university-full
v(0.25em)
university-short
v(1.25em)
align(left)[
#set par(first-line-indent: 0pt)
#institute\
#department
]
v(0.75em)
grid(
columns: (1fr, 1fr),
gutter: 12pt,
align: (left, left),
[],
[
К защите допустить:\
#dept-head-position\
#v(0.6em)
#pz-sig-line(dept-head-name)\
#box(width: 5.5cm)[#year г.]
],
)
v(1.25em)
text(weight: "bold")[ВЫПУСКНАЯ КВАЛИФИКАЦИОННАЯ РАБОТА]
v(0.75em)
[по образовательной программе\
#quote[#program]\
направления #direction Программная инженерия]
v(0.75em)
[на тему:]
v(0.35em)
text(weight: "bold")[#quote[#topic]]
v(1.25em)
align(left)[
#set par(first-line-indent: 0pt)
Руководитель ВКР:\
#supervisor-position\
#v(0.6em)
#pz-sig-line(supervisor-name)\
(подпись, дата)\
#v(0.75em)
Нормоконтроль:\
#norm-position\
#v(0.6em)
#pz-sig-line(norm-name)\
(подпись, дата)\
#v(0.75em)
Выполнил:\
#student-position\
#v(0.6em)
#pz-sig-line(student-name)\
(подпись, дата)
]
v(1fr)
[#city #year]
}
#pz-title-page(
ministry: [Министерство науки и высшего образования Российской Федерации],
university-full: [Федеральное государственное автономное образовательное учреждение высшего образования «Южный федеральный университет»],
university-short: [(ЮЖНЫЙ ФЕДЕРАЛЬНЫЙ УНИВЕРСИТЕТ)],
institute: [Институт компьютерных технологий и информационной безопасности],
department: [Кафедра математического обеспечения и применения ЭВМ (МОП ЭВМ)],
topic: [#pz-thesis-topic],
program: [Методы и средства разработки программного обеспечения],
direction: [09.03.04],
student-name: [Пытков Р. Е.],
student-position: [студент группы КТбо4-9],
supervisor-name: [Беликов А. Н.],
supervisor-position: [старший преподаватель кафедры системного анализа и телекоммуникаций],
norm-name: [Беликов А. Н.],
norm-position: [старший преподаватель кафедры системного анализа и телекоммуникаций],
dept-head-name: [Беликов А. Н.],
dept-head-position: [Зав. кафедрой системного анализа и телекоммуникаций],
city: [Таганрог],
year: [2026],
)

2
Report/listings/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
generated/
generated-tests/

View File

@@ -0,0 +1,65 @@
repo_root: ".."
report_root: "."
scan_root: ".."
include_extensions:
.kt: kotlin
.kts: kotlin
.xml: xml
.pro: text
.properties: text
.toml: toml
include_filenames:
gradlew: bash
exclude_dirs:
- build
- .gradle
- .idea
- Report
- captures
- .cxx
- app/release
- node_modules
exclude_globs:
- "**/generated/**"
- "**/*.jar"
- "**/*.apk"
- "**/*.jks"
- "**/*.keystore"
- "**/*.hprof"
- local.properties
- google-services.json
- "**/*.png"
- "**/*.webp"
- "**/*.jpg"
- "**/*.gif"
- "**/*.md"
- "**/*.puml"
- "**/*.gaphor"
- "**/*.pdf"
- "**/*.typ"
modules_order:
- root
- app
- domain
- usecases
- ui
- domain-vault
- infrastructure-android
- vault-contracts
- task-runtime
root_build_files:
- settings.gradle.kts
- build.gradle.kts
- gradle.properties
- gradle/libs.versions.toml
- gradle/wrapper/gradle-wrapper.properties
- gradlew
pagebreak_per_module: false
pagebreak_per_file: false

View File

@@ -0,0 +1,37 @@
# Листинги автотестов (src/test, src/androidTest) — для testing-export и гл. 5.
scan_root: ".."
generated_subdir: "generated-tests"
appendix_filename: "appendix-tests.typ"
caption_prefix: "Автотест "
pagebreak_after_listing: false
pagebreak_per_module: true
only_path_substrings:
- "/src/test/"
- "/src/androidTest/"
only_test_filenames: true
include_extensions:
.kt: kotlin
exclude_dirs:
- build
- .gradle
- .idea
- Report
- captures
- .cxx
- node_modules
exclude_globs:
- "**/generated/**"
modules_order:
- domain
- domain-vault
- usecases
- ui
- task-runtime
- infrastructure-android
- app

View File

@@ -0,0 +1,12 @@
# Преддипломная практика — задание и дневник
Материалы для переноса в отчёт (.docx).
| Файл | Назначение |
|------|------------|
| `zadanie.csv`, `dnevnik.csv`, `analiz.csv` | Импорт в Word: *Вставка → Таблица → Текст в таблицу* (разделитель — запятая, UTF-8) |
| `zadanie.typ`, `dnevnik.typ`, `analiz.typ` | Исходники PDF (раздел IV — анализ работ) |
| `out/*.pdf` | После `bash build.sh` |
| `otzyv-organizaciya.md`, `otzyv-universitet.md` | Отзывы руководителей (копировать в отчёт) |
Сроки: **07.05.2026 04.06.2026**, ООО НМФ «Нейротех», проект Wallenc.

View File

@@ -0,0 +1,16 @@
№ п/п,Выполненные мероприятия в соответствии с заданием на практику,Анализ проведённой работы
1,Актуализация постановки задачи ВКР,"Уточнены цели преддипломной практики с опорой на уже реализованное ядро Wallenc: приоритет отдан доведению синхронизации storage, групп хранилищ, 2FA и текстовых секретов до демонстрационного уровня. Сформирован перечень доработок, согласованный с темой ВКР и результатами производственной практики, что исключило повторение начального аналитического цикла и сфокусировало сроки на инженерной и отчётной составляющих."
2,Реструктуризация модулей Gradle,"Выделение domain-storage и перенос логики localVault упростили границы ответственности между слоями: доменные контракты storage отделены от vault-навигации. Реструктуризация выполнена до внедрения крупных функций sync и секретов, поэтому последующие изменения локализовались в целевых модулях без каскадных правок по всему репозиторию."
3,Интеграция с Яндекс.Диск,"Развит REST-клиента, репозитория и сценариев удалённого vault довело OAuth-контур до устойчивой эксплуатации: учтены сетевые сбои на экране vault, сужена инвалидация кэша Disk API, добавлен учёт запросов. Пользователь получает рабочий поток подключения аккаунта и операций с удалённым storage без передачи ключей шифрования провайдеру, что соответствует исходной модели угроз Wallenc."
4,Синхронизация storage,"Реализован StorageSyncEngine с журналом изменений по путям, группами DbStorageSyncGroup в Room и фоновым исполнением через очередь задач. Отработаны блокировки sync, cooperative-отмена, foreground-задача и заголовки уведомлений; исправлены ошибки журнала при DELETE/TRASH. Синхронизация переведена из проектного статуса в проверяемый пользовательский сценарий с отображением прогресса на экране задач."
5,Секреты и TOTP 2FA,"Добавлены маршруты и экраны текстовых секретов и 2FA; генерация TOTP вынесена в фоновые задачи вместе с прочими длительными операциями. Замена зависимости mlkit на свободную библиотеку и доработка UI прогресса 2FA повысили воспроизводимость сценария на устройстве. Функции второго этапа практики интегрированы в общую модель «storage → содержимое» без отдельного backend."
6,Доработка UI/UX,"Унифицированы индикаторы занятости storage и подписи прогресса задач; улучшены карточки vault и обработка ошибок сети. Deep links и открытие экрана задач из уведомления сократили путь пользователя к статусу синхронизации. Иконка приложения и переключение языка завершили визуальную и локализационную подготовку к демонстрации на защите."
7,Шифрование и метаданные storage,"Исправлены сценарии шифрования и метаданных для Yandex vault и первого открытия storage; добавлена опция сохранения пароля при включении шифрования. Запрет включения зашифрованных storage в несовместимые группы sync снижает риск рассогласования состояния. Доменная логика Encryptor и ManageStoragesEncryptionUseCase согласована с фактическим поведением UI."
8,Тестирование,"Расширен набор unit-тестов (в том числе после рефакторинга sync и DI); выполнены ручные прогоны T-7…T-12 на эмуляторе. Подтверждены создание storage, шифрование, OAuth Яндекс, фоновые задачи и уведомления. Результаты зафиксированы для включения в раздел тестирования пояснительной записки и отчёта по практике."
9,Стабилизация сборки,"Устранена значительная часть предупреждений компилятора и deprecated API; упорядочены Hilt-модули. Оптимизированы debounce-sync, pipeline задач и обработка отсутствия journal/lock при синхронизации. Сборка assembleDebug и прогон тестов стали регулярной процедурой контроля качества перед оформлением отчётности."
10,Актуализация технического задания,"ТЗ приведено в соответствие с реализованным функционалом (sync, 2FA, секреты, очередь задач) и структурой по ГОСТ 7.322017. Зафиксированы критерии приёмки и границы доработок, что обеспечило согласованность между кодом, пояснительной запиской и заданием на практику."
11,Оформление пояснительной записки ВКР,"Подготовлены разделы обзора аналогов, архитектуры, проектирования UI, реализации и тестирования с опорой на фактическую кодовую базу. Текст ПЗ согласован с иерархией vault → storage → файлы и терминологией проекта. Объём работ отражает завершение цикла «разработка проверка описание», а не только документирование без изменений в приложении."
12,Иллюстрации и приложения ПЗ,"Подготовлены скриншоты ключевых экранов, схемы архитектуры и потоков, листинги модулей для приложений ПЗ. Подписи рисунков приведены в соответствие с содержанием (в т.ч. OAuth, sync, 2FA). Иллюстрации привязаны к перекрёстным ссылкам в тексте глав 1, 3 и 5."
13,Руководство пользователя и демонстрация,"Составлено руководство пользователя (приложение Б): пошаговые сценарии локального и удалённого vault, шифрования, секретов, 2FA и фоновых задач. Подготовлена актуальная сборка APK и сценарий показа для защиты ВКР с акцентом на zero-knowledge и синхронизацию без утечки ключей."
14,Отчёт по практике и дневник,"Ведён дневник практики с привязкой записей к фактическим этапам разработки (1119.05) и оформлению отчётности (2024.05). Итоговый отчёт объединяет задание, дневник, анализ работ и ссылки на артефакты Wallenc; зафиксированы сроки и соответствие заданию на преддипломную практику."
15,Подготовка к защите практики,"Систематизированы результаты для представления руководителю от организации и университета: демонстрация приложения, ПЗ, отчёт. Выявленные на финальной проверке дефекты sync/Yandex устранены или отражены как направления развития. Практика завершена в установленный срок с полным комплектом отчётных материалов."
1 № п/п Выполненные мероприятия в соответствии с заданием на практику Анализ проведённой работы
2 1 Актуализация постановки задачи ВКР Уточнены цели преддипломной практики с опорой на уже реализованное ядро Wallenc: приоритет отдан доведению синхронизации storage, групп хранилищ, 2FA и текстовых секретов до демонстрационного уровня. Сформирован перечень доработок, согласованный с темой ВКР и результатами производственной практики, что исключило повторение начального аналитического цикла и сфокусировало сроки на инженерной и отчётной составляющих.
3 2 Реструктуризация модулей Gradle Выделение domain-storage и перенос логики localVault упростили границы ответственности между слоями: доменные контракты storage отделены от vault-навигации. Реструктуризация выполнена до внедрения крупных функций sync и секретов, поэтому последующие изменения локализовались в целевых модулях без каскадных правок по всему репозиторию.
4 3 Интеграция с Яндекс.Диск Развит REST-клиента, репозитория и сценариев удалённого vault довело OAuth-контур до устойчивой эксплуатации: учтены сетевые сбои на экране vault, сужена инвалидация кэша Disk API, добавлен учёт запросов. Пользователь получает рабочий поток подключения аккаунта и операций с удалённым storage без передачи ключей шифрования провайдеру, что соответствует исходной модели угроз Wallenc.
5 4 Синхронизация storage Реализован StorageSyncEngine с журналом изменений по путям, группами DbStorageSyncGroup в Room и фоновым исполнением через очередь задач. Отработаны блокировки sync, cooperative-отмена, foreground-задача и заголовки уведомлений; исправлены ошибки журнала при DELETE/TRASH. Синхронизация переведена из проектного статуса в проверяемый пользовательский сценарий с отображением прогресса на экране задач.
6 5 Секреты и TOTP 2FA Добавлены маршруты и экраны текстовых секретов и 2FA; генерация TOTP вынесена в фоновые задачи вместе с прочими длительными операциями. Замена зависимости mlkit на свободную библиотеку и доработка UI прогресса 2FA повысили воспроизводимость сценария на устройстве. Функции второго этапа практики интегрированы в общую модель «storage → содержимое» без отдельного backend.
7 6 Доработка UI/UX Унифицированы индикаторы занятости storage и подписи прогресса задач; улучшены карточки vault и обработка ошибок сети. Deep links и открытие экрана задач из уведомления сократили путь пользователя к статусу синхронизации. Иконка приложения и переключение языка завершили визуальную и локализационную подготовку к демонстрации на защите.
8 7 Шифрование и метаданные storage Исправлены сценарии шифрования и метаданных для Yandex vault и первого открытия storage; добавлена опция сохранения пароля при включении шифрования. Запрет включения зашифрованных storage в несовместимые группы sync снижает риск рассогласования состояния. Доменная логика Encryptor и ManageStoragesEncryptionUseCase согласована с фактическим поведением UI.
9 8 Тестирование Расширен набор unit-тестов (в том числе после рефакторинга sync и DI); выполнены ручные прогоны T-7…T-12 на эмуляторе. Подтверждены создание storage, шифрование, OAuth Яндекс, фоновые задачи и уведомления. Результаты зафиксированы для включения в раздел тестирования пояснительной записки и отчёта по практике.
10 9 Стабилизация сборки Устранена значительная часть предупреждений компилятора и deprecated API; упорядочены Hilt-модули. Оптимизированы debounce-sync, pipeline задач и обработка отсутствия journal/lock при синхронизации. Сборка assembleDebug и прогон тестов стали регулярной процедурой контроля качества перед оформлением отчётности.
11 10 Актуализация технического задания ТЗ приведено в соответствие с реализованным функционалом (sync, 2FA, секреты, очередь задач) и структурой по ГОСТ 7.32–2017. Зафиксированы критерии приёмки и границы доработок, что обеспечило согласованность между кодом, пояснительной запиской и заданием на практику.
12 11 Оформление пояснительной записки ВКР Подготовлены разделы обзора аналогов, архитектуры, проектирования UI, реализации и тестирования с опорой на фактическую кодовую базу. Текст ПЗ согласован с иерархией vault → storage → файлы и терминологией проекта. Объём работ отражает завершение цикла «разработка – проверка – описание», а не только документирование без изменений в приложении.
13 12 Иллюстрации и приложения ПЗ Подготовлены скриншоты ключевых экранов, схемы архитектуры и потоков, листинги модулей для приложений ПЗ. Подписи рисунков приведены в соответствие с содержанием (в т.ч. OAuth, sync, 2FA). Иллюстрации привязаны к перекрёстным ссылкам в тексте глав 1, 3 и 5.
14 13 Руководство пользователя и демонстрация Составлено руководство пользователя (приложение Б): пошаговые сценарии локального и удалённого vault, шифрования, секретов, 2FA и фоновых задач. Подготовлена актуальная сборка APK и сценарий показа для защиты ВКР с акцентом на zero-knowledge и синхронизацию без утечки ключей.
15 14 Отчёт по практике и дневник Ведён дневник практики с привязкой записей к фактическим этапам разработки (11–19.05) и оформлению отчётности (20–24.05). Итоговый отчёт объединяет задание, дневник, анализ работ и ссылки на артефакты Wallenc; зафиксированы сроки и соответствие заданию на преддипломную практику.
16 15 Подготовка к защите практики Систематизированы результаты для представления руководителю от организации и университета: демонстрация приложения, ПЗ, отчёт. Выявленные на финальной проверке дефекты sync/Yandex устранены или отражены как направления развития. Практика завершена в установленный срок с полным комплектом отчётных материалов.

View File

@@ -0,0 +1,58 @@
#import "preamble.typ": *
#practice_title[IV. Анализ проведённой работы в период прохождения практики обучающимся]
#table(
columns: (1cm, 3.6cm, 1fr),
align: (center, left, left),
table.header(
[ п/п],
[Выполненные мероприятия в соответствии с заданием на практику],
[Анализ проведённой работы],
),
[1],
[Актуализация постановки задачи ВКР],
[Уточнены цели преддипломной практики с опорой на уже реализованное ядро Wallenc: приоритет отдан доведению синхронизации storage, групп хранилищ, 2FA и текстовых секретов до демонстрационного уровня. Сформирован перечень доработок, согласованный с темой ВКР и результатами производственной практики, что исключило повторение начального аналитического цикла и сфокусировало сроки на инженерной и отчётной составляющих.],
[2],
[Реструктуризация модулей Gradle],
[Выделение `domain-storage` и перенос логики localVault упростили границы ответственности между слоями: доменные контракты storage отделены от vault-навигации. Реструктуризация выполнена до внедрения крупных функций sync и секретов, поэтому последующие изменения локализовались в целевых модулях без каскадных правок по всему репозиторию.],
[3],
[Интеграция с Яндекс.Диск],
[Развит REST-клиента, репозитория и сценариев удалённого vault довело OAuth-контур до устойчивой эксплуатации: учтены сетевые сбои на экране vault, сужена инвалидация кэша Disk API, добавлен учёт запросов. Пользователь получает рабочий поток подключения аккаунта и операций с удалённым storage без передачи ключей шифрования провайдеру, что соответствует исходной модели угроз Wallenc.],
[4],
[Синхронизация storage],
[Реализован `StorageSyncEngine` с журналом изменений по путям, группами `DbStorageSyncGroup` в Room и фоновым исполнением через очередь задач. Отработаны блокировки sync, cooperative-отмена, foreground-задача и заголовки уведомлений; исправлены ошибки журнала при DELETE/TRASH. Синхронизация переведена из проектного статуса в проверяемый пользовательский сценарий с отображением прогресса на экране задач.],
[5],
[Секреты и TOTP 2FA],
[Добавлены маршруты и экраны текстовых секретов и 2FA; генерация TOTP вынесена в фоновые задачи вместе с прочими длительными операциями. Замена зависимости mlkit на свободную библиотеку и доработка UI прогресса 2FA повысили воспроизводимость сценария на устройстве. Функции второго этапа практики интегрированы в общую модель «storage содержимое» без отдельного backend.],
[6],
[Доработка UI/UX],
[Унифицированы индикаторы занятости storage и подписи прогресса задач; улучшены карточки vault и обработка ошибок сети. Deep links и открытие экрана задач из уведомления сократили путь пользователя к статусу синхронизации. Иконка приложения и переключение языка завершили визуальную и локализационную подготовку к демонстрации на защите.],
[7],
[Шифрование и метаданные storage],
[Исправлены сценарии шифрования и метаданных для Yandex vault и первого открытия storage; добавлена опция сохранения пароля при включении шифрования. Запрет включения зашифрованных storage в несовместимые группы sync снижает риск рассогласования состояния. Доменная логика `Encryptor` и `ManageStoragesEncryptionUseCase` согласована с фактическим поведением UI.],
[8],
[Тестирование],
[Расширен набор unit-тестов том числе после рефакторинга sync и DI); выполнены ручные прогоны T-7…T-12 на эмуляторе. Подтверждены создание storage, шифрование, OAuth Яндекс, фоновые задачи и уведомления. Результаты зафиксированы для включения в раздел тестирования пояснительной записки и отчёта по практике.],
[9],
[Стабилизация сборки],
[Устранена значительная часть предупреждений компилятора и deprecated API; упорядочены Hilt-модули. Оптимизированы debounce-sync, pipeline задач и обработка отсутствия journal/lock при синхронизации. Сборка `assembleDebug` и прогон тестов стали регулярной процедурой контроля качества перед оформлением отчётности.],
[10],
[Актуализация технического задания],
[ТЗ приведено в соответствие с реализованным функционалом (sync, 2FA, секреты, очередь задач) и структурой по ГОСТ 7.322017. Зафиксированы критерии приёмки и границы доработок, что обеспечило согласованность между кодом, пояснительной запиской и заданием на практику.],
[11],
[Оформление пояснительной записки ВКР],
[Подготовлены разделы обзора аналогов, архитектуры, проектирования UI, реализации и тестирования с опорой на фактическую кодовую базу. Текст ПЗ согласован с иерархией vault storage файлы и терминологией проекта. Объём работ отражает завершение цикла «разработка проверка описание», а не только документирование без изменений в приложении.],
[12],
[Иллюстрации и приложения ПЗ],
[Подготовлены скриншоты ключевых экранов, схемы архитектуры и потоков, листинги модулей для приложений ПЗ. Подписи рисунков приведены в соответствие с содержанием т.ч. OAuth, sync, 2FA). Иллюстрации привязаны к перекрёстным ссылкам в тексте глав 1, 3 и 5.],
[13],
[Руководство пользователя и демонстрация],
[Составлено руководство пользователя (приложение Б): пошаговые сценарии локального и удалённого vault, шифрования, секретов, 2FA и фоновых задач. Подготовлена актуальная сборка APK и сценарий показа для защиты ВКР с акцентом на zero-knowledge и синхронизацию без утечки ключей.],
[14],
[Отчёт по практике и дневник],
[Ведён дневник практики с привязкой записей к фактическим этапам разработки (1119.05) и оформлению отчётности (2024.05). Итоговый отчёт объединяет задание, дневник, анализ работ и ссылки на артефакты Wallenc; зафиксированы сроки и соответствие заданию на преддипломную практику.],
[15],
[Подготовка к защите практики],
[Систематизированы результаты для представления руководителю от организации и университета: демонстрация приложения, ПЗ, отчёт. Выявленные на финальной проверке дефекты sync/Yandex устранены или отражены как направления развития. Практика завершена в установленный срок с полным комплектом отчётных материалов.],
)

View File

@@ -0,0 +1,10 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
OUT="$ROOT/prediploma-practice/out"
mkdir -p "$OUT"
cd "$ROOT/prediploma-practice"
typst compile zadanie.typ "$OUT/zadanie.pdf"
typst compile dnevnik.typ "$OUT/dnevnik.pdf"
typst compile analiz.typ "$OUT/analiz.pdf"
echo "OK: $OUT/zadanie.pdf, $OUT/dnevnik.pdf, $OUT/analiz.pdf"

View File

@@ -0,0 +1,12 @@
Дата,Выполненные мероприятия в соответствии с заданием на практику
07.05.2026,"Инструктаж по ознакомлению с требованиями охраны труда, техники безопасности, правилам внутреннего распорядка. Согласование плана доработок Wallenc к защите ВКР."
08.05.2026,"Актуализация технического задания и требований: синхронизация storage, группы хранилищ, сценарии 2FA и текстовых секретов."
09.05.2026 10.05.2026,"Проектирование механизма синхронизации (журнал изменений, группы DbStorageSyncGroup), схемы пользовательских потоков для экрана задач и уведомлений."
11.05.2026,"Реструктуризация Gradle-модулей (domain-storage, перенос localVault); deep links и запуск экрана задач из уведомления; оптимизация работы с API Яндекс.Диск."
12.05.2026 13.05.2026,"Реализация StorageSyncEngine: группы синхронизации, снятие блокировки sync; маршруты и экраны текстовых секретов и 2FA; компоненты отображения статуса операций."
14.05.2026 16.05.2026,"Доработка UI/UX списков storage и vault; уточнение логики фоновых операций и индикаторов загрузки Яндекс.Диск."
17.05.2026,"Рабочий TOTP 2FA; группы синхронизации в Room и контроль совместимости storage; переподключение remote vault; перенос операций с секретами в очередь задач."
18.05.2026,"Унификация доменных ошибок и статуса failed у задач; подписи прогресса; улучшение фоновой синхронизации; переключение языка; наведение порядка в DI (Hilt)."
19.05.2026,"Добавление и прогон unit-тестов; устранение предупреждений компилятора; сборка и проверка сценариев шифрования, OAuth и sync на эмуляторе."
20.05.2026 24.05.2026,"Составление отчёта по преддипломной практике; оформление пояснительной записки ВКР и иллюстраций; при необходимости финальные правки sync и Yandex vault (стабилизация журнала, UX, foreground-задачи)."
04.06.2026,Защита практики.
1 Дата Выполненные мероприятия в соответствии с заданием на практику
2 07.05.2026 Инструктаж по ознакомлению с требованиями охраны труда, техники безопасности, правилам внутреннего распорядка. Согласование плана доработок Wallenc к защите ВКР.
3 08.05.2026 Актуализация технического задания и требований: синхронизация storage, группы хранилищ, сценарии 2FA и текстовых секретов.
4 09.05.2026 – 10.05.2026 Проектирование механизма синхронизации (журнал изменений, группы DbStorageSyncGroup), схемы пользовательских потоков для экрана задач и уведомлений.
5 11.05.2026 Реструктуризация Gradle-модулей (domain-storage, перенос localVault); deep links и запуск экрана задач из уведомления; оптимизация работы с API Яндекс.Диск.
6 12.05.2026 – 13.05.2026 Реализация StorageSyncEngine: группы синхронизации, снятие блокировки sync; маршруты и экраны текстовых секретов и 2FA; компоненты отображения статуса операций.
7 14.05.2026 – 16.05.2026 Доработка UI/UX списков storage и vault; уточнение логики фоновых операций и индикаторов загрузки Яндекс.Диск.
8 17.05.2026 Рабочий TOTP 2FA; группы синхронизации в Room и контроль совместимости storage; переподключение remote vault; перенос операций с секретами в очередь задач.
9 18.05.2026 Унификация доменных ошибок и статуса failed у задач; подписи прогресса; улучшение фоновой синхронизации; переключение языка; наведение порядка в DI (Hilt).
10 19.05.2026 Добавление и прогон unit-тестов; устранение предупреждений компилятора; сборка и проверка сценариев шифрования, OAuth и sync на эмуляторе.
11 20.05.2026 – 24.05.2026 Составление отчёта по преддипломной практике; оформление пояснительной записки ВКР и иллюстраций; при необходимости – финальные правки sync и Yandex vault (стабилизация журнала, UX, foreground-задачи).
12 04.06.2026 Защита практики.

View File

@@ -0,0 +1,34 @@
#import "preamble.typ": *
#practice_title[Дневник преддипломной практики]
#table(
columns: (3.2cm, 1fr),
align: (left, left),
table.header(
[Дата],
[Выполненные мероприятия в соответствии с заданием на практику],
),
[07.05.2026],
[Инструктаж по ознакомлению с требованиями охраны труда, техники безопасности, правилам внутреннего распорядка. Согласование плана доработок Wallenc к защите ВКР.],
[08.05.2026],
[Актуализация технического задания и требований: синхронизация storage, группы хранилищ, сценарии 2FA и текстовых секретов.],
[09.05.2026 10.05.2026],
[Проектирование механизма синхронизации (журнал изменений, группы `DbStorageSyncGroup`), схемы пользовательских потоков для экрана задач и уведомлений.],
[11.05.2026],
[Реструктуризация Gradle-модулей (`domain-storage`, перенос localVault); deep links и запуск экрана задач из уведомления; оптимизация работы с API Яндекс.Диск.],
[12.05.2026 13.05.2026],
[Реализация `StorageSyncEngine`: группы синхронизации, снятие блокировки sync; маршруты и экраны текстовых секретов и 2FA; компоненты отображения статуса операций.],
[14.05.2026 16.05.2026],
[Доработка UI/UX списков storage и vault; уточнение логики фоновых операций и индикаторов загрузки Яндекс.Диск.],
[17.05.2026],
[Рабочий TOTP 2FA; группы синхронизации в Room и контроль совместимости storage; переподключение remote vault; перенос операций с секретами в очередь задач.],
[18.05.2026],
[Унификация доменных ошибок и статуса failed у задач; подписи прогресса; улучшение фоновой синхронизации; переключение языка; наведение порядка в DI (Hilt).],
[19.05.2026],
[Добавление и прогон unit-тестов; устранение предупреждений компилятора; сборка и проверка сценариев шифрования, OAuth и sync на эмуляторе.],
[20.05.2026 24.05.2026],
[Составление отчёта по преддипломной практике; оформление пояснительной записки ВКР и иллюстраций; при необходимости финальные правки sync и Yandex vault (стабилизация журнала, UX, foreground-задачи).],
[04.06.2026],
[Защита практики.],
)

View File

@@ -0,0 +1 @@
См. [`otzyv-organizaciya.txt`](otzyv-organizaciya.txt) — связный отзыв без нумерованного перечня (акцент на работе в организации).

View File

@@ -0,0 +1,20 @@
ОТЗЫВ РУКОВОДИТЕЛЯ ПРАКТИКИ ОТ ПРОФИЛЬНОЙ ОРГАНИЗАЦИИ
Студент 4 курса Пытков Роман Евгеньевич, направления 09.03.04 «Программная инженерия», прошёл в 8 семестре преддипломную практику в компании ООО НМФ «Нейротех».
В период практики Пытков Р. Е. работал разработчиком мобильного приложения Wallenc (Android, Kotlin). Основной вклад — доведение продукта до состояния, пригодного для демонстрации заказчику и защиты ВКР: реструктуризация модулей проекта, развитие интеграции с Яндекс.Диск, внедрение синхронизации storage с журналом изменений и фоновыми задачами, экраны секретов и 2FA, улучшение UX и стабилизация сборки. Отдельно отмечу аккуратную работу с требованиями безопасности (ключи не передаются провайдеру, служебные данные в Room отделены от пользовательского контента) и самостоятельное устранение дефектов по результатам тестов.
По итогам практики подготовлены рабочая сборка APK, руководство пользователя, материалы для пояснительной записки; отчёт и дневник ведены в срок. Замечаний по дисциплине и взаимодействию с руководителем не имею.
За время прохождения практики Пытков Р. Е. показал высокий уровень теоретической подготовки, высокую степень умения и навыков применять знания, полученные в университете, для решения поставленных перед ним практических задач.
Пытковым Р. Е. проявлены следующие личностные и профессиональные качества: инициативность, ответственность, исполнительность, ориентация на результат, внимание к качеству кода и защите данных.
Считаю, что проявленные профессиональные качества полностью удовлетворяют потребностям предприятия, программа практики выполнена в полном объёме, сроки выполнения заданий соблюдались полностью.
Руководитель практики
от профильной организации
______________________ / Алексеев Д. М. /
подпись расшифровка подписи

View File

@@ -0,0 +1 @@
См. [`otzyv-universitet.txt`](otzyv-universitet.txt) — отзыв по шаблону с перечнем из 15 пунктов задания и оценкой.

View File

@@ -0,0 +1,34 @@
ОТЗЫВ РУКОВОДИТЕЛЯ ПРАКТИКИ ОТ УНИВЕРСИТЕТА
Студент 4 курса группы КТбо4-9 Пытков Роман Евгеньевич, направления подготовки 09.03.04 «Программная инженерия» (профиль «Методы и средства разработки программного обеспечения»), в 8 семестре прошёл преддипломную практику в компании ООО НМФ «Нейротех».
В период преддипломной практики Пытков Р. Е. работал в качестве разработчика программного обеспечения. Им были решены следующие задачи:
1. Актуализирована постановка задачи ВКР и требования к мобильному приложению Wallenc.
2. Выполнена реструктуризация модулей Gradle; унифицированы интерфейсы storage и навигация.
3. Развита интеграция с Яндекс.Диск: REST-клиент, OAuth, удалённые vault.
4. Реализована синхронизация storage: журнал изменений, группы в Room, фоновые задачи.
5. Реализованы экраны текстовых секретов и генерация TOTP 2FA.
6. Доработан пользовательский интерфейс на Jetpack Compose.
7. Доработаны сценарии шифрования и метаданных storage (в т.ч. Yandex vault).
8. Расширено автоматизированное и ручное тестирование пользовательских сценариев.
9. Стабилизирована сборка приложения; упорядочена конфигурация DI (Hilt).
10. Приведено техническое задание в соответствие с реализацией (ГОСТ 7.322017).
11. Оформлены разделы пояснительной записки ВКР (анализ, архитектура, реализация, тесты).
12. Подготовлены иллюстрации и приложения к пояснительной записке.
13. Составлено руководство пользователя и подготовлена демонстрация APK.
14. Составлен отчёт по преддипломной практике; ведён дневник.
15. Выполнена подготовка к защите преддипломной практики.
За время прохождения практики Пытков Р. Е. показал высокий уровень теоретической подготовки, высокую степень умения и навыков применять знания, полученные в университете, для решения поставленных перед ним практических задач.
Пытковым Р. Е. проявлены следующие личностные и профессиональные качества: самостоятельность, системность в проектировании, внимание к информационной безопасности, умение работать с технической документацией, аккуратность при оформлении отчётных материалов.
Считаю, что проявленные профессиональные качества полностью удовлетворяют потребностям профильной организации, программа практики выполнена в полном объёме, сроки выполнения заданий соблюдались полностью.
Оценка отлично
Руководитель практики
от Университета
______________________ / Беликов А. Н. /
подпись расшифровка подписи

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,33 @@
#set page(
paper: "a4",
margin: (left: 2.5cm, right: 1.5cm, top: 2cm, bottom: 2cm),
numbering: none,
)
#set text(font: "Times New Roman", size: 14pt, lang: "ru")
#set par(justify: true, first-line-indent: 0pt)
#set table(stroke: 0.5pt + black, inset: (x: 6pt, y: 5pt))
#let practice_meta = (
kind: "преддипломную",
period: "07.05.2026 04.06.2026",
org: "ООО НМФ «Нейротех»",
student: "Пытков Роман Евгеньевич",
group: "КТбо4-9",
direction: "09.03.04 «Программная инженерия»",
project: "мобильное приложение Wallenc (Android)",
)
#let practice_title(body) = {
align(center)[
#text(weight: "bold", size: 14pt)[#body]
]
v(0.5em)
align(center)[
#practice_meta.student, группа #practice_meta.group \
Направление #practice_meta.direction \
#practice_meta.org \
Сроки: #practice_meta.period \
Проект: #practice_meta.project
]
v(1em)
}

View File

@@ -0,0 +1,16 @@
,Содержание задания (выполнялось в рамках темы ВКР и проекта Wallenc)
1,"Актуализация постановки задачи ВКР и уточнение требований к мобильному приложению Wallenc с учётом результатов производственной практики."
2,"Реструктуризация модулей проекта (Gradle): выделение domain-storage, унификация интерфейсов storage и навигации."
3,"Развитие интеграции с Яндекс.Диск: REST-клиент, OAuth, репозиторий, удалённые vault; обработка сетевых ошибок и кэширование API."
4,"Реализация синхронизации storage: журнал изменений, группы синхронизации в Room, фоновые задачи, уведомления и экран очереди задач."
5,"Реализация экранов текстовых секретов и TOTP 2FA; выполнение операций через очередь фоновых задач."
6,"Доработка UI/UX на Jetpack Compose: индикация статусов storage и задач, deep links, иконка приложения, локализация."
7,"Доработка шифрования и метаданных storage (включая сценарии Yandex vault и первое открытие зашифрованного storage)."
8,"Расширение автоматизированного тестирования (unit-тесты, инструментальные прогоны UI) и ручная проверка сценариев T-7…T-12."
9,"Стабилизация сборки приложения: устранение предупреждений компилятора, наведение порядка в DI, оптимизация фоновой синхронизации."
10,"Приведение технического задания в соответствие с реализованным функционалом и ГОСТ 7.322017."
11,"Оформление пояснительной записки ВКР: обзор аналогов, архитектура, проектирование UI, описание реализации и тестирования."
12,"Подготовка иллюстраций и приложений ПЗ: скриншоты интерфейса, схемы, листинги исходного кода."
13,"Подготовка руководства пользователя и материалов для демонстрации APK на защите ВКР."
14,"Составление отчёта по преддипломной практике и ведение дневника практики."
15,"Подготовка к защите преддипломной практики; представление результатов руководителю от организации и университета."
1 Содержание задания (выполнялось в рамках темы ВКР и проекта Wallenc)
2 1 Актуализация постановки задачи ВКР и уточнение требований к мобильному приложению Wallenc с учётом результатов производственной практики.
3 2 Реструктуризация модулей проекта (Gradle): выделение domain-storage, унификация интерфейсов storage и навигации.
4 3 Развитие интеграции с Яндекс.Диск: REST-клиент, OAuth, репозиторий, удалённые vault; обработка сетевых ошибок и кэширование API.
5 4 Реализация синхронизации storage: журнал изменений, группы синхронизации в Room, фоновые задачи, уведомления и экран очереди задач.
6 5 Реализация экранов текстовых секретов и TOTP 2FA; выполнение операций через очередь фоновых задач.
7 6 Доработка UI/UX на Jetpack Compose: индикация статусов storage и задач, deep links, иконка приложения, локализация.
8 7 Доработка шифрования и метаданных storage (включая сценарии Yandex vault и первое открытие зашифрованного storage).
9 8 Расширение автоматизированного тестирования (unit-тесты, инструментальные прогоны UI) и ручная проверка сценариев T-7…T-12.
10 9 Стабилизация сборки приложения: устранение предупреждений компилятора, наведение порядка в DI, оптимизация фоновой синхронизации.
11 10 Приведение технического задания в соответствие с реализованным функционалом и ГОСТ 7.32–2017.
12 11 Оформление пояснительной записки ВКР: обзор аналогов, архитектура, проектирование UI, описание реализации и тестирования.
13 12 Подготовка иллюстраций и приложений ПЗ: скриншоты интерфейса, схемы, листинги исходного кода.
14 13 Подготовка руководства пользователя и материалов для демонстрации APK на защите ВКР.
15 14 Составление отчёта по преддипломной практике и ведение дневника практики.
16 15 Подготовка к защите преддипломной практики; представление результатов руководителю от организации и университета.

View File

@@ -0,0 +1,26 @@
#import "preamble.typ": *
#practice_title[Задание обучающегося на преддипломную практику]
Содержание задания (выполняется в рамках темы ВКР и проекта Wallenc):
#table(
columns: (1.2cm, 1fr),
align: (center, left),
table.header([], [Содержание задания]),
[1], [Актуализация постановки задачи ВКР и уточнение требований к мобильному приложению Wallenc с учётом результатов производственной практики.],
[2], [Реструктуризация модулей проекта (Gradle): выделение `domain-storage`, унификация интерфейсов storage и навигации.],
[3], [Развитие интеграции с Яндекс.Диск: REST-клиент, OAuth, репозиторий, удалённые vault; обработка сетевых ошибок и кэширование API.],
[4], [Реализация синхронизации storage: журнал изменений, группы синхронизации в Room, фоновые задачи, уведомления и экран очереди задач.],
[5], [Реализация экранов текстовых секретов и TOTP 2FA; выполнение операций через очередь фоновых задач.],
[6], [Доработка UI/UX на Jetpack Compose: индикация статусов storage и задач, deep links, иконка приложения, локализация.],
[7], [Доработка шифрования и метаданных storage (включая сценарии Yandex vault и первое открытие зашифрованного storage).],
[8], [Расширение автоматизированного тестирования (unit-тесты, инструментальные прогоны UI) и ручная проверка сценариев T-7…T-12.],
[9], [Стабилизация сборки приложения: устранение предупреждений компилятора, наведение порядка в DI, оптимизация фоновой синхронизации.],
[10], [Приведение технического задания в соответствие с реализованным функционалом и ГОСТ 7.322017.],
[11], [Оформление пояснительной записки ВКР: обзор аналогов, архитектура, проектирование UI, описание реализации и тестирования.],
[12], [Подготовка иллюстраций и приложений ПЗ: скриншоты интерфейса, схемы, листинги исходного кода.],
[13], [Подготовка руководства пользователя и материалов для демонстрации APK на защите ВКР.],
[14], [Составление отчёта по преддипломной практике и ведение дневника практики.],
[15], [Подготовка к защите преддипломной практики; представление результатов руководителю от организации и университета.],
)

2
Report/puml/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
# Растр только в Report/images/ (render_puml.sh)
*.png

34
Report/puml/README.md Normal file
View File

@@ -0,0 +1,34 @@
# Диаграммы PlantUML для ПЗ
## Что требует ГОСТ 7.32 (и чек-лист)
| Пункт | Требование |
|-------|------------|
| 6.5.2 | Чертежи, схемы, диаграммы — по **ЕСКД**; для блок-схем ПО — **ГОСТ 19.701-90** (аналог ISO 5807) |
| 6.5.3 | Без «объёмных» рисунков, только по делу |
| 6.5.7 | Подпись **в Typst**: «Рисунок N Наименование» (среднее тире), не внутри PNG |
| 6.5.8 | Наименование с прописной буквы, без точки в конце |
**ГОСТ 19.701-90 (кратко):** процесс — прямоугольник; решение — ромб; начало/конец — овал; линии — сплошные со стрелками; ч/б контуры; без декоративной заливки.
## Оформление в репозитории
- `plantuml-gost.cfg` — ч/б, прямоугольные блоки activity, шрифт с засечками, без тени (подключается в `render_puml.sh`).
- `_gost-theme.inc.puml` — напоминание не дублировать `title` в `.puml`.
- `scripts/normalize_puml_gost.py` — перед рендером убирает локальные `skinparam`/`title`, добавляет `!include _gost-theme.inc.puml`.
Подписи к рисункам задаются только в `includes/*.typ` через `pz-fig(...)`.
## Сборка
```bash
cd Report/scripts
./render_puml.sh
```
PNG: `Report/images/fig_*.png`.
## Ограничения PlantUML
- **BPMN**, **CJM**, **DFD** в `.puml` — упрощённые схемы; строгий ГОСТ 19.702 (схемы данных) или ЕСКД 2.x вручную в Visio/draw.io при необходимости нормоконтроля.
- Ввод/вывод (параллелограмм по ГОСТ) в activity-диаграмме PlantUML не выводится автоматически — для I/O используются обычные блоки процесса.

View File

@@ -0,0 +1,3 @@
' Подключать после @startuml: !include _gost-theme.inc.puml
' Базовые параметры задаёт plantuml-gost.cfg (-config в render_puml.sh).
' Не используйте title в .puml — подпись «Рисунок N …» только в Typst (pz-fig).

View File

@@ -0,0 +1,54 @@
@startuml fig_01_start_sync
!include _gost-theme.inc.puml
' Увеличенный растр для вставки в отчёт (Word / печать)
scale 3
start
:Старт приложения (Android);
:Инициализация Room,
загрузка метаданных vault;
if (Есть LocalVault / storage?) then (нет)
:Первый запуск;
else (да)
endif
if (Нужен удалённый провайдер
и нет учётной записи?) then (да)
:Экран удалённых vault /
OAuth Яндекс;
else (нет)
endif
partition "**Основной поток (UI)**" {
:(A) Main: LocalVault — список storage,
вкладка удалённых — список IVault;
:Действия с **storage**
(шифрование, открытие, файлы…);
}
fork
partition "**Фон: синхронизация (по таймеру)**" #E8F5E9 {
note right
**Реализовано:** StorageSyncEngine,
журнал изменений по путям, merge ревизий
• Room: DbStorageSyncGroup (UUID группы)
• WorkManager / debounce — запуск sync
• Ключи шифрования провайдеру не передаются
Подробный алгоритм — гл. 4, рис. 35
end note
:Запуск sync (Worker / debounce);
:StorageSyncEngine.syncAllGroups;
while (Есть группа синхронизации?) is (да)
:Слияние журналов → победитель по пути;
:findSourceStorage → applyEntry
на отстающие Storage;
endwhile (нет)
}
end fork
stop
@enduml

View File

@@ -0,0 +1,42 @@
@startuml fig_02_vault_lifecycle
!include _gost-theme.inc.puml
scale 3
state "(Б) Список storage (LocalVault / VaultBrowser)" as List
List --> Create : Создать storage (FAB)
Create --> List : Storage создан
List --> EncryptDlg : Включить шифрование storage
EncryptDlg --> Encrypting : Подтверждение, ключ
state Encrypting {
state "Шифрование + метаданные Room" as EncWork
}
Encrypting --> List : Готово
note right of Encrypting
Запись в журнал sync (проект):
UUID **storage** → очередь
группы DbStorageSyncGroup
end note
List --> OpenDlg : Открыть зашифрованный storage
OpenDlg --> Opened : Ключ верный
OpenDlg --> List : Отмена / неверный ключ
state Opened {
state "Файлы, секреты, 2FA (StorageHome)" as Browse
}
Opened --> List : Закрыть / заблокировать storage
List --> RenameDel : Переименовать / удалить storage
RenameDel --> List : Подтверждение
note bottom of List
**Синхронизация:** изменения в **storage**
пишутся в журнал; StorageSyncEngine
согласует группы storage по ревизиям
(зашифрованное содержимое, без ключей).
end note
@enduml

View File

@@ -0,0 +1,48 @@
@startuml fig_03_navigation_hub
!include _gost-theme.inc.puml
scale 2
start
partition "**Main: нижняя навигация**" {
:(A) Вкладка LocalVault
(один LocalVault на устройстве);
note right
На экране — список **IStorage**,
не список vault
end note
repeat
:Ожидание действия;
backward:Назад с вложенного экрана;
switch (Действие?)
case (FAB)
:createStorage()
новый **storage** в LocalVault;
case (Выбор storage)
:StorageHome:
файлы, 2FA, текстовые секреты;
case (Вкладка «Удалённые vault»)
:Список **IVault** (Yandex по OAuth);
if (Нужен OAuth?) then (да)
:Авторизация Яндекс;
endif
:Выбор vault → VaultBrowser;
:Список **storage** в выбранном vault;
:StorageHome;
endswitch
repeat while (Пользователь в Main?) is (да)
-> нет;
}
stop
floating note right
**Фон: StorageSyncEngine**
• DbStorageSyncGroup: UUID **storage** в группе
• Журнал изменений по путям, merge ревизий
• Независимо от текущего экрана UI
• Ключи шифрования провайдеру не передаются
end note
@enduml

View File

@@ -0,0 +1,351 @@
@startuml fig_04_domain_class
!include _gost-theme.inc.puml
scale 1.15
' render_puml.sh: PLANTUML_LIMIT_SIZE=16384
package usecases {
class ManageStoragesEncryptionUseCase {
+ enableEncryption(IStorageInfo, EncryptKey, boolean): Unit
+ openStorage(IStorageInfo, EncryptKey, boolean): Unit
+ clearAndDisableEncryption(IStorageInfo): Unit
+ closeStorage(IStorageInfo): Unit
+ canEncrypt(IStorageInfo): Unit
+ changePassword(IStorageInfo, EncryptKey, boolean): Unit
}
class RemoveStorageUseCase {
+ remove(IStorageInfo): Unit
}
class GetOpenedStoragesUseCase {
+ getOpenedStorages(): StateFlow<Map<UUID, IStorageInfo>>
}
class ManageVaultUseCase {
+ find(UUID): IVault?
+ storagesOf(UUID): Flow<List<IStorage>>
+ createStorage(UUID): IStorage
}
class FindStorageUseCase {
+ find(UUID): IStorage?
}
class StorageFileManagementUseCase {
+ getAllDirs(): Unit
+ setStorage(IStorageInfo): void
+ getAllFiles(): Unit
}
class RenameStorageUseCase {
+ rename(IStorageInfo, String): Unit
}
}
package tasks {
class TaskLogLine {
+ getTimestampMs(): long
+ getLevel(): TaskLogLevel
+ getMessage(): String
}
class PipelineWork {
+ run(TaskContext): Unit
}
class TaskContext {
+ reportProgress(Float, String): Unit
+ reportProgress(TaskProgress): Unit
+ log(TaskLogLevel, String): void
+ getTaskId(): TaskId
}
class PipelineState {
+ getTasks(): List
+ getRunningTaskIds(): Set
}
class ITaskOrchestrator {
+ getLogLines(): StateFlow<List<TaskLogLine>>
+ enqueue(String, PipelineWork): TaskId
+ getPipelineState(): StateFlow<PipelineState>
+ getForegroundUi(): StateFlow<TaskForegroundUiState>
+ cancel(TaskId): boolean
+ cancelAll(): void
}
class TaskProgress {
+ getLabel(): String
+ getFraction(): Float
}
class TaskForegroundUiState {
}
class TaskForegroundItem {
+ getProgress(): TaskProgress
+ getTitle(): String
+ getTaskId(): TaskId
}
class TaskLogLevel {
+ Debug
+ Warn
+ Error
+ Info
+ values(): TaskLogLevel[]
+ valueOf(String): TaskLogLevel
+ getEntries(): EnumEntries<TaskLogLevel>
}
class TaskRunState {
}
class TaskId {
+ getUuid(): UUID
}
class PipelineTask {
+ getId(): TaskId
+ getDispatcher(): CoroutineDispatcher
+ getState(): TaskRunState
+ getTitle(): String
}
}
package interfaces {
class ILogger {
+ debug(String, String): void
}
class IMetaInfo {
+ getSize(): long
+ isDeleted(): boolean
+ isHidden(): boolean
+ getLastModified(): Instant
+ getPath(): String
}
class IStorage {
+ rename(String): Unit
+ setEncInfo(StorageEncryptionInfo): Unit
+ isEmpty(): Flow<Boolean>
+ getUuid(): UUID
+ getAccessor(): IStorageAccessor
+ isAvailable(): StateFlow<Boolean>
+ getSize(): StateFlow<Long>
+ getNumberOfFiles(): StateFlow<Integer>
+ clearAllContent(): Unit
+ getMetaInfo(): StateFlow<IStorageMetaInfo>
+ isVirtualStorage(): boolean
}
class IVaultsManager {
+ vaults: StateFlow<List<IVault>>
+ allStorages: StateFlow<List<IStorage>>
+ unlockManager: IUnlockManager
}
class IStorageMetaInfo {
+ getEncInfo(): StorageEncryptionInfo
+ getName(): String
+ getLastModified(): Instant
}
class IFile {
+ getMetaInfo(): IMetaInfo
}
class IStorageInfo {
+ isEmpty(): Flow<Boolean>
+ getUuid(): UUID
+ isAvailable(): StateFlow<Boolean>
+ getSize(): StateFlow<Long>
+ getNumberOfFiles(): StateFlow<Integer>
+ getMetaInfo(): StateFlow<IStorageMetaInfo>
+ isVirtualStorage(): boolean
}
class IVaultInfo {
+ uuid: UUID
}
class IUnlockManager {
+ openedStorages: StateFlow<Map<UUID, IStorage>>
+ getOpenedStorageKey(UUID): EncryptKey
+ open(IStorage, EncryptKey, boolean): IStorage
+ rememberKey(IStorage, EncryptKey): Unit
+ close(IStorage): Unit
+ close(UUID): Unit
}
class IDirectory {
+ getMetaInfo(): IMetaInfo
+ getElementsCount(): Integer
}
class IStorageAccessor {
+ getFilesFlow(String): Flow<DataPackage<List<IFile>>>
+ getDirs(String): Unit
+ getDirsUpdates(): SharedFlow<DataPackage<List<IDirectory>>>
+ touchDir(String): Unit
+ getFiles(String): Unit
+ getSize(): StateFlow<Long>
+ isAvailable(): StateFlow<Boolean>
+ getDirInfo(String): Unit
+ openRead(String): Unit
+ moveToTrash(String): Unit
+ delete(String): Unit
+ touchFile(String): Unit
+ getAllDirs(): Unit
+ openWrite(String): Unit
+ getAllFiles(): Unit
+ getDirsFlow(String): Flow<DataPackage<List<IDirectory>>>
+ getNumberOfFiles(): StateFlow<Integer>
+ getFilesUpdates(): SharedFlow<DataPackage<List<IFile>>>
+ setHidden(String, boolean): Unit
+ getFileInfo(String): Unit
}
class IVault {
+ uuid: UUID
+ storages: StateFlow<List<IStorage>>
+ storagesScanInProgress: StateFlow<Boolean>
+ isAvailable: StateFlow<Boolean>
+ totalSpace: StateFlow<Long>
+ availableSpace: StateFlow<Long>
+ createStorage(): IStorage
+ createStorage(StorageEncryptionInfo): IStorage
+ remove(IStorage): Unit
+ rescanStorages(): Unit
}
}
package encrypt {
class EncryptorWithStaticIv {
+ decryptBytesbyte[](byte[])
+ encryptStream(java.io.OutputStream): java.io.OutputStream
+ decryptStream(java.io.InputStream): java.io.InputStream
+ encryptBytesbyte[](byte[])
+ dispose(): void
+ encryptString(String): String
+ decryptString(String): String
}
class Encryptor {
+ AES_SETTINGS: String
+ IV_LEN: int
+ decryptBytesbyte[](byte[])
+ encryptStream(java.io.OutputStream): java.io.OutputStream
+ decryptStream(java.io.InputStream): java.io.InputStream
+ encryptBytesbyte[](byte[])
+ dispose(): void
+ encryptString(String): String
+ decryptString(String): String
}
}
package datatypes {
class Tree {
+ getValue(): Unit
+ getChildren(): List
+ setChildren(List): void
}
class EncryptKey {
+ toAesKey(): SecretKeySpec
+ getBytes(): byte[]
}
class DataPackage {
+ getData(): Unit
+ isError(): Boolean
+ isLoading(): Boolean
}
class DataPage {
+ getPageLength(): int
+ getPageIndex(): int
+ getHasNext(): Boolean
}
class StorageEncryptionInfo {
+ getEncryptedTestData(): String
+ getPathIv(): byte[]
}
}
package common.impl {
class CommonDirectory {
+ getElementsCount(): Integer
+ getMetaInfo(): CommonMetaInfo
}
class CommonStorageMetaInfo {
+ getEncInfo(): StorageEncryptionInfo
+ getName(): String
+ getLastModified(): Instant
}
class CommonMetaInfo {
+ getSize(): long
+ getPath(): String
+ isDeleted(): boolean
+ isHidden(): boolean
+ getLastModified(): Instant
}
class CommonFile {
+ getMetaInfo(): IMetaInfo
}
}
usecases.ManageStoragesEncryptionUseCase ..> interfaces.IStorageMetaInfo
usecases.ManageStoragesEncryptionUseCase ..> interfaces.IStorageInfo
usecases.ManageStoragesEncryptionUseCase ..> tasks.TaskProgress
usecases.ManageStoragesEncryptionUseCase ..> datatypes.EncryptKey
usecases.ManageStoragesEncryptionUseCase ..> encrypt.Encryptor
usecases.ManageStoragesEncryptionUseCase ..> interfaces.IStorage
usecases.ManageStoragesEncryptionUseCase ..> interfaces.IUnlockManager
usecases.ManageStoragesEncryptionUseCase ..> datatypes.StorageEncryptionInfo
usecases.RemoveStorageUseCase ..> usecases.ManageStoragesEncryptionUseCase
usecases.RemoveStorageUseCase ..> interfaces.IStorage
usecases.RemoveStorageUseCase ..> interfaces.IStorageInfo
usecases.RemoveStorageUseCase ..> interfaces.IVaultsManager
usecases.RemoveStorageUseCase ..> interfaces.IUnlockManager
usecases.RemoveStorageUseCase ..> interfaces.IVault
tasks.TaskLogLine ..> tasks.TaskLogLevel
tasks.PipelineWork ..> tasks.TaskContext
usecases.GetOpenedStoragesUseCase ..> interfaces.IStorageInfo
usecases.GetOpenedStoragesUseCase ..> interfaces.IUnlockManager
tasks.TaskContext ..> tasks.TaskLogLevel
tasks.TaskContext ..> tasks.TaskProgress
tasks.TaskContext ..> tasks.TaskId
usecases.ManageVaultUseCase ..> interfaces.IStorage
usecases.ManageVaultUseCase ..> interfaces.IVaultsManager
usecases.ManageVaultUseCase ..> interfaces.IVault
usecases.FindStorageUseCase ..> interfaces.IStorage
usecases.FindStorageUseCase ..> interfaces.IVaultsManager
usecases.StorageFileManagementUseCase ..> interfaces.IFile
usecases.StorageFileManagementUseCase ..> interfaces.IStorage
usecases.StorageFileManagementUseCase ..> interfaces.IStorageInfo
usecases.StorageFileManagementUseCase ..> interfaces.IDirectory
usecases.StorageFileManagementUseCase ..> interfaces.IStorageAccessor
interfaces.IStorageInfo <|.. interfaces.IStorage
interfaces.IStorage ..> interfaces.IStorageMetaInfo
interfaces.IStorage ..> interfaces.IStorageInfo
interfaces.IStorage ..> tasks.TaskProgress
interfaces.IStorage ..> interfaces.IStorageAccessor
interfaces.IStorage ..> datatypes.StorageEncryptionInfo
interfaces.IVaultsManager ..> interfaces.IStorage
interfaces.IVaultsManager ..> interfaces.IUnlockManager
interfaces.IVaultsManager ..> interfaces.IVault
interfaces.IDirectory <|.. common.impl.CommonDirectory
common.impl.CommonDirectory ..> common.impl.CommonMetaInfo
common.impl.CommonDirectory ..> interfaces.IMetaInfo
common.impl.CommonDirectory ..> interfaces.IDirectory
tasks.PipelineState ..> tasks.TaskId
tasks.PipelineState ..> tasks.PipelineTask
interfaces.IStorageMetaInfo <|.. common.impl.CommonStorageMetaInfo
common.impl.CommonStorageMetaInfo ..> interfaces.IStorageMetaInfo
common.impl.CommonStorageMetaInfo ..> datatypes.StorageEncryptionInfo
interfaces.IMetaInfo <|.. common.impl.CommonMetaInfo
common.impl.CommonMetaInfo ..> interfaces.IMetaInfo
interfaces.IStorageMetaInfo ..> datatypes.StorageEncryptionInfo
interfaces.IFile ..> interfaces.IMetaInfo
tasks.ITaskOrchestrator ..> tasks.TaskLogLine
tasks.ITaskOrchestrator ..> tasks.PipelineState
tasks.ITaskOrchestrator ..> tasks.TaskForegroundUiState
tasks.ITaskOrchestrator ..> tasks.PipelineWork
tasks.ITaskOrchestrator ..> tasks.TaskId
interfaces.IStorageInfo ..> interfaces.IStorageMetaInfo
interfaces.IStorageInfo ..> interfaces.IStorage
usecases.RenameStorageUseCase ..> interfaces.IStorage
usecases.RenameStorageUseCase ..> interfaces.IStorageInfo
interfaces.IVaultInfo ..> interfaces.IVault
tasks.TaskForegroundItem ..> tasks.TaskProgress
tasks.TaskForegroundItem ..> tasks.TaskId
interfaces.IUnlockManager ..> interfaces.IStorage
interfaces.IUnlockManager ..> datatypes.EncryptKey
interfaces.IDirectory ..> interfaces.IMetaInfo
interfaces.IStorageAccessor ..> interfaces.IFile
interfaces.IStorageAccessor ..> interfaces.IDirectory
interfaces.IStorageAccessor ..> datatypes.DataPackage
interfaces.IVaultInfo <|.. interfaces.IVault
interfaces.IVault ..> interfaces.IStorage
interfaces.IVault ..> interfaces.IVaultInfo
interfaces.IVault ..> datatypes.StorageEncryptionInfo
datatypes.DataPackage <|-- datatypes.DataPage
datatypes.DataPage ..> datatypes.DataPackage
interfaces.IFile <|.. common.impl.CommonFile
common.impl.CommonFile ..> interfaces.IMetaInfo
common.impl.CommonFile ..> interfaces.IFile
tasks.PipelineTask ..> tasks.TaskRunState
tasks.PipelineTask ..> tasks.TaskId
@enduml

Some files were not shown because too many files have changed in this diff Show More