Compare commits

...

1029 Commits

Author SHA1 Message Date
aj-rosado
bee09de972 [PM-18936] Show key connector domain (#5034) 2025-04-17 19:20:50 +00:00
David Perez
33da0d8138 PM-20375: Events to use their own scope (#5072) 2025-04-17 17:52:20 +00:00
Patrick Honkonen
d5d8da2410 [PM-20373] Migrate Digital Asset Link API to network module (#5071) 2025-04-17 16:55:01 +00:00
Patrick Honkonen
3722a45359 [PM-20305] Migrate BaseUrlInterceptor and BaseUrlInterceptors to network module (#5068) 2025-04-17 16:28:05 +00:00
Patrick Honkonen
f23079b5ac [PM-20196] Migrate SyncService to the network module (#5056) 2025-04-17 15:59:33 +00:00
David Perez
524ddb6d0c Update Androidx libraries (#5070) 2025-04-17 14:38:38 +00:00
Patrick Honkonen
0d40d1e569 [PM-20195] Migrate SendsService to network module (#5055) 2025-04-17 13:54:30 +00:00
David Perez
4f65044179 PM-19593: Update expiration string to be 'Expires on <date>' (#5069) 2025-04-16 18:56:34 +00:00
David Perez
d67e74e48b PM-19620: Auto-delete flight recorder logs after 30 days (#5062) 2025-04-16 17:09:31 +00:00
Patrick Honkonen
b760b58669 Update user-agent header in Authenticator app (#5067) 2025-04-16 17:03:08 +00:00
Patrick Honkonen
36e6fbc14c [PM-20193] Migrate DownloadService to network module (#5053) 2025-04-16 16:46:30 +00:00
Patrick Honkonen
0be26c1eda [PM-20306] Migrate Auth Token Interceptor (#5065) 2025-04-16 16:41:43 +00:00
Patrick Honkonen
3311086dfc [PM-20309] Migrate Environment and EnvironmentUrlDataJson to data module (#5063) 2025-04-16 16:37:29 +00:00
David Perez
c912a3f12a PM-19622: Add ability to share flight recorder logs (#5060) 2025-04-16 15:43:14 +00:00
David Perez
e67790438e Update to latest Mockk (#5066) 2025-04-16 15:37:58 +00:00
Patrick Honkonen
ff72efe0ed [PM-20127] Only prompt passkey user verification once (#5026) 2025-04-16 15:35:25 +00:00
Patrick Honkonen
9dd71eaea2 [PM-20304] Migrate HeadersInterceptor to network module (#5061) 2025-04-16 15:09:00 +00:00
Patrick Honkonen
2d416eade5 [PM-20192] Migrate CiphersService to network module (#5052) 2025-04-16 15:08:17 +00:00
Patrick Honkonen
83de8b888d [PM-20191] Migrate OrganizationService to network module (#5049) 2025-04-15 19:42:21 +00:00
Patrick Honkonen
e35be360d7 [PM-20190] Migrate NewAuthRequestService to network module (#5048) 2025-04-15 18:20:25 +00:00
Patrick Honkonen
899689ba7b [PM-20189] Migrate IdentityService and related components to network module (#5047) 2025-04-15 16:35:47 +00:00
Patrick Honkonen
71237cb3a7 Make MobileErrorReporting flag remotely configured (#5015) 2025-04-15 16:07:49 +00:00
Álison Fernandes
18c7333cf3 Revert "[PM-19821] Consolidate scan.yml and scan-ci.yml" (#5058) 2025-04-15 14:42:01 +00:00
Patrick Honkonen
b6d9bee266 Update fastlane (#5025) 2025-04-15 14:23:57 +00:00
Patrick Honkonen
868f8091d2 [PM-20188] Migrate HaveIBeenPwnedService to network module (#5046) 2025-04-15 14:01:21 +00:00
Patrick Honkonen
dcd2d26d6c [PM-20187] Migrate DevicesService to the network module (#5040) 2025-04-14 21:28:34 +00:00
Patrick Honkonen
548c9ee092 [PM-20194] Migrate FolderService and related models to network module (#5054) 2025-04-14 20:55:50 +00:00
David Perez
186741e052 Use explicit timezone in tests (#5057) 2025-04-14 20:52:25 +00:00
André Bispo
d989c61bc2 [PM-19613] Save attachment error message update (#5013) 2025-04-14 20:50:59 +00:00
David Perez
8f311861e3 PM-19593: Display all flight recorder logs (#5050) 2025-04-14 20:00:20 +00:00
David Perez
942b95927f Limit network logs in production (#5051) 2025-04-14 18:29:56 +00:00
Patrick Honkonen
f087b6aac7 [PM-20186] Migrate AuthRequestsService to the network module (#5039) 2025-04-14 18:28:55 +00:00
Patrick Honkonen
87ef0e2961 [PM-20066] Migrate EventService and EventServiceImpl to network module (#5036) 2025-04-14 14:31:17 +00:00
David Perez
b60f30d8a1 PM-19592: Enabled and disable flight recorder (#5035) 2025-04-14 14:17:55 +00:00
github-actions[bot]
c553d2caec Update Google privileged browsers list (#5045)
Co-authored-by: GitHub Actions Bot <actions@github.com>
Co-authored-by: Patrick Honkonen <phonkonen@bitwarden.com>
2025-04-14 13:47:21 +00:00
Patrick Honkonen
d1ba730012 [PM-20185] Migrate AccountService to network module (#5038) 2025-04-11 21:33:05 +00:00
Patrick Honkonen
944eb64562 [PM-20067] Migrate PushService to the network module (#5037) 2025-04-11 21:09:14 +00:00
David Perez
12ce1e5229 PM-20133: Add initial logging logic (#5029) 2025-04-11 17:33:06 +00:00
David Perez
b72da1ba69 PM-20172: Display errors from WebAuthN results (#5032) 2025-04-11 15:53:40 +00:00
Patrick Honkonen
ed97695228 [PM-20073] Migrate SyncApi to the network module (#5028) 2025-04-11 14:18:09 +00:00
bw-ghapp[bot]
05b1266b1b Autosync Crowdin Translations (#5030)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2025-04-11 13:08:49 +00:00
Patrick Honkonen
8153b25d89 [PM-20072] Migrate SendsApi to network module (#5027) 2025-04-11 13:05:11 +00:00
Patrick Honkonen
2fd5ab6f8d [PM-20071] Migrate FoldersApi to network module (#5023) 2025-04-10 22:30:25 +00:00
Patrick Honkonen
392695cb54 [PM-20069] Migrate CiphersApi to network module (#5021) 2025-04-10 21:09:21 +00:00
David Perez
d7f771990e PM-20123: Add flight recorder metadata (#5024) 2025-04-10 20:57:24 +00:00
Patrick Honkonen
dd0f08d759 [PM-19864] Migrate UnauthenticatedOrganizationApi to network module (#5019) 2025-04-10 20:42:26 +00:00
Patrick Honkonen
dd9ca853a7 [PM-20070] Migrate DownloadApi to network module (#5022) 2025-04-10 20:09:44 +00:00
Patrick Honkonen
2ef0ca3620 [PM-20068] Migrate AzureApi to network module (#5020) 2025-04-10 20:03:47 +00:00
Patrick Honkonen
97ef0ec004 [PM-19863] Migrate UnauthenticatedKeyConnectorApi to network module (#5018) 2025-04-10 19:16:04 +00:00
Patrick Honkonen
9d7df2bc10 [PM-19862] Migrate UnauthenticatedIdentityApi to the network module (#5017) 2025-04-10 15:19:46 +00:00
Patrick Honkonen
cb05787256 [PM-19861] Migrate UnauthenticatedDevicesApi to network module (#5016) 2025-04-10 15:15:50 +00:00
Patrick Honkonen
8767f2dd24 Update androidx.credentials (#4919) 2025-04-09 15:35:06 +00:00
David Perez
bf1ed690a4 PM-20005: Make the BitwardenOverflowActionItem more generic (#5011) 2025-04-09 15:04:53 +00:00
Patrick Honkonen
a12f325815 [PM-19958] Migrate Text to the UI module (#5010) 2025-04-08 20:33:45 +00:00
Auj625197595
c2872b6b9b [PM-18559] add kitobrowser series support (#4770)
Co-authored-by: Patrick Honkonen <1883101+SaintPatrck@users.noreply.github.com>
2025-04-08 19:53:12 +00:00
Patrick Honkonen
622d68e40c [PM-19952] Provide SharedPreference from the data module (#5006) 2025-04-08 19:46:16 +00:00
David Perez
6ebece1b1e PM-19978: Build out flight recorder UI (#5009) 2025-04-08 19:11:05 +00:00
Patrick Honkonen
c540d3ef47 [PM-19951] Move EncryptedPreferences to the correct di package (#5004) 2025-04-08 17:00:04 +00:00
Patrick Honkonen
385f5efac5 [PM-19959] Introduce a new UI module (#5008) 2025-04-08 16:12:12 +00:00
Patrick Honkonen
6668af58d2 [PM-19949] Provide ConfigDiskSource from data module (#5003) 2025-04-08 16:03:04 +00:00
Patrick Honkonen
c5e216783e [PM-19948] Migrate ServerConfigRepository to data module (#5002) 2025-04-07 21:58:39 +00:00
Álison Fernandes
acf222d151 [PM-19821] Consolidate scan.yml and scan-ci.yml (#4969)
Co-authored-by: Matt Andreko <mandreko@bitwarden.com>
2025-04-07 20:19:29 +00:00
Patrick Honkonen
1df96fdb62 [PM-19947] Provide system clock in the core module (#5000) 2025-04-07 20:03:53 +00:00
David Perez
942f6e2475 PM-19938: Add empty and loading state to the recorded logs screen (#5001) 2025-04-07 18:33:15 +00:00
Patrick Honkonen
2176b61cd3 [PM-19905] Migrate DispatcherManager to data module (#4999) 2025-04-07 18:16:04 +00:00
David Perez
4a63a709b8 PM-19937: Make navigation rail scrollable (#5005) 2025-04-07 18:15:23 +00:00
Patrick Honkonen
62cfcbbd72 Remove unused FeatureFlagRepository and FeatureFlagDiskSource (#4998) 2025-04-07 13:07:13 +00:00
Patrick Honkonen
538f1feb2e [PM-19872] Migrate UnencryptedPreferences to data module (#4994) 2025-04-04 20:09:20 +00:00
David Perez
70e42a27db Bump internal version name (#4997) 2025-04-04 19:45:45 +00:00
David Perez
4d9a19f43c PM-19645: Remove the new device UI email access flow (#4996) 2025-04-04 19:41:34 +00:00
Patrick Honkonen
dda8237ce5 [PM-19870] Migrate ServerConfig and ConfigDiskSource to the data module (#4992) 2025-04-04 18:33:54 +00:00
Patrick Honkonen
c4f54ee93c [PM-19860] Migrate UnauthenticateAuthRequestsApi and models to network module (#4990) 2025-04-04 17:33:55 +00:00
Patrick Honkonen
cac22346dd [PM-19859] Migrate UnauthenticatedAccountsApi and models to network module (#4989) 2025-04-04 16:45:20 +00:00
Patrick Honkonen
3f35ace6e9 [PM-19871] Migrate EncryptedPreferences to data module (#4993) 2025-04-04 14:38:50 +00:00
Patrick Honkonen
d1e4078c5a [PM-19851] Migrate network AuthenticatedAccountsApi to network module (#4982) 2025-04-04 14:17:37 +00:00
bw-ghapp[bot]
241a89fe13 Autosync Crowdin Translations (#4995)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2025-04-04 13:55:25 +00:00
David Perez
4df1b245e8 PM-19873: Migrate the screen capture feature from user-scoped to app-scoped (#4972) 2025-04-03 22:02:53 +00:00
Patrick Honkonen
853069ee1c [PM-19866] Migrate BaseEncryptedDiskSource to data module (#4991) 2025-04-03 21:52:54 +00:00
Patrick Honkonen
1149e91dd5 [PM-19858] Migrate HaveIBeenPwnedApi to network module (#4988) 2025-04-03 21:34:39 +00:00
Patrick Honkonen
d5de173431 [PM-19857] Migrate AuthenticatedOrganizationApi and models to network module (#4987) 2025-04-03 21:34:35 +00:00
Patrick Honkonen
f6d5302a73 [PM-19856] Migrate KeyConnectorApi and models to network module (#4986) 2025-04-03 21:34:30 +00:00
Patrick Honkonen
0ea7d00e93 [PM-19855] Migrate AuthenticatedDevicesApi and models to network module (#4985) 2025-04-03 21:34:10 +00:00
Patrick Honkonen
ebdf5f816a [PM-19854] Migrate AuthenticatedAuthRequestsApi to network module (#4984) 2025-04-03 21:33:32 +00:00
Patrick Honkonen
85109a2e4b [PM-19849] Move PushApi and PushTokenRequest to network module (#4979) 2025-04-03 21:18:12 +00:00
Patrick Honkonen
5017e935d7 [PM-19845] Migrate BaseDiskSource to data module (#4978) 2025-04-03 19:34:52 +00:00
David Perez
ce0aa7adda Update Firbase BOM to the latest version (33.12.0) (#4981) 2025-04-03 18:44:10 +00:00
David Perez
111d141fac PM-19850: Update dialog scrim (#4980) 2025-04-03 18:32:43 +00:00
Patrick Honkonen
4676f4bf8c [PM-19841] Migrate Event API and models to network module (#4976) 2025-04-03 18:22:53 +00:00
Patrick Honkonen
321a764f20 [PM-19831] Migrate ConfigService to network module (#4971) 2025-04-03 18:15:51 +00:00
David Perez
5d4df86bc9 PM-19812: Navigation Rail design feedback (#4977) 2025-04-03 17:37:03 +00:00
Patrick Honkonen
291c568583 [PM-19840] Migrate BaseEnumeratedIntSerializer to core module (#4975) 2025-04-03 16:06:36 +00:00
Patrick Honkonen
7938c8c2bb [PM-19832] Create data module (#4973) 2025-04-03 14:42:12 +00:00
David Perez
1fecd4af5f PM-19591: Initial flight recorder UI (#4970) 2025-04-03 13:42:13 +00:00
David Perez
1e6f896328 PM-19830: Updating padding for last sync time label (#4974) 2025-04-03 13:41:38 +00:00
Patrick Honkonen
e67a143474 [PM-19813] Migrate BaseServiceTest to Network module and enable test fixtures (#4967) 2025-04-02 21:12:36 +00:00
David Perez
a6862bb791 PM-19812: Add navigation rail (#4966) 2025-04-02 21:04:29 +00:00
Patrick Honkonen
d70e658c8b [PM-19783] Migrate ConfigApi and ConfigResponseJson to network module (#4964) 2025-04-02 20:07:41 +00:00
Álison Fernandes
f43702cb83 Set SARIF upload branch to the merge commit ref/sha retrieved from GH CLI (#4958) 2025-04-02 19:32:59 +00:00
Patrick Honkonen
20bda929b3 [PM-19820] Replace ResultCallAdapterFactory in authenticator module (#4968) 2025-04-02 18:48:45 +00:00
Patrick Honkonen
ce139623d6 [PM-19793] Migrate ZonedDateTimeSerializer to core module (#4960) 2025-04-02 17:42:51 +00:00
David Perez
8e7de92609 PM-19807: Remove IconResource class (#4963) 2025-04-02 15:21:58 +00:00
Patrick Honkonen
b82b1ad570 Update CODEOWNERS (#4965) 2025-04-02 15:19:29 +00:00
Patrick Honkonen
56cce8ffdd [PM-19782] Migrate network error handling to network module (#4957) 2025-04-02 15:04:59 +00:00
Patrick Honkonen
7abae5e86a Update network module minSdk version (#4961) 2025-04-02 14:18:35 +00:00
Patrick Honkonen
862384db3a [PM-19738] Migrate NetworkResultCallAdapter to network module (#4949) 2025-04-02 13:40:43 +00:00
David Perez
01c4a3db03 PM-19705: Log reason for logout (#4959) 2025-04-02 13:00:17 +00:00
David Perez
e5ddeb44fd PM-19645: Remove new device feature flags (#4950) 2025-04-02 12:59:48 +00:00
David Perez
a476436bff Update the Hilt and Androidx dependencies (#4954) 2025-04-01 21:40:22 +00:00
Álison Fernandes
d3e14e8f52 [PM-19772] Remove scan-authenticator.yml (#4952) 2025-04-01 17:20:19 +00:00
Álison Fernandes
36fa907d87 Remove scan-authenticator.yml 2025-04-01 17:50:35 +01:00
Phil Cappelli
a4ee8017ae PM-19131 - Custom hidden fields not working properly if TOTP also configured (#4916) 2025-04-01 16:38:18 +00:00
Patrick Honkonen
aa35e2f93c [PM-19625] Migrate DataStateExtensions to core module (#4943) 2025-04-01 16:09:20 +00:00
Philip Cappelli
40179429bf Merge branch 'main' into phil/PM-19131-Custom-hidden-fields-not-working-properly-if-TOTP-also-configured 2025-04-01 11:59:15 -04:00
Philip Cappelli
bdf50fd1a6 PR comments 2025-04-01 11:59:03 -04:00
Patrick Honkonen
445d6ec3b8 [PM-19738] Create network module (#4948) 2025-04-01 14:32:54 +00:00
Patrick Honkonen
c304a4306f [PM-19627] Migrate JsonExtensions to core module (#4932) 2025-04-01 14:14:40 +00:00
Philip Cappelli
f82bda5bce lint fixes 2025-03-31 17:21:28 -04:00
Patrick Honkonen
a2f4e4f3b5 [PM-19738] Create network module
Create a library module responsible for communicating with the Bitwarden network API.

Additionally, the following changes were made to checks and workflows:
- Updated `build.gradle.kts` to include Kover for the `network` module.
- Updated `Fastfile` to include lint, test and kover report generation tasks for the `network` module.
2025-03-31 17:19:51 -04:00
Philip Cappelli
d4c079140d remove space 2025-03-31 17:09:30 -04:00
Philip Cappelli
f1c82eb027 Merge branch 'main' into phil/PM-19131-Custom-hidden-fields-not-working-properly-if-TOTP-also-configured 2025-03-31 17:08:35 -04:00
Philip Cappelli
932eced64e clean up 2025-03-31 17:07:15 -04:00
Patrick Honkonen
1eced037a4 [PM-19627] Move Json extension functions to core module
Move JsonExtensions and related tests to `core` module.

- Moved `decodeFromStringOrNull` and related testing from `app` and `authenticator` modules to `core` module.
- Updated all usages of `decodeFromStringOrNull` in `app` and `authenticator` to the new location in `core`.
- Deleted unused `JsonExtensionsTest` and `JsonExtensions.kt` in `app` and `authenticator`.
- Updated dependencies for core.
2025-03-31 17:06:06 -04:00
Philip Cappelli
a4fb50d3d8 revert previous changes and go back to hash solution 2025-03-31 17:05:19 -04:00
Patrick Honkonen
9a50399116 Migrate DataStateExtensions to core module
Move `DataStateExtensions` to the `core` module.

- Deleted `DataStateExtensions.kt` from `authenticator` and `app` modules.
- Updated all imports.
- Added `junit5` and `kotlinx.coroutines.test` test implementation to `core` module.
2025-03-31 16:55:09 -04:00
Patrick Honkonen
5d5bc25a45 [PM-19652] Consolidate check configurations (#4937) 2025-03-31 21:46:19 +01:00
Patrick Honkonen
af528fdd82 Add matching fallback for the beta build type to release (#4946) 2025-03-31 19:56:26 +00:00
David Perez
7d119fb552 Update to AGP 8.9.1 (#4947) 2025-03-31 19:56:18 +00:00
David Perez
81b43d13b0 PM-19404: Improve email validation and validation error parsing (#4945) 2025-03-31 19:36:01 +00:00
Patrick Honkonen
cd86413ff6 Fix detekt issues in authenticator tests (#4944) 2025-03-31 19:20:10 +00:00
Dave Severns
05094cf6e7 PM-19723 Group "no folder" items when there is a collection present. (#4941) 2025-03-31 19:19:25 +00:00
Patrick Honkonen
75af4868e2 [PM-19640] Migrate SpecialCharWithPrecedenceComparator to core module (#4942) 2025-03-31 19:19:03 +00:00
Patrick Honkonen
b7948948f0 [PM-19628] Migrate ResultExtensions to core module (#4934) 2025-03-31 17:38:45 +00:00
David Perez
efec5cb4ca PM-19653: Add tooltip and subtext tupport for the switch (#4936) 2025-03-31 15:15:58 +00:00
Patrick Honkonen
6369b20f18 Run detekt on authenticatorbridge module (#4940) 2025-03-31 14:22:18 +00:00
Patrick Honkonen
2e11c81f45 [PM-19626] Migrate SharedFlowExtensions to core module (#4931) 2025-03-31 12:52:18 +00:00
Philip Cappelli
3926fbca7f Revert "WIP"
This reverts commit f698d09b43.
2025-03-28 14:27:40 -04:00
Philip Cappelli
29838b19e0 Revert "WIP"
This reverts commit 66dae199c8.
2025-03-28 14:27:30 -04:00
Philip Cappelli
66dae199c8 WIP 2025-03-28 13:52:16 -04:00
Philip Cappelli
f698d09b43 WIP 2025-03-28 11:44:42 -04:00
bw-ghapp[bot]
9486f4c4e2 Autosync Crowdin Translations (#4935)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2025-03-28 13:50:14 +00:00
David Perez
6664ccc53f PM-19466: Fix snackbar ime padding (#4933) 2025-03-28 13:40:54 +00:00
Patrick Honkonen
d62e3164dc [PM-19624] Migrate DataState to core module (#4930) 2025-03-28 13:29:39 +00:00
Philip Cappelli
b0729e8cd2 WIP 2025-03-27 17:32:59 -04:00
Philip Cappelli
c51f61c585 WIP 2025-03-27 17:32:53 -04:00
Patrick Honkonen
6340c2dd04 [PM-19616] Move OmitFromCoverage annotation to core module (#4928) 2025-03-27 21:11:53 +00:00
David Perez
42df9733c8 PM-19543: Add flight recorder feature flag (#4929) 2025-03-27 20:51:29 +00:00
Patrick Honkonen
cfa753cb12 [PM-19013] Create core module (#4836) 2025-03-27 19:53:29 +00:00
David Perez
71250a28fa Update the README dependencies section and clean up toml file (#4926) 2025-03-27 19:51:24 +00:00
David Perez
5279e6d18c PM-19547: Delay the delete account success dialog to avoid flicker (#4927) 2025-03-27 18:52:38 +00:00
aj-rosado
8dd5a9df9f [PM-8331] Using default send url when on US cloud (#4925) 2025-03-27 18:30:49 +00:00
Patrick Honkonen
346961856f Update passkey privileged app community list (#4923) 2025-03-27 13:53:24 +00:00
Dave Severns
4cae0823f2 PM-19559 Remove "Give feedback" from Settings > About (#4924) 2025-03-26 20:08:55 +00:00
Dave Severns
98b6f68821 PM-19550: Accept app language settings "System default" (#4922) 2025-03-26 19:37:19 +00:00
github-actions[bot]
a6d1f210c8 Update Google privileged browsers list (#4910)
Co-authored-by: GitHub Actions Bot <actions@github.com>
Co-authored-by: Patrick Honkonen <1883101+SaintPatrck@users.noreply.github.com>
2025-03-26 19:05:33 +00:00
Philip Cappelli
c0707ae08c Revert "PM-19131 - moved isVisible logic for hidden custom fields to be managed on view side vs model side"
This reverts commit bc76e46185.
2025-03-26 14:10:15 -04:00
David Perez
3e15b60178 PM-19498: Update cipher migration logic (#4920) 2025-03-26 17:29:28 +00:00
David Perez
6081b7e932 Replace landscape and portrait logic with sceen width logic (#4915) 2025-03-26 16:09:41 +00:00
Philip Cappelli
bc76e46185 PM-19131 - moved isVisible logic for hidden custom fields to be managed on view side vs model side 2025-03-25 17:21:29 -04:00
David Perez
b4b4f753ca PM-8953: Require 4 digits for pin entry (#4914) 2025-03-25 21:20:15 +00:00
David Perez
22376bfe4b PM-19403: Add better error messages for username generation (#4911) 2025-03-24 18:32:24 +00:00
David Perez
ab270cd243 PM-19466: Handle the IME padding internal to the BitwardenScaffold (#4912) 2025-03-24 17:13:35 +00:00
David Perez
8ed9b97805 Update protobuf lib to 4.30.1 (#4909) 2025-03-21 21:18:01 +00:00
David Perez
27e4c6a2b4 Update Compose BOM to 2025.03.00 (#4908) 2025-03-21 21:17:41 +00:00
David Perez
a6ed702a95 BWA-154: Fix privacy policy padding (#4907) 2025-03-21 20:25:06 +00:00
David Perez
0792f44b6b Update to Firebase 33.11.0 (#4906) 2025-03-21 18:36:54 +00:00
David Perez
94a91702cc PM-19399: Do not show 'Share error details' button when user enter incorrect password (#4905) 2025-03-21 16:29:38 +00:00
David Perez
21af60f4de Update to Junit 5.12.1 (#4903) 2025-03-21 16:11:12 +00:00
Matt Andreko
1add57d56c Fix SARIF upload branch ref/sha (#4899) 2025-03-21 13:13:00 +00:00
bw-ghapp[bot]
b0421c774b Autosync Crowdin Translations (#4902)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2025-03-21 13:06:20 +00:00
David Perez
7d846ae383 PM-18862: Update IME handling for BitwardenScaffold and VaultUnlockedNavBarScreen (#4901) 2025-03-20 20:44:46 +00:00
David Perez
29371bdcb5 PM-19389: Handle encoding error when migrating biometric key (#4900) 2025-03-20 19:15:44 +00:00
Álison Fernandes
3eed1c1abe [PM-19049] Add workflow to regularly fetch updates to fido2_privileged_google.json (#4858)
Co-authored-by: Patrick Honkonen <1883101+SaintPatrck@users.noreply.github.com>
2025-03-19 21:13:40 +00:00
aj-rosado
ad6bc883b8 [PM-13257] Checking if user is navigating from vault before showing prompt for biometrics (#4846) 2025-03-19 10:23:50 +00:00
David Perez
f4f669683e PM-19334: Propagate errors to the UI (#4893) 2025-03-18 17:27:41 +00:00
Dave Severns
475a82e0fb PM-19335 add throwable val to generator error results (#4894) 2025-03-18 17:08:11 +00:00
Phil Cappelli
f22156389b BWA-119 - Unable to Access Manual Code Entry After Denying Camera Permissions on (#4891) 2025-03-18 15:50:51 +00:00
Phil Cappelli
4f09f5dae4 PM-18872 - When a Folder name is long, the View/Edit Item > Folder selection screen doesn't adjust well (#4892) 2025-03-18 15:50:39 +00:00
David Perez
3934bc9ae2 PM-19314: Propagate remaining auth errors to the UI (#4888) 2025-03-18 15:24:17 +00:00
David Perez
72c9149d27 PM-19295: Propagate password errors to the UI (#4884) 2025-03-18 14:05:36 +00:00
David Perez
a040a38ce8 PM-19296: Propagate login errors to the UI (#4885) 2025-03-18 14:05:07 +00:00
Dave Severns
ef3b7730d0 PM-19289 propagating remaining vault result errors. (#4881) 2025-03-18 13:51:30 +00:00
David Perez
ad8d8d271a PM-19294: Propagate the Register errors to the UI (#4883) 2025-03-17 19:54:43 +00:00
David Perez
4954e57007 Update gem dependencies (#4882) 2025-03-17 19:29:22 +00:00
Dave Severns
6f50fffd17 PM-19275 propagate the errors for the vault unlock error result types (#4878) 2025-03-17 19:24:13 +00:00
David Perez
44c5755301 PM-19284: Propagate SSO flow errors to the UI (#4880) 2025-03-17 19:14:17 +00:00
David Perez
b20eece3aa PM-19283: Propagate error from email token and known device flows (#4879) 2025-03-17 17:47:07 +00:00
renovate[bot]
869a3b00a5 [deps]: Lock file maintenance (#4875)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-17 15:47:03 +00:00
David Perez
7f8e848c46 PM-19245: Propagate error from password validation to UI (#4877) 2025-03-17 15:36:01 +00:00
Dave Severns
6c784d28eb PM-19272 propagate errors from cipher results (#4876) 2025-03-17 15:18:05 +00:00
Dave Severns
dfcdc72499 PM-19241 folder result errors propagated to UI (#4870) 2025-03-17 12:20:44 +00:00
Dave Severns
db287ddce5 PM-19243 send result errors propagated to UI (#4872) 2025-03-14 22:01:42 +00:00
David Perez
9ea85917b1 PM-19239: Propagate delete account errors to the UI (#4871) 2025-03-14 21:27:56 +00:00
Dave Severns
6db4165c4c PM-19234 propagates attachment result errors to UI (#4869) 2025-03-14 20:47:25 +00:00
David Perez
18ce45e7e5 PM-19233: Propagate auth request errors to the UI (#4868) 2025-03-14 18:33:39 +00:00
Dave Severns
6fe9eba620 PM-11356 Adjust autofocus delay to be greater than screen refresh delay. (#4866) 2025-03-14 16:48:34 +00:00
David Perez
b084987758 PM-19226: Propagate error from create auth request flow to UI (#4867) 2025-03-14 15:47:03 +00:00
bw-ghapp[bot]
5d0593026f Autosync Crowdin Translations (#4865)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2025-03-14 14:39:07 +00:00
David Perez
47abeb7843 PM-14435: Improve accessibility service detection (#4864) 2025-03-14 14:16:25 +00:00
Phil Cappelli
e2779e4edb PM-18681 - Update Showing Coach Mark Tour Logic To Only Consider User's Personal Vault (#4863) 2025-03-13 20:46:14 +00:00
Álison Fernandes
90cc9f77c5 [PM-19207] Add Passkey / FIDO2 Bug Report template (#4859) 2025-03-13 20:31:30 +00:00
David Perez
40760f270a Use immutable map in debug menu (#4861) 2025-03-13 19:28:27 +00:00
David Perez
8a773141a4 Simplify RootNavScreenTest (#4860) 2025-03-13 19:20:14 +00:00
David Perez
0c149abdd9 PM-18844: Update BitwardenBasicDialog to allow it to share error logs (#4855) 2025-03-13 18:16:22 +00:00
David Perez
f540f86b19 PM-19199: hoist debug menu up to top level of the app (#4857) 2025-03-13 17:51:22 +00:00
Dave Severns
ca64ce2176 PM-18877 Respect system app specific language selection on Android 13 and up. (#4849) 2025-03-13 13:14:41 +00:00
André Bispo
da63c9e36b [PM-17995] Adjust custom fields section (#4835) 2025-03-13 11:33:39 +00:00
André Bispo
e16ad44d5e [PM-17242] While on autofill search on all item types. (#4824) 2025-03-13 11:33:12 +00:00
Phil Cappelli
d26a2ee52a PM-18681 - Update Showing Coach Mark Tour Logic To Account for Org Only Policy (#4854) 2025-03-12 20:20:44 +00:00
Dave Severns
1eb741ab58 PM-11356 prevent extra soft-keyboard showing. (#4845) 2025-03-11 20:10:46 +00:00
David Perez
e10ca9a6ec PM-19099: Centralize app metadata (#4847) 2025-03-11 19:47:07 +00:00
David Perez
3fca61ad3e Remove the language change dialog (#4658) 2025-03-11 14:33:22 +00:00
David Perez
409529b9ca Update AndroidX Activity to 1.10.1 (#4844) 2025-03-11 13:51:39 +00:00
David Perez
4568dd53d4 Update Firebase BOM to 33.10.0 (#4843) 2025-03-10 21:20:56 +00:00
David Perez
b9b90165bf PM-10725: Always show share sheet after creating send regardless of how it was made (#4841) 2025-03-10 20:43:49 +00:00
David Perez
778a630012 Update to AGP 8.9.0 (#4840) 2025-03-10 15:46:30 +00:00
Dave Severns
4809066ad7 PM-17087 update notification payloads to support camelCase JSON keys. (#4823) 2025-03-10 14:54:58 +00:00
Patrick Honkonen
d03c6c243d [PM-18873] Refactor ItemHeader.kt to improve location display (#4814) 2025-03-07 17:02:15 +00:00
bw-ghapp[bot]
d19ab498ff Autosync Crowdin Translations (#4832)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2025-03-07 16:48:31 +00:00
Phil Cappelli
efc3d21fde PM-18681 - Update Showing Coach Mark Tour Logic To Only Consider User's Personal Vault (#4821) 2025-03-05 16:01:41 +00:00
Phil Cappelli
35e585a60e PM-18570 Update Owner Selection Field to Bottom Sheet Selector (#4810) 2025-03-04 22:24:31 +00:00
Patrick Honkonen
39787f9bf0 [deps] Update mockk to 1.13.17 (#4818) 2025-03-04 20:17:42 +00:00
Patrick Honkonen
3940997ef9 [deps] Update junit5 to 5.11.4 (#4819) 2025-03-04 20:17:23 +00:00
Patrick Honkonen
ce482e744d [deps] Update testng to 7.11.0 (#4820) 2025-03-04 19:36:18 +00:00
Patrick Honkonen
b0157d10e2 [deps] Update detekt to 1.23.8 (#4817) 2025-03-04 19:36:00 +00:00
renovate[bot]
cf3c2fb56d [deps]: migrate renovate config (#4815)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-04 17:56:09 +00:00
Dave Severns
a88a173e00 PM-18773 update the keyName for the ChromeAutofill flag (#4812) 2025-03-03 15:31:02 +00:00
aj-rosado
ac6ff98041 [PM-14435] Accessibility enabled settings changes to address older and custom Android phone versions (#4756) 2025-02-28 22:25:11 +00:00
David Perez
ec030f2c2e Update AGP to 8.8.2 (#4809) 2025-02-28 18:39:52 +00:00
Patrick Honkonen
be08c1a536 Refactor .editorconfig to focus on Kotlin and common file types (#4808) 2025-02-28 16:16:28 +00:00
bw-ghapp[bot]
84edf4ead0 Autosync Crowdin Translations (#4806)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2025-02-28 14:34:04 +00:00
aj-rosado
915aac561c [PM-17215] Remove get_login_creds from Fido2OriginManagerImpl.filterMatchingAppStatementsOrNull (#4804) 2025-02-28 10:46:04 +00:00
Patrick Honkonen
2027a66f02 [PM-18714] Display Card brand icon when it is known (#4805) 2025-02-27 21:57:28 +00:00
David Perez
ef6d9bc68c PM-18677: Policies for disabled organizations apply (#4801) 2025-02-27 21:54:47 +00:00
Dave Severns
3ceda9e40a PM-18388 add hyphens on segmented button labels (#4777) 2025-02-27 21:50:37 +00:00
Phil Cappelli
3f1a6e97fd PM-17568 - Authenticator Sync: Sometimes synced verification codes only display the TOTP Key, not Issuer/username (#4783) 2025-02-27 21:42:38 +00:00
André Bispo
4448ab05ce [PM-17739] Show password history (#4803) 2025-02-27 19:49:57 +00:00
aj-rosado
d584391843 [PM-8223] 🍒 New device verification continue button enabled at 8 digit (#4802) 2025-02-27 19:48:24 +00:00
Patrick Honkonen
e0d91d7682 [PM-18067] Consolidate item name fields into ItemHeader (#4766) 2025-02-27 17:31:39 +00:00
Patrick Honkonen
e8a98dd3ed Add spacers to VaultItemIdentityContent, VaultItemSecureNoteContent, and VaultItemCardContent 2025-02-27 12:05:09 -05:00
Patrick Honkonen
b6163cf53c Fix item key in VaultItemCardContent for security code field 2025-02-27 11:14:40 -05:00
Patrick Honkonen
a24c6f2719 Update ic_organization.xml vector drawable 2025-02-27 11:13:04 -05:00
Patrick Honkonen
b3219d4040 Use CardStyle.Bottom when last item is drawn 2025-02-27 11:05:06 -05:00
Patrick Honkonen
27043e28a8 Revert formatting changes 2025-02-27 10:30:41 -05:00
Patrick Honkonen
7c36b7cb82 [PM-14303] Update Bitwarden SDK and load bitwarden_uniffi on older Android versions (#4793) 2025-02-27 15:23:55 +00:00
Patrick Honkonen
fcfcf48cee Uncomment & update previews 2025-02-27 09:17:50 -05:00
Patrick Honkonen
757994ec18 Merge remote-tracking branch 'origin/main' into PM-18067/view-item-favicon 2025-02-27 09:17:16 -05:00
Álison Fernandes
548de56d60 [PM-18434] Welcome Authenticator app! (#4798) 2025-02-27 12:54:51 +00:00
Patrick Honkonen
71cd917328 Merge branch 'main' into PM-14303/update-bitwarden-sdk 2025-02-26 17:15:35 -05:00
Álison Fernandes
01188c21b2 Update BWA secret names 2025-02-26 22:14:56 +00:00
Patrick Honkonen
5c076871ab Implement NativeLibraryManager and NativeLibraryManagerImpl for loading native libraries
This commit introduces the `NativeLibraryManager` interface and its implementation, `NativeLibraryManagerImpl`.
- `NativeLibraryManager` defines a method `loadLibrary` for loading native libraries.
- `NativeLibraryManagerImpl` uses `System.loadLibrary` to load libraries and handles potential `UnsatisfiedLinkError`.
- `PlatformManagerModule` is updated to provide `NativeLibraryManager` instance and to inject it into `SdkClientManager`.
2025-02-26 17:14:51 -05:00
Álison Fernandes
dca7284230 Merge remote-tracking branch 'bwa-android/main' into bwa-monorepo
# Conflicts:
#	.checkmarx/config.yml
#	.github/CODEOWNERS
#	.github/ISSUE_TEMPLATE/bug.yml
#	.github/ISSUE_TEMPLATE/config.yml
#	.github/renovate.json
#	.github/workflows/build-authenticator.yml
#	.github/workflows/crowdin-pull-authenticator.yml
#	.github/workflows/crowdin-push-authenticator.yml
#	.github/workflows/scan-authenticator.yml
#	.github/workflows/test-authenticator.yml
#	.gitignore
#	Gemfile
#	Gemfile.lock
#	README.md
#	build.gradle.kts
#	fastlane/Fastfile
#	gradle.properties
#	gradle/libs.versions.toml
#	gradle/wrapper/gradle-wrapper.properties
#	gradlew.bat
#	settings.gradle.kts
2025-02-26 22:13:24 +00:00
Patrick Honkonen
07d3849c4b Add keys and animation to all content items 2025-02-26 17:10:10 -05:00
David Perez
33c3fd28e9 itemHeader as LazyListScope extension 2025-02-26 14:45:25 -06:00
Patrick Honkonen
88609c2f5b Update OrganizationType.OWNER mock data in VaultItemViewModelTest.kt 2025-02-26 14:45:25 -06:00
Patrick Honkonen
af8cfcd2f0 Move expansion indicator outside of crossfade animation 2025-02-26 14:45:25 -06:00
Patrick Honkonen
7cc8108498 Adjust ItemHeader expanding header fill the available width 2025-02-26 14:45:25 -06:00
Patrick Honkonen
a31c499b15 Remove unnecessary mockkStatic call in CipherViewExtensionsTest.kt 2025-02-26 14:45:24 -06:00
Patrick Honkonen
d7d099477f Refactor ItemHeader to use LazyColumn and Crossfade for smoother transitions
- Migrates `ItemHeader` to `LazyColumn` to improve performance.
- Introduces `Crossfade` for animating title changes in `BitwardenExpandingHeader`.
- Adjusts icon sizing in `ItemHeaderIcon`.
- Removes unnecessary column scope and animated visibility from `ExpandingItemLocationContent`.
- Refactors to use `LazyItemScope` and adds `animateItem()` to `ItemLocationListItem`.
- Adds conditional handling for expanding the item locations list.
2025-02-26 14:45:24 -06:00
Patrick Honkonen
0ba240852f Refactor ExpandingItemLocationContent to use ColumnScope 2025-02-26 14:45:24 -06:00
Patrick Honkonen
727d943fae Use persistentListOfNotNull instead of buildList and toImmutableList 2025-02-26 14:45:24 -06:00
Patrick Honkonen
234f49a92c Replace HorizontalDivider with BitwardenHorizontalDivider and add Spacer in ItemHeader.kt 2025-02-26 14:45:24 -06:00
Patrick Honkonen
4f49d3d504 Reduce Spacer height in VaultItemLoginContent.kt from 24.dp to 12.dp 2025-02-26 14:45:24 -06:00
Patrick Honkonen
aba8344df1 Revert unintentional change 2025-02-26 14:45:23 -06:00
Patrick Honkonen
6da8e2c47b Adjust height of Spacer in BitwardenTextField based on cardStyle presence 2025-02-26 14:45:23 -06:00
Patrick Honkonen
78d5965271 Refactor ItemHeader to use LazyColumn for overflow locations 2025-02-26 14:45:23 -06:00
Patrick Honkonen
6953d5e132 Migrate VaultItem related locations to ImmutableList 2025-02-26 14:45:23 -06:00
Patrick Honkonen
7073124495 Make cardStyle parameter optional in BitwardenTextField 2025-02-26 14:45:23 -06:00
Patrick Honkonen
0cc7067808 Add divider to ItemHeader in vault item view 2025-02-26 14:45:23 -06:00
Patrick Honkonen
9e920f1cf5 Refactor ItemHeader to use cardStyle and remove custom card styling. 2025-02-26 14:45:22 -06:00
Patrick Honkonen
537e743891 Replaced standardHorizontalMargin with explicit horizontal padding 2025-02-26 14:45:22 -06:00
Patrick Honkonen
60da236f3e Add illustration colors 2025-02-26 14:45:22 -06:00
Patrick Honkonen
7804d8430f [PM-18067] Consolidate item name fields into ItemHeader
This commit introduces `ItemHeader`, a new composable that replaces `ItemNameField` to display the item name, favorite status, and related details like organization, collections, and folder.

Key changes:
- Removes `ItemNameField`
- Adds `ItemHeader` for displaying item name and favorite status, along with item location information.
- Introduces a new `ic_organization` icon.
- Adds the logic for showing item locations (organization, collections, folders) in a collapsible view.
- Removes `ItemNameField` from `VaultItemLoginContent`, `VaultItemIdentityContent`, `VaultItemSecureNoteContent`, `VaultItemCardContent`, `VaultItemSshKeyContent` and replace it with `ItemHeader`
- Adds the logic to fetch and display the item icon in `ItemHeader` based on item type
- Adds an `ItemLocationListItem` for displaying location details.
- Adds a `VaultItemLocation` data class for representing item locations.
- Adds new `baseIconUrl` and `isIconLoadingDisabled` variables to the `VaultItemState` to handle icon display.
- Updates `CipherView.toIconData` to handle the item icon.
- Adds new `show_more`, `no_folder` and `show_less` string resources.
- Updates the `BitwardenShapes` to include `favicon` shapes.
- Updates the `BitwardenColorScheme` to include `faviconForeground` and `faviconBackground`.
- Updates `BitwardenExpandingHeader` to include expandedText, collapsedText and showExpansionIndicator properties.
2025-02-26 14:45:22 -06:00
Dave Severns
2893c3871f PM-18636 Hide coach mark card if any login ciphers exist (#4787)
Co-authored-by: Patrick Honkonen <1883101+SaintPatrck@users.noreply.github.com>
Co-authored-by: Philip Cappelli <phil@livefront.com>
2025-02-26 18:48:10 +00:00
André Bispo
d04ac5e672 [PM-18451] Elevated privileges do not exempt from remove pin unlock policy (#4791) 2025-02-26 16:48:56 +00:00
Álison Fernandes
55e03565a6 [PM-18655] sync with bitwarden/template (#4795) 2025-02-26 15:24:00 +00:00
Álison Fernandes
768f7a3fd9 [PM-16534] Monorepo prep - Update checkmarx, renovate and gitignore (#4794) 2025-02-26 15:23:01 +00:00
Álison Fernandes
1d02737093 [PM-18651] Add Bitwarden Authenticator issue template and update existing bug template (#4792) 2025-02-26 15:22:44 +00:00
Patrick Honkonen
dab06b0ed4 [PM-14303] Update Bitwarden SDK and load bitwarden_uniffi on older Android versions
The Bitwarden SDK dependency was updated to version 1.0.0-20250225.125021-120.

The SDK requires access to Android APIs that were made public in API 31 in order to generate email aliases. To address this limitation for devices with earlier API versions, the `bitwarden_uniffi` library is now loaded manually before initializing any `Client` instance.
2025-02-26 10:04:24 -05:00
Álison Fernandes
fb792a668b [PM-17412] Retrieve firebase files from container and remove ui-test version ref (#357)
Co-authored-by: Patrick Honkonen <1883101+SaintPatrck@users.noreply.github.com>
2025-02-26 08:59:37 -05:00
Álison Fernandes
64da29ffaa [PM-16534] Merge authenticator-android libs and fastlane files (#4782) 2025-02-25 22:49:26 +00:00
Bernd Schoolmann
675cbb7c4f [PM-15149] Remove ssh feature flag (#4761) 2025-02-25 22:33:36 +00:00
Patrick Honkonen
6b63218839 [PM-17142] Remove ExampleInstrumentedTest (#358) 2025-02-25 22:21:41 +00:00
André Bispo
30a1bba796 [PM-15873] Fix PTR in sends listing page (#4784) 2025-02-25 19:06:45 +00:00
David Perez
c2d9e4858b Standardize all compose tests with theme and back handling (#4779) 2025-02-25 12:34:10 -06:00
André Bispo
d8e42083b7 [PM-18451] Validate remove pin policy against user privileges (#4774)
Co-authored-by: Dave Severns <149429124+dseverns-livefront@users.noreply.github.com>
2025-02-25 16:14:27 +00:00
André Bispo
ac7fbfd129 [PM-15873] Fix PTR on item listing page (#4778) 2025-02-25 16:14:18 +00:00
Peter Dave Hello
25dfa74bdf [BWA-153] Fix the GitHub Workflow badge in README.md (#351) 2025-02-25 10:33:29 -05:00
renovate[bot]
85a98e86c4 [deps]: Lock file maintenance (#352)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-25 10:33:03 -05:00
Álison Fernandes
078d80d3f6 [PM-17412] Rename debug keystore (#356) 2025-02-25 10:30:14 -05:00
David Perez
00eb78f02e Simplify the RegisterResponseJson error models (#4776) 2025-02-24 15:32:41 -06:00
David Perez
eadfac5ea8 Simplify error response models (#4775) 2025-02-24 14:52:00 -06:00
David Perez
a651d9b1fc Update Kotlin and ksp to latest versions (#4773) 2025-02-24 13:13:14 -06:00
Álison Fernandes
6308aed34a [PM-17412] Update libs.versions.toml ahead of repo merge (#349) 2025-02-24 12:22:27 -06:00
Dave Severns
011d637f7c PM-18129 add authenticator illustration for 2fa screen (#4763) 2025-02-24 09:51:54 -05:00
David Perez
0b03d2c0d5 Update hilt v2.55 (#4769) 2025-02-23 11:28:40 -06:00
David Perez
bb7e4061cc Update firebase (#4765) 2025-02-22 13:20:53 -06:00
David Perez
d1308cb936 Update Compose BOM to 2025.02.00 (#4764) 2025-02-21 15:12:35 -06:00
Dave Severns
892f817b2a PM-18315 add UI when 3pa is available for each chrome channel which s… (#4758) 2025-02-21 15:09:57 -05:00
David Perez
86e5789f30 Add NetworkErrorCode enum to make error parsing more readable (#4762) 2025-02-21 13:02:47 -06:00
David Perez
80bd1bfde2 PM-18496: Propagate prevalidateSso API error message (#4759) 2025-02-21 08:34:18 -06:00
bw-ghapp[bot]
4943df24b3 Autosync Crowdin Translations (#4760)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2025-02-21 08:32:04 -06:00
Dave Severns
b7464b87d9 PM-18314 & PM-18450 Check for Chrome browser 3rd party autofill. (#4752) 2025-02-20 15:59:39 -05:00
David Perez
3fb7904a36 PM-18480 Update BitwardenSwitch padding (#4757) 2025-02-20 14:57:13 -06:00
David Perez
9d9f9e3e72 PM-18121: Use correct cipher type for edit screen (#4755) 2025-02-20 12:11:49 -06:00
André Bispo
a061cbb1d3 [PM-15873] Add delay to PTR to remove the spinning wheel (#4750) 2025-02-20 17:49:00 +00:00
David Perez
ad03f8c996 PM-18452: Update BitwardenMultiSelectionButton (#4754) 2025-02-20 11:12:42 -06:00
André Bispo
aac2345a64 [PM-18545] Hide section when no unlock option are available (#4751) 2025-02-20 14:30:39 +00:00
David Perez
61c48bf673 PM-18121, PM-18294: Add, Edit, and View cipher screens require cipher type for top app bar title (#4746) 2025-02-19 14:56:55 -05:00
Phil Cappelli
77b631b021 PM-18292 - Swap "Notes" title with "Additional Options" (#4749) 2025-02-19 14:35:59 -05:00
Phil Cappelli
c59a28a9df PM 18033 - Only show Setup Unlock and Autofill Setup onboarding steps after new account creation (#4748) 2025-02-19 14:35:46 -05:00
Dave Severns
1349165156 PM-18421 Remove adding a folder option from within folder view. (#4747) 2025-02-19 12:55:54 -05:00
Patrick Honkonen
65cc0a0dd8 [PM-3553] Support SimpleLogin self hosted servers (#4723) 2025-02-19 10:20:28 -05:00
Dave Severns
0ba67f5887 PM-18032 Adding a new folder while adding or editing an item. (#4731) 2025-02-18 17:25:51 -05:00
David Perez
87f64d7aba PM-18370: Update space between label and tooltip (#4744) 2025-02-18 15:03:26 -06:00
David Perez
063003b4aa PM-18410: Remove cipher type dropdown from add item screen (#4743) 2025-02-18 14:27:36 -06:00
André Bispo
f2eb524da4 [PM-18281] Change cipher key encryption flag default value to false (#4742) 2025-02-18 20:07:45 +00:00
David Perez
e929ca8a7d PM-18370: Allow selecting type of cipher to add from collection list (#4741) 2025-02-18 10:59:31 -06:00
André Bispo
acc5e30b7a [PM-17882] Cannot select autofill after creating new login (#4728) 2025-02-18 15:13:29 +00:00
David Perez
26d4a397a6 Update to gradle plugin 8.8.1 (#4740) 2025-02-18 08:35:18 -06:00
Álison Fernandes
ba9a0d8884 [PM-17412] Renamed crowdin.yml to crowdin-bwa.yml (#348) 2025-02-18 09:33:05 -05:00
Patrick Honkonen
bb2e98eac6 [PM-14936] Add AnonAddy self-hosted server URL support (#4708) 2025-02-18 08:57:47 -05:00
Álison Fernandes
133d8548e8 [PM-18384] Add placeholder workflows for the Authenticator (#4736) 2025-02-17 22:17:49 +00:00
David Perez
9f19a99eb9 PM-18275: Add totp tooltip on view item screen (#4732) 2025-02-17 15:44:55 -06:00
David Perez
22931bbd38 Improve the error messaging when an enum is not parsed correctly (#4730) 2025-02-17 15:03:05 -06:00
David Perez
359dedc9d7 Fix minor typo (#4734) 2025-02-17 20:37:06 +00:00
Phil Cappelli
be416f87de PM-18292 - Update section headers for all items to align with V3 Design (#4729) 2025-02-17 14:09:51 -05:00
Phil Cappelli
6a3a534304 PM-17566 - Authenticator Sync: In Dark theme, the Action card in the Authenticator is too dark (#347) 2025-02-17 13:42:42 -05:00
Patrick Honkonen
09254a2285 Update Bitwarden SDK and use sdk-android-temp (#346) 2025-02-14 15:44:04 +00:00
aj-rosado
f0d6599eb3 [PM-8223] Add new device verification when no OTP (#4712) 2025-02-14 14:03:15 +00:00
bw-ghapp[bot]
d7a50b092b Autosync Crowdin Translations (#4725) 2025-02-14 09:01:57 -05:00
Dave Severns
9f616b67c9 PM-18123 Update the reset password screen. (#4719) 2025-02-13 16:07:17 -05:00
Dave Severns
1198de2a74 PM-17766 add new strings for ssh keys empty item (#4722) 2025-02-13 16:07:01 -05:00
Patrick Honkonen
3db7b17e3a [PM-3553] Add SimpleLogin self-hosted alias feature flag (#4715) 2025-02-13 12:51:07 -05:00
Álison Fernandes
631be3fca5 [PM-16534] Update gradle invocations to specify app module (#4720)
Co-authored-by: Patrick Honkonen <1883101+SaintPatrck@users.noreply.github.com>
2025-02-12 20:01:02 -05:00
Patrick Honkonen
e8e6040318 Rename CI workflows for Authenticator (#333)
Co-authored-by: Álison Fernandes <vvolkgang@users.noreply.github.com>
2025-02-13 00:12:21 +00:00
Patrick Honkonen
5790397a27 [PM-16534] Update gradle invocations to specify authenticator module (#344) 2025-02-12 23:41:31 +00:00
Álison Fernandes
8b98e8f461 [PM-16534] Update fastfile check lane (#342)
Co-authored-by: Patrick Honkonen <1883101+SaintPatrck@users.noreply.github.com>
2025-02-12 18:21:13 -05:00
Álison Fernandes
f3482bd3e2 [PM-16534] Update test lane name (#343) 2025-02-12 18:18:28 -05:00
Álison Fernandes
14ef7351bd [PM-16534] Fix fastfile typo (#341) 2025-02-12 23:00:34 +00:00
Patrick Honkonen
0820061466 [PM-11886] Update handling of unprivileged apps and improve error messaging (#4694) 2025-02-12 17:52:12 -05:00
Álison Fernandes
c5616b9e38 [PM-16534] Fix Fastfile lanes (#340) 2025-02-12 22:51:13 +00:00
Patrick Honkonen
0538becf7f Update output filenames (#335)
Co-authored-by: Álison Fernandes <vvolkgang@users.noreply.github.com>
2025-02-12 22:31:20 +00:00
Patrick Honkonen
2ae3d5e100 Rename Fastfile lanes to be Authenticator-specific (#334)
Co-authored-by: Álison Fernandes <vvolkgang@users.noreply.github.com>
2025-02-12 17:24:45 -05:00
André Bispo
2aa371a972 [PM-18050] Remove pin policy (#4718) 2025-02-12 22:16:35 +00:00
Patrick Honkonen
72ac8d3699 Rename app package to authenticator (#305)
Co-authored-by: Álison Fernandes <vvolkgang@users.noreply.github.com>
2025-02-12 22:13:07 +00:00
Dave Severns
00f30c922d PM-18058 and PM-18059 Choose which type of vault item to add from Vault screen and inside a Folder. (#4703) 2025-02-12 09:21:15 -05:00
Dave Severns
a68b370cba PM-18083 Ensure segmented buttons on generator fill entire width evenly. (#4713) 2025-02-11 18:08:35 -05:00
Patrick Honkonen
60a6b2a545 [PM-14936] Add AnonAddySelfHostAlias feature flag (#4711) 2025-02-11 18:06:45 -05:00
Patrick Honkonen
2c63c1ab94 [PM-14936] Move prefixHttpsIfNecessaryOrNull to StringExtensions (#4709) 2025-02-11 18:06:17 -05:00
André Bispo
fdc92712c4 [PM-17368] After cut, update text and clear selection. (#4714) 2025-02-11 23:02:08 +00:00
Phil Cappelli
4eb4d5f256 PM-17566 - Authenticator Sync: In Dark theme, the Action card in the Authenticator is too dark (#337) 2025-02-11 11:41:54 -05:00
Álison Fernandes
6fdb39026f [PM-18193] Remove scan.yml from Merge Queue status check and update test.yml report job skip (#4710) 2025-02-10 23:31:01 +00:00
David Perez
ac6c90eb7a PM-18013: Update the View Item screens (#4699) 2025-02-10 13:59:44 -05:00
Phil Cappelli
eb3bc838d6 PM-17838 - Add help button for authenticator key (#4697) 2025-02-10 18:58:26 +00:00
Dave Severns
30e882d7d1 PM-17769 add thinner version of vault icon for the settings (#4704) 2025-02-10 13:58:02 -05:00
Patrick Honkonen
3adc80fdf8 [PM-18082] Force incognito keyboard on input fields (#4700) 2025-02-10 13:38:29 -05:00
bw-ghapp[bot]
e70b21c951 Autosync Crowdin Translations (#4702) 2025-02-10 13:37:58 -05:00
rohm1
571b8368e1 [PM-16157] Support self-host servers using TLS with Client Authentication (mTLS) (#4486) 2025-02-10 18:33:28 +00:00
Patrick Honkonen
fd26472f71 [PM-17405] Configure mutual-tls FlagKey as remotely configured (#4701) 2025-02-10 13:09:16 -05:00
bw-ghapp[bot]
6ffe9df353 Autosync Crowdin Translations (#336) 2025-02-07 09:34:52 -05:00
David Perez
7e0fc07a7c PM-17968: Create unique secret keys per user and handle decoding error (#4696) 2025-02-06 15:38:12 +00:00
David Perez
d27ab1d31a Apply formatter to the entire app (#4698) 2025-02-06 14:52:09 +00:00
Matt Andreko
7b9cbb7bef Enabled SonarQube scanning for PRs (#332) 2025-02-05 13:06:08 -05:00
Dave Severns
a1a3d55656 PM-17848 update copy on generator modal (#4691) 2025-02-05 14:32:33 +00:00
Patrick Honkonen
c672bff18c [PM-17694] Only update FIDO2 user verification status during single-tap sign-in (#4680) 2025-02-05 13:46:52 +00:00
ifernandezdiaz
7ab5972893 QA-1056: Adding testTag to Stepper value text (#4690) 2025-02-05 13:45:45 +00:00
ifernandezdiaz
13aa3251d5 QA-1061: Adding testTags for new WelcomePage (#4695) 2025-02-05 13:45:40 +00:00
Dave Severns
a5f6864512 PM-17847 update text style for add account label (#4693) 2025-02-05 13:11:19 +00:00
David Perez
c3506c1c25 PM-17841: Hide additional options behind expandable section (#4687) 2025-02-04 22:43:27 +00:00
Lucas
1710b563c0 [PM-16862] FIDO2 Community: Remove DivestOS-developed browsers (#4533) 2025-02-04 22:30:18 +00:00
Matt Andreko
01b646afa7 Enabled SonarQube scanning for PRs (#4692) 2025-02-04 20:22:47 +00:00
Álison Fernandes
445bd90f67 [PM-17962] Exclude generated Hilt .java files from code coverage (#4689) 2025-02-04 17:37:38 +00:00
David Perez
de7416d96e PM-17958: Remove language supporting text (#4688) 2025-02-04 17:22:23 +00:00
Álison Fernandes
5e29df5f30 [PM-10515] fix: Missing build information in About screen version copy (#4679) 2025-02-04 15:58:27 +00:00
David Perez
5f48d7bebd PM-17958: Update appearance text (#4686) 2025-02-04 15:52:25 +00:00
Álison Fernandes
b342f21cbb [PM-17939] Restrict test.yml coverage upload to On Push and Pull Request triggers (#4681) 2025-02-03 23:03:55 +00:00
Patrick Honkonen
4b3ec1d918 [PM-17930] Remove default arguments from CoachMarkHighlight (#4677) 2025-02-03 22:18:16 +00:00
Dave Severns
5f52c49a68 PM-17769 Icons added to the settings menu rows (#4673) 2025-02-03 20:18:32 +00:00
David Perez
efe1475fbf PM-17851: Update manual code entry screen (#4676) 2025-02-03 19:40:27 +00:00
Patrick Honkonen
ffe5ddb4a6 [PM-16136] Update Bitwarden SDK (#4675) 2025-02-03 18:38:35 +00:00
Dave Severns
6f82251332 PM-17766 Updated empty states for grouped types and send types. (#4667) 2025-02-03 18:33:40 +00:00
David Perez
76e780f813 PM-17839 PM-17827 PM-17824 PM-17832 PM-17836 PM-17840: VaultAddEditScreen and VaultMoveToOrganizationScreen (#4668) 2025-02-03 17:19:25 +00:00
Patrick Honkonen
161d8517a4 [PM-9535] Show toast when copying values prior to Android 13 (#4654) 2025-02-03 16:59:44 +00:00
Dave Severns
0786ab98a7 PM-17910 Prevent back events from system when coach mark tour is in progress. (#4674) 2025-02-03 16:46:41 +00:00
bw-ghapp[bot]
3d12c98969 Autosync Crowdin Translations (#330) 2025-02-03 11:24:51 -05:00
Phil Cappelli
07fe6e53ea PM-17845 PM-17718 - Enable Remote Configuration for the import flow & Rename Authenticator Sync Feature Flag Name (#4666) 2025-02-03 15:00:14 +00:00
Phil Cappelli
2bbadf8726 BWA-144 - Consolidate feature flags for sync between the Password Manager Authenticator (#331) 2025-02-03 10:00:05 -05:00
Dave Severns
f004e10d41 PM-17801 and PM-17791 updates some string resources related to coach marks and Autofill casing. (#4664) 2025-02-03 14:34:14 +00:00
David Perez
38c9d6cfbc Fix vault favorites spacing (#4665) 2025-01-31 18:15:26 +00:00
David Perez
62d26491d5 Update TOTP coachmark layout (#4663) 2025-01-31 17:52:57 +00:00
Dave Severns
925db01b44 PM-17797 and PM-17798 set onboarding features to be remotely configurable (#4662) 2025-01-31 15:49:56 +00:00
Dave Severns
2b79cc9a17 PM-17765 & PM-17767 Adjust spacing in vault screen and adjust account switcher icon size and minimum row height (#4661) 2025-01-31 15:23:10 +00:00
David Perez
f141465b41 Simplify modifier extensions (#4657) 2025-01-31 15:03:03 +00:00
Phil Cappelli
ecc5d4ce30 PM-16861 - Update Behavior When Tapping Same Generator Tab Already Viewing (#4653) 2025-01-31 15:02:17 +00:00
bw-ghapp[bot]
de558bf94d Autosync Crowdin Translations (#4656)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2025-01-31 00:42:43 +00:00
David Perez
9b3bb32c87 PM-17376, PM-17380, PM-17385: Update card padding and layout (#4652) 2025-01-30 21:21:14 +00:00
Dave Severns
6f5cb0df96 PM-17764 update the email verification to completely match new design. (#4655) 2025-01-30 21:04:11 +00:00
mpbw2
2195d07f78 [PM-13351] Prevent editing of TOTP key in 'can edit except passwords' collection (#4583) 2025-01-30 20:12:28 +00:00
Phil Cappelli
c09b02f1d0 PM-17384 PM-17386 - Create Account Design Audit (#4647) 2025-01-29 21:33:15 +00:00
Dave Severns
76c7f8c41d PM-17388 Update existing and v3 email verification screen to match design audit (#4645) 2025-01-29 20:41:44 +00:00
David Perez
6a1f37b243 PM-17721: Update app dropdown menus (#4646) 2025-01-29 20:26:50 +00:00
Dave Severns
590fc21820 PM-16625 PM-16626 PM-16627 Coach marks 4-6 on generator screen (#4640) 2025-01-29 17:57:56 +00:00
Patrick Honkonen
4575fa96c1 [deps]: Update AndroidX library versions (#324) 2025-01-29 11:31:22 -05:00
David Perez
1a7ddbef7a PM-17205: Check accessibility service status on start up (#4644) 2025-01-29 15:46:07 +00:00
Patrick Honkonen
555d30645f [deps]: Update Hilt (#325) 2025-01-29 09:41:00 -06:00
Patrick Honkonen
80d04fdf9c [deps]: Update ProtoBufJava (#326) 2025-01-29 09:40:20 -06:00
Patrick Honkonen
4ea92223c0 [deps]: Update Google Guava (#327) 2025-01-29 09:22:38 -06:00
Patrick Honkonen
f8e41caaf2 [deps]: Update Firebase BoM (#328) 2025-01-29 09:19:48 -06:00
Patrick Honkonen
3963623407 [deps]: Update JUnit 5 (#323) 2025-01-29 09:18:54 -06:00
renovate[bot]
bbb2d8dc05 [deps]: Update androidx.compose:compose-bom to v2025 (#319) 2025-01-29 10:10:42 -05:00
bw-ghapp[bot]
db035bbd7d Autosync Crowdin Translations (#322) 2025-01-29 09:23:48 -05:00
David Perez
69f33ddca9 PM-17680: Overwrite the expiration date to the deletion date (#4642) 2025-01-28 22:20:47 +00:00
David Perez
2eca28d571 PM-17684: Update the cursor color throughout the app (#4643) 2025-01-28 22:17:30 +00:00
David Perez
d9ac445149 Fix minor formatting issues in Authenticator Bridge Readme (#4641) 2025-01-28 20:55:07 +00:00
aj-rosado
fe06bf48e7 [PM-13626] Remember last opened view for 5 minutes (#4574)
Signed-off-by: Andre Rosado <arosado@bitwarden.com>
Co-authored-by: Dave Severns <dseverns@livefront.com>
2025-01-28 19:41:11 +00:00
Robyn MacCallum
3f1f9983e3 Update SingleTapPasskeyAuthentication and SingleTapPassskeyCreation to be remote flags (#4639) 2025-01-28 18:37:12 +00:00
Dave Severns
a681402956 PM-16622 PM-16623 and PM-16624 Add the first three coach marks to the generator tour (#4613) 2025-01-28 18:33:19 +00:00
Phil Cappelli
3c7262d2b3 PM-17382 - Update “Logging in as…” text and link style on log in screen (#4638) 2025-01-28 17:18:43 +00:00
Dave Severns
7a25aafc23 PM-17650 Implement custom tool tip state to prevent tool tips from dismissing. (#4637) 2025-01-28 15:20:15 +00:00
Dave Severns
b2c4fbb593 Back port Reverts PM-14995 (#4633) (#4635) 2025-01-28 14:49:33 +00:00
David Perez
e2dec1f2fe PM-17638: Add card background for the manual totp screen (#4634) 2025-01-27 22:39:26 +00:00
David Perez
5f1ef71b3b PM-17378: Update remember me text (#4624) 2025-01-24 22:47:39 +00:00
David Perez
b3550bc933 Fix the login test tag (#4625) 2025-01-24 22:43:25 +00:00
David Perez
09b898d655 PM-17377: Update text, formatting, and style for the environment selector (#4623) 2025-01-24 21:34:42 +00:00
Patrick Honkonen
92b92403c6 [PM-17531] Add dialog for client certificate import (#4622) 2025-01-24 21:08:21 +00:00
Phil Cappelli
b84d393208 [PM-17374] [PM-17375] [PM-17379] - LandingScreen Design Audit (#4611) 2025-01-24 21:02:21 +00:00
Patrick Honkonen
464f8de5f5 [PM-17424] Implement KeyManager for handling private keys (#4608) 2025-01-24 20:55:33 +00:00
David Perez
3a6db38172 Update BitwardenPasswordField TestTags (#4621) 2025-01-24 17:08:33 +00:00
Dave Severns
dbefcfe342 PM-16630 PM-16621 Add logins action card and add explore generator card to be able to trigger coach marks (#4616) 2025-01-24 16:19:13 +00:00
bw-ghapp[bot]
e2d2d7fd7d Autosync Crowdin Translations (#4619)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2025-01-24 14:08:06 +00:00
David Perez
31ccf490ae PM-17409: Allow nullable labels text fields (#4617) 2025-01-23 23:07:23 +00:00
David Perez
50ae0902f0 PM-14179: Update internal placement of test tags for the BitwardenTextField (#4612) 2025-01-23 22:10:18 +00:00
David Perez
873f416fec PM-15804, PM-17130: Add logic to monitor when the screen on state to ensure the vault locks properly (#4610) 2025-01-23 21:58:11 +00:00
David Perez
70e72da404 PM-17410: Update password hint font (#4614) 2025-01-23 21:54:07 +00:00
Phil Cappelli
d573ce6e10 PM-17074-PM-17802 - Send Screen Design Updates (#4604) 2025-01-23 17:23:24 +00:00
Patrick Honkonen
e9159cc8c1 [PM-15906] Implement single tap passkey flows (#4547) 2025-01-23 16:38:36 +00:00
David Perez
bf60f8f9e3 PM-17404: Set app delegate on theme change (#4605) 2025-01-22 20:02:18 +00:00
Patrick Honkonen
2787edbf45 [PM-17405] Add mutual TLS feature flag (#4606) 2025-01-22 14:41:57 +00:00
SymphonicDeviation
bb5aeaaf15 [PM-17099] Re-Sort Quetta Browser Alphabetically (#4562) 2025-01-21 21:52:08 +00:00
ifernandezdiaz
f68f44615c [QA-980] Adding missing testTags for Custom fields (#4569) 2025-01-21 21:47:07 +00:00
renovate[bot]
66ff5942e4 [deps]: Update gh minor (#4591)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-21 19:48:48 +00:00
renovate[bot]
a11d8eff4a [deps]: Update kotlin (#309) 2025-01-21 19:46:30 +00:00
Patrick Honkonen
b233505c24 [PM-16391] Update Gradle and Android Gradle Plugin (#321) 2025-01-21 14:41:30 -05:00
renovate[bot]
ad564f98ef [deps]: Update gh minor (#318) 2025-01-21 19:38:39 +00:00
renovate[bot]
d488e7d5dd [deps]: Lock file maintenance (#320) 2025-01-21 14:36:35 -05:00
bw-ghapp[bot]
2d66fb5702 Autosync Crowdin Translations (#317) 2025-01-21 14:19:45 -05:00
Dave Severns
2b94e01c56 PM-16631 Applying CoachMarkContainer to the AddLoginItem content. (#4571) 2025-01-21 16:31:45 +00:00
David Perez
08e51fde98 Update the AndroidX Activity library to 1.10.0 (#4599) 2025-01-20 22:17:00 +00:00
David Perez
e25743e3f0 Update Firebase to the latest version v33.8.0 (#4598) 2025-01-20 20:28:28 +00:00
Phil Cappelli
055c598491 PM-16850-PM-16851-PM-16852 - Updating full screen loading indicator (#4581) 2025-01-20 19:24:55 +00:00
renovate[bot]
a185a94d56 [deps]: Update androidx.compose:compose-bom to v2025 (#4593)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-20 16:02:49 +00:00
renovate[bot]
f1ee9c89c0 [deps]: Lock file maintenance (#4594)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-20 15:03:00 +00:00
renovate[bot]
5b81c4dc7c [deps]: Update org.jetbrains.kotlinx.kover to v0.9.1 (#4592)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-20 15:02:34 +00:00
David Perez
e54d9ab5a9 Remove outer box on EnvironmentSelector (#4577) 2025-01-17 15:07:11 +00:00
David Perez
f9fc61ecf5 Add spacer between type and name when creating a login cipher (#4579) 2025-01-17 15:06:54 +00:00
bw-ghapp[bot]
a0eadc282b Autosync Crowdin Translations (#4580)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2025-01-17 15:05:32 +00:00
David Perez
412649ed9e Add correct card padding to BitwardenHiddenPasswordField (#4576) 2025-01-16 16:52:42 +00:00
André Bispo
be4014962c [PM-16905] Add back button to new device notice (#4570) 2025-01-16 15:05:36 +00:00
David Perez
5840bcdf85 PM-14179: Create and apply card style to UI (#4567) 2025-01-15 22:51:14 +00:00
ifernandezdiaz
2672f426dc [QA-969] Adding missing testTag for Folder Name textfield (#4564) 2025-01-15 18:59:37 +00:00
ifernandezdiaz
20f9421ea4 [QA-968] Adding missing testTag for MP hint email field (#4565) 2025-01-15 18:59:00 +00:00
Álison Fernandes
c1bb58cf17 [PM-17119] Add domains to network config (#4568) 2025-01-15 16:54:20 +00:00
Matt Andreko
f92d748de3 Update SonarQube GitHub Action (#316) 2025-01-14 09:48:46 -05:00
Phil Cappelli
21597ba746 PM-16830 - Update global loading screen component to new reskinned version (#4558) 2025-01-13 19:09:29 +00:00
Patrick Honkonen
efbb959ecc [PM-17011] Move network managers to network package (#4559) 2025-01-13 18:59:21 +00:00
David Perez
b128d5de0a Update AGP to v8.8.0 (#4557) 2025-01-13 16:08:44 +00:00
Álison Fernandes
41d9e96406 [PM-16827] Only report coverage when tests pass (#4550) 2025-01-10 20:20:58 +00:00
renovate[bot]
ac6bb35d76 [deps]: Update gh minor (#4551)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-10 19:26:56 +00:00
Patrick Honkonen
1316882ef4 [BWA-141] Use files instead of file in codecov action (#315) 2025-01-10 10:55:07 -05:00
renovate[bot]
7fdb669786 [deps]: Update sonarsource/sonarcloud-github-action action to v4 (#297) 2025-01-10 15:37:03 +00:00
Phil Cappelli
f595cee7f6 BWA-106 - Import process failures with limited error feedback (#313) 2025-01-10 10:32:34 -05:00
bw-ghapp[bot]
43844739a2 Autosync Crowdin Translations (#314) 2025-01-10 09:54:23 -05:00
SymphonicDeviation
a3096c04f1 [PM-14240] Add Quetta Browser to Privileged Apps (#4189) 2025-01-10 14:49:24 +00:00
Patrick Honkonen
0684011bda [PM-15918] Update bitwarden SDK (#4529) 2025-01-10 14:39:04 +00:00
bw-ghapp[bot]
eef0b1cbbb Autosync Crowdin Translations (#4546)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2025-01-10 14:30:06 +00:00
celenityy
542d2ad1e9 [PM-16870] Add support for IronFox (#4534)
Co-authored-by: Patrick Honkonen <1883101+SaintPatrck@users.noreply.github.com>
2025-01-10 14:24:47 +00:00
André Bispo
e63a549485 [PM-16808] Add question mark to copy (#4544) (#4545) 2025-01-10 09:44:08 +00:00
André Bispo
3e552564dc [PM-16670] Add check for 2fa status #4542 (#4543) 2025-01-09 22:44:06 +00:00
Patrick Honkonen
0a8d1fa0f5 [PM-9439] Use passkey icon for items with FIDO2 credentials in search results (#4541) 2025-01-09 21:10:02 +00:00
David Perez
f2c87d1f66 PM-15356: Resolve biometrics bypass (#4448) 2025-01-09 20:45:14 +00:00
mpbw2
2f2db1a03f [PM-13349] Hide Edit option in cipher list item overflow when editing not permitted (#4539) 2025-01-09 20:29:08 +00:00
Álison Fernandes
0493710cb4 [PM-16827] Fix test.yml sdk package access and refactor test jobs (#4538) 2025-01-09 20:17:04 +00:00
aj-rosado
b5d73c98fe [PM-16695] Learn more new device verification (#4527)
Co-authored-by: André Bispo <abispo@bitwarden.com>
2025-01-09 14:40:19 +00:00
André Bispo
6b6e95aa3f [PM-16670] Force app to sync after 2FA notice (#4525) (#4536) 2025-01-09 00:25:38 +00:00
André Bispo
f35ee76c95 [PM-16809] Fix remind me later date (#4526) (#4535) 2025-01-08 22:57:10 +00:00
David Perez
bb66150b5c PM-14179: Update generator screen copy button (#4530) 2025-01-08 19:14:00 +00:00
Patrick Honkonen
9c2a902b51 [PM-16120] Defer passkey authentication until vault data is loaded (#4524) 2025-01-07 21:00:49 +00:00
renovate[bot]
69da467b7c [deps]: Lock file maintenance (#4502)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-07 20:58:20 +00:00
renovate[bot]
c8d3f341a8 [deps]: Update gh minor (#4496)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Patrick Honkonen <1883101+SaintPatrck@users.noreply.github.com>
2025-01-07 20:26:31 +00:00
David Perez
3717e33d00 PM-16821: remove padding on right side of the vault screen dividers (#4528) 2025-01-07 16:54:22 +00:00
Dave Severns
7ab72543a3 PM-14333 fix case of crowdin translation not adding annotations on string with format args (#4505) 2025-01-07 15:30:34 +00:00
Dave Severns
80f31cdff9 PM-16474 Adding custom field issues when another text field holds focus (#4511) 2025-01-07 14:52:12 +00:00
bw-ghapp[bot]
40056f3181 Autosync Crowdin Translations (#306) 2025-01-07 13:58:44 +00:00
renovate[bot]
b0e9703d9f [deps]: Update kotlin (#4501)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-06 22:03:10 +00:00
Phil Cappelli
0ca97de1b2 BWA-118 Tutorial text cut off in landscape mode (#312) 2025-01-06 14:55:21 -05:00
mpbw2
b958734946 [PM-13349] Hide edit button unless item is in at least one non-readOnly collection (#4430) 2025-01-06 17:59:30 +00:00
renovate[bot]
2acede98f3 [deps]: Lock file maintenance (#310) 2025-01-06 17:57:59 +00:00
renovate[bot]
fb2edf43be [deps]: Update gh minor (#307) 2025-01-06 17:56:49 +00:00
bw-ghapp[bot]
484faedcc5 Autosync Crowdin Translations (#4503)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
Co-authored-by: Patrick Honkonen <1883101+SaintPatrck@users.noreply.github.com>
2025-01-06 17:31:37 +00:00
André Bispo
8bde8741dd [PM-8217] Add local feature flag to ignore environment validation (#4521) 2025-01-06 15:32:53 +00:00
André Bispo
d5a02e6285 [PM-15969] Users with Can Edit access cannot assign collections (#4522) 2025-01-06 15:11:16 +00:00
André Bispo
a35ec8cf3c [PM-8217] New device two factor notice (#4508)
Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>
2024-12-27 15:03:33 +00:00
Dave Severns
ae8db9256c Update the text field to not use passed in modifier. (#4506) 2024-12-23 20:51:11 +00:00
André Bispo
688dd3a39b [PM-8217] Add creationDate and isTwoFactorEnable properties (#4504) 2024-12-23 18:56:55 +00:00
Dave Severns
6223f362c3 PM-16062 Prevent account locks for ongoing autofill requests (#4498) 2024-12-20 22:05:30 +00:00
Dave Severns
1148e4821c PM-14333 Complete fix for crash caused by spannable text creation (#4479) 2024-12-20 21:45:55 +00:00
Patrick Honkonen
f32eecc0d7 [PM-15864] Add copy private key action for SSH keys (#4462) 2024-12-20 19:44:31 +00:00
renovate[bot]
2ba516f50f [deps]: Lock file maintenance (#4497)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-20 19:20:14 +00:00
André Bispo
c4c7af54ff [PM-8217] New device notice two factor UI (#4401) 2024-12-20 18:08:23 +00:00
Patrick Honkonen
ae0bf6318d [PM-15176] Update script path for CI build info (#4493) 2024-12-20 18:06:56 +00:00
Patrick Honkonen
3be2242431 [PM-15643] Show FAB in empty item type filters (#4490) 2024-12-20 17:52:04 +00:00
Patrick Honkonen
d9c0911238 [PM-12391] Respect PIN unlock setting during FIDO user verification (#4483) 2024-12-20 17:51:29 +00:00
Patrick Honkonen
5aa8369ac5 [PM-15863] Request master password before revealing private SSH key (#4481) 2024-12-20 17:48:01 +00:00
Patrick Honkonen
35e8cecdcf [PM-15970] Allow assigning collections if user has correct permissions (#4461) 2024-12-20 17:33:09 +00:00
André Bispo
a7939414ae [PM-8217] New device notice email access UI (#4400) 2024-12-20 16:53:30 +00:00
Dave Severns
6c355ae5b7 PM-15383 PM-15381 - Show the google play review prompt (#4455)
Co-authored-by: Patrick Honkonen <1883101+SaintPatrck@users.noreply.github.com>
2024-12-20 15:30:39 +00:00
Álison Fernandes
843247b02d [PM-16211] chore(ci): Fix hotfix branch creation workflow by retrieving the last tag across all branches (#4491) 2024-12-20 14:30:46 +00:00
Patrick Honkonen
efbb8446e3 [PM-15057] Update AndroidX Credentials to 1.5.0-alpha04 (#4447) 2024-12-20 14:27:41 +00:00
bw-ghapp[bot]
a279a2b1a3 Autosync Crowdin Translations (#4494)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-12-20 14:19:32 +00:00
Lucas
3329dfaf20 [PM-15912] Fix alphabetical order in FIDO2 privileged browser community list (#4451) 2024-12-19 18:22:07 +00:00
Álison Fernandes
b615bfa664 [PM-16208] chore(ci): Split scan workflow for protected branches and migrate to new sonarqube action (#4489) 2024-12-18 23:49:41 +00:00
Álison Fernandes
f6bd467ec8 [PM-16207] chore(ci): Fix codecov usage and remove secrets from test.yml (#4488) 2024-12-18 21:48:12 +00:00
Dave Severns
8548c74d58 PM-16058 add test tag parameter to be applied to the text field (#4487) 2024-12-18 18:01:40 +00:00
Dave Severns
e2b93ec08c Add Phil to CODEOWNERS (#4480) 2024-12-17 19:07:27 +00:00
Dave Severns
e565a5a118 PM-15890 TLS related error propagation (#4454) 2024-12-17 17:56:29 +00:00
aj-rosado
3a41138f39 [PM-10515] CI build info on version copy (#4456)
Co-authored-by: Álison Fernandes <vvolkgang@users.noreply.github.com>
2024-12-17 17:36:46 +00:00
Dave Severns
889457ae96 PM-15037 Correct the text for the confirm error dialog on import logins screen (#4478) 2024-12-16 18:27:06 +00:00
Patrick Honkonen
be88cdf42e [PM-15176] Rename bundle and apk files to match applicationId and flavor (#4474) 2024-12-14 14:36:01 +00:00
David Perez
e37cefeb1d PM-16058 - Add default environments via autocomplete dropdown (#4473) 2024-12-13 23:04:02 +00:00
Phil Cappelli
ae20e55b1a PM-16053 - Text in Prompt to Restart App After Changing Language in Settings (#4472) 2024-12-13 22:26:00 +00:00
Patrick Honkonen
bd29e1738c [PM-16052] Add CI_INFO build config field (#4471) 2024-12-13 20:49:09 +00:00
David Perez
f28f5ee688 Update camera libraries (#4468) 2024-12-13 20:39:13 +00:00
David Perez
c92c334e03 Update the Firebase libraries (#4469) 2024-12-13 19:36:05 +00:00
David Perez
fb8f260a94 Update the Compose BOM (#4470) 2024-12-13 19:17:03 +00:00
Patrick Honkonen
9635bd9b43 [BWA-33]: Publish release bundles to Play Store when requested (#303) 2024-12-13 13:36:54 -05:00
Álison Fernandes
9f5e97b8c9 [PM-15176] chore(ci): Fix fastlane build artifacts names and filepaths (#4458) 2024-12-13 18:25:42 +00:00
bw-ghapp[bot]
8dd9ba65d0 Autosync Crowdin Translations (#302) 2024-12-13 12:48:37 -05:00
Patrick Honkonen
1aec94ee7d [PM-15176] Rename AAB outputs to match APK naming convention (#4467) 2024-12-13 16:43:49 +00:00
David Perez
d4b153107a Update to Hilt 2.53.1 (#4466) 2024-12-13 16:34:59 +00:00
Phil Cappelli
d405a0d04b PM-15976 - App crashes when non-english language user tries to create account (#4460) 2024-12-13 16:24:35 +00:00
David Perez
86587258c9 Remove unused google-services.json.enc (#4465) 2024-12-13 15:57:42 +00:00
bw-ghapp[bot]
7571d35fb3 Autosync Crowdin Translations (#4463)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-12-13 14:50:33 +00:00
André Bispo
c7bef56639 [PM-15553] Add remote flag to control cipher key encryption. (#4457) 2024-12-12 22:08:18 +00:00
Lucas
d5a75c2d53 [PM-15911] Add Firefox Nightly to FIDO2 community list (#4450) 2024-12-12 16:53:05 +00:00
Phil Cappelli
ad90785c39 BWA-124 - 'Copy' Option Missing from Long-Press Menu (#300) 2024-12-12 00:28:59 -05:00
Patrick Honkonen
e11b0c7839 [PM-15862] Remove Linked Fields option from SSH keys (#4453) 2024-12-12 00:11:12 +00:00
David Perez
a6929f2d8d Ensure DebugTree is only planted once (#4452) 2024-12-11 21:33:01 +00:00
Dave Severns
9b064eea90 PM-15380 Track user interactions which would trigger a potential showing of the app review prompt. (#4415) 2024-12-11 18:32:15 +00:00
Patrick Honkonen
d9ef87e21f [PM-15609] Move FIDO2 origin validation logic to Fido2OriginManager (#4426) 2024-12-11 18:17:51 +00:00
Patrick Honkonen
7b3ad98698 [PM-15176] Update build output filenames (#4446) 2024-12-11 17:20:40 +00:00
Patrick Honkonen
ea1a4e4710 Update CODEOWNERS (#301) 2024-12-11 10:31:45 -05:00
Phil Cappelli
2f678ba32e BWA-118 - Tutorial text cut off in landscape mode (#299) 2024-12-11 10:13:51 -05:00
David Perez
c00cdc7407 Run formatter on the app (#4444) 2024-12-09 22:21:49 +00:00
renovate[bot]
4bab5a59fc [deps]: Update sonarsource/sonarcloud-github-action action to v4 (#4434)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-09 21:47:32 +00:00
Dave Severns
4932353003 PM-15037 Add missing title to empty sync import logins error dialog (#4443) 2024-12-09 19:31:38 +00:00
David Perez
5997579330 PM-15599: Allow for custom TextToolbars (#4440) 2024-12-09 19:22:44 +00:00
renovate[bot]
7abb52b42d [deps]: Lock file maintenance (#4435)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-09 18:36:49 +00:00
renovate[bot]
5b0b3b6c70 [deps]: Update gradle minor (#295) 2024-12-09 13:35:35 -05:00
renovate[bot]
65d361625e [deps]: Lock file maintenance (#298) 2024-12-09 18:33:21 +00:00
renovate[bot]
8d09d9d604 [deps]: Update gh minor (#296) 2024-12-09 13:32:25 -05:00
Andrew Haisting
41cf116e6d BITAU-217 Update "Move to Bitwarden" copy to "Copy to Bitwarden" (#283) 2024-12-09 18:31:30 +00:00
Phil Cappelli
ddfd9bd0d8 PM-15831 - Enable remote configuration of enable-authenticator-sync-android feature flag (#4441) 2024-12-09 18:19:25 +00:00
renovate[bot]
5abdf1e4b0 [deps]: Update gh minor (#4433)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-09 14:55:41 +00:00
aj-rosado
4234008337 [PM-11139] Setting icon on passkeys (#4409) 2024-12-09 12:51:40 +00:00
Dave Severns
7b7c2da67c PM-15624 Align handling of no network states with iOS app. (#4431) 2024-12-06 22:24:38 +00:00
Patrick Honkonen
12dd865cec [PM-15116] Add common fields to SSH Key add/edit screen (#4428) 2024-12-06 21:49:16 +00:00
github-actions[bot]
ff09bdd110 Autosync Crowdin Translations (#293) 2024-12-06 12:04:08 -05:00
Andy Pixley
ec788f2472 [BRE-471] Update to use app generated token (#294) 2024-12-06 11:57:33 -05:00
bw-ghapp[bot]
0c53fa6c0b Autosync Crowdin Translations (#4427)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-12-06 14:28:32 +00:00
aj-rosado
b2eae2c33f [PM-13513] Keeping "androidapp" scheme on uri when saving from Android Apps (#4420) 2024-12-06 10:14:53 +00:00
Patrick Honkonen
7da1e48aa5 [PM-15057] Rename toFido2RequestOrNull to toFido2CreateRequestOrNull (#4425) 2024-12-05 18:20:29 +00:00
Álison Fernandes
10b6f533b2 [PM-9328] Mobile team owns changes to the .github folder (#4423) 2024-12-05 17:54:23 +00:00
David Perez
6a77dbe8b5 PM-15599: Update copy toast to not display copied value (#4424) 2024-12-05 17:00:53 +00:00
Álison Fernandes
6f7aedbcd2 [PM-15583] chore: Adds Autofill failure report form to GitHub issues menu (#4422) 2024-12-05 16:45:02 +00:00
Dave Severns
97285f463e PM-15514 add feature flag key for app review prompt (#4414) 2024-12-03 20:49:37 +00:00
renovate[bot]
6e643cb43b [deps]: Update kotlin (#285) 2024-12-03 12:41:34 -05:00
renovate[bot]
6cb734fb94 [deps]: Update gradle minor (#286) 2024-12-03 11:52:32 -05:00
Phil Cappelli
df846374f5 PM-15147 - MasterPasswordGuidanceScreen PR Cleanup (#4411) 2024-12-03 16:35:15 +00:00
Patrick Honkonen
65ff843ada [PM-15057] Add utility for loading FIDO2 icons (#4371) 2024-12-03 16:04:08 +00:00
renovate[bot]
26a7876525 [deps]: Update org.sonarqube to v6 (#4381)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-03 16:03:12 +00:00
renovate[bot]
718cece22e [deps]: Update kotlin (#4378)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: David Perez <david@livefront.com>
2024-12-03 15:37:26 +00:00
github-actions[bot]
00ce3c038b Autosync Crowdin Translations (#282) 2024-12-03 15:30:29 +00:00
renovate[bot]
d4e7211156 [deps]: Update org.sonarqube to v6 (#288) 2024-12-03 15:29:53 +00:00
renovate[bot]
1b3e254a3f [deps]: Lock file maintenance (#289) 2024-12-03 10:28:15 -05:00
renovate[bot]
52748ba66f [deps]: Update codecov/codecov-action action to v5 (#287) 2024-12-03 10:26:28 -05:00
André Bispo
86057b5923 [BWA-110] Retrofit ProGuard missing rule (#291) 2024-12-03 15:24:47 +00:00
Dave Severns
ef8223bd8b PM-15431 allow background activities to start by NFC manager for the … (#4410) 2024-12-03 15:24:36 +00:00
renovate[bot]
7d1e2fec90 [deps]: Update gh minor (#284) 2024-12-03 15:23:04 +00:00
David Perez
382597f356 Update Dagger Hilt library (#4406) 2024-12-02 23:29:19 +00:00
Phil Cappelli
45200d0480 PM-15067 - Replace "account" with "vault" in subtitle (#4402) 2024-12-02 22:25:53 +00:00
David Perez
1534fb598b Update to latest AGP (#4404) 2024-12-02 20:33:03 +00:00
Dave Severns
819cc625a1 PM-14995 Hide TOTP for non premium org items even if individual user has premium account (#4390) 2024-12-02 20:06:11 +00:00
Patrick Honkonen
7e82b6e400 [PM-15116] Add common vault item content to SSH keys (#4365) 2024-12-02 19:51:59 +00:00
renovate[bot]
02c44f514a [deps]: Update codecov/codecov-action action to v5 (#4380)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-02 19:51:08 +00:00
renovate[bot]
6ef5d4b6aa [deps]: Lock file maintenance (#4382)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-02 19:47:15 +00:00
David Perez
cb8c1341e2 Update to Robolectric 4.14.1 (#4403) 2024-12-02 19:46:23 +00:00
Phil Cappelli
b2391dd66a PM-15147 - Design Audit - Master Password Guidance Screen (#4383) 2024-12-02 19:38:45 +00:00
bw-ghapp[bot]
bca9f5e859 Autosync Crowdin Translations (#4396)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-12-02 16:49:41 +00:00
renovate[bot]
b654ef1b43 [deps]: Update gh minor (#4379)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-02 16:48:58 +00:00
Opeyemi
348abcefe9 [BRE-443] - Fix bwwl Linting pre Deployment (#4384) 2024-11-28 11:28:19 +00:00
Dave Severns
a96fcd944e PM-15412 Pull-to-refresh operations should not invoke a forced sync. (#4388) 2024-11-26 21:00:26 +00:00
David Perez
05aa52b032 Remove unused lastDatabaseSchemeChangeInstant from settings disk source (#4374) 2024-11-25 22:59:17 +00:00
David Perez
cce9befe8c Ensure lastSyncTime is updated before resyncing the vault (#4375) 2024-11-25 15:03:58 +00:00
David Perez
8e7ec7af4c PM-15177: Improve destructive fallback logic (#4373) 2024-11-22 22:21:15 +00:00
Dave Severns
2e1845887c PM-15022 Auto login when user completes a YubiKey login trigger. (#4368) 2024-11-22 18:01:49 +00:00
David Perez
c1be5be188 M-15177: All user input syncs should be forced (#4369) 2024-11-22 17:40:40 +00:00
Patrick Honkonen
76b6853f90 [PM-15113] Disable add button in SSH Keys screen (#4364) 2024-11-22 17:31:47 +00:00
Patrick Honkonen
89935ac42b [PM-15054] Add API for importing ciphers (#4339) 2024-11-22 17:30:59 +00:00
Phil Cappelli
050b3b3007 PM-15067 - Design Audit - Prevent Account Lockout Screen (#4361) 2024-11-22 17:14:18 +00:00
Patrick Honkonen
249dbdaaf8 [PM-15057] Rename Fido2CredentialRequest to Fido2CreateCredentialRequest (#4362) 2024-11-22 15:49:00 +00:00
Patrick Honkonen
dbb006d745 [PM-15064] Add feature flags for CXP import and export (#4337) 2024-11-22 15:22:14 +00:00
Patrick Honkonen
5d4197076c [PM-15050] Track vault registration for CXP export in settings (#4335) 2024-11-22 15:10:00 +00:00
Dave Severns
1e223b1a2a PM-15109 only accept numeric values for account pin lock value (#4359) 2024-11-22 13:59:01 +00:00
bw-ghapp[bot]
b19e7e1495 Autosync Crowdin Translations (#4363)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-11-22 01:54:52 +00:00
David Perez
1ef7e2173b Remove the BasicDialogState (#4360) 2024-11-21 21:19:12 +00:00
David Perez
bef6ffc094 Remove unused constant (#4358) 2024-11-21 20:50:37 +00:00
Dave Severns
06b08d595b Refactor usage of the ContentCard to always use the ContentBlock component. (#4357) 2024-11-21 20:27:18 +00:00
David Perez
4fb031d76c Simplify the usage of the BitwardenLoadingDialog (#4356) 2024-11-21 20:16:02 +00:00
David Perez
c4f13fc8bd Share the environment flow and provide better default (#4355) 2024-11-21 20:15:44 +00:00
David Perez
8a534d11d2 Simplify url check in BaseUrlInterceptor (#4354) 2024-11-21 20:15:24 +00:00
David Perez
57ea58fc3c Log vault deserialization errors (#4353) 2024-11-21 19:35:57 +00:00
David Perez
245fcd7502 PM-14963: Add toast when login via device succeeds (#4351) 2024-11-21 15:42:24 +00:00
Dave Severns
dbeb00ba1c PM-15036 Show visual feedback for the send code on export vault. (#4346) 2024-11-21 15:25:44 +00:00
David Perez
cbfd7ad1b1 Simplify the usage of basic dialogs (#4347) 2024-11-21 15:23:40 +00:00
Álison Fernandes
d4033a7705 [PM-11598] GitHub Release - Improve tag name and refactor inputs casing (#4349) 2024-11-21 12:52:59 +00:00
David Perez
96bd25eae5 PM-12733: Add error dialog to be displayed if TOTP code is blank (#4345) 2024-11-20 22:00:08 +00:00
Dave Severns
ec8e934bf4 PM-15062 Checking if the user has a no longer supported biometric as their only way of unlocking their account. (#4338) 2024-11-20 20:45:26 +00:00
David Perez
3092ba1fc6 PM-15110: Ensure all network requests always use the current environment data (#4344) 2024-11-20 19:36:43 +00:00
David Perez
5ea17700b3 PM-15025: Update sendVerificationEmail to handle error responses (#4336) 2024-11-19 20:03:07 +00:00
aj-rosado
d418444dc0 [PM-13831] Add copy button identity and note fields (#4302) 2024-11-19 16:31:00 +00:00
Dave Severns
531b003347 PM-15049 PW strength indicator design audit (#4334) 2024-11-19 15:16:27 +00:00
Dave Severns
dca88a58e7 PM-15037 Update Import Logins for design audit (#4333) 2024-11-19 15:16:16 +00:00
David Perez
da878a9fab PM-15041: Update stepper buttons (#4330) 2024-11-19 15:04:41 +00:00
David Perez
95552a7a55 PM-15040: Update Login screen button icons (#4329) 2024-11-19 15:04:16 +00:00
David Perez
90b638eff0 PM-15039: Update welcome screen for design audit (#4328) 2024-11-19 15:03:51 +00:00
David Perez
ccd4fd9aba PM-15038: Update custom switches to use standard component (#4327) 2024-11-19 15:03:23 +00:00
github-actions[bot]
5e94b1b689 Autosync Crowdin Translations (#277) 2024-11-18 22:43:19 +00:00
André Bispo
f3f51cf244 [BWA-86] Debug Menu #4 (#276) 2024-11-18 22:39:03 +00:00
André Bispo
5f109b5085 [BWA-86] Debug Menu #3 - feature flag service (#275) 2024-11-18 22:24:06 +00:00
David Perez
2d15c4864f Log JWT parsing errors (#4326) 2024-11-18 22:13:55 +00:00
ifernandezdiaz
b183f7af42 QA-999: Adding testTags for account switching options (#4324) 2024-11-18 22:03:19 +00:00
Dave Severns
2ece0856d4 PM-12761 Talkback UI Focus misalignment bug. (#4325) 2024-11-18 21:41:50 +00:00
David Perez
429c76ce03 PM-14200: Add count to sends type header (#4323) 2024-11-18 21:35:38 +00:00
André Bispo
a0cc8a8a3d [BWA-86] Debug Menu #2 - config service (#274) 2024-11-18 21:31:25 +00:00
André Bispo
5b53b50b01 [BWA-86] Debug Menu #1 - network layer (#272) 2024-11-18 21:11:09 +00:00
bw-ghapp[bot]
506d0f13c7 Autosync Crowdin Translations (#4322)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-11-18 18:22:19 +00:00
Vince Grassia
6a0a7d70bc BRE-438 - Update Crowdin workflow to use app token (#4321) 2024-11-18 17:52:04 +00:00
David Perez
3742940290 Update the Firebase BOM (#4317) 2024-11-18 15:06:08 +00:00
David Perez
1cb647b7ae Update compose BOM to 2024.11.0 (#4316) 2024-11-18 15:05:51 +00:00
David Perez
ffeae93728 PM-12733: Trim totp codes before saving them (#4315) 2024-11-18 15:05:00 +00:00
Patrick Honkonen
e90bd136f6 [PM-10483] Fix collection manage check for delete permission (#4313) 2024-11-18 14:26:58 +00:00
David Perez
30eb11b85e PM-14409: Add realtime check for when the accessibility service is enabled or disabled (#4314) 2024-11-15 22:15:19 +00:00
github-actions[bot]
a04598c77a Autosync Crowdin Translations (#4307)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-11-15 20:32:21 +00:00
David Perez
cad2df79b6 PM-14934: Allow accessibility autofill to fill just a username or just a password (#4312) 2024-11-15 20:15:02 +00:00
David Perez
089136552b PM-12259: Use validatePin SDK to validate the users pin (#4311) 2024-11-15 19:56:55 +00:00
Dave Severns
1b0bc13903 Fix typos in generator actions (#4310) 2024-11-15 16:32:09 +00:00
Patrick Honkonen
d125fab0b7 [PM-14843] Allow deletion of items in collections with manage permission (#4299) 2024-11-15 16:10:32 +00:00
Dave Severns
13210343db PM-14429 Set the min and max range of the slider to match the restrictions not update the min with the computed min. (#4305) 2024-11-14 20:36:01 +00:00
Patrick Honkonen
3c4ac8b01a [PM-14596] Sync on database scheme change (#4304) 2024-11-14 19:18:25 +00:00
Patrick Honkonen
0a888a72c8 [PM-14553] Make canManage property of collections optional (#4284) 2024-11-14 19:18:04 +00:00
André Bispo
40f33dff89 [PM-11304] Ownership Not Defaulting To Org and Collection (#4254) 2024-11-14 08:15:01 +00:00
Álison Fernandes
5938e38070 [PM-11598] GitHub Release Workflow (#4285) 2024-11-13 22:34:36 +00:00
Álison Fernandes
31bc171d6b [PM-14879] Release Branch creation workflow (#4294) 2024-11-13 22:23:48 +00:00
David Perez
0967234ad8 PM-14411: Autofill logic to work better with QuickTile (#4300) 2024-11-13 21:38:08 +00:00
David Perez
911c9e4704 Update androidx dependecies and target API (#4212) 2024-11-13 16:22:55 +00:00
Dave Severns
072c3a992c PM-14414 hides autofill card for all users if autofill service is enabled. (#4297) 2024-11-13 15:41:14 +00:00
Álison Fernandes
1e0e4831b8 [PM-14897] Enhance build.yml run summary and fix f-droid distribution (#4296) 2024-11-13 13:27:56 +00:00
David Perez
e804dbd48e PM-14851: Blank names should be considered null (#4292) 2024-11-12 21:30:07 +00:00
Dave Severns
9a5aa217e6 PM-14352 Dismiss Snackbar when user clicks it as a default unless the specific dismiss action is present. (#4291) 2024-11-12 18:39:30 +00:00
Andrew Haisting
c6beaec102 BITAU-200 Log non-fatal authenticator bridge errors (#4228)
Co-authored-by: Patrick Honkonen <phonkonen@bitwarden.com>
2024-11-12 16:15:21 +00:00
renovate[bot]
5a9944f79d [deps]: Update gh minor (#4279)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-12 15:52:20 +00:00
David Perez
89c267aa5d PM-14854: Keep NetworkResult to avoid obfuscation crash in release (#4289) 2024-11-12 15:38:49 +00:00
David Perez
f33296b44f PM-14805: Use network result in all Retrofit API requests (#4286) 2024-11-11 22:17:57 +00:00
David Perez
a8416b073e Improve accessibility autofill performance (#4276) 2024-11-11 21:08:05 +00:00
Dave Severns
fd4a7c5716 PM-14597 remove notification if device login is decleined (#4256) 2024-11-11 21:02:35 +00:00
David Perez
771e719963 PM-14805: Ensure results cannot be double wrapped from 'asSuccess' (#4283) 2024-11-11 20:46:55 +00:00
renovate[bot]
abbf6565d4 [deps]: Update gradle minor (#280) 2024-11-11 19:48:34 +00:00
renovate[bot]
5263329ab5 [deps]: Lock file maintenance (#281) 2024-11-11 14:28:49 -05:00
renovate[bot]
c3d2c17830 [deps]: Update kotlin (#279) 2024-11-11 14:27:56 -05:00
renovate[bot]
29213421ec [deps]: Update gh minor (#278) 2024-11-11 14:27:07 -05:00
Patrick Honkonen
c5293715e1 [PM-14526] Add JsonNames annotation to SyncResponseJson (#4269)
Co-authored-by: David Perez <david@livefront.com>
2024-11-11 17:56:27 +00:00
Patrick Honkonen
2c40a7f105 [PM-14589] Prevent SSH key item creation (#4251) 2024-11-11 16:52:53 +00:00
André Bispo
a3ed2bc068 [PM-11303] Add button missing for folders (#4250) 2024-11-11 16:26:04 +00:00
David Perez
16cc70f344 Clean up the generator screen and handlers (#4270) 2024-11-11 16:07:16 +00:00
Dave Severns
6dd783051f PM-13803 Check to see if an existing admin request is pending before … (#4271) 2024-11-11 15:53:11 +00:00
renovate[bot]
1bb85d0fa0 [deps]: Update com.google.devtools.ksp to v2.0.21-1.0.27 (#4278)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-11 15:51:29 +00:00
Dave Severns
dfeb87be10 PM-13988 Hide the action card if the user makes a selection but does not click continue on setup unlock (#4249) 2024-11-11 15:31:21 +00:00
renovate[bot]
dae50a7b88 [deps]: Lock file maintenance (#4280)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-11 15:19:22 +00:00
David Perez
63324fcec1 PM-14458: Fix notifications prompt on first use (#4275) 2024-11-08 23:02:58 +00:00
Patrick Honkonen
49642f5a1d [PM-14656] Add default value to BaseEnumeratedIntSerializer (#4272) 2024-11-08 22:21:16 +00:00
David Perez
016d0f889c PM-14411: Allow accessibility autofill to run when app is already in background (#4255) 2024-11-08 21:30:14 +00:00
Andrew Haisting
5184be0e98 BITAU-185 Show "Local codes" header on item list screen (#269) 2024-11-08 15:30:10 -06:00
Andrew Haisting
0643ecc00b BITAU-195 Allow issuer to be null for shared items (#268) 2024-11-08 15:03:19 -06:00
Andrew Haisting
7e5dcd3814 BITAU-197 Update copy in all Save to Bitwarden scenarios (#271) 2024-11-08 14:47:37 -06:00
Patrick Honkonen
fe84feb184 PM-14433: Null domain data (#4268)
Co-authored-by: Dave Severns <149429124+dseverns-livefront@users.noreply.github.com>
Co-authored-by: David Perez <david@livefront.com>
2024-11-08 20:18:18 +00:00
aj-rosado
54d3b34876 [PM-11753] Listening to vaultUnlock state on mutableCiphers, folders, collections and send state flow (#4214) 2024-11-08 18:33:49 +00:00
Patrick Honkonen
b6dfc3d17b PM-14433 update flow type to nullable so we can handle gracefully and avoid crash (#4263)
Co-authored-by: Dave Severns <149429124+dseverns-livefront@users.noreply.github.com>
2024-11-08 18:23:39 +00:00
github-actions[bot]
96c6b9c214 Autosync Crowdin Translations (#4260)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
Co-authored-by: Patrick Honkonen <1883101+SaintPatrck@users.noreply.github.com>
2024-11-08 17:47:22 +00:00
David Perez
27666c193e PM-14644: Segmented control should be conditionally displayed for modal generator screen (#4262) 2024-11-08 17:03:54 +00:00
Dave Severns
b76f7202a4 PM-14621 update the copy for step three instruction and cta button (#4259) 2024-11-08 14:36:54 +00:00
Patrick Honkonen
7ccba88780 [PM-13360] Respect manage permission to assign collections (#4190) 2024-11-07 20:47:22 +00:00
Patrick Honkonen
87d324b063 [PM-12922] Disable delete if user can't manage collection (#4179) 2024-11-06 23:42:06 +00:00
Dave Severns
e397c036e4 PM-14353 : Clean up consumed snackbar on quick resubmission due to state based nav. (#4235) 2024-11-06 19:39:55 +00:00
David Perez
29384596d4 PM-14410: App restart timeout action (#4237) 2024-11-06 17:40:54 +00:00
github-actions[bot]
88a741c93a Autosync Crowdin Translations (#4217)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
Co-authored-by: Patrick Honkonen <1883101+SaintPatrck@users.noreply.github.com>
2024-11-06 17:26:48 +00:00
David Perez
db3490f61a PM-14480: Update IntentManager to be able to launch apps (#4233) 2024-11-05 17:36:10 +00:00
David Perez
4930c1032e PM-14458: Update notifications permissions request (#4229) 2024-11-05 17:16:58 +00:00
Dave Severns
202b4de5ca PM-13848 Handle URIs with ports and host matching (#4203) 2024-11-05 15:29:05 +00:00
Andrew Haisting
6763bfd997 BITAU-198 Update some copy on the manual entry screen (#273) 2024-11-05 08:54:59 -06:00
github-actions[bot]
aeba03a769 Autosync Crowdin Translations (#267)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-11-05 13:52:59 +00:00
Andrew Haisting
8f9585e4bc Bump authenticatorbridge sdk version to 1.0.0 (#4221) 2024-11-04 14:24:36 +00:00
André Bispo
e5e0464929 [PM-12406] Introduce new endpoint and replace SSO details response flow (#4177) 2024-11-04 10:53:57 +00:00
Andrew Haisting
7f25fa07a4 Use authenticatorbridge version 1.0.0 (#270) 2024-11-01 16:41:40 -05:00
David Perez
c2537f329d PM-14036: Add extra slider padding (#4220) 2024-11-01 19:12:55 +00:00
ifernandezdiaz
b7ffa3966d QA-970: Adding testTags for radiobutton and floating options elements (#4188)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Dave Severns <149429124+dseverns-livefront@users.noreply.github.com>
Co-authored-by: David Perez <david@livefront.com>
Co-authored-by: Patrick Honkonen <1883101+SaintPatrck@users.noreply.github.com>
Co-authored-by: Álison Fernandes <vvolkgang@users.noreply.github.com>
2024-11-01 19:03:44 +00:00
David Perez
9240fb82e4 PM-14379: Stop storing 'null' in as the word separator (#4219) 2024-11-01 19:03:03 +00:00
David Perez
2eb41e932b PM-14044: Fix line-breaking logic (#4218) 2024-11-01 16:21:48 +00:00
David Perez
51e299998f Update to AGP 8.7.2 (#4216) 2024-11-01 15:19:03 +00:00
Andrew Haisting
8b00773c84 BITAU-193 Make first time sync snackbar correct in dark theme (#266) 2024-10-31 16:54:58 -05:00
Andrew Haisting
b42ec0ae13 BITAU-89 Have SettingsViewModel obsererve DefaultSaveOption (#265)
Co-authored-by: Patrick Honkonen <1883101+SaintPatrck@users.noreply.github.com>
2024-10-31 21:33:02 +00:00
Andrew Haisting
78d7865207 BITAU-178 Show shared codes on the search screen (#264)
Co-authored-by: Patrick Honkonen <1883101+SaintPatrck@users.noreply.github.com>
2024-10-31 21:32:52 +00:00
Patrick Honkonen
2f6578fd5a [PM-14273] Add copy functionality for SSH key fields (#4204) 2024-10-31 20:41:13 +00:00
Patrick Honkonen
0844939eca [PM-14271] Disable editing SSH key fields in edit mode (#4201) 2024-10-31 20:40:33 +00:00
Patrick Honkonen
8f2d55c146 [PM-14346] Run alias generation on the IO dispatcher (#4215) 2024-10-31 20:39:11 +00:00
David Perez
3e5e6ce3ab Update compose BOM to 2024.10.01 (#4213) 2024-10-31 20:11:31 +00:00
David Perez
4831750ffd PM-14255: Remove accessibility logic to improve overall performance (#4206) 2024-10-31 18:05:33 +00:00
David Perez
7d7380d622 Update genrator icons on bottom nav (#4211) 2024-10-31 17:29:41 +00:00
David Perez
a0b9e92ae9 Update the camera library (#4210) 2024-10-31 16:27:28 +00:00
Álison Fernandes
ce180f1bbb [PM-14261] Update README and bugs template to remove Beta references (#4198) 2024-10-31 10:43:41 +00:00
David Perez
c99e5ce2de PM-13842: Hide ownership when the user has no organizations (#4199) 2024-10-30 20:13:15 +00:00
Andrew Haisting
5ac5e31dd2 BITAU-184 Allow user to save to Bitwarden when adding a code manually (#263) 2024-10-30 15:12:54 -05:00
Patrick Honkonen
eaa7923d1f [PM-14186] Update SDK to make SSH key properties required (#4200) 2024-10-30 18:42:33 +00:00
Andrew Haisting
bcc7e6756e BITAU-84 Show Snackbar the first time a user syncs accounts (#259) 2024-10-30 11:12:47 -05:00
Patrick Honkonen
56367cc14e [PM-13900] Update Bitwarden SDK to add canManage to Collection objects (#4169) 2024-10-30 15:53:30 +00:00
Dave Severns
6e0ce3b742 PM-13155 add shortcuts file to beta source set for beta app id (#4196) 2024-10-30 15:53:12 +00:00
Andrew Haisting
aa42e0ad18 BITAU-189 BITAU-188 Only show default save option row when sync is en… (#261) 2024-10-30 10:32:57 -05:00
Andrew Haisting
c0abc1d647 BITAU-76 Update copy on download Bitwarden action card (#260) 2024-10-30 10:06:34 -05:00
Andrew Haisting
5de6dc3473 BITAU-89 Show save location dialog on the QR scan screen (#258) 2024-10-30 10:02:03 -05:00
Patrick Honkonen
fab018782c [PM-14254] Keep Android verifier for JNI usage (#4197) 2024-10-30 14:48:49 +00:00
aj-rosado
0211729525 [PM-14241] Backport Timber hotfix (#4195) 2024-10-30 12:49:25 +00:00
ifernandezdiaz
540ece5a40 QA-954: Add testtags to Send screen elements (#4162) 2024-10-29 23:21:08 +00:00
Patrick Honkonen
bb6255b6a4 Disable ExtraTranslation lint check (#262) 2024-10-29 18:12:21 -04:00
Patrick Honkonen
78e7adfbc1 [PM-10405] Add SSH key cipher type (#4158) 2024-10-29 21:40:20 +00:00
David Perez
6f26ae50ea PM-14044: Update generator line breaks to account for padding on both sides (#4187) 2024-10-29 21:20:44 +00:00
Álison Fernandes
a5e57f1836 [PM-14224] Automate Play Store prod variant publishing (#4183) 2024-10-29 18:33:21 +00:00
David Perez
9e5fefa3ee Update copy and generate icons (#4185) 2024-10-29 18:31:12 +00:00
Dave Severns
8b16135955 PM-11188 show snackbar after import success. PM-13943 add relay for snackbar events across screen contexts. (#4152) 2024-10-29 18:23:00 +00:00
David Perez
a1108889cb PM-14200: Update the eyebrows throughout the app (#4181) 2024-10-29 13:57:16 +00:00
David Perez
150c8e0312 PM-14201: Update the default divider thickness (#4182) 2024-10-29 13:56:57 +00:00
Dave Severns
f3916b4ef6 PM-13988 observe changes to unlock status on settings screen (#4180) 2024-10-29 13:27:46 +00:00
ifernandezdiaz
8df4292e08 QA-957: Adding missing testTag for collection list container (#4178) 2024-10-29 12:12:07 +00:00
Dave Severns
05c768610e PM-13908 fixing copy on step2 and step3 and making vault url dynamic (#4154) 2024-10-28 19:11:25 +00:00
Dave Severns
21a5242abe PM-14009 complete fix importlogins card show logic (#4175) 2024-10-28 18:22:30 +00:00
Patrick Honkonen
deb9eb8d9b [PM-13908] Disable ExtraTranslation lint warning (#4176) 2024-10-28 17:47:27 +00:00
David Perez
4a91d87d9d PM-14184: Update the switch thoughout the app (#4170) 2024-10-28 17:09:29 +00:00
renovate[bot]
408d01d546 [deps]: Update com.google.devtools.ksp to v2.0.21-1.0.26 (#254) 2024-10-28 12:43:00 -04:00
renovate[bot]
3b9e42a256 [deps]: Update gradle minor (#256) 2024-10-28 14:46:02 +00:00
github-actions[bot]
6d500e52e7 Autosync Crowdin Translations (#253)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
2024-10-28 14:43:02 +00:00
renovate[bot]
3f72b49286 [deps]: Lock file maintenance (#257) 2024-10-28 14:42:34 +00:00
renovate[bot]
8b9f13e9e7 [deps]: Update gh minor (#255) 2024-10-28 10:38:52 -04:00
Dave Severns
064db9fb6a PM-13698 only dismiss the card if the user dismisses or completes the… (#4165) 2024-10-28 14:38:17 +00:00
ifernandezdiaz
c47f8606cd QA-953: Adding testTag to elements in Add TOTP screen (#4160) 2024-10-28 14:28:07 +00:00
renovate[bot]
3e2f10a5b9 [deps]: Update com.google.devtools.ksp to v2.0.21-1.0.26 (#4172)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-28 14:27:53 +00:00
renovate[bot]
b060b70a6b [deps]: Update gh minor (#4173)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-28 14:26:16 +00:00
renovate[bot]
7ea7d78e66 [deps]: Lock file maintenance (#4174)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-28 14:19:02 +00:00
David Perez
b64175ff6e Update fab design (#4168) 2024-10-25 21:29:16 +00:00
Andrew Haisting
164cc09f19 BITAU-182 BITAU-107 Don't show authetnicator sync toggle below API 31 (#4156) 2024-10-25 21:27:59 +00:00
David Perez
0960f61c37 Simplify usages of turbineScope (#4167) 2024-10-25 19:50:43 +00:00
github-actions[bot]
f8bf864fc9 Autosync Crowdin Translations (#4159)
Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com>
Co-authored-by: Patrick Honkonen <1883101+SaintPatrck@users.noreply.github.com>
2024-10-25 19:44:01 +00:00
David Perez
93aece75cf PM-14036: Update the slider UI (#4164) 2024-10-25 18:11:54 +00:00
Andrew Haisting
5159258de5 Make isBuildVersionBelow in authenticatorbridge internal (#4157) 2024-10-25 16:39:53 +00:00
ifernandezdiaz
68a834ac14 QA-955: Adding testTag to About screen rows (#4163) 2024-10-25 16:32:27 +00:00
ifernandezdiaz
33a430419c QA-952: Adding test tags for Toggle items (#4153) 2024-10-25 15:53:48 +00:00
Dave Severns
eb4ffebba0 PM-14009 Refactor storing first time values to the first time action manager (#4161) 2024-10-25 15:40:18 +00:00
David Perez
53d4c4c03e Remove query params from network logging (#4155) 2024-10-24 20:18:20 +00:00
David Perez
e80585f77e PM-13937: Update button padding, remove unused buttons, and rename button (#4151) 2024-10-24 18:21:38 +00:00
ifernandezdiaz
0ff2fe6d6a QA-951: Adding missing IDs for Attachment rows (#4148) 2024-10-24 17:31:56 +00:00
Dave Severns
b0885ff60a PM-13886 show dialog when no logins were imported (#4139) 2024-10-24 17:06:30 +00:00
David Perez
a55fbca16a Update Firebase BOM to 33.5.1 (#4150) 2024-10-24 15:59:51 +00:00
ifernandezdiaz
fcd69e3e6f QA-950: Adding testTag for VaultUnlockedNavBar component (#4146) 2024-10-24 15:42:21 +00:00
David Perez
28e87fe216 PM-13937: Consolidate button UI and logic (#4149) 2024-10-24 15:27:06 +00:00
Andrew Haisting
1d2095b0b3 BITAU-181 Allow user to update default save options from settings (#252) 2024-10-24 09:49:07 -05:00
Dave Severns
1c10a94109 PM-11187 show import success bottom sheet after success import sync (#4125) 2024-10-24 14:44:14 +00:00
David Perez
6f535c0abe PM-13937: Replace tonal buttons with outline buttons (#4147) 2024-10-24 13:58:40 +00:00
Andrew Haisting
5fdfb26950 BITAU-180 Show "Move to Bitwarden" long press action (#250) 2024-10-24 08:38:55 -05:00
Patrick Honkonen
bdb6136d36 [PM-13980] Add SSH Key Cipher Item Types feature flag (#4144) 2024-10-24 13:16:31 +00:00
Patrick Honkonen
2d9451cc34 [PM-13900] Track last database scheme change (#4124) 2024-10-24 13:15:54 +00:00
ifernandezdiaz
6217532237 QA-948: Adding missing testTags on SSO/TDE views (#4145) 2024-10-23 19:28:03 +00:00
David Perez
ef1e8403e1 PM-13939: Remove hyphen from auto-fill (#4141) 2024-10-23 17:58:28 +00:00
Patrick Honkonen
24c8406ed8 Upload test reports on test and build workflow failures (#4143) 2024-10-23 16:53:20 +00:00
David Perez
51c87625cb Ensure unmockk static is called in test teardown (#4142) 2024-10-23 16:18:45 +00:00
Andrew Haisting
fa248243b6 BITAU-112 Support deep link into add item flow from Authenticator app (#4128) 2024-10-23 16:17:31 +00:00
rad
f1d7d1a530 [PM-13857] Add Iceraven to privlieged browsers (#4122)
Co-authored-by: Patrick Honkonen <1883101+SaintPatrck@users.noreply.github.com>
2024-10-23 15:14:46 +00:00
David Perez
79ebd2ba33 User TImber instead of LogsManager directly (#4140) 2024-10-23 15:11:31 +00:00
Andrew Haisting
5d7656323b Add queries to AndroidManifest (#251) 2024-10-23 09:53:16 -05:00
Dave Severns
a23fc319de PM-13943 : PT1 Custom snackbar UI (#4135) 2024-10-23 14:44:59 +00:00
vphan916
932c26dfc6 QA Automation - Locators for Android (#214) 2024-10-23 13:48:22 +00:00
renovate[bot]
5c38522ec6 [deps]: Update kotlin (#235) 2024-10-23 09:39:59 -04:00
David Perez
c5a266dfc0 PM-13020: During totp flow master password reprompt should be honored (#4136) 2024-10-23 12:51:28 +00:00
David Perez
fca00d38f5 PM-13024: After saving cipher in totp flow, app should not close (#4137) 2024-10-22 22:12:36 +00:00
David Perez
65380095f0 Update Compose BOM and Androidx activity libs (#4134) 2024-10-22 19:37:55 +00:00
David Perez
002fd06b72 This PR adds Timber to the app (#4116) 2024-10-22 17:37:03 +00:00
David Perez
5d85060260 Update Firebase BOM to 33.5.0 (#4133) 2024-10-22 16:26:56 +00:00
David Perez
4dfee643a0 Update Junit 5 (5.11.3) (#4132) 2024-10-22 16:25:04 +00:00
David Perez
7aab846244 Apply the formatter to the entire app (#4129) 2024-10-22 14:00:10 +00:00
Andrew Haisting
ab4d9b9984 BITAU-78 Show SyncWithBitwarden action card (#248) 2024-10-21 17:21:26 -05:00
github-actions[bot]
74b1bc8303 Autosync Crowdin Translations (#247) 2024-10-21 22:07:01 +00:00
Patrick Honkonen
25e01c86bc Mark IntentManagerImpl as not covered by code coverage (#249) 2024-10-21 16:25:14 -05:00
Dave Severns
c704cd2eca PM-13627 show action card on vault settings if applicable (#4101) 2024-10-21 20:51:49 +00:00
David Perez
09c11f4890 Fix authenticator test (#4127) 2024-10-21 20:46:30 +00:00
David Perez
df6e842201 Create single helper method to clean up the MainViewModelTest (#4126) 2024-10-21 20:42:11 +00:00
David Perez
b82614e5fa PM-13847: Totp click on search should go directly to edit screen (#4123) 2024-10-21 19:48:54 +00:00
David Perez
27beb25bf7 PM-13690: Add dialog before switching account during passwordless login (#4114) 2024-10-21 19:35:19 +00:00
Andrew Haisting
d1f13e49a4 Use array to define knownCerts for authenticator bridge permission (#4103) 2024-10-21 18:47:40 +00:00
Andrew Haisting
b127021701 Distribute Firebase builds to livefront group. (#243) 2024-10-17 15:25:00 -05:00
Andrew Haisting
123d227c13 Add closing paren (#245) 2024-10-17 15:24:46 -05:00
Andrew Haisting
88b96812de BITAU-82 Show shared codes on the item listing screen (#241) 2024-10-17 15:24:21 -05:00
Andrew Haisting
8abe62e53e Remove Firebase APK distribution (#244) 2024-10-17 11:51:57 -05:00
Andrew Haisting
d07a9dcf81 BITAU-90 Add "Sync with Bitwarden App" row to settings (#239) 2024-10-15 15:38:53 -05:00
Andrew Haisting
1820073b65 BITAU-68 Specify correct authenticatorbridge connection type (#240)
Co-authored-by: Patrick Honkonen <1883101+SaintPatrck@users.noreply.github.com>
2024-10-15 15:32:50 -05:00
Andrew Haisting
1669fa4044 Only run debug test variant on CI (#242) 2024-10-15 15:31:31 -05:00
renovate[bot]
d3c1f8e26a [deps]: Update gh minor (#234) 2024-10-15 15:17:03 -04:00
renovate[bot]
a78f81014e [deps]: Lock file maintenance (#236) 2024-10-15 15:13:52 -04:00
github-actions[bot]
4c74f720eb Autosync Crowdin Translations (#233) 2024-10-14 17:28:29 +00:00
Andrew Haisting
03251abe88 Update authenticatorbridge .aar after removing lastSyncTime (#237) 2024-10-14 11:56:46 -05:00
Andrew Haisting
43fb630393 BITAU-76 Show download Bitwarden action card (#229) 2024-10-10 15:22:54 -05:00
Andrew Haisting
54e59cc61b Return noop bridge manager when password sync feature flag is off (#232)
Co-authored-by: Brian Yencho <brian@livefront.com>
2024-10-10 10:40:46 -05:00
Andrew Haisting
d6ae7da44c Fix empty state for totp codes (#231) 2024-10-09 09:08:41 -05:00
renovate[bot]
280aa35b73 [deps]: Update gradle minor (#222) 2024-10-09 10:01:06 -04:00
Andrew Haisting
f3bce23942 Use correct feature for syncing with Bitwarden (#230) 2024-10-08 15:08:35 -05:00
Andrew Haisting
1015654d27 BITAU-70 Implement symmetric key storage (#226) 2024-10-08 13:01:55 -05:00
Patrick Honkonen
d8c80f7e28 [BWA-10] Handle exception when checking for suspicious intents (#228) 2024-10-08 08:20:44 -05:00
Patrick Honkonen
3aaa93675d Update Android Gradle plugin and Gradle wrapper (#225) 2024-10-08 08:18:29 -05:00
Andrew Haisting
af0f894dd3 BITAU-83 Refactor AuthenticatorRepository to accomodate shared codes (#220)
Co-authored-by: Patrick Honkonen <1883101+SaintPatrck@users.noreply.github.com>
2024-10-07 19:47:05 -05:00
renovate[bot]
6e7f5a31f1 [deps]: Update androidx.compose:compose-bom to v2024.09.03 (#221) 2024-10-07 09:37:47 -04:00
github-actions[bot]
79b7866e30 Autosync Crowdin Translations (#219) 2024-10-07 13:35:38 +00:00
renovate[bot]
d8da7a8cc2 [deps]: Update gh minor (#223) 2024-10-07 09:32:09 -04:00
renovate[bot]
e36e9e878f [deps]: Lock file maintenance (#224) 2024-10-07 13:27:22 +00:00
Andrew Haisting
93423d4b13 BITAU-68 Specify build specific bridge permission in manifest (#218) 2024-10-03 13:45:27 -05:00
Patrick Honkonen
ee165491c9 [BWA-90] Remove WorkManager for clipboard clearing (#216) 2024-10-02 14:57:22 -04:00
Patrick Honkonen
8ad9bc5eed [BWA-91] Add OmitFromCoverage annotation (#217) 2024-10-02 14:43:07 -04:00
renovate[bot]
e2e49a4555 [deps]: Update kotlin (#193) 2024-10-02 10:22:58 -04:00
Patrick Honkonen
a92fd5b35d [deps]: Update androidxLifecycle to 2.8.6 (#213) 2024-10-02 15:49:39 +02:00
Patrick Honkonen
5e5b29677a [deps]: Update compose to 2024.09.02 (#212) 2024-10-02 15:49:22 +02:00
Andrew Haisting
9f4a85d373 remove unused getAuthCodeFlow() (#215) 2024-10-01 15:33:21 -05:00
vphan916
b0edb7cf3b Added locators for the settings page and the Add Code button (#203) 2024-09-30 13:45:27 -07:00
Andrew Haisting
0d5bbc177c BITAU-67 Add custom authenticator bridge permission to manifest (#211) 2024-09-30 14:40:02 -05:00
github-actions[bot]
f7c14890f1 Autosync Crowdin Translations (#202) 2024-09-30 19:39:15 +00:00
renovate[bot]
924ff87979 [deps]: Update gradle minor (#206) 2024-09-30 19:36:01 +00:00
renovate[bot]
713bb51974 [deps]: Update org.testng:testng to v7 (#208) 2024-09-30 15:35:28 -04:00
renovate[bot]
6faf720076 [deps]: Update gh minor (#207) 2024-09-30 15:34:51 -04:00
renovate[bot]
a13a7c363f [deps]: Update ubuntu to v24 (#209) 2024-09-30 17:58:29 +00:00
renovate[bot]
9d6192ba1f [deps]: Lock file maintenance (#210) 2024-09-30 17:57:45 +00:00
Patrick Honkonen
ddeece0a5c [BWA-79] Sanitize manual entry code before validation (#200) 2024-09-26 10:22:10 -04:00
Andrew Haisting
3445191a05 BITAU-66 Use fixed keystore for signing debug build (#201) 2024-09-25 14:17:36 -05:00
github-actions[bot]
2d75679586 Autosync Crowdin Translations (#197) 2024-09-25 12:48:38 +00:00
Álison Fernandes
d676a6c76c [BWA-77] Setup codeowners (#198) 2024-09-25 08:45:06 -04:00
renovate[bot]
26eeb39f88 [deps]: Update gradle minor (#192) 2024-09-19 16:35:00 -04:00
Patrick Honkonen
49c2e3030c [PM-12166] Update detekt (#196) 2024-09-19 18:06:34 +01:00
renovate[bot]
ea267a0298 [deps]: Lock file maintenance (#195) 2024-09-19 12:47:26 -04:00
renovate[bot]
6e0063c977 [deps]: Update gh minor (#194) 2024-09-19 12:47:00 -04:00
Patrick Honkonen
1f89314bbb [BWA-65] Sort items alphabetically with special char precedence (#190) 2024-09-18 13:10:16 -03:00
github-actions[bot]
afd7097b53 Autosync Crowdin Translations (#186) 2024-09-18 15:00:57 +00:00
Andrew Haisting
087cd5a093 BITAU-158 Add PasswordManagerSync local feature flag (#189) 2024-09-18 09:12:13 -05:00
renovate[bot]
06d742bd16 [deps]: Update gradle minor (#183) 2024-09-03 14:13:38 -04:00
renovate[bot]
05bcaf9dbc [deps]: Lock file maintenance (#184) 2024-09-03 13:13:05 +00:00
renovate[bot]
d37fa652bb [deps]: Update org.jetbrains.kotlinx:kotlinx-serialization-json to v1.7.2 (#181) 2024-09-03 13:12:48 +00:00
renovate[bot]
170e74db55 [deps]: Update gh minor (#182) 2024-09-03 13:11:12 +00:00
github-actions[bot]
b75133516d Autosync Crowdin Translations (#180) 2024-09-03 13:09:49 +00:00
Patrick Honkonen
e90c41a3b7 [BWA-61] Fix pre-existing detekt issues (#179) 2024-09-03 09:05:20 -04:00
Patrick Honkonen
5758b34dcf [BWA-60] Configure detekt scanning (#178) 2024-08-29 13:50:17 -04:00
Patrick Honkonen
b49d8a18a2 [BWA-59] Define feature flag manager (#177) 2024-08-29 13:24:27 -04:00
Patrick Honkonen
0d77e7085b [BWA-58] Define feature flag repo (#176) 2024-08-29 14:26:24 +00:00
Patrick Honkonen
d0203eedc4 [BWA-57] Define feature flag disk source (#175) 2024-08-29 10:22:14 -04:00
Patrick Honkonen
3c74a342d1 [BWA-56] Add network module (#174) 2024-08-29 09:43:51 -04:00
renovate[bot]
8503610b0b [deps]: Update com.google.firebase:firebase-bom to v33.2.0 (#172) 2024-08-27 15:37:43 -04:00
renovate[bot]
ae1225b948 [deps]: Update kotlin (#171) 2024-08-27 14:54:24 -04:00
github-actions[bot]
959361adc2 Autosync Crowdin Translations (#169) 2024-08-23 17:43:56 +00:00
renovate[bot]
f9ccb766c2 [deps]: Lock file maintenance (#173) 2024-08-23 17:42:12 +00:00
renovate[bot]
e5e5d3c67c [deps]: Update github/codeql-action action to v3.26.4 (#170) 2024-08-23 17:41:25 +00:00
renovate[bot]
ea1813a1b6 [deps]: Lock file maintenance (#166) 2024-08-21 09:30:21 -04:00
renovate[bot]
b1f0a10a55 [deps]: Update sonarsource/sonarcloud-github-action action to v3 (#165) 2024-08-21 09:29:52 -04:00
renovate[bot]
1af16bfbef [deps]: Update github/codeql-action action to v3.26.3 (#164) 2024-08-21 09:29:19 -04:00
Martini
524e0941e4 [BWA-53] Fix: Resolve issue with code copying in search results (#163) 2024-08-20 09:17:32 -04:00
github-actions[bot]
bcea5a6b25 Autosync Crowdin Translations (#158) 2024-08-19 13:28:34 +00:00
renovate[bot]
b2a256e2fc [deps]: Update gradle minor (#160) 2024-08-19 09:23:40 -04:00
renovate[bot]
83c8db6867 [deps]: Update github/codeql-action action to v3.26.2 (#159) 2024-08-19 09:23:11 -04:00
renovate[bot]
96e2985aed [deps]: Update gradle/actions action to v4 (#161) 2024-08-19 09:22:37 -04:00
renovate[bot]
5e544ff1f9 [deps]: Lock file maintenance (#162) 2024-08-19 09:22:02 -04:00
github-actions[bot]
58c31937a2 Autosync Crowdin Translations (#153) 2024-08-13 16:22:53 +00:00
Patrick Honkonen
01ac1f3f93 [BWA-52] Disable MissingTranslation lint error (#156) 2024-08-13 12:19:10 -04:00
renovate[bot]
f93d11fe36 [deps]: Update kotlin (#151) 2024-08-13 12:12:50 -04:00
renovate[bot]
44a53f18d3 [deps]: Update gradle minor (#150) 2024-08-13 11:04:53 -04:00
renovate[bot]
72218b643d [deps]: Update gh minor (#148) 2024-08-13 10:48:24 -04:00
Patrick Honkonen
d00a77e247 [BWA-47] Update gradle wrapper validation GH action (#145) 2024-08-13 10:18:44 -03:00
Patrick Honkonen
6e9f9d62a1 [BWA-28] Read Crowdin API token from secrets (#152) 2024-08-13 09:18:07 -04:00
renovate[bot]
48f30f022d [deps]: Update gradle minor (#149) 2024-07-23 13:08:54 +00:00
Matt Bishop
2f526a7725 Exclude tests from Sonar (#147) 2024-07-12 16:45:20 -04:00
renovate[bot]
43855ab555 [deps]: Update kotlin (#142) 2024-07-08 09:47:07 -04:00
renovate[bot]
9fdeb8e639 [deps]: Update gradle minor (#138) 2024-07-08 09:46:40 -04:00
renovate[bot]
a19523a3f8 [deps]: Lock file maintenance (#134) 2024-07-08 09:46:00 -04:00
renovate[bot]
023e5dd2fc [deps]: Update ubuntu to v22 (#133) 2024-07-08 09:45:31 -04:00
renovate[bot]
0af9bb887a [deps]: Update crowdin/github-action action to v2 (#131) 2024-07-08 09:44:58 -04:00
renovate[bot]
1e1beec6e4 [deps]: Update gh minor (#117) 2024-07-08 09:33:44 -04:00
Patrick Honkonen
9a72c0c8e5 [BWA-45] Update Androidx Lifecycle components to fix runtime crash (#140) 2024-07-01 17:09:33 -04:00
Matt Bishop
cff5c7b9a2 Adjust Sonar scan paths (#139) 2024-06-24 14:15:07 -04:00
Matt Bishop
be96f1b9d4 Remove Renovate SDK customizations (#137) 2024-06-21 11:31:36 -04:00
Matt Bishop
b8511ec4f8 Configure registry URL for SDK (#135) 2024-06-21 11:07:30 -04:00
Matt Bishop
e5f7488a7c Add Maven as supported Renovate manager (#136) 2024-06-21 10:34:00 -04:00
Patrick Honkonen
a2b7132cf3 [BWA-28] Sync translations with Crowdin (#129) 2024-06-17 12:26:17 -04:00
Patrick Honkonen
8be57a6ef7 Escape and use correct apostrophe symbol (#130) 2024-06-17 12:25:48 -04:00
renovate[bot]
49a5ff34df [deps]: Update gradle minor (#67) 2024-06-14 15:43:04 -04:00
renovate[bot]
53ac8ac49c [deps]: Update kotlin (#107) 2024-06-14 15:32:28 -04:00
renovate[bot]
2b721ac52c [deps]: Update googleProtoBufJava to v4 (major) (#118) 2024-06-14 15:19:59 -04:00
renovate[bot]
31f45ae5a4 [deps]: Update kotlin to v2 (major) (#108) 2024-06-14 15:09:01 -04:00
Patrick Honkonen
f912e00d21 [BWA-10] Sanitize launch intents before processing (#128) 2024-06-14 15:06:45 -04:00
Patrick Honkonen
7ca1eab4b2 [BWA-32] Sort verification codes by issuer (#127) 2024-06-14 15:06:15 -04:00
Patrick Honkonen
cbb469e5ab Extract account name from Aegis imports (#126) 2024-06-14 15:05:38 -04:00
Patrick Honkonen
17c9008f95 Improve 2FAS import (#124) 2024-06-13 16:02:27 -04:00
Patrick Honkonen
2640d28468 Display a descriptive error when import fails (#122) 2024-06-12 13:13:17 -05:00
Patrick Honkonen
f695254fc1 Update 2FAS import option label (#123) 2024-06-12 14:12:27 -04:00
Patrick Honkonen
18e5b711bb Fix Bitwarden import (#120) 2024-06-12 11:46:11 -04:00
Patrick Honkonen
9912123293 Remove language switcher (#121) 2024-06-12 11:38:22 -04:00
Michał Chęciński
e737f3260d [BRE-101] Remove dept-devops from CODEOWNERS (#116) 2024-06-11 18:19:32 -04:00
renovate[bot]
be8562b4db [deps]: Lock file maintenance (#109) 2024-06-11 18:19:06 -04:00
Matt Bishop
dc0413b416 Code coverage configuration (#115) 2024-06-05 16:02:24 -04:00
Patrick Honkonen
d1b5f3078e Add example test for MainViewModel (#113) 2024-06-03 13:43:13 -04:00
Matt Bishop
c8194545a4 Repo tuneup (#112) 2024-06-03 10:17:36 -04:00
Matt Bishop
c67a5d1a8d Checkmarx configuration (#111) 2024-05-31 12:31:40 -04:00
Patrick Honkonen
00b35bd3ab [BWA-16] Import Google Authenticator exports via scanner (#101) 2024-05-28 13:01:07 -04:00
renovate[bot]
a40f3b91db [deps]: Update gh minor (#106)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-28 12:57:01 -04:00
Patrick Honkonen
9537bfaf6f [BWA-21] Disable screen capture (#110) 2024-05-28 12:50:30 -04:00
Patrick Honkonen
e9fb28d374 [BWA-18] Add Backup option to settings (#103) 2024-05-22 22:11:48 +00:00
Patrick Honkonen
1161c3c446 [BWA-22] Prevent FAB from hiding verification codes (#102) 2024-05-22 22:06:00 +00:00
Patrick Honkonen
d3e4b56d30 Add androidx.lifecycle dependencies to kotlin group (#104) 2024-05-22 18:04:56 -04:00
Dillon Beresford
f9dd295188 [PM-7288] Include changes to actions in scan workflow (#87) 2024-05-17 17:49:48 -05:00
Patrick Honkonen
6fae5b8b77 [BWA-12] Update workflow to accept custom inputs (#96) 2024-05-17 10:57:00 -04:00
Patrick Honkonen
2529ed3fb9 [BWA-5] Support importing Aegis exports (#99) 2024-05-17 09:53:23 -04:00
Patrick Honkonen
a1e7e92d6d [BWA-14] Support importing LastPass exports (#98) 2024-05-16 15:30:48 -04:00
Patrick Honkonen
15251c840c Separate androidxTest and androidxTestRules dependency versions (#97) 2024-05-16 15:19:55 -04:00
Patrick Honkonen
663265d7b3 [BWA-11] Import 2FAS exports (#95) 2024-05-16 12:30:53 -04:00
Patrick Honkonen
f2365771ff [BWA-9] Set task affinity to mitigate task hijacking (#94) 2024-05-15 15:13:47 -04:00
renovate[bot]
97e6758449 [deps]: Update kotlin (#90) 2024-05-15 14:50:28 -04:00
renovate[bot]
91e197db2c [deps]: Update com.google.firebase.crashlytics to v3 (#91) 2024-05-14 15:19:42 -04:00
renovate[bot]
8e10170347 [deps]: Lock file maintenance (#93) 2024-05-13 12:26:59 -04:00
renovate[bot]
74b6fc7e2e [deps]: Update com.google.firebase:firebase-bom to v33 (#92) 2024-05-13 09:29:48 -04:00
renovate[bot]
6b4f959b12 [deps]: Update gh minor (#89)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-13 09:19:12 -04:00
Patrick Honkonen
578efd3b7f Define issue template for bugs (#86) 2024-05-08 23:24:54 -04:00
Patrick Honkonen
31669cf457 Force scanned codes to uppercase alpha characters (#85) 2024-05-07 15:50:43 -04:00
Patrick Honkonen
d9e29d3c81 Allow users to select favorites (#84) 2024-05-07 13:21:30 -04:00
Patrick Honkonen
5c38d62a61 Allow users to import exported JSON vault files (#81) 2024-05-06 20:58:27 -04:00
Patrick Honkonen
9b5449f657 Enforce Base32 key values (#82) 2024-05-03 16:14:49 -04:00
Patrick Honkonen
6da31f797d Declare ruby version globally (#80) 2024-05-03 16:14:01 -04:00
Patrick Honkonen
f546bd2640 Fix version name generation (#78) 2024-05-02 12:28:38 -05:00
Patrick Honkonen
d32ed06516 Build release bundles and publish to Firebase (#50) 2024-05-02 10:24:37 -04:00
Patrick Honkonen
26c22295fc Use valid authenticators on Android Q and below (#77) 2024-05-01 21:20:29 -04:00
Patrick Honkonen
de9aebbda9 Remove nulab password strength library (#73) 2024-05-01 20:09:31 -05:00
Kyle Spearrin
f7ea3a7972 Fix screenshot dimensions in readme (#76) 2024-05-01 13:51:00 -04:00
Patrick Honkonen
51c3f2b04a Add README and code style guidelines (#75) 2024-05-01 09:50:36 -04:00
renovate[bot]
61d80793e7 [deps]: Update ruby to v3.3.1 (#30)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Patrick Honkonen <phonkonen@bitwarden.com>
Co-authored-by: Patrick Honkonen <1883101+SaintPatrck@users.noreply.github.com>
2024-05-01 07:16:42 -04:00
renovate[bot]
b59814aa83 [deps]: Update kotlin (#66) 2024-04-30 18:23:21 -04:00
Patrick Honkonen
5c46e913a2 Allow users to toggle crash logging (#72) 2024-04-30 17:41:55 -04:00
Patrick Honkonen
1a592273bf Enable crashlytics (#48) 2024-04-30 17:27:44 -04:00
Patrick Honkonen
b597a2fb62 Update label for OTP type input field (#71) 2024-04-30 17:14:43 -04:00
Patrick Honkonen
ea55cbc914 Convert digits input to stepper (#70) 2024-04-29 22:19:29 -04:00
Vince Grassia
86d8a2ed8d DEVOPS-1952 - Update Gradle validation action (#69) 2024-04-29 12:33:58 -06:00
renovate[bot]
4bfe787f01 [deps]: Update gh minor (#65)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-29 09:48:36 -04:00
Patrick Honkonen
c0b04694d9 Release prep and refinement (#64) 2024-04-27 14:49:06 -04:00
Patrick Honkonen
dd4a0a502d Add About section to settings (#63) 2024-04-26 14:13:38 -04:00
Patrick Honkonen
1eb4a9770e Update empty list screen text to omit sync references (#62) 2024-04-26 14:00:05 -04:00
Patrick Honkonen
165ae77113 Allow fallback to device credential when biometrics fails (#61) 2024-04-26 12:31:03 -04:00
Patrick Honkonen
dad060e8c1 Increase spacing in tutorial slide content (#60) 2024-04-26 11:37:32 -04:00
Patrick Honkonen
75fb200eaf Remove bottom nav bar when relaunching tutorial (#59) 2024-04-26 11:17:47 -04:00
Patrick Honkonen
2bd0df2910 Adjust top app bar (#58) 2024-04-26 11:02:18 -04:00
Patrick Honkonen
2b6fe1b28a Increase spacing around Help header (#57) 2024-04-26 10:22:18 -04:00
Patrick Honkonen
f39669e615 Display correct icons when app overrides system theme. (#56) 2024-04-25 16:48:54 -05:00
Patrick Honkonen
1f0881f74e Make issuer required and account name optional (#55) 2024-04-25 17:17:15 -04:00
Patrick Honkonen
cb158b0947 Update toast message when a code is added (#53) 2024-04-25 16:48:00 -04:00
Patrick Honkonen
3adc43683a Display dark mode asset for empty vault (#54) 2024-04-25 13:40:17 -04:00
Patrick Honkonen
3ae4c72b46 Create dedicated unlock screen (#52) 2024-04-25 08:42:04 -05:00
Patrick Honkonen
7e93c1a74b Fix routing on tutorial completion (#51) 2024-04-24 19:29:48 -04:00
Patrick Honkonen
f191f02296 Allow users to enable biometric unlock from settings (#44) 2024-04-24 08:40:54 -05:00
Patrick Honkonen
9e86332c5e Cache build output separate from gradle cache (#49) 2024-04-24 10:53:21 +02:00
Patrick Honkonen
d85904748e Correct tutorial screen behavior during rotation (#45) 2024-04-24 01:23:43 -04:00
Patrick Honkonen
5167b2c491 Flip name and key fields in manual entry screen (#46) 2024-04-24 05:05:13 +00:00
Patrick Honkonen
6b88bcf02f Establish infrastructure to support biometric lock and unlock (#43) 2024-04-24 00:44:46 -04:00
Patrick Honkonen
eb97a33e8e Define proguard rules for release builds (#47) 2024-04-24 00:03:04 -04:00
Patrick Honkonen
e829e1e2ca Assemble, sign, upload and publish release builds (#38) 2024-04-22 13:49:16 -04:00
Patrick Honkonen
015cbdd37a Prompt for camera permission from Add Code button (#42) 2024-04-22 10:38:14 -04:00
Patrick Honkonen
66d834c7e9 Users can export unencrypted data to JSON or CSV (#41) 2024-04-22 10:30:04 -04:00
Patrick Honkonen
066d5c5628 Remove Import button from empty item listing screen (#40) 2024-04-22 10:29:03 -04:00
Patrick Honkonen
34c547a431 Remove Sync with Bitwarden button (#39) 2024-04-21 22:07:58 -04:00
Patrick Honkonen
af1ab4c953 Remove x8bit references from project (#37) 2024-04-17 08:34:54 -04:00
Patrick Honkonen
186766960f Include app database in auto backup and restore (#36) 2024-04-16 23:29:20 -04:00
Patrick Honkonen
de02a6999d Prompt for camera permissions from item listing screen (#34) 2024-04-16 22:19:51 -05:00
Patrick Honkonen
736761ee93 Update settings tutorial label (#35) 2024-04-16 22:19:36 -05:00
Patrick Honkonen
7bad184849 Box in list item context menu (#33) 2024-04-16 09:04:42 -05:00
Patrick Honkonen
612c8e8aa3 Implement context menu on item long press (#31) 2024-04-15 20:08:26 -05:00
Patrick Honkonen
8da98f95e1 Copy auth code on item click (#28) 2024-04-15 19:39:45 -05:00
Patrick Honkonen
6d4df646af Allow backup & restore to cloud and device transfer (#32) 2024-04-15 20:18:48 -04:00
renovate[bot]
d98db6ee67 [deps]: Update gh minor (#29)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-15 16:10:17 -04:00
Patrick Honkonen
12e5314c61 Show Welcome Tutorial on first launch (#27) 2024-04-15 10:12:58 -04:00
Patrick Honkonen
b6a165aef5 Allow modification of appearance settings (#26) 2024-04-14 17:03:34 -04:00
Patrick Honkonen
6b025832d7 DIsplay base settings screen (#25) 2024-04-13 20:39:03 -04:00
Patrick Honkonen
ea58313e31 Display bottom app bar for navigation (#24) 2024-04-13 18:06:20 -04:00
Patrick Honkonen
a27002af0f Allow users to search items (#23) 2024-04-12 11:34:13 -05:00
Patrick Honkonen
8ad7c184f1 Remove View Item screen (#22) 2024-04-11 10:08:26 -04:00
Patrick Honkonen
168e662a38 Update item listing header text (#21) 2024-04-11 10:03:16 -04:00
Patrick Honkonen
a00452faf1 Edit item UI (#20) 2024-04-11 09:14:11 -04:00
Patrick Honkonen
32b0c90f17 Refactor primary data model (#19) 2024-04-09 10:01:57 -04:00
Patrick Honkonen
cd992a6994 Allow users to save items to local storage (#18) 2024-04-05 15:12:25 -05:00
renovate[bot]
38a92042de [deps]: Update kotlin (#12) 2024-04-05 13:11:17 -04:00
renovate[bot]
65263ebad9 [deps]: Update gradle minor (#11) 2024-04-05 13:03:53 -04:00
Patrick Honkonen
50530b57c6 Define local database (#17) 2024-04-05 12:49:04 -04:00
Patrick Honkonen
04a565e5a2 Include Compose compiler in kotlin renovate group (#16) 2024-04-03 18:11:20 -05:00
Patrick Honkonen
9aa091818d Display no items content when user has no items saved (#13) 2024-04-03 13:10:36 -04:00
Patrick Honkonen
6a226f21bd Correct renovate ruby manager declaration to use bundler (#15) 2024-04-03 12:39:30 -04:00
Patrick Honkonen
67d641572e Trigger checks during CI execution (#10) 2024-04-02 22:35:00 -05:00
Patrick Honkonen
a92ed17d65 QR Scanner and Manual Key Entry screens (#7) 2024-04-02 17:45:33 -04:00
Patrick Honkonen
580a0eecdd Configure Renovate to monitor Android project dependencies (#9) 2024-04-02 16:19:38 -05:00
Patrick Honkonen
d28a754ee9 Introduce Expandable floating action button (#6) 2024-04-02 10:34:13 -04:00
Matt Bishop
5fd8593095 Provide prefix for Renovate 2024-03-29 11:30:47 -04:00
Patrick Honkonen
bcaf00dc97 Change color of countdown indicator when period nears expiration (#5) 2024-03-29 00:02:01 -04:00
Matt Bishop
c012e3cb7e Disable SARIF upload until repo becomes public (#8) 2024-03-28 11:05:03 -04:00
Patrick Honkonen
4575f52fe0 Splash, Item Listing, & View Item screens (#4) 2024-03-28 09:27:33 -04:00
Matt Bishop
c01a101f83 Adjust scan permissions 2024-03-27 10:02:59 -04:00
Patrick Honkonen
8553fb3995 Set mobile dev team as default code owners (#3) 2024-03-27 10:31:20 +01:00
Patrick Honkonen
3636da5c4e Update .gitignore (#2) 2024-03-26 14:22:54 -04:00
Matt Bishop
8bd430b793 Initial commit 2024-03-26 12:09:32 -04:00
1931 changed files with 119441 additions and 29622 deletions

View File

@@ -8,4 +8,4 @@ checkmarx:
configs:
sast:
# Exclude test directories
filter: "!app/src/test/**"
filter: "**/test/**,!**/androidTest/**,!**/commonTest/**,!**/jvmTest/**,!**/jsTest/**,!**/iosTest/**"

View File

@@ -12,127 +12,20 @@ end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
guidelines = 120
# Code files
[*.{cs,csx,vb,vbx}]
indent_size = 4
# Xml project files
[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
indent_size = 2
# Xml config files
[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]
indent_size = 2
# JSON files
[*.json]
indent_size = 2
# JS files
[*.{js,ts,scss,html}]
# Kotlin files
# noinspection EditorConfigKeyCorrectness
[*.{kt,kts}]
# https://pinterest.github.io/ktlint/1.0.1/rules/configuration-ktlint/#trailing-comma-on-declaration-site
ij_kotlin_allow_trailing_comma = true
# https://pinterest.github.io/ktlint/1.0.1/rules/configuration-ktlint/#trailing-comma-on-declaration-site
trailing-comma-on-declaration-site = true
# https://pinterest.github.io/ktlint/1.0.1/rules/configuration-ktlint/#trailing-comma-on-call-site
ij_kotlin_allow_trailing_comma_on_call_site = true
[*.{scss,yml}]
indent_size = 2
[*.{ts}]
quote_type = single
[*.{scss,yml,csproj}]
indent_size = 2
[*.sln]
indent_style = tab
# Dotnet code style settings:
[*.{cs,vb}]
# Sort using and Import directives with System.* appearing first
dotnet_sort_system_directives_first = true
# Avoid "this." and "Me." if not necessary
dotnet_style_qualification_for_field = false:suggestion
dotnet_style_qualification_for_property = false:suggestion
dotnet_style_qualification_for_method = false:suggestion
dotnet_style_qualification_for_event = false:suggestion
# Use language keywords instead of framework type names for type references
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
dotnet_style_predefined_type_for_member_access = true:suggestion
# Suggest more modern language features when available
dotnet_style_object_initializer = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_explicit_tuple_names = true:suggestion
# Prefix private members with underscore
dotnet_naming_rule.private_members_with_underscore.symbols = private_fields
dotnet_naming_rule.private_members_with_underscore.style = prefix_underscore
dotnet_naming_rule.private_members_with_underscore.severity = suggestion
dotnet_naming_symbols.private_fields.applicable_kinds = field
dotnet_naming_symbols.private_fields.applicable_accessibilities = private
dotnet_naming_style.prefix_underscore.capitalization = camel_case
dotnet_naming_style.prefix_underscore.required_prefix = _
# Async methods should have "Async" suffix
dotnet_naming_rule.async_methods_end_in_async.symbols = any_async_methods
dotnet_naming_rule.async_methods_end_in_async.style = end_in_async
dotnet_naming_rule.async_methods_end_in_async.severity = suggestion
dotnet_naming_symbols.any_async_methods.applicable_kinds = method
dotnet_naming_symbols.any_async_methods.applicable_accessibilities = *
dotnet_naming_symbols.any_async_methods.required_modifiers = async
dotnet_naming_style.end_in_async.required_prefix =
dotnet_naming_style.end_in_async.required_suffix = Async
dotnet_naming_style.end_in_async.capitalization = pascal_case
dotnet_naming_style.end_in_async.word_separator =
# Obsolete warnings, this should be removed or changed to warning once we address some of the obsolete items.
dotnet_diagnostic.CS0618.severity = suggestion
# Obsolete warnings, this should be removed or changed to warning once we address some of the obsolete items.
dotnet_diagnostic.CS0612.severity = suggestion
# Remove unnecessary using directives https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0005
dotnet_diagnostic.IDE0005.severity = warning
# CSharp code style settings:
[*.cs]
# Prefer "var" everywhere
csharp_style_var_for_built_in_types = true:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
csharp_style_var_elsewhere = true:suggestion
# Prefer method-like constructs to have a expression-body
csharp_style_expression_bodied_methods = true:none
csharp_style_expression_bodied_constructors = true:none
csharp_style_expression_bodied_operators = true:none
# Prefer property-like constructs to have an expression-body
csharp_style_expression_bodied_properties = true:none
csharp_style_expression_bodied_indexers = true:none
csharp_style_expression_bodied_accessors = true:none
# Suggest more modern language features when available
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
csharp_style_inlined_variable_declaration = true:suggestion
csharp_style_throw_expression = true:suggestion
csharp_style_conditional_delegate_call = true:suggestion
# Newline settings
csharp_new_line_before_open_brace = all
csharp_new_line_before_else = true
csharp_new_line_before_catch = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_members_in_anonymous_types = true
# Namespace settings
csharp_style_namespace_declarations = file_scoped:warning
# Switch expression
dotnet_diagnostic.CS8509.severity = error # missing switch case for named enum value
dotnet_diagnostic.CS8524.severity = none # missing switch case for unnamed enum value

4
.github/CODEOWNERS vendored
View File

@@ -5,10 +5,10 @@
# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners
# Default file owners.
* @bitwarden/team-android @brian-livefront @david-livefront @dseverns-livefront @ahaisting-livefront
* @bitwarden/team-android @brian-livefront @david-livefront
# Actions and workflow changes.
.github/workflows @bitwarden/dept-development-mobile
.github/ @bitwarden/dept-development-mobile
# Auth
# app/src/main/java/com/x8bit/bitwarden/data/auth @bitwarden/team-auth-dev

84
.github/ISSUE_TEMPLATE/bug-bwa.yml vendored Normal file
View File

@@ -0,0 +1,84 @@
name: Authenticator Android App Bug Report
description: File a bug report
labels: [ "app:authenticator", "bug" ]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
Please do not submit feature requests. The [Community Forums](https://community.bitwarden.com) has a section for submitting, voting for, and discussing product feature requests.
- type: textarea
id: reproduce
attributes:
label: Steps To Reproduce
description: How can we reproduce the behavior.
value: |
1. Go to '...'
2. Click on '...'
3. Scroll down to '...'
4. Click on '...'
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected Result
description: A clear and concise description of what you expected to happen.
validations:
required: true
- type: textarea
id: actual
attributes:
label: Actual Result
description: A clear and concise description of what is happening.
validations:
required: true
- type: textarea
id: screenshots
attributes:
label: Screenshots or Videos
description: If applicable, add screenshots and/or a short video to help explain your problem.
- type: textarea
id: additional-context
attributes:
label: Additional Context
description: Add any other context about the problem here.
- type: input
id: version
attributes:
label: Build Version
description: What version of our software are you running?
validations:
required: true
- type: dropdown
id: server-region
attributes:
label: What server are you connecting to?
options:
- US
- EU
- Self-host
- N/A
validations:
required: true
- type: input
id: server-version
attributes:
label: Self-host Server Version
description: If self-hosting, what version of Bitwarden Server are you running?
- type: textarea
id: environment-details
attributes:
label: Environment Details
placeholder: |
- Device: [e.g. Pixel Tablet, Samsung Galaxy S24 ]
- OS Version: [e.g. API 32, Tiramisu ]
- type: checkboxes
id: issue-tracking-info
attributes:
label: Issue Tracking Info
description: |
Issue tracking information
options:
- label: I understand that work is tracked outside of Github. A PR will be linked to this issue should one be opened to address it, but Bitwarden doesn't use fields like "assigned", "milestone", or "project" to track progress.

64
.github/ISSUE_TEMPLATE/bug-passkey.yml vendored Normal file
View File

@@ -0,0 +1,64 @@
name: Passkey Bug Report
description: File a Passkey / FIDO2 related bug report
labels: [ "app:password-manager", "bug-passkey" ]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this Passkey-related bug report!
Please provide as much detail as possible to help us investigate the issue.
- type: dropdown
id: origin
attributes:
label: Origin
description: Are you using a web browser or a native application?
options:
- Web (Browser)
- Native Application (non-browser app)
validations:
required: true
- type: input
id: rp-id
attributes:
label: Web URL or App name
description: The website domain or app name you were trying to use the Passkey with
placeholder: "e.g. example.com or ExampleApp"
validations:
required: true
- type: checkboxes
id: operation-type
attributes:
label: Passkey Action
description: What passkey related action(s) were you trying to perform?
options:
- label: Creating new passkey (Registration)
- label: Signing in (Authentication)
validations:
required: true
- type: textarea
id: build-info
attributes:
label: Build Information
description: Please retrieve the build information from the About screen by tapping the Version number field
validations:
required: true
- type: textarea
id: additional-info
attributes:
label: Additional Information
description: Any additional context, steps to reproduce, error messages, or relevant information about the issue
- type: checkboxes
id: issue-tracking-info
attributes:
label: Issue Tracking Info
description: |
Issue tracking information
options:
- label: I understand that work is tracked outside of Github. A PR will be linked to this issue should one be opened to address it, but Bitwarden doesn't use fields like "assigned", "milestone", or "project" to track progress.

View File

@@ -1,25 +1,13 @@
name: Android Beta Bug Report
name: Password Manager Android App Bug Report
description: File a bug report
labels: [ bug ]
labels: [ "app:password-manager", "bug" ]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
> [!WARNING]
> This is the new native Bitwarden Beta app repository. For the publicly available apps in App Store / Play Store, submit your report in [bitwarden/mobile](https://github.com/bitwarden/mobile)
Please do not submit feature requests. The [Community Forums](https://community.bitwarden.com) has a section for submitting, voting for, and discussing product feature requests.
- type: checkboxes
id: beta
attributes:
label: Bitwarden Beta
options:
- label: "I'm using the new native Bitwarden Beta app and I'm aware that legacy .NET app bugs should be reported in [bitwarden/mobile](https://github.com/bitwarden/mobile)"
validations:
required: true
- type: textarea
id: reproduce
attributes:
@@ -63,6 +51,22 @@ body:
description: What version of our software are you running?
validations:
required: true
- type: dropdown
id: server-region
attributes:
label: What server are you connecting to?
options:
- US
- EU
- Self-host
- N/A
validations:
required: true
- type: input
id: server-version
attributes:
label: Self-host Server Version
description: If self-hosting, what version of Bitwarden Server are you running?
- type: textarea
id: environment-details
attributes:

View File

@@ -1,8 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Legacy Android Bug Reports
url: https://github.com/bitwarden/mobile/issues
about: Bugs found in the publicly available .NET MAUI app should be reported in [bitwarden/mobile](https://github.com/bitwarden/mobile)
- name: Feature Requests
url: https://community.bitwarden.com/c/feature-requests/
about: Request new features using the Community Forums. Please search existing feature requests before making a new one.
@@ -15,3 +12,5 @@ contact_links:
- name: Security Issues
url: https://hackerone.com/bitwarden
about: We use HackerOne to manage security disclosures.
- name: Report mobile autofill failure
url: https://docs.google.com/forms/d/e/1FAIpQLScMopHyN7KGJs8hW562VTzbIGL4KcFnx0wJcsW0GYE1BnPiGA/viewform

View File

@@ -15,10 +15,11 @@
- Contributor guidelines followed
- All formatters and local linters executed and passed
- Written new unit and / or integration tests where applicable
- Protected functional changes with optionality (feature flags)
- Used internationalization (i18n) for all UI strings
- CI builds passed
- Communicated to DevOps any deployment requirements
- Updated any necessary documentation or informed the documentation team
- Updated any necessary documentation (Confluence, contributing docs) or informed the documentation team
## 🦮 Reviewer guidelines
@@ -27,8 +28,7 @@
- 👍 (`:+1:`) or similar for great changes
- 📝 (`:memo:`) or (`:information_source:`) for notes or general info
- ❓ (`:question:`) for questions
- 🤔 (`:thinking:`) or 💭 (`:thought_balloon:`) for more open inquiry that's not quite a confirmed
issue and could potentially benefit from discussion
- 🤔 (`:thinking:`) or 💭 (`:thought_balloon:`) for more open inquiry that's not quite a confirmed issue and could potentially benefit from discussion
- 🎨 (`:art:`) for suggestions / improvements
- ❌ (`:x:`) or ⚠️ (`:warning:`) for more significant problems or concerns needing attention
- 🌱 (`:seedling:`) or ♻️ (`:recycle:`) for future improvements or indications of technical debt

2
.github/codecov.yml vendored Normal file
View File

@@ -0,0 +1,2 @@
ignore:
- "src/test/**" # Tests

50
.github/renovate.json vendored
View File

@@ -1,32 +1,56 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["github>bitwarden/renovate-config"],
"enabledManagers": ["github-actions", "gradle", "bundler"],
"extends": [
"github>bitwarden/renovate-config"
],
"enabledManagers": [
"github-actions",
"gradle",
"bundler"
],
"packageRules": [
{
"groupName": "gh minor",
"matchManagers": ["github-actions"],
"matchUpdateTypes": ["minor", "patch"]
"matchManagers": [
"github-actions"
],
"matchUpdateTypes": [
"minor",
"patch"
]
},
{
"groupName": "gradle minor",
"matchUpdateTypes": ["minor", "patch"],
"matchManagers": ["gradle"]
"matchUpdateTypes": [
"minor",
"patch"
],
"matchManagers": [
"gradle"
]
},
{
"groupName": "kotlin",
"description": "Kotlin and Compose dependencies that must be updated together to maintain compatibility.",
"matchPackagePatterns": [
"androidx.compose:compose-bom",
"org.jetbrains.kotlin.*",
"com.google.devtools.ksp"
"matchManagers": [
"gradle"
],
"matchManagers": ["gradle"]
"matchPackageNames": [
"/androidx.compose:compose-bom/",
"/androidx.lifecycle:*/",
"/org.jetbrains.kotlin.*/",
"/com.google.devtools.ksp/"
]
},
{
"groupName": "bundler minor",
"matchUpdateTypes": ["minor", "patch"],
"matchManagers": ["bundler"]
"matchUpdateTypes": [
"minor",
"patch"
],
"matchManagers": [
"bundler"
]
}
]
}

View File

@@ -0,0 +1 @@
3.13

39
.github/scripts/validate-json/README.md vendored Normal file
View File

@@ -0,0 +1,39 @@
# JSON Validation Scripts
Utility scripts for validating JSON files and checking for duplicate package names between Google and Community privileged browser lists.
## Usage
### Validate a JSON file
```bash
python validate_json.py validate <json_file>
```
### Check for duplicates between two JSON files
```bash
python validate_json.py duplicates <json_file1> <json_file2> [output_file]
```
If `output_file` is not specified, duplicates will be saved to `duplicates.txt`.
## Running Tests
```bash
# Run all tests
python -m unittest test_validate_json.py
# Run the invalid JSON test individually
python -m unittest test_validate_json.TestValidateJson.test_validate_json_invalid
```
## Examples
```bash
# Validate Google privileged browsers list
python validate_json.py validate ../../app/src/main/assets/fido2_privileged_google.json
# Check for duplicates between Google and Community lists
python validate_json.py duplicates ../../app/src/main/assets/fido2_privileged_google.json ../../app/src/main/assets/fido2_privileged_community.json duplicates.txt
```

View File

@@ -0,0 +1,20 @@
{
"apps": [
"type": "android",
"info": {
"package_name": "com.android.chrome",
"signatures": [
{
"build": "release",
"cert_fingerprint_sha256": "F0:FD:6C:5B:41:0F:25:CB:25:C3:B5:33:46:C8:97:2F:AE:30:F8:EE:74:11:DF:91:04:80:AD:6B:2D:60:DB:83"
},
{
"build": "userdebug",
"cert_fingerprint_sha256": "19:75:B2:F1:71:77:BC:89:A5:DF:F3:1F:9E:64:A6:CA:E2:81:A5:3D:C1:D1:D5:9B:1D:14:7F:E1:C8:2A:FA:00"
}
]
}
}
]
}

View File

@@ -0,0 +1,48 @@
{
"apps": [
{
"type": "android",
"info": {
"package_name": "com.android.chrome",
"signatures": [
{
"build": "release",
"cert_fingerprint_sha256": "F0:FD:6C:5B:41:0F:25:CB:25:C3:B5:33:46:C8:97:2F:AE:30:F8:EE:74:11:DF:91:04:80:AD:6B:2D:60:DB:83"
},
{
"build": "userdebug",
"cert_fingerprint_sha256": "19:75:B2:F1:71:77:BC:89:A5:DF:F3:1F:9E:64:A6:CA:E2:81:A5:3D:C1:D1:D5:9B:1D:14:7F:E1:C8:2A:FA:00"
}
]
}
},
{
"type": "android",
"info": {
"package_name": "com.chrome.dev",
"signatures": [
{
"build": "release",
"cert_fingerprint_sha256": "90:44:EE:5F:EE:4B:BC:5E:21:DD:44:66:54:31:C4:EB:1F:1F:71:A3:27:16:A0:BC:92:7B:CB:B3:92:33:CA:BF"
},
{
"build": "release",
"cert_fingerprint_sha256": "3D:7A:12:23:01:9A:A3:9D:9E:A0:E3:43:6A:B7:C0:89:6B:FB:4F:B6:79:F4:DE:5F:E7:C2:3F:32:6C:8F:99:4A"
}
]
}
},
{
"type": "android",
"info": {
"package_name": "com.chrome.canary",
"signatures": [
{
"build": "release",
"cert_fingerprint_sha256": "DF:A1:FB:23:EF:BF:70:C5:BC:D1:44:3C:5B:EA:B0:4F:3F:2F:F4:36:6E:9A:C1:E3:45:76:39:A2:4C:FC"
}
]
}
}
]
}

View File

@@ -0,0 +1,20 @@
{
"apps": [
{
"type": "android",
"info": {
"package_name": "org.chromium.chrome",
"signatures": [
{
"build": "release",
"cert_fingerprint_sha256": "C6:AD:B8:B8:3C:6D:4C:17:D2:92:AF:DE:56:FD:48:8A:51:D3:16:FF:8F:2C:11:C5:41:02:23:BF:F8:A7:DB:B3"
},
{
"build": "userdebug",
"cert_fingerprint_sha256": "19:75:B2:F1:71:77:BC:89:A5:DF:F3:1F:9E:64:A6:CA:E2:81:A5:3D:C1:D1:D5:9B:1D:14:7F:E1:C8:2A:FA:00"
}
]
}
}
]
}

View File

@@ -0,0 +1,48 @@
#!/usr/bin/env python3
import unittest
import os
import json
from validate_json import validate_json, find_duplicates, get_package_names
from unittest.mock import patch
import io
class TestValidateJson(unittest.TestCase):
def setUp(self):
self.valid_file = os.path.join(os.path.dirname(__file__), "fixtures/sample-valid1.json")
self.valid_file2 = os.path.join(os.path.dirname(__file__), "fixtures/sample-valid2.json")
self.invalid_file = os.path.join(os.path.dirname(__file__), "fixtures/sample-invalid.json")
# Suppress stdout
self.stdout_patcher = patch('sys.stdout', new=io.StringIO())
self.stdout_patcher.start()
def tearDown(self):
self.stdout_patcher.stop()
def test_validate_json_valid(self):
"""Test validation of valid JSON file"""
self.assertTrue(validate_json(self.valid_file))
def test_validate_json_invalid(self):
"""Test validation of invalid JSON file"""
self.assertFalse(validate_json(self.invalid_file))
def test_find_duplicates(self):
"""Test when using the same file (should find duplicates)"""
expected_package_names = get_package_names(self.valid_file)
duplicates = find_duplicates(self.valid_file, self.valid_file)
self.assertEqual(len(duplicates), len(expected_package_names))
for package_name in expected_package_names:
self.assertIn(package_name, duplicates)
def test_find_duplicates_returns_empty_list_when_no_duplicates(self):
"""Test when using different files (should not find duplicates)"""
duplicates = find_duplicates(self.valid_file, self.valid_file2)
self.assertEqual(len(duplicates), 0)
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,145 @@
#!/usr/bin/env python3
import json
import sys
import os
from typing import List, Dict, Any, Set
def get_package_names(file_path: str) -> Set[str]:
"""
Extracts package names from a JSON file.
Args:
file_path: Path to the JSON file
Returns:
Set of package names
"""
with open(file_path, 'r') as f:
data = json.load(f)
package_names = set()
for app in data["apps"]:
package_names.add(app["info"]["package_name"])
return package_names
def validate_json(file_path: str) -> bool:
"""
Validates if a JSON file is correctly formatted by attempting to deserialize it.
Args:
file_path: Path to the JSON file to validate
Returns:
True if valid, False otherwise
"""
try:
if not os.path.exists(file_path):
print(f"Error: File {file_path} does not exist")
return False
with open(file_path, 'r') as f:
json.load(f)
print(f"✅ JSON file {file_path} is valid")
return True
except json.JSONDecodeError as e:
print(f"❌ Invalid JSON in {file_path}: {str(e)}")
return False
except Exception as e:
print(f"❌ Error validating {file_path}: {str(e)}")
return False
def find_duplicates(file1_path: str, file2_path: str) -> List[str]:
"""
Checks for duplicate package_name entries between two JSON files.
Args:
file1_path: Path to the first JSON file
file2_path: Path to the second JSON file
Returns:
List of duplicate package names, empty list if none found
"""
try:
# Get package names from both files
packages1 = get_package_names(file1_path)
packages2 = get_package_names(file2_path)
# Find duplicates
duplicates = list(packages1.intersection(packages2))
if duplicates:
print(f"❌ Found {len(duplicates)} duplicate package names between {file1_path} and {file2_path}:")
for dup in duplicates:
print(f" - {dup}")
return duplicates
else:
print(f"✅ No duplicate package names found between {file1_path} and {file2_path}")
return []
except Exception as e:
print(f"❌ Error checking duplicates: {str(e)}")
return []
def save_duplicates_to_file(duplicates: List[str], output_file: str) -> None:
"""
Saves the list of duplicates to a file.
Args:
duplicates: List of duplicate package names
output_file: Path to save the list of duplicates
"""
try:
with open(output_file, 'w') as f:
for dup in duplicates:
f.write(f"{dup}\n")
print(f"Duplicates saved to {output_file}")
except Exception as e:
print(f"❌ Error saving duplicates to file: {str(e)}")
def main():
if len(sys.argv) < 2:
print("Usage:")
print(" Validate JSON: python validate_json.py validate <json_file>")
print(" Check duplicates: python validate_json.py duplicates <json_file1> <json_file2> [output_file]")
sys.exit(1)
command = sys.argv[1]
match command:
case "validate":
if len(sys.argv) < 3:
print("Error: Missing JSON file path")
sys.exit(1)
file_path = sys.argv[2]
success = validate_json(file_path)
sys.exit(0 if success else 1)
case "duplicates":
if len(sys.argv) < 4:
print("Error: Missing JSON file paths")
sys.exit(1)
file1_path = sys.argv[2]
file2_path = sys.argv[3]
output_file = sys.argv[4] if len(sys.argv) > 4 else "duplicates.txt"
duplicates = find_duplicates(file1_path, file2_path)
if duplicates:
save_duplicates_to_file(duplicates, output_file)
sys.exit(0)
case _:
print(f"Unknown command: {command}")
sys.exit(1)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,289 @@
name: Build Authenticator
on:
push:
branches:
- main
workflow_dispatch:
inputs:
version-name:
description: "Optional. Version string to use, in X.Y.Z format. Overrides default in the project."
required: false
type: string
version-code:
description: "Optional. Build number to use. Overrides default of GitHub run number."
required: false
type: number
distribute-to-firebase:
description: "Optional. Distribute artifacts to Firebase."
required: false
default: false
type: boolean
publish-to-play-store:
description: "Optional. Deploy bundle artifact to Google Play Store"
required: false
default: false
type: boolean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
JAVA_VERSION: 17
jobs:
build:
name: Build Authenticator
runs-on: ubuntu-24.04
steps:
- name: Check out repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Validate Gradle wrapper
uses: gradle/actions/wrapper-validation@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2
- name: Cache Gradle files
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-v2-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', '**/libs.versions.toml') }}
restore-keys: |
${{ runner.os }}-gradle-v2-
- name: Cache build output
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
with:
path: |
${{ github.workspace }}/build-cache
key: ${{ runner.os }}-build-cache-${{ github.sha }}
restore-keys: |
${{ runner.os }}-build-
- name: Configure JDK
uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0
with:
distribution: "temurin"
java-version: ${{ env.JAVA_VERSION }}
- name: Configure Ruby
uses: ruby/setup-ruby@28c4deda893d5a96a6b2d958c5b47fc18d65c9d3 # v1.213.0
with:
bundler-cache: true
- name: Install Fastlane
run: |
gem install bundler:2.2.27
bundle config path vendor/bundle
bundle install --jobs 4 --retry 3
- name: Check Authenticator
run: bundle exec fastlane check
- name: Build Authenticator
run: bundle exec fastlane buildAuthenticatorDebug
publish_playstore:
name: Publish Authenticator Play Store artifacts
needs:
- build
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
variant: ["aab", "apk"]
steps:
- name: Check out repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Configure Ruby
uses: ruby/setup-ruby@28c4deda893d5a96a6b2d958c5b47fc18d65c9d3 # v1.213.0
with:
bundler-cache: true
- name: Install Fastlane
run: |
gem install bundler:2.2.27
bundle config path vendor/bundle
bundle install --jobs 4 --retry 3
- name: Log in to Azure
uses: Azure/login@cb79c773a3cfa27f31f25eb3f677781210c9ce3d # v1.6.1
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
- name: Retrieve secrets
env:
ACCOUNT_NAME: bitwardenci
CONTAINER_NAME: mobile
run: |
mkdir -p ${{ github.workspace }}/secrets
mkdir -p ${{ github.workspace }}/keystores
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME \
--name authenticator_apk-keystore.jks --file ${{ github.workspace }}/keystores/authenticator_apk-keystore.jks --output none
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME \
--name authenticator_aab-keystore.jks --file ${{ github.workspace }}/keystores/authenticator_aab-keystore.jks --output none
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME \
--name com.bitwarden.authenticator-google-services.json --file ${{ github.workspace }}/authenticator/src/google-services.json --output none
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME \
--name com.bitwarden.authenticator.dev-google-services.json --file ${{ github.workspace }}/authenticator/src/debug/google-services.json --output none
- name: Download Firebase credentials
if : ${{ inputs.distribute-to-firebase || github.event_name == 'push' }}
env:
ACCOUNT_NAME: bitwardenci
CONTAINER_NAME: mobile
run: |
mkdir -p ${{ github.workspace }}/secrets
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME \
--name authenticator_play_firebase-creds.json --file ${{ github.workspace }}/secrets/authenticator_play_firebase-creds.json --output none
- name: Download Play Store credentials
if: ${{ inputs.publish-to-play-store }}
env:
ACCOUNT_NAME: bitwardenci
CONTAINER_NAME: mobile
run: |
mkdir -p ${{ github.workspace }}/secrets
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME \
--name authenticator_play_store-creds.json --file ${{ github.workspace }}/secrets/authenticator_play_store-creds.json --output none
- name: Verify Play Store credentials
if: ${{ inputs.publish-to-play-store }}
run: |
bundle exec fastlane run validate_play_store_json_key \
json_key:${{ github.workspace }}/secrets/authenticator_play_store-creds.json }}
- name: Validate Gradle wrapper
uses: gradle/actions/wrapper-validation@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2
- name: Cache Gradle files
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-v2-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', '**/libs.versions.toml') }}
restore-keys: |
${{ runner.os }}-gradle-v2-
- name: Cache build output
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
with:
path: |
${{ github.workspace }}/build-cache
key: ${{ runner.os }}-build-cache-${{ github.sha }}
restore-keys: |
${{ runner.os }}-build-
- name: Configure JDK
uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0
with:
distribution: "temurin"
java-version: ${{ env.JAVA_VERSION }}
- name: Increment version
run: |
DEFAULT_VERSION_CODE=$GITHUB_RUN_NUMBER
VERSION_CODE="${{ inputs.version-code || '$DEFAULT_VERSION_CODE' }}"
bundle exec fastlane setAuthenticatorBuildVersionInfo \
versionCode:$VERSION_CODE \
versionName:${{ inputs.version-name || '' }}
regex='versionName = "([^"]+)"'
if [[ "$(cat authenticator/build.gradle.kts)" =~ $regex ]]; then
VERSION_NAME="${BASH_REMATCH[1]}"
fi
echo "Version Name: ${VERSION_NAME}" >> $GITHUB_STEP_SUMMARY
echo "Version Number: $VERSION_CODE" >> $GITHUB_STEP_SUMMARY
- name: Generate release Play Store bundle
if: ${{ matrix.variant == 'aab' }}
run: |
bundle exec fastlane bundleAuthenticatorRelease \
storeFile:${{ github.workspace }}/keystores/authenticator_aab-keystore.jks \
storePassword:'${{ secrets.BWA_AAB_KEYSTORE_STORE_PASSWORD }}' \
keyAlias:authenticatorupload \
keyPassword:'${{ secrets.BWA_AAB_KEYSTORE_KEY_PASSWORD }}'
- name: Generate release Play Store APK
if: ${{ matrix.variant == 'apk' }}
run: |
bundle exec fastlane buildAuthenticatorRelease \
storeFile:${{ github.workspace }}/keystores/authenticator_apk-keystore.jks \
storePassword:'${{ secrets.BWA_APK_KEYSTORE_STORE_PASSWORD }}' \
keyAlias:bitwardenauthenticator \
keyPassword:'${{ secrets.BWA_APK_KEYSTORE_KEY_PASSWORD }}'
- name: Upload release Play Store .aab artifact
if: ${{ matrix.variant == 'aab' }}
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: com.bitwarden.authenticator.aab
path: authenticator/build/outputs/bundle/release/com.bitwarden.authenticator.aab
if-no-files-found: error
- name: Upload release .apk artifact
if: ${{ matrix.variant == 'apk' }}
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: com.bitwarden.authenticator.apk
path: authenticator/build/outputs/apk/release/com.bitwarden.authenticator.apk
if-no-files-found: error
- name: Create checksum file for Release AAB
if: ${{ matrix.variant == 'aab' }}
run: |
sha256sum "authenticator/build/outputs/bundle/release/com.bitwarden.authenticator.aab" \
> ./authenticator-android-aab-sha256.txt
- name: Create checksum for release .apk artifact
if: ${{ matrix.variant == 'apk' }}
run: |
sha256sum "authenticator/build/outputs/apk/release/com.bitwarden.authenticator.apk" \
> ./authenticator-android-apk-sha256.txt
- name: Upload .apk SHA file for release
if: ${{ matrix.variant == 'apk' }}
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: authenticator-android-apk-sha256.txt
path: ./authenticator-android-apk-sha256.txt
if-no-files-found: error
- name: Upload .aab SHA file for release
if: ${{ matrix.variant == 'aab' }}
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: authenticator-android-aab-sha256.txt
path: ./authenticator-android-aab-sha256.txt
if-no-files-found: error
- name: Install Firebase app distribution plugin
if: ${{ inputs.distribute-to-firebase || github.event_name == 'push' }}
run: bundle exec fastlane add_plugin firebase_app_distribution
- name: Publish release bundle to Firebase
if: ${{ matrix.variant == 'aab' && (inputs.distribute-to-firebase || github.event_name == 'push') }}
env:
FIREBASE_CREDS_PATH: ${{ github.workspace }}/secrets/authenticator_play_firebase-creds.json
run: |
bundle exec fastlane distributeAuthenticatorReleaseBundleToFirebase \
serviceCredentialsFile:${{ env.FIREBASE_CREDS_PATH }}
# Only publish bundles to Play Store when `publish-to-play-store` is true while building
# bundles
- name: Publish release bundle to Google Play Store
if: ${{ inputs.publish-to-play-store && matrix.variant == 'aab' }}
env:
PLAY_STORE_CREDS_FILE: ${{ github.workspace }}/secrets/authenticator_play_store-creds.json
run: |
bundle exec fastlane publishAuthenticatorReleaseToGooglePlayStore \
serviceCredentialsFile:${{ env.PLAY_STORE_CREDS_FILE }} \

View File

@@ -37,13 +37,13 @@ jobs:
steps:
- name: Check out repo
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Validate Gradle wrapper
uses: gradle/actions/wrapper-validation@d156388eb19639ec20ade50009f3d199ce1e2808 # v4.1.0
uses: gradle/actions/wrapper-validation@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2
- name: Cache Gradle files
uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
with:
path: |
~/.gradle/caches
@@ -53,7 +53,7 @@ jobs:
${{ runner.os }}-gradle-v2-
- name: Cache build output
uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
with:
path: |
${{ github.workspace }}/build-cache
@@ -62,13 +62,13 @@ jobs:
${{ runner.os }}-build-
- name: Configure JDK
uses: actions/setup-java@b36c23c0d998641eff861008f374ee103c25ac73 # v4.4.0
uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0
with:
distribution: "temurin"
java-version: ${{ env.JAVA_VERSION }}
- name: Configure Ruby
uses: ruby/setup-ruby@f26937343756480a8cb3ae1f623b9c8d89ed6984 # v1.196.0
uses: ruby/setup-ruby@28c4deda893d5a96a6b2d958c5b47fc18d65c9d3 # v1.213.0
with:
bundler-cache: true
@@ -84,6 +84,13 @@ jobs:
- name: Build
run: bundle exec fastlane assembleDebugApks
- name: Upload test reports on failure
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
if: failure()
with:
name: test-reports
path: app/build/reports/tests/
publish_playstore:
name: Publish Play Store artifacts
needs:
@@ -96,10 +103,10 @@ jobs:
artifact: ["apk", "aab"]
steps:
- name: Check out repo
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Configure Ruby
uses: ruby/setup-ruby@f26937343756480a8cb3ae1f623b9c8d89ed6984 # v1.196.0
uses: ruby/setup-ruby@28c4deda893d5a96a6b2d958c5b47fc18d65c9d3 # v1.213.0
with:
bundler-cache: true
@@ -150,10 +157,10 @@ jobs:
--name app_play_prod_firebase-creds.json --file ${{ github.workspace }}/secrets/app_play_prod_firebase-creds.json --output none
- name: Validate Gradle wrapper
uses: gradle/actions/wrapper-validation@d156388eb19639ec20ade50009f3d199ce1e2808 # v4.1.0
uses: gradle/actions/wrapper-validation@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2
- name: Cache Gradle files
uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
with:
path: |
~/.gradle/caches
@@ -163,7 +170,7 @@ jobs:
${{ runner.os }}-gradle-v2-
- name: Cache build output
uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
with:
path: |
${{ github.workspace }}/build-cache
@@ -172,11 +179,20 @@ jobs:
${{ runner.os }}-build-
- name: Configure JDK
uses: actions/setup-java@b36c23c0d998641eff861008f374ee103c25ac73 # v4.4.0
uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0
with:
distribution: "temurin"
java-version: ${{ env.JAVA_VERSION }}
- name: Update app CI Build info
run: |
./scripts/update_app_ci_build_info.sh \
$GITHUB_REPOSITORY \
$GITHUB_REF_NAME \
$GITHUB_SHA \
$GITHUB_RUN_ID \
$GITHUB_RUN_ATTEMPT
- name: Increment version
run: |
DEFAULT_VERSION_CODE=$((11000+$GITHUB_RUN_NUMBER))
@@ -237,78 +253,78 @@ jobs:
- name: Upload release Play Store .aab artifact
if: ${{ (matrix.variant == 'prod') && (matrix.artifact == 'aab') }}
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: com.x8bit.bitwarden.aab
path: app/build/outputs/bundle/standardRelease/com.x8bit.bitwarden-standard-release.aab
path: app/build/outputs/bundle/standardRelease/com.x8bit.bitwarden.aab
if-no-files-found: error
- name: Upload beta Play Store .aab artifact
if: ${{ (matrix.variant == 'prod') && (matrix.artifact == 'aab') }}
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: com.x8bit.bitwarden.beta.aab
path: app/build/outputs/bundle/standardBeta/com.x8bit.bitwarden-standard-beta.aab
path: app/build/outputs/bundle/standardBeta/com.x8bit.bitwarden.beta.aab
if-no-files-found: error
- name: Upload release .apk artifact
if: ${{ (matrix.variant == 'prod') && (matrix.artifact == 'apk') }}
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: com.x8bit.bitwarden.apk
path: app/build/outputs/apk/standard/release/com.x8bit.bitwarden-standard-release.apk
path: app/build/outputs/apk/standard/release/com.x8bit.bitwarden.apk
if-no-files-found: error
- name: Upload beta .apk artifact
if: ${{ (matrix.variant == 'prod') && (matrix.artifact == 'apk') }}
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: com.x8bit.bitwarden.beta.apk
path: app/build/outputs/apk/standard/beta/com.x8bit.bitwarden-standard-beta.apk
path: app/build/outputs/apk/standard/beta/com.x8bit.bitwarden.beta.apk
if-no-files-found: error
# When building variants other than 'prod'
- name: Upload debug .apk artifact
if: ${{ (matrix.variant != 'prod') && (matrix.artifact == 'apk') }}
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: com.x8bit.bitwarden.${{ matrix.variant }}.apk
path: app/build/outputs/apk/standard/debug/com.x8bit.bitwarden-standard-debug.apk
path: app/build/outputs/apk/standard/debug/com.x8bit.bitwarden.dev.apk
if-no-files-found: error
- name: Create checksum for release .apk artifact
if: ${{ (matrix.variant == 'prod') && (matrix.artifact == 'apk') }}
run: |
sha256sum "app/build/outputs/apk/standard/release/com.x8bit.bitwarden-standard-release.apk" \
sha256sum "app/build/outputs/apk/standard/release/com.x8bit.bitwarden.apk" \
> ./com.x8bit.bitwarden.apk-sha256.txt
- name: Create checksum for beta .apk artifact
if: ${{ (matrix.variant == 'prod') && (matrix.artifact == 'apk') }}
run: |
sha256sum "app/build/outputs/apk/standard/beta/com.x8bit.bitwarden-standard-beta.apk" \
sha256sum "app/build/outputs/apk/standard/beta/com.x8bit.bitwarden.beta.apk" \
> ./com.x8bit.bitwarden.beta.apk-sha256.txt
- name: Create checksum for release .aab artifact
if: ${{ (matrix.variant == 'prod') && (matrix.artifact == 'aab') }}
run: |
sha256sum "app/build/outputs/bundle/standardRelease/com.x8bit.bitwarden-standard-release.aab" \
sha256sum "app/build/outputs/bundle/standardRelease/com.x8bit.bitwarden.aab" \
> ./com.x8bit.bitwarden.aab-sha256.txt
- name: Create checksum for beta .aab artifact
if: ${{ (matrix.variant == 'prod') && (matrix.artifact == 'aab') }}
run: |
sha256sum "app/build/outputs/bundle/standardBeta/com.x8bit.bitwarden-standard-beta.aab" \
sha256sum "app/build/outputs/bundle/standardBeta/com.x8bit.bitwarden.beta.aab" \
> ./com.x8bit.bitwarden.beta.aab-sha256.txt
- name: Create checksum for Debug .apk artifact
if: ${{ (matrix.variant != 'prod') && (matrix.artifact == 'apk') }}
run: |
sha256sum "app/build/outputs/apk/standard/debug/com.x8bit.bitwarden-standard-debug.apk" \
sha256sum "app/build/outputs/apk/standard/debug/com.x8bit.bitwarden.dev.apk" \
> ./com.x8bit.bitwarden.${{ matrix.variant }}.apk-sha256.txt
- name: Upload .apk SHA file for release
if: ${{ (matrix.variant == 'prod') && (matrix.artifact == 'apk') }}
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: com.x8bit.bitwarden.apk-sha256.txt
path: ./com.x8bit.bitwarden.apk-sha256.txt
@@ -316,7 +332,7 @@ jobs:
- name: Upload .apk SHA file for beta
if: ${{ (matrix.variant == 'prod') && (matrix.artifact == 'apk') }}
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: com.x8bit.bitwarden.beta.apk-sha256.txt
path: ./com.x8bit.bitwarden.beta.apk-sha256.txt
@@ -324,7 +340,7 @@ jobs:
- name: Upload .aab SHA file for release
if: ${{ (matrix.variant == 'prod') && (matrix.artifact == 'aab') }}
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: com.x8bit.bitwarden.aab-sha256.txt
path: ./com.x8bit.bitwarden.aab-sha256.txt
@@ -332,7 +348,7 @@ jobs:
- name: Upload .aab SHA file for beta
if: ${{ (matrix.variant == 'prod') && (matrix.artifact == 'aab') }}
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: com.x8bit.bitwarden.beta.aab-sha256.txt
path: ./com.x8bit.bitwarden.beta.aab-sha256.txt
@@ -340,7 +356,7 @@ jobs:
- name: Upload .apk SHA file for debug
if: ${{ (matrix.variant != 'prod') && (matrix.artifact == 'apk') }}
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: com.x8bit.bitwarden.${{ matrix.variant }}.apk-sha256.txt
path: ./com.x8bit.bitwarden.${{ matrix.variant }}.apk-sha256.txt
@@ -375,7 +391,9 @@ jobs:
- name: Publish Play Store bundle
if: ${{ matrix.variant == 'prod' && matrix.artifact == 'aab' && (inputs.publish-to-play-store || github.ref_name == 'main') }}
run: bundle exec fastlane publishBetaToPlayStore
run: |
bundle exec fastlane publishProdToPlayStore
bundle exec fastlane publishBetaToPlayStore
publish_fdroid:
name: Publish F-Droid artifacts
@@ -384,10 +402,10 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Check out repo
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Configure Ruby
uses: ruby/setup-ruby@f26937343756480a8cb3ae1f623b9c8d89ed6984 # v1.196.0
uses: ruby/setup-ruby@28c4deda893d5a96a6b2d958c5b47fc18d65c9d3 # v1.213.0
with:
bundler-cache: true
@@ -424,10 +442,10 @@ jobs:
--name app_fdroid_firebase-creds.json --file ${{ github.workspace }}/secrets/app_fdroid_firebase-creds.json --output none
- name: Validate Gradle wrapper
uses: gradle/actions/wrapper-validation@d156388eb19639ec20ade50009f3d199ce1e2808 # v4.1.0
uses: gradle/actions/wrapper-validation@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2
- name: Cache Gradle files
uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
with:
path: |
~/.gradle/caches
@@ -437,7 +455,7 @@ jobs:
${{ runner.os }}-gradle-v2-
- name: Cache build output
uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
with:
path: |
${{ github.workspace }}/build-cache
@@ -446,19 +464,35 @@ jobs:
${{ runner.os }}-build-
- name: Configure JDK
uses: actions/setup-java@b36c23c0d998641eff861008f374ee103c25ac73 # v4.4.0
uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0
with:
distribution: "temurin"
java-version: ${{ env.JAVA_VERSION }}
- name: Update app CI Build info
run: |
./scripts/update_app_ci_build_info.sh \
$GITHUB_REPOSITORY \
$GITHUB_REF_NAME \
$GITHUB_SHA \
$GITHUB_RUN_ID \
$GITHUB_RUN_ATTEMPT
# Start from 11000 to prevent collisions with mobile build version codes
- name: Increment version
run: |
DEFAULT_VERSION_CODE=$((11000+$GITHUB_RUN_NUMBER))
VERSION_CODE="${{ inputs.version-code || '$DEFAULT_VERSION_CODE' }}"
bundle exec fastlane setBuildVersionInfo \
versionCode:${{ inputs.version-code || '$DEFAULT_VERSION_CODE' }} \
versionCode:$VERSION_CODE \
versionName:${{ inputs.version-name || '' }}
regex='versionName = "([^"]+)"'
if [[ "$(cat app/build.gradle.kts)" =~ $regex ]]; then
VERSION_NAME="${BASH_REMATCH[1]}"
fi
echo "Version Name: ${VERSION_NAME}" >> $GITHUB_STEP_SUMMARY
echo "Version Number: $VERSION_CODE" >> $GITHUB_STEP_SUMMARY
- name: Generate F-Droid artifacts
env:
FDROID_STORE_PASSWORD: ${{ secrets.FDROID_KEYSTORE_PASSWORD }}
@@ -481,49 +515,49 @@ jobs:
keyPassword:"${{ env.FDROID_BETA_KEY_PASSWORD }}"
- name: Upload F-Droid .apk artifact
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: com.x8bit.bitwarden-fdroid.apk
path: app/build/outputs/apk/fdroid/release/com.x8bit.bitwarden-fdroid-release.apk
path: app/build/outputs/apk/fdroid/release/com.x8bit.bitwarden-fdroid.apk
if-no-files-found: error
- name: Create checksum for F-Droid artifact
run: |
sha256sum "app/build/outputs/apk/fdroid/release/com.x8bit.bitwarden-fdroid-release.apk" \
sha256sum "app/build/outputs/apk/fdroid/release/com.x8bit.bitwarden-fdroid.apk" \
> ./com.x8bit.bitwarden-fdroid.apk-sha256.txt
- name: Upload F-Droid SHA file
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: com.x8bit.bitwarden-fdroid.apk-sha256.txt
path: ./com.x8bit.bitwarden-fdroid.apk-sha256.txt
if-no-files-found: error
- name: Upload F-Droid Beta .apk artifact
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: com.x8bit.bitwarden.beta-fdroid.apk
path: app/build/outputs/apk/fdroid/beta/com.x8bit.bitwarden-fdroid-beta.apk
path: app/build/outputs/apk/fdroid/beta/com.x8bit.bitwarden.beta-fdroid.apk
if-no-files-found: error
- name: Create checksum for F-Droid Beta artifact
run: |
sha256sum "app/build/outputs/apk/fdroid/beta/com.x8bit.bitwarden-fdroid-beta.apk" \
sha256sum "app/build/outputs/apk/fdroid/beta/com.x8bit.bitwarden.beta-fdroid.apk" \
> ./com.x8bit.bitwarden.beta-fdroid.apk-sha256.txt
- name: Upload F-Droid Beta SHA file
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: com.x8bit.bitwarden.beta-fdroid.apk-sha256.txt
path: ./com.x8bit.bitwarden.beta-fdroid.apk-sha256.txt
if-no-files-found: error
- name: Install Firebase app distribution plugin
if: ${{ inputs.distribute_to_firebase || github.event_name == 'push' }}
if: ${{ inputs.distribute-to-firebase || github.event_name == 'push' }}
run: bundle exec fastlane add_plugin firebase_app_distribution
- name: Publish release F-Droid artifacts to Firebase
if: ${{ inputs.distribute_to_firebase || github.event_name == 'push' }}
if: ${{ inputs.distribute-to-firebase || github.event_name == 'push' }}
env:
APP_FDROID_FIREBASE_CREDS_PATH: ${{ github.workspace }}/secrets/app_fdroid_firebase-creds.json
run: |

View File

@@ -0,0 +1,98 @@
name: Cron / Sync Google Privileged Browsers List
on:
schedule:
# Run weekly on Monday at 00:00 UTC
- cron: '0 0 * * 1'
workflow_dispatch:
env:
SOURCE_URL: https://www.gstatic.com/gpm-passkeys-privileged-apps/apps.json
GOOGLE_FILE: app/src/main/assets/fido2_privileged_google.json
COMMUNITY_FILE: app/src/main/assets/fido2_privileged_community.json
jobs:
sync-privileged-browsers:
name: Sync Google Privileged Browsers List
runs-on: ubuntu-24.04
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2
- name: Download Google Privileged Browsers List
run: curl -s $SOURCE_URL -o $GOOGLE_FILE
- name: Check for changes
id: check-changes
run: |
if git diff --quiet -- $GOOGLE_FILE; then
echo "👀 No changes detected, skipping..."
echo "has_changes=false" >> $GITHUB_OUTPUT
exit 0
fi
echo "has_changes=true" >> $GITHUB_OUTPUT
echo "👀 Changes detected, validating fido2_privileged_google.json..."
python .github/scripts/validate-json/validate_json.py validate $GOOGLE_FILE
if [ $? -ne 0 ]; then
echo "::error::JSON validation failed for $GOOGLE_FILE"
exit 1
fi
echo "👀 fido2_privileged_google.json is valid, checking for duplicates..."
# Check for duplicates between Google and Community files
python .github/scripts/validate-json/validate_json.py duplicates $GOOGLE_FILE $COMMUNITY_FILE duplicates.txt
if [ -f duplicates.txt ]; then
echo "::warning::Duplicate package names found between Google and Community files."
echo "duplicates_found=true" >> $GITHUB_OUTPUT
else
echo "✅ No duplicate package names found between Google and Community files"
echo "duplicates_found=false" >> $GITHUB_OUTPUT
fi
- name: Create branch and commit
if: steps.check-changes.outputs.has_changes == 'true'
run: |
echo "👀 Committing fido2_privileged_google.json..."
BRANCH_NAME="cron-sync-privileged-browsers/$GITHUB_RUN_NUMBER-sync"
git config user.name "GitHub Actions Bot"
git config user.email "actions@github.com"
git checkout -b $BRANCH_NAME
git add $GOOGLE_FILE
git commit -m "Update Google privileged browsers list"
git push origin $BRANCH_NAME
echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_ENV
echo "🌱 Branch created: $BRANCH_NAME"
- name: Create Pull Request
if: steps.check-changes.outputs.has_changes == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DUPLICATES_FOUND: ${{ steps.check-changes.outputs.duplicates_found }}
BASE_PR_URL: ${{ github.server_url }}/${{ github.repository }}/pull/
run: |
PR_BODY="Updates the Google privileged browsers list with the latest data from $SOURCE_URL"
if [ "$DUPLICATES_FOUND" = "true" ]; then
PR_BODY="$PR_BODY\n\n> [!WARNING]\n> :suspect: The following package(s) appear in both Google and Community files:"
while IFS= read -r line; do
PR_BODY="$PR_BODY\n> - $line"
done < duplicates.txt
fi
# Use echo -e to interpret escape sequences and pipe to gh pr create
PR_URL=$(echo -e "$PR_BODY" | gh pr create \
--title "Update Google privileged browsers list" \
--body-file - \
--base main \
--head $BRANCH_NAME \
--label "automated-pr" \
--label "t:ci")

View File

@@ -0,0 +1,56 @@
name: Crowdin Sync - Authenticator
on:
workflow_dispatch:
inputs: {}
schedule:
- cron: '0 0 * * 5'
jobs:
crowdin-sync:
name: Autosync
runs-on: ubuntu-24.04
env:
_CROWDIN_PROJECT_ID: "673718"
steps:
- name: Check out repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Log in to Azure - CI Subscription
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
- name: Retrieve secrets
id: retrieve-secrets
uses: bitwarden/gh-actions/get-keyvault-secrets@main
with:
keyvault: "bitwarden-ci"
secrets: "github-gpg-private-key, github-gpg-private-key-passphrase"
- name: Generate GH App token
uses: actions/create-github-app-token@c1a285145b9d317df6ced56c09f525b5c2b6f755 # v1.11.1
id: app-token
with:
app-id: ${{ secrets.BW_GHAPP_ID }}
private-key: ${{ secrets.BW_GHAPP_KEY }}
- name: Download translations
uses: crowdin/github-action@d1632879d4d4da358f2d040f79fa094571c9a649 # v2.5.1
env:
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
CROWDIN_API_TOKEN: ${{ secrets.CROWDIN_API_TOKEN }}
with:
config: crowdin-bwa.yml
upload_sources: false
upload_translations: false
download_translations: true
github_user_name: "bitwarden-devops-bot"
github_user_email: "106330231+bitwarden-devops-bot@users.noreply.github.com"
commit_message: "Autosync the updated translations"
localization_branch_name: crowdin-auto-sync
create_pull_request: true
pull_request_title: "Autosync Crowdin Translations"
pull_request_body: "Autosync the updated translations"
gpg_private_key: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key }}
gpg_passphrase: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key-passphrase }}

View File

@@ -2,7 +2,7 @@ name: Crowdin Sync
on:
workflow_dispatch:
inputs: { }
inputs: {}
schedule:
- cron: '0 0 * * 5'
@@ -14,7 +14,7 @@ jobs:
_CROWDIN_PROJECT_ID: "269690"
steps:
- name: Checkout repo
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Login to Azure - CI Subscription
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
@@ -28,10 +28,17 @@ jobs:
keyvault: "bitwarden-ci"
secrets: "crowdin-api-token, github-gpg-private-key, github-gpg-private-key-passphrase"
- name: Generate GH App token
uses: actions/create-github-app-token@c1a285145b9d317df6ced56c09f525b5c2b6f755 # v1.11.1
id: app-token
with:
app-id: ${{ secrets.BW_GHAPP_ID }}
private-key: ${{ secrets.BW_GHAPP_KEY }}
- name: Download translations
uses: crowdin/github-action@95d6e895e871c3c7acf0cfb962f296baa41e63c6 # v2.2.0
uses: crowdin/github-action@d1632879d4d4da358f2d040f79fa094571c9a649 # v2.5.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}
with:
config: crowdin.yml

View File

@@ -0,0 +1,30 @@
name: Crowdin Push - Authenticator
on:
workflow_dispatch:
push:
branches:
- "main"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
JAVA_VERSION: 17
jobs:
crowdin-push:
name: Crowdin Push
runs-on: ubuntu-24.04
env:
_CROWDIN_PROJECT_ID: "673718"
steps:
- name: Check out repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Upload sources
uses: crowdin/github-action@d1632879d4d4da358f2d040f79fa094571c9a649 # v2.5.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CROWDIN_API_TOKEN: ${{ secrets.CROWDIN_API_TOKEN }}
with:
config: crowdin-bwa.yml
upload_sources: true
upload_translations: false

View File

@@ -14,7 +14,7 @@ jobs:
_CROWDIN_PROJECT_ID: "269690"
steps:
- name: Check out repo
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Log in to Azure
uses: Azure/login@cb79c773a3cfa27f31f25eb3f677781210c9ce3d # v1.6.1
@@ -23,13 +23,13 @@ jobs:
- name: Retrieve secrets
id: retrieve-secrets
uses: bitwarden/gh-actions/get-keyvault-secrets@2bd1450c2cdb2a8ac886232b8589696f22794229 # v0.2.0
uses: bitwarden/gh-actions/get-keyvault-secrets@main
with:
keyvault: "bitwarden-ci"
secrets: "crowdin-api-token"
- name: Upload sources
uses: crowdin/github-action@95d6e895e871c3c7acf0cfb962f296baa41e63c6 # v2.2.0
uses: crowdin/github-action@d1632879d4d4da358f2d040f79fa094571c9a649 # v2.5.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}

129
.github/workflows/github-release.yml vendored Normal file
View File

@@ -0,0 +1,129 @@
name: Create GitHub Release
on:
workflow_dispatch:
inputs:
version-name:
description: 'Version Name - E.g. "2024.11.1"'
required: true
type: string
version-number:
description: 'Version Number - E.g. "123456"'
required: true
type: string
artifact-run-id:
description: 'GitHub Action Run ID containing artifacts'
required: true
type: string
draft:
description: 'Create as draft release'
type: boolean
default: true
prerelease:
description: 'Mark as pre-release'
type: boolean
default: true
make-latest:
description: 'Set as the latest release'
type: boolean
branch-protection-type:
description: 'Branch protection type'
type: choice
options:
- Branch Name
- GitHub API
default: Branch Name
env:
ARTIFACTS_PATH: artifacts
jobs:
create-release:
name: Create GitHub Release
runs-on: ubuntu-24.04
permissions:
contents: write
actions: read
steps:
- name: Check out repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
- name: Get branch from workflow run
id: get_release_branch
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ARTIFACT_RUN_ID: ${{ inputs.artifact-run-id }}
BRANCH_PROTECTION_TYPE: ${{ inputs.branch-protection-type }}
run: |
release_branch=$(gh run view $ARTIFACT_RUN_ID --json headBranch -q .headBranch)
case "$BRANCH_PROTECTION_TYPE" in
"Branch Name")
if [[ "$release_branch" != "main" && ! "$release_branch" =~ ^release/ ]]; then
echo "::error::Branch '$release_branch' is not 'main' or a release branch starting with 'release/'. Releases must be created from protected branches."
exit 1
fi
;;
"GitHub API")
#NOTE requires token with "administration:read" scope
if ! gh api "repos/${{ github.repository }}/branches/$release_branch/protection" | grep -q "required_status_checks"; then
echo "::error::Branch '$release_branch' is not protected. Releases must be created from protected branches. If that's not correct, confirm if the github token user has the 'administration:read' scope."
exit 1
fi
;;
*)
echo "::error::Unsupported branch protection type: $BRANCH_PROTECTION_TYPE"
exit 1
;;
esac
echo "release_branch=$release_branch" >> $GITHUB_OUTPUT
- name: Download artifacts
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ARTIFACT_RUN_ID: ${{ inputs.artifact-run-id }}
run: |
gh run download $ARTIFACT_RUN_ID -D $ARTIFACTS_PATH
file_count=$(find $ARTIFACTS_PATH -type f | wc -l)
echo "Downloaded $file_count file(s)."
if [ "$file_count" -gt 0 ]; then
echo "Downloaded files:"
find $ARTIFACTS_PATH -type f
fi
- name: Create Release
id: create_release
uses: softprops/action-gh-release@c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda # v2.2.1
with:
tag_name: "v${{ inputs.version-name }}"
name: "${{ inputs.version-name }} (${{ inputs.version-number }})"
prerelease: ${{ inputs.prerelease }}
draft: ${{ inputs.draft }}
make_latest: ${{ inputs.make-latest }}
target_commitish: ${{ steps.get_release_branch.outputs.release_branch }}
generate_release_notes: true
files: |
artifacts/**/*
- name: Update Release Description
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RELEASE_ID: ${{ steps.create_release.outputs.id }}
RELEASE_URL: ${{ steps.create_release.outputs.url }}
ARTIFACT_RUN_ID: ${{ inputs.artifact-run-id }}
run: |
# Get current release body
current_body=$(gh api /repos/${{ github.repository }}/releases/$RELEASE_ID --jq .body)
# Append build source to the end
updated_body="${current_body}
**Builds Source:** https://github.com/${{ github.repository }}/actions/runs/$ARTIFACT_RUN_ID"
# Update release
gh api --method PATCH /repos/${{ github.repository }}/releases/$RELEASE_ID \
-f body="$updated_body"
echo "# :rocket: Release ready at:" >> $GITHUB_STEP_SUMMARY
echo "$RELEASE_URL" >> $GITHUB_STEP_SUMMARY

58
.github/workflows/release-branch.yml vendored Normal file
View File

@@ -0,0 +1,58 @@
name: Cut Release Branch
on:
workflow_dispatch:
inputs:
release_type:
description: 'Release Type'
required: true
type: choice
options:
- RC
- Hotfix
jobs:
create-release-branch:
name: Create Release Branch
runs-on: ubuntu-24.04
permissions:
contents: write
steps:
- name: Check out repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
- name: Create RC Branch
if: inputs.release_type == 'RC'
env:
RC_PREFIX_DATE: "true" # replace with input if needed
run: |
if [ "$RC_PREFIX_DATE" = "true" ]; then
current_date=$(date +'%Y.%m')
branch_name="release/${current_date}-rc${{ github.run_number }}"
else
branch_name="release/rc${{ github.run_number }}"
fi
git switch main
git switch -c $branch_name
git push origin $branch_name
echo "# :cherry_blossom: RC branch: ${branch_name}" >> $GITHUB_STEP_SUMMARY
- name: Create Hotfix Branch
if: inputs.release_type == 'Hotfix'
run: |
latest_tag=$(git tag -l --sort=-creatordate | head -n 1)
if [ -z "$latest_tag" ]; then
echo "::error::No tags found in the repository"
exit 1
fi
branch_name="release/hotfix-${latest_tag}"
echo "🌿 branch name: $branch_name"
if git show-ref --verify --quiet "refs/remotes/origin/$branch_name"; then
echo "# :fire: :warning: Hotfix branch already exists: ${branch_name}" >> $GITHUB_STEP_SUMMARY
exit 0
fi
git switch -c $branch_name $latest_tag
git push origin $branch_name
echo "# :fire: Hotfix branch: ${branch_name}" >> $GITHUB_STEP_SUMMARY

60
.github/workflows/scan-ci.yml vendored Normal file
View File

@@ -0,0 +1,60 @@
name: Scan Protected Branches On Push
on:
workflow_dispatch:
push:
branches:
- "main"
jobs:
sast:
name: SAST scan
runs-on: ubuntu-24.04
permissions:
contents: read
security-events: write
steps:
- name: Check out repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
- name: Scan with Checkmarx
uses: checkmarx/ast-github-action@184bf2f64f55d1c93fd6636d539edf274703e434 # 2.0.41
with:
project_name: ${{ github.repository }}
cx_tenant: ${{ secrets.CHECKMARX_TENANT }}
base_uri: https://ast.checkmarx.net/
cx_client_id: ${{ secrets.CHECKMARX_CLIENT_ID }}
cx_client_secret: ${{ secrets.CHECKMARX_SECRET }}
additional_params: |
--report-format sarif \
--filter "state=TO_VERIFY;PROPOSED_NOT_EXPLOITABLE;CONFIRMED;URGENT" \
--output-path .
- name: Upload Checkmarx results to GitHub
uses: github/codeql-action/upload-sarif@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c # v3.28.1
with:
sarif_file: cx_result.sarif
quality:
name: Quality scan
runs-on: ubuntu-24.04
permissions:
contents: read
steps:
- name: Check out repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
- name: Scan with SonarCloud
uses: sonarsource/sonarqube-scan-action@bfd4e558cda28cda6b5defafb9232d191be8c203 # v4.2.1
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
with:
args: >
-Dsonar.organization=${{ github.repository_owner }}
-Dsonar.projectKey=${{ github.repository_owner }}_${{ github.event.repository.name }}

View File

@@ -1,16 +1,9 @@
name: Scan
name: Scan Pull Requests
on:
workflow_dispatch:
push:
branches:
- "main"
- "rc"
- "hotfix-rc"
pull_request_target:
types: [opened, synchronize]
merge_group:
types: [checks_requested]
jobs:
check-run:
@@ -28,12 +21,12 @@ jobs:
steps:
- name: Check out repo
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Scan with Checkmarx
uses: checkmarx/ast-github-action@f0869bd1a37fddc06499a096101e6c900e815d81 # 2.0.36
uses: checkmarx/ast-github-action@184bf2f64f55d1c93fd6636d539edf274703e434 # 2.0.41
env:
INCREMENTAL: "${{ contains(github.event_name, 'pull_request') && '--sast-incremental' || '' }}"
with:
@@ -48,9 +41,11 @@ jobs:
--output-path . ${{ env.INCREMENTAL }}
- name: Upload Checkmarx results to GitHub
uses: github/codeql-action/upload-sarif@f779452ac5af1c261dce0346a8f964149f49322b # v3.26.13
uses: github/codeql-action/upload-sarif@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c # v3.28.1
with:
sarif_file: cx_result.sarif
sha: ${{ contains(github.event_name, 'pull_request') && github.event.pull_request.head.sha || github.sha }}
ref: ${{ contains(github.event_name, 'pull_request') && format('refs/pull/{0}/head', github.event.pull_request.number) || github.ref }}
quality:
name: Quality scan
@@ -62,17 +57,17 @@ jobs:
steps:
- name: Check out repo
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
ref: ${{ github.event.pull_request.head.sha }}
- name: Scan with SonarCloud
uses: sonarsource/sonarcloud-github-action@383f7e52eae3ab0510c3cb0e7d9d150bbaeab838 # v3.1.0
uses: sonarsource/sonarqube-scan-action@bfd4e558cda28cda6b5defafb9232d191be8c203 # v4.2.1
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
args: >
-Dsonar.organization=${{ github.repository_owner }}
-Dsonar.projectKey=${{ github.repository_owner }}_${{ github.event.repository.name }}
-Dsonar.pullrequest.key=${{ github.event.pull_request.number }}

View File

@@ -6,42 +6,34 @@ on:
- "main"
- "rc"
- "hotfix-rc"
pull_request_target:
pull_request:
types: [opened, synchronize]
merge_group:
type: [checks_requested]
workflow_dispatch:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
JAVA_VERSION: 17
_JAVA_VERSION: 17
_GITHUB_ACTION_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}/attempts/${{ github.run_attempt }}
jobs:
check-run:
name: Check PR run
uses: bitwarden/gh-actions/.github/workflows/check-run.yml@main
test:
name: Test
runs-on: ubuntu-24.04
needs: check-run
permissions:
contents: read
issues: write
packages: read
pull-requests: write
steps:
- name: Check out repo
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
with:
ref: ${{ github.event.pull_request.head.sha }}
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Validate Gradle wrapper
uses: gradle/actions/wrapper-validation@d156388eb19639ec20ade50009f3d199ce1e2808 # v4.1.0
uses: gradle/actions/wrapper-validation@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2
- name: Cache Gradle files
uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
with:
path: |
~/.gradle/caches
@@ -51,7 +43,7 @@ jobs:
${{ runner.os }}-gradle-v2-
- name: Cache build output
uses: actions/cache@3624ceb22c1c5a301c8db4169662070a689d9ea8 # v4.1.1
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
with:
path: |
${{ github.workspace }}/build-cache
@@ -60,15 +52,15 @@ jobs:
${{ runner.os }}-build-
- name: Configure Ruby
uses: ruby/setup-ruby@f26937343756480a8cb3ae1f623b9c8d89ed6984 # v1.196.0
uses: ruby/setup-ruby@28c4deda893d5a96a6b2d958c5b47fc18d65c9d3 # v1.213.0
with:
bundler-cache: true
- name: Configure JDK
uses: actions/setup-java@b36c23c0d998641eff861008f374ee103c25ac73 # v4.4.0
uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0
with:
distribution: "temurin"
java-version: ${{ env.JAVA_VERSION }}
java-version: ${{ env._JAVA_VERSION }}
- name: Install Fastlane
run: |
@@ -77,12 +69,48 @@ jobs:
bundle install --jobs 4 --retry 3
- name: Build and test
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Used in settings.gradle.kts to download the SDK from GitHub Maven Packages
run: |
bundle exec fastlane check
- name: Upload to codecov.io
uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4.6.0
- name: Upload test reports
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
if: always()
with:
file: app/build/reports/kover/reportStandardDebug.xml
name: test-reports
path: |
build/reports/kover/reportMergedCoverage.xml
app/build/reports/tests/
authenticator/build/reports/tests/
authenticatorbridge/build/reports/tests/
core/build/reports/tests/
data/build/reports/tests/
network/build/reports/tests/
ui/build/reports/tests/
- name: Upload to codecov.io
id: upload-to-codecov
uses: codecov/codecov-action@1e68e06f1dbfde0e4cefc87efeba9e4643565303 # v5.1.2
if: github.event_name == 'push' || github.event_name == 'pull_request'
continue-on-error: true
with:
os: linux
files: build/reports/kover/reportMergedCoverage.xml
fail_ci_if_error: true
disable_search: true
- name: Comment PR if tests failed
if: steps.upload-to-codecov.outcome == 'failure' && (github.event_name == 'push' || github.event_name == 'pull_request')
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
PR_NUMBER: ${{ github.event.number }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RUN_ACTOR: ${{ github.triggering_actor }}
run: |
echo "> [!WARNING]" >> $GITHUB_STEP_SUMMARY
echo "> Uploading code coverage report failed. Please check the \"Upload to codecov.io\" step of \"Process Test Reports\" job for more details." >> $GITHUB_STEP_SUMMARY
if [ ! -z "$PR_NUMBER" ]; then
message=$'> [!WARNING]\n> @'$RUN_ACTOR' Uploading code coverage report failed. Please check the "Upload to codecov.io" step of [Process Test Reports job]('$_GITHUB_ACTION_RUN_URL') for more details.'
gh pr comment --repo $GITHUB_REPOSITORY $PR_NUMBER --body "$message"
fi

10
.gitignore vendored
View File

@@ -25,5 +25,13 @@ user.properties
# Secrets
/keystores/*.jks
/app/src/standardDebug/google-services.json
/app/src/standardBeta/google-services.json
/app/src/standardRelease/google-services.json
/authenticator/src/google-services.json
# Python
.python-version
__pycache__/
# Generated by .github/scripts/validate-json/validate-json.py
duplicates.txt

1
.husky/pre-commit Executable file
View File

@@ -0,0 +1 @@
npx lint-staged

View File

@@ -9,21 +9,23 @@ GEM
public_suffix (>= 2.0.2, < 7.0)
artifactory (3.0.17)
atomos (0.1.3)
aws-eventstream (1.3.0)
aws-partitions (1.989.0)
aws-sdk-core (3.209.1)
aws-eventstream (1.3.2)
aws-partitions (1.1084.0)
aws-sdk-core (3.222.1)
aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.651.0)
aws-partitions (~> 1, >= 1.992.0)
aws-sigv4 (~> 1.9)
base64
jmespath (~> 1, >= 1.6.1)
aws-sdk-kms (1.94.0)
aws-sdk-core (~> 3, >= 3.207.0)
logger
aws-sdk-kms (1.99.0)
aws-sdk-core (~> 3, >= 3.216.0)
aws-sigv4 (~> 1.5)
aws-sdk-s3 (1.167.0)
aws-sdk-core (~> 3, >= 3.207.0)
aws-sdk-s3 (1.183.0)
aws-sdk-core (~> 3, >= 3.216.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.5)
aws-sigv4 (1.10.0)
aws-sigv4 (1.11.0)
aws-eventstream (~> 1, >= 1.0.2)
babosa (1.0.4)
base64 (0.2.0)
@@ -32,9 +34,9 @@ GEM
colored2 (3.1.2)
commander (4.6.0)
highline (~> 2.0.0)
date (3.3.4)
date (3.4.1)
declarative (0.0.20)
digest-crc (0.6.5)
digest-crc (0.7.0)
rake (>= 12.0.0, < 14.0.0)
domain_name (0.6.20240107)
dotenv (2.8.1)
@@ -59,8 +61,8 @@ GEM
faraday-em_synchrony (1.0.0)
faraday-excon (1.1.0)
faraday-httpclient (1.0.1)
faraday-multipart (1.0.4)
multipart-post (~> 2)
faraday-multipart (1.1.0)
multipart-post (~> 2.0)
faraday-net_http (1.0.2)
faraday-net_http_persistent (1.2.0)
faraday-patron (1.0.0)
@@ -68,8 +70,8 @@ GEM
faraday-retry (1.0.3)
faraday_middleware (1.2.1)
faraday (~> 1.0)
fastimage (2.3.1)
fastlane (2.224.0)
fastimage (2.4.0)
fastlane (2.227.1)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.8, < 3.0.0)
artifactory (~> 3.0)
@@ -85,6 +87,7 @@ GEM
faraday-cookie_jar (~> 0.0.6)
faraday_middleware (~> 1.0)
fastimage (>= 2.1.0, < 3.0.0)
fastlane-sirp (>= 1.0.0)
gh_inspector (>= 1.1.2, < 2.0.0)
google-apis-androidpublisher_v3 (~> 0.3)
google-apis-playcustomapp_v1 (~> 0.1)
@@ -108,11 +111,13 @@ GEM
tty-spinner (>= 0.8.0, < 1.0.0)
word_wrap (~> 1.0.0)
xcodeproj (>= 1.13.0, < 2.0.0)
xcpretty (~> 0.3.0)
xcpretty (~> 0.4.1)
xcpretty-travis-formatter (>= 0.0.3, < 2.0.0)
fastlane-plugin-firebase_app_distribution (0.9.1)
fastlane-plugin-firebase_app_distribution (0.10.0)
google-apis-firebaseappdistribution_v1 (~> 0.3.0)
google-apis-firebaseappdistribution_v1alpha (~> 0.2.0)
fastlane-sirp (1.0.0)
sysrandom (~> 1.0)
gh_inspector (1.1.3)
google-apis-androidpublisher_v3 (0.54.0)
google-apis-core (>= 0.11.0, < 2.a)
@@ -134,12 +139,12 @@ GEM
google-apis-core (>= 0.11.0, < 2.a)
google-apis-storage_v1 (0.31.0)
google-apis-core (>= 0.11.0, < 2.a)
google-cloud-core (1.7.1)
google-cloud-core (1.8.0)
google-cloud-env (>= 1.0, < 3.a)
google-cloud-errors (~> 1.0)
google-cloud-env (1.6.0)
faraday (>= 0.17.3, < 3.0)
google-cloud-errors (1.4.0)
google-cloud-errors (1.5.0)
google-cloud-storage (1.47.0)
addressable (~> 2.8)
digest-crc (~> 0.4)
@@ -155,23 +160,26 @@ GEM
os (>= 0.9, < 2.0)
signet (>= 0.16, < 2.a)
highline (2.0.3)
http-cookie (1.0.7)
http-cookie (1.0.8)
domain_name (~> 0.5)
httpclient (2.8.3)
httpclient (2.9.0)
mutex_m
jmespath (1.6.2)
json (2.7.2)
jwt (2.9.3)
json (2.10.2)
jwt (2.10.1)
base64
logger (1.7.0)
mini_magick (4.13.2)
mini_mime (1.1.5)
multi_json (1.15.0)
multipart-post (2.4.1)
nanaimo (0.3.0)
mutex_m (0.3.0)
nanaimo (0.4.0)
naturally (2.2.1)
nkf (0.2.0)
optparse (0.5.0)
optparse (0.6.0)
os (1.1.4)
plist (3.7.1)
plist (3.7.2)
public_suffix (6.0.1)
rake (13.2.1)
representable (3.2.0)
@@ -179,10 +187,10 @@ GEM
trailblazer-option (>= 0.1.1, < 0.2.0)
uber (< 0.2.0)
retriable (3.1.2)
rexml (3.3.8)
rouge (2.0.7)
rexml (3.4.1)
rouge (3.28.0)
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
rubyzip (2.4.1)
security (0.1.5)
signet (0.19.0)
addressable (~> 2.8)
@@ -192,10 +200,11 @@ GEM
simctl (1.6.10)
CFPropertyList
naturally
sysrandom (1.0.5)
terminal-notifier (2.0.0)
terminal-table (3.0.2)
unicode-display_width (>= 1.1.1, < 3)
time (0.4.0)
time (0.4.1)
date
trailblazer-option (0.1.2)
tty-cursor (0.7.1)
@@ -205,15 +214,15 @@ GEM
uber (0.1.0)
unicode-display_width (2.6.0)
word_wrap (1.0.0)
xcodeproj (1.25.1)
xcodeproj (1.27.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0)
colored2 (~> 3.1)
nanaimo (~> 0.3.0)
nanaimo (~> 0.4.0)
rexml (>= 3.3.6, < 4.0)
xcpretty (0.3.0)
rouge (~> 2.0.7)
xcpretty (0.4.1)
rouge (~> 3.28.0)
xcpretty-travis-formatter (1.0.1)
xcpretty (~> 0.2, >= 0.0.7)
@@ -229,4 +238,4 @@ RUBY VERSION
ruby 3.3.1p55
BUNDLED WITH
2.5.9
2.6.6

61
README-bwa.md Normal file
View File

@@ -0,0 +1,61 @@
[![Github Workflow build on main](https://github.com/bitwarden/authenticator-android/actions/workflows/build-authenticator.yml/badge.svg?branch=main)](https://github.com/bitwarden/authenticator-android/actions/workflows/build-authenticator.yml?query=branch:main)
[![Join the chat at https://gitter.im/bitwarden/Lobby](https://badges.gitter.im/bitwarden/Lobby.svg)](https://gitter.im/bitwarden/Lobby)
# Bitwarden Authenticator Android App
<a href="https://play.google.com/store/apps/details?id=com.bitwarden.authenticator" target="_blank"><img alt="Get it on Google Play" src="https://imgur.com/YQzmZi9.png" width="153" height="46"></a>
Bitwarden Authenticator allows you easily store and generate two-factor authentication codes on your device. The Bitwarden Authenticator Android application is written in Kotlin.
<img src="https://raw.githubusercontent.com/bitwarden/brand/master/screenshots/authenticator-android-codes.png" alt="" width="325" height="650" />
## Compatibility
- **Minimum SDK**: 28
- **Target SDK**: 34
- **Device Types Supported**: Phone and Tablet
- **Orientations Supported**: Portrait and Landscape
## Setup
1. Clone the repository:
```sh
$ git clone https://github.com/bitwarden/authenticator-android
```
2. Create a `user.properties` file in the root directory of the project and add the following properties:
- `gitHubToken`: A "classic" Github Personal Access Token (PAT) with the `read:packages` scope (ex: `gitHubToken=gph_xx...xx`). These can be generated by going to the [Github tokens page](https://github.com/settings/tokens). See [the Github Packages user documentation concerning authentication](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-gradle-registry#authenticating-to-github-packages) for more details.
3. Setup the code style formatter:
All code must follow the guidelines described in the [Code Style Guidelines document](docs/STYLE_AND_BEST_PRACTICES.md). To aid in adhering to these rules, all contributors should apply `docs/bitwarden-style.xml` as their code style scheme. In IntelliJ / Android Studio:
- Navigate to `Preferences > Editor > Code Style`.
- Hit the `Manage` button next to `Scheme`.
- Select `Import`.
- Find the `bitwarden-style.xml` file in the project's `docs/` directory.
- Import "from" `BitwardenStyle` "to" `BitwardenStyle`.
- Hit `Apply` and `OK` to save the changes and exit Preferences.
Note that in some cases you may need to restart Android Studio for the changes to take effect.
All code should be formatted before submitting a pull request. This can be done manually but it can also be helpful to create a macro with a custom keyboard binding to auto-format when saving. In Android Studio on OS X:
- Select `Edit > Macros > Start Macro Recording`
- Select `Code > Optimize Imports`
- Select `Code > Reformat Code`
- Select `File > Save All`
- Select `Edit > Macros > Stop Macro Recording`
This can then be mapped to a set of keys by navigating to `Android Studio > Preferences` and editing the macro under `Keymap` (ex : shift + command + s).
Please avoid mixing formatting and logical changes in the same commit/PR. When possible, fix any large formatting issues in a separate PR before opening one to make logical changes to the same code. This helps others focus on the meaningful code changes when reviewing the code.
## Contribute
Code contributions are welcome! Please commit any pull requests against the `main` branch. Learn more about how to contribute by reading the [Contributing Guidelines](https://contributing.bitwarden.com/contributing/). Check out the [Contributing Documentation](https://contributing.bitwarden.com/) for how to get started with your first contribution.
Security audits and feedback are welcome. Please open an issue or email us privately if the report is sensitive in nature. You can read our security policy in the [`SECURITY.md`](SECURITY.md) file.

View File

@@ -1,7 +1,4 @@
# Bitwarden Android (BETA)
> [!TIP]
> This repo has the new native Android app, currently in [Beta](https://community.bitwarden.com/t/about-the-beta-program/39185). Looking for the legacy .NET MAUI apps? Head on over to [bitwarden/mobile](https://github.com/bitwarden/mobile)
# Bitwarden Android
## Contents
@@ -12,7 +9,7 @@
## Compatibility
- **Minimum SDK**: 29
- **Target SDK**: 34
- **Target SDK**: 35
- **Device Types Supported**: Phone and Tablet
- **Orientations Supported**: Portrait and Landscape
@@ -61,6 +58,11 @@
The following is a list of all third-party dependencies included as part of the application beyond the standard Android SDK.
- **AndroidX Activity**
- https://developer.android.com/jetpack/androidx/releases/activity
- Purpose: Allows access composable APIs built on top of Activity.
- License: Apache 2.0
- **AndroidX Appcompat**
- https://developer.android.com/jetpack/androidx/releases/appcompat
- Purpose: Allows access to new APIs on older API versions.
@@ -81,7 +83,7 @@ The following is a list of all third-party dependencies included as part of the
- Purpose: Displays webpages with the user's default browser.
- License: Apache 2.0
- **AndroidX CameraX Camera2**
- **AndroidX Camera**
- https://developer.android.com/jetpack/androidx/releases/camera
- Purpose: Display and capture images for barcode scanning.
- License: Apache 2.0
@@ -91,9 +93,9 @@ The following is a list of all third-party dependencies included as part of the
- Purpose: A Kotlin-based declarative UI framework.
- License: Apache 2.0
- **AndroidX Core SplashScreen**
- **AndroidX Core**
- https://developer.android.com/jetpack/androidx/releases/core
- Purpose: Backwards compatible SplashScreen API implementation.
- Purpose: Backwards compatible platform features and APIs.
- License: Apache 2.0
- **AndroidX Credentials**
@@ -106,6 +108,11 @@ The following is a list of all third-party dependencies included as part of the
- Purpose: Lifecycle aware components and tooling.
- License: Apache 2.0
- **AndroidX Navigation**
- https://developer.android.com/jetpack/androidx/releases/navigation
- Purpose: Provides a consistent API for navigating between Android components.
- License: Apache 2.0
- **AndroidX Room**
- https://developer.android.com/jetpack/androidx/releases/room
- Purpose: A convenient SQLite-based persistence layer for Android.
@@ -126,16 +133,6 @@ The following is a list of all third-party dependencies included as part of the
- Purpose: Dependency injection framework.
- License: Apache 2.0
- **Firebase Cloud Messaging**
- https://github.com/firebase/firebase-android-sdk
- Purpose: Allows for push notification support. (**NOTE:** This dependency is not included in builds distributed via F-Droid.)
- License: Apache 2.0
- **Firebase Crashlytics**
- https://github.com/firebase/firebase-android-sdk
- Purpose: SDK for crash and non-fatal error reporting. (**NOTE:** This dependency is not included in builds distributed via F-Droid.)
- License: Apache 2.0
- **Glide**
- https://github.com/bumptech/glide
- Purpose: Image loading and caching.
@@ -156,11 +153,6 @@ The following is a list of all third-party dependencies included as part of the
- Purpose: JSON serialization library for Kotlin.
- License: Apache 2.0
- **kotlinx.serialization converter**
- https://github.com/square/retrofit/tree/trunk/retrofit-converters/kotlinx-serialization
- Purpose: Converter for Retrofit 2 and kotlinx.serialization.
- License: Apache 2.0
- **OkHttp 3**
- https://github.com/square/okhttp
- Purpose: An HTTP client used by the library to intercept and log traffic.
@@ -171,16 +163,33 @@ The following is a list of all third-party dependencies included as part of the
- Purpose: A networking layer interface.
- License: Apache 2.0
- **zxcvbn4j**
- https://github.com/nulab/zxcvbn4j
- Purpose: Password strength estimation.
- License: MIT
- **Timber**
- https://github.com/JakeWharton/timber
- Purpose: Extensible logging library for Android.
- License: Apache 2.0
- **ZXing**
- https://github.com/zxing/zxing
- Purpose: Barcode scanning and generation.
- License: Apache 2.0
The following is an additional list of third-party dependencies that are only included in the non-F-Droid build variants of the application.
- **Firebase Cloud Messaging**
- https://github.com/firebase/firebase-android-sdk
- Purpose: Allows for push notification support.
- License: Apache 2.0
- **Firebase Crashlytics**
- https://github.com/firebase/firebase-android-sdk
- Purpose: SDK for crash and non-fatal error reporting.
- License: Apache 2.0
- **Google Play Reviews**
- https://developer.android.com/reference/com/google/android/play/core/release-notes
- Purpose: On standard builds provide an interface to add a review for the password manager application in Google Play.
- License: Apache 2.0
### Development Environment Dependencies
The following is a list of additional third-party dependencies used as part of the local development environment. This includes test-related artifacts as well as tools related to code quality and linting. These are not present in the final packaged application.

View File

@@ -1,21 +1,32 @@
Bitwarden believes that working with security researchers across the globe is crucial to keeping our users safe. If you believe you've found a security issue in our product or service, we encourage you to please submit a report through our [HackerOne Program](https://hackerone.com/bitwarden/). We welcome working with you to resolve the issue promptly. Thanks in advance!
Bitwarden believes that working with security researchers across the globe is crucial to keeping our
users safe. If you believe you've found a security issue in our product or service, we encourage you
to please submit a report through our [HackerOne Program](https://hackerone.com/bitwarden/). We
welcome working with you to resolve the issue promptly. Thanks in advance!
# Disclosure Policy
- Let us know as soon as possible upon discovery of a potential security issue, and we'll make every effort to quickly resolve the issue.
- Provide us a reasonable amount of time to resolve the issue before any disclosure to the public or a third-party. We may publicly disclose the issue before resolving it, if appropriate.
- Make a good faith effort to avoid privacy violations, destruction of data, and interruption or degradation of our service. Only interact with accounts you own or with explicit permission of the account holder.
- If you would like to encrypt your report, please use the PGP key with long ID `0xDE6887086F892325FEC04CC0D847525B6931381F` (available in the public keyserver pool).
- Let us know as soon as possible upon discovery of a potential security issue, and we'll make every
effort to quickly resolve the issue.
- Provide us a reasonable amount of time to resolve the issue before any disclosure to the public or
a third-party. We may publicly disclose the issue before resolving it, if appropriate.
- Make a good faith effort to avoid privacy violations, destruction of data, and interruption or
degradation of our service. Only interact with accounts you own or with explicit permission of the
account holder.
- If you would like to encrypt your report, please use the PGP key with long ID
`0xDE6887086F892325FEC04CC0D847525B6931381F` (available in the public keyserver pool).
While researching, we'd like to ask you to refrain from:
- Denial of service
- Spamming
- Social engineering (including phishing) of Bitwarden staff or contractors
- Any physical attempts against Bitwarden property or data centers
- Denial of service
- Spamming
- Social engineering (including phishing) of Bitwarden staff or contractors
- Any physical attempts against Bitwarden property or data centers
# We want to help you!
If you have something that you feel is close to exploitation, or if you'd like some information regarding the internal API, or generally have any questions regarding the app that would help in your efforts, please email us at https://bitwarden.com/contact and ask for that information. As stated above, Bitwarden wants to help you find issues, and is more than willing to help.
If you have something that you feel is close to exploitation, or if you'd like some information
regarding the internal API, or generally have any questions regarding the app that would help in
your efforts, please email us at https://bitwarden.com/contact and ask for that information. As
stated above, Bitwarden wants to help you find issues, and is more than willing to help.
Thank you for helping keep Bitwarden and our users safe!

View File

@@ -1,6 +1,9 @@
import com.android.build.gradle.internal.api.BaseVariantOutputImpl
import com.android.utils.cxx.io.removeExtensionIfPresent
import com.google.firebase.crashlytics.buildtools.gradle.tasks.InjectMappingFileIdTask
import com.google.firebase.crashlytics.buildtools.gradle.tasks.UploadMappingFileTask
import com.google.gms.googleservices.GoogleServicesTask
import dagger.hilt.android.plugin.util.capitalize
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import java.io.FileInputStream
import java.util.Properties
@@ -10,16 +13,13 @@ plugins {
// Crashlytics is enabled for all builds initially but removed for FDroid builds in gradle and
// standardDebug builds in the merged manifest.
alias(libs.plugins.crashlytics)
alias(libs.plugins.detekt)
alias(libs.plugins.hilt)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose.compiler)
alias(libs.plugins.kotlin.parcelize)
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.kotlinx.kover)
alias(libs.plugins.ksp)
alias(libs.plugins.google.services)
alias(libs.plugins.sonarqube)
}
/**
@@ -32,6 +32,16 @@ val userProperties = Properties().apply {
}
}
/**
* Loads CI-specific build properties that are not checked into source control.
*/
val ciProperties = Properties().apply {
val ciPropsFile = File(rootDir, "ci.properties")
if (ciPropsFile.exists()) {
FileInputStream(ciPropsFile).use { load(it) }
}
}
android {
namespace = "com.x8bit.bitwarden"
compileSdk = libs.versions.compileSdk.get().toInt()
@@ -41,7 +51,7 @@ android {
minSdk = libs.versions.minSdk.get().toInt()
targetSdk = libs.versions.targetSdk.get().toInt()
versionCode = 1
versionName = "2024.9.0"
versionName = "2025.4.0"
setProperty("archivesBaseName", "com.x8bit.bitwarden")
@@ -51,6 +61,12 @@ android {
}
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
buildConfigField(
type = "String",
name = "CI_INFO",
value = "${ciProperties.getOrDefault("ci.info", "\"local\"")}",
)
}
androidResources {
@@ -75,6 +91,7 @@ android {
isMinifyEnabled = false
buildConfigField(type = "boolean", name = "HAS_DEBUG_MENU", value = "true")
buildConfigField(type = "boolean", name = "HAS_LOGS_ENABLED", value = "true")
}
// Beta and Release variants are identical except beta has a different package name
@@ -82,22 +99,25 @@ android {
applicationIdSuffix = ".beta"
isDebuggable = false
isMinifyEnabled = true
matchingFallbacks += listOf("release")
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
"proguard-rules.pro",
)
buildConfigField(type = "boolean", name = "HAS_DEBUG_MENU", value = "false")
buildConfigField(type = "boolean", name = "HAS_LOGS_ENABLED", value = "false")
}
release {
isDebuggable = false
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
"proguard-rules.pro",
)
buildConfigField(type = "boolean", name = "HAS_DEBUG_MENU", value = "false")
buildConfigField(type = "boolean", name = "HAS_LOGS_ENABLED", value = "false")
}
}
@@ -112,6 +132,39 @@ android {
}
}
applicationVariants.all {
val bundlesDir = "${layout.buildDirectory.get()}/outputs/bundle"
outputs
.mapNotNull { it as? BaseVariantOutputImpl }
.forEach { output ->
val fileNameWithoutExtension = when (flavorName) {
"fdroid" -> "$applicationId-$flavorName"
"standard" -> "$applicationId"
else -> output.outputFileName.removeExtensionIfPresent(".apk")
}
// Set the APK output filename.
output.outputFileName = "$fileNameWithoutExtension.apk"
val variantName = name
val renameTaskName = "rename${variantName.capitalize()}AabFiles"
tasks.register(renameTaskName) {
group = "build"
description = "Renames the bundle files for $variantName variant"
doLast {
renameFile(
"$bundlesDir/$variantName/$namespace-$flavorName-${buildType.name}.aab",
"$fileNameWithoutExtension.aab",
)
}
}
// Force renaming task to execute after the variant is built.
tasks
.getByName("bundle${variantName.capitalize()}")
.finalizedBy(renameTaskName)
}
}
compileOptions {
sourceCompatibility(libs.versions.jvmTarget.get())
targetCompatibility(libs.versions.jvmTarget.get())
@@ -125,14 +178,16 @@ android {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
}
@Suppress("UnstableApiUsage")
testOptions {
// Required for Robolectric
unitTests.isIncludeAndroidResources = true
unitTests.isReturnDefaultValues = true
}
lint {
disable.add("MissingTranslation")
disable += listOf(
"MissingTranslation",
"ExtraTranslation",
)
}
}
@@ -156,8 +211,12 @@ dependencies {
add("standardImplementation", dependencyNotation)
}
// TODO: this should use a versioned AAR instead of referencing a local AAR BITAU-94
implementation(files("libs/authenticatorbridge-0.1.0-SNAPSHOT-release.aar"))
implementation(files("libs/authenticatorbridge-1.0.0-release.aar"))
implementation(project(":core"))
implementation(project(":data"))
implementation(project(":network"))
implementation(project(":ui"))
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.appcompat)
@@ -170,6 +229,7 @@ dependencies {
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.compose.animation)
implementation(libs.androidx.compose.material3)
implementation(libs.androidx.compose.material3.adaptive)
implementation(libs.androidx.compose.runtime)
implementation(libs.androidx.compose.ui)
implementation(libs.androidx.compose.ui.graphics)
@@ -194,12 +254,12 @@ dependencies {
implementation(libs.kotlinx.collections.immutable)
implementation(libs.kotlinx.coroutines.android)
implementation(libs.kotlinx.serialization)
implementation(libs.nulab.zxcvbn4j)
implementation(libs.square.okhttp)
implementation(libs.square.okhttp.logging)
implementation(platform(libs.square.retrofit.bom))
implementation(libs.square.retrofit)
implementation(libs.square.retrofit.kotlinx.serialization)
implementation(libs.timber)
implementation(libs.zxing.zxing.core)
// For now we are restricted to running Compose tests for debug builds only
@@ -210,9 +270,16 @@ dependencies {
standardImplementation(libs.google.firebase.cloud.messaging)
standardImplementation(platform(libs.google.firebase.bom))
standardImplementation(libs.google.firebase.crashlytics)
standardImplementation(libs.google.play.review)
// Pull in test fixtures from other modules
testImplementation(testFixtures(project(":data")))
testImplementation(testFixtures(project(":network")))
testImplementation(libs.androidx.compose.ui.test)
testImplementation(libs.google.hilt.android.testing)
testImplementation(platform(libs.junit.bom))
testRuntimeOnly(libs.junit.platform.launcher)
testImplementation(libs.junit.junit5)
testImplementation(libs.junit.vintage)
testImplementation(libs.kotlinx.coroutines.test)
@@ -220,117 +287,39 @@ dependencies {
testImplementation(libs.robolectric.robolectric)
testImplementation(libs.square.okhttp.mockwebserver)
testImplementation(libs.square.turbine)
detektPlugins(libs.detekt.detekt.formatting)
detektPlugins(libs.detekt.detekt.rules)
}
detekt {
autoCorrect = true
config.from(files("$rootDir/detekt-config.yml"))
}
kover {
currentProject {
sources {
excludeJava = true
}
}
reports {
filters {
excludes {
androidGeneratedClasses()
annotatedBy(
// Compose previews
"androidx.compose.ui.tooling.preview.Preview",
"androidx.compose.ui.tooling.preview.PreviewScreenSizes",
// Manually excluded classes/files/etc.
"com.x8bit.bitwarden.data.platform.annotation.OmitFromCoverage",
)
classes(
// Navigation helpers
"*.*NavigationKt*",
// Composable singletons
"*.*ComposableSingletons*",
// Generated classes related to interfaces with default values
"*.*DefaultImpls*",
// Databases
"*.database.*Database*",
"*.dao.*Dao*",
// Dagger Hilt
"dagger.hilt.*",
"hilt_aggregated_deps.*",
"*_Factory",
"*_Factory\$*",
"*_*Factory",
"*_*Factory\$*",
"*.Hilt_*",
"*_HiltModules",
"*_HiltModules\$*",
"*_Impl",
"*_Impl\$*",
"*_MembersInjector",
)
packages(
// Dependency injection
"*.di",
// Models
"*.model",
// Custom UI components
"com.x8bit.bitwarden.ui.platform.components",
// Theme-related code
"com.x8bit.bitwarden.ui.platform.theme",
)
}
}
}
}
tasks {
getByName("check") {
// Add detekt with type resolution to check
dependsOn("detekt")
}
withType<io.gitlab.arturbosch.detekt.Detekt>().configureEach {
jvmTarget = libs.versions.jvmTarget.get()
}
withType<io.gitlab.arturbosch.detekt.DetektCreateBaselineTask>().configureEach {
jvmTarget = libs.versions.jvmTarget.get()
}
withType<Test> {
useJUnitPlatform()
maxHeapSize = "2g"
maxParallelForks = Runtime.getRuntime().availableProcessors()
jvmArgs = jvmArgs.orEmpty() + "-XX:+UseParallelGC"
android.sourceSets["main"].res.srcDirs("src/test/res")
}
}
afterEvaluate {
// Disable Fdroid-specific tasks that we want to exclude
val tasks = tasks.withType<GoogleServicesTask>() +
val fdroidTasksToDisable = tasks.withType<GoogleServicesTask>() +
tasks.withType<InjectMappingFileIdTask>() +
tasks.withType<UploadMappingFileTask>()
tasks
fdroidTasksToDisable
.filter { it.name.contains("Fdroid") }
.forEach { it.enabled = false }
}
sonar {
properties {
property("sonar.projectKey", "bitwarden_android")
property("sonar.organization", "bitwarden")
property("sonar.host.url", "https://sonarcloud.io")
property("sonar.sources", "app/src/")
property("sonar.tests", "app/src/")
property("sonar.test.inclusions", "app/src/test/")
property("sonar.exclusions", "app/src/test/")
private fun renameFile(path: String, newName: String) {
val originalFile = File(path)
if (!originalFile.exists()) {
println("File $originalFile does not exist!")
return
}
}
tasks {
getByName("sonar") {
dependsOn("check")
val newFile = File(originalFile.parentFile, newName)
if (originalFile.renameTo(newFile)) {
println("Renamed $originalFile to $newFile")
} else {
throw RuntimeException("Failed to rename $originalFile to $newFile")
}
}

Binary file not shown.

View File

@@ -6,6 +6,10 @@
# we keep it here.
-keep class com.bitwarden.** { *; }
# The Android Verifier component must be kept because it looks like dead code. Proguard is unable to
# see any JNI usage, so our rules must manually opt into keeping it.
-keep, includedescriptorclasses class org.rustls.platformverifier.** { *; }
################################################################################
# Bitwarden Models
################################################################################

View File

@@ -0,0 +1,256 @@
{
"formatVersion": 1,
"database": {
"version": 4,
"identityHash": "f7906c69e0a2c065d4d3be140fc721b6",
"entities": [
{
"tableName": "ciphers",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `user_id` TEXT NOT NULL, `cipher_type` TEXT NOT NULL, `cipher_json` TEXT NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "userId",
"columnName": "user_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "cipherType",
"columnName": "cipher_type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "cipherJson",
"columnName": "cipher_json",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_ciphers_user_id",
"unique": false,
"columnNames": [
"user_id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_ciphers_user_id` ON `${TABLE_NAME}` (`user_id`)"
}
],
"foreignKeys": []
},
{
"tableName": "collections",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `user_id` TEXT NOT NULL, `organization_id` TEXT NOT NULL, `should_hide_passwords` INTEGER NOT NULL, `name` TEXT NOT NULL, `external_id` TEXT, `read_only` INTEGER NOT NULL, `manage` INTEGER NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "userId",
"columnName": "user_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "organizationId",
"columnName": "organization_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "shouldHidePasswords",
"columnName": "should_hide_passwords",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "externalId",
"columnName": "external_id",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "isReadOnly",
"columnName": "read_only",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "canManage",
"columnName": "manage",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_collections_user_id",
"unique": false,
"columnNames": [
"user_id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_collections_user_id` ON `${TABLE_NAME}` (`user_id`)"
}
],
"foreignKeys": []
},
{
"tableName": "domains",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_id` TEXT NOT NULL, `domains_json` TEXT NOT NULL, PRIMARY KEY(`user_id`))",
"fields": [
{
"fieldPath": "userId",
"columnName": "user_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "domainsJson",
"columnName": "domains_json",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"user_id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "folders",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `user_id` TEXT NOT NULL, `name` TEXT, `revision_date` INTEGER NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "userId",
"columnName": "user_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "revisionDate",
"columnName": "revision_date",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_folders_user_id",
"unique": false,
"columnNames": [
"user_id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_folders_user_id` ON `${TABLE_NAME}` (`user_id`)"
}
],
"foreignKeys": []
},
{
"tableName": "sends",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `user_id` TEXT NOT NULL, `send_type` TEXT NOT NULL, `send_json` TEXT NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "userId",
"columnName": "user_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sendType",
"columnName": "send_type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sendJson",
"columnName": "send_json",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_sends_user_id",
"unique": false,
"columnNames": [
"user_id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_sends_user_id` ON `${TABLE_NAME}` (`user_id`)"
}
],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f7906c69e0a2c065d4d3be140fc721b6')"
]
}
}

View File

@@ -0,0 +1,256 @@
{
"formatVersion": 1,
"database": {
"version": 5,
"identityHash": "ee697e71290c92fe5b607d0b7665481b",
"entities": [
{
"tableName": "ciphers",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `user_id` TEXT NOT NULL, `cipher_type` TEXT NOT NULL, `cipher_json` TEXT NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "userId",
"columnName": "user_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "cipherType",
"columnName": "cipher_type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "cipherJson",
"columnName": "cipher_json",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_ciphers_user_id",
"unique": false,
"columnNames": [
"user_id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_ciphers_user_id` ON `${TABLE_NAME}` (`user_id`)"
}
],
"foreignKeys": []
},
{
"tableName": "collections",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `user_id` TEXT NOT NULL, `organization_id` TEXT NOT NULL, `should_hide_passwords` INTEGER NOT NULL, `name` TEXT NOT NULL, `external_id` TEXT, `read_only` INTEGER NOT NULL, `manage` INTEGER NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "userId",
"columnName": "user_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "organizationId",
"columnName": "organization_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "shouldHidePasswords",
"columnName": "should_hide_passwords",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "externalId",
"columnName": "external_id",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "isReadOnly",
"columnName": "read_only",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "canManage",
"columnName": "manage",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_collections_user_id",
"unique": false,
"columnNames": [
"user_id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_collections_user_id` ON `${TABLE_NAME}` (`user_id`)"
}
],
"foreignKeys": []
},
{
"tableName": "domains",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_id` TEXT NOT NULL, `domains_json` TEXT, PRIMARY KEY(`user_id`))",
"fields": [
{
"fieldPath": "userId",
"columnName": "user_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "domainsJson",
"columnName": "domains_json",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"user_id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "folders",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `user_id` TEXT NOT NULL, `name` TEXT, `revision_date` INTEGER NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "userId",
"columnName": "user_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "revisionDate",
"columnName": "revision_date",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_folders_user_id",
"unique": false,
"columnNames": [
"user_id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_folders_user_id` ON `${TABLE_NAME}` (`user_id`)"
}
],
"foreignKeys": []
},
{
"tableName": "sends",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `user_id` TEXT NOT NULL, `send_type` TEXT NOT NULL, `send_json` TEXT NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "userId",
"columnName": "user_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sendType",
"columnName": "send_type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sendJson",
"columnName": "send_json",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_sends_user_id",
"unique": false,
"columnNames": [
"user_id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_sends_user_id` ON `${TABLE_NAME}` (`user_id`)"
}
],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'ee697e71290c92fe5b607d0b7665481b')"
]
}
}

View File

@@ -0,0 +1,256 @@
{
"formatVersion": 1,
"database": {
"version": 6,
"identityHash": "ee158c483edfe5102504670f3d9845d4",
"entities": [
{
"tableName": "ciphers",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `user_id` TEXT NOT NULL, `cipher_type` TEXT NOT NULL, `cipher_json` TEXT NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "userId",
"columnName": "user_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "cipherType",
"columnName": "cipher_type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "cipherJson",
"columnName": "cipher_json",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_ciphers_user_id",
"unique": false,
"columnNames": [
"user_id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_ciphers_user_id` ON `${TABLE_NAME}` (`user_id`)"
}
],
"foreignKeys": []
},
{
"tableName": "collections",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `user_id` TEXT NOT NULL, `organization_id` TEXT NOT NULL, `should_hide_passwords` INTEGER NOT NULL, `name` TEXT NOT NULL, `external_id` TEXT, `read_only` INTEGER NOT NULL, `manage` INTEGER, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "userId",
"columnName": "user_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "organizationId",
"columnName": "organization_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "shouldHidePasswords",
"columnName": "should_hide_passwords",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "externalId",
"columnName": "external_id",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "isReadOnly",
"columnName": "read_only",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "canManage",
"columnName": "manage",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_collections_user_id",
"unique": false,
"columnNames": [
"user_id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_collections_user_id` ON `${TABLE_NAME}` (`user_id`)"
}
],
"foreignKeys": []
},
{
"tableName": "domains",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_id` TEXT NOT NULL, `domains_json` TEXT, PRIMARY KEY(`user_id`))",
"fields": [
{
"fieldPath": "userId",
"columnName": "user_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "domainsJson",
"columnName": "domains_json",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"user_id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "folders",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `user_id` TEXT NOT NULL, `name` TEXT, `revision_date` INTEGER NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "userId",
"columnName": "user_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "revisionDate",
"columnName": "revision_date",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_folders_user_id",
"unique": false,
"columnNames": [
"user_id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_folders_user_id` ON `${TABLE_NAME}` (`user_id`)"
}
],
"foreignKeys": []
},
{
"tableName": "sends",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `user_id` TEXT NOT NULL, `send_type` TEXT NOT NULL, `send_json` TEXT NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "userId",
"columnName": "user_id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sendType",
"columnName": "send_type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sendJson",
"columnName": "send_json",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "index_sends_user_id",
"unique": false,
"columnNames": [
"user_id"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `index_sends_user_id` ON `${TABLE_NAME}` (`user_id`)"
}
],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'ee158c483edfe5102504670f3d9845d4')"
]
}
}

View File

@@ -5,5 +5,5 @@
AuthenticatorBridgeService in the beta variant. If later another variant of the
Bitwarden Authenticator app is added, a SHA-256 digest of that variant's APK can be added here.
-->
<string name="known_authenticator_app_cert">@null</string>
<string-array name="known_authenticator_app_certs" />
</resources>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
<shortcut
android:enabled="true"
android:icon="@mipmap/ic_generator_shortcut"
android:shortcutId="bitwarden_password_generator"
android:shortcutLongLabel="@string/password_generator"
android:shortcutShortLabel="@string/password_generator">
<intent
android:action="android.intent.action.VIEW"
android:data="bitwarden://password_generator"
android:targetClass="com.x8bit.bitwarden.MainActivity"
android:targetPackage="com.x8bit.bitwarden.beta" />
</shortcut>
<shortcut
android:enabled="true"
android:icon="@mipmap/ic_vault_shortcut"
android:shortcutId="bitwarden_my_vault"
android:shortcutLongLabel="@string/my_vault"
android:shortcutShortLabel="@string/my_vault">
<intent
android:action="android.intent.action.VIEW"
android:data="bitwarden://my_vault"
android:targetClass="com.x8bit.bitwarden.MainActivity"
android:targetPackage="com.x8bit.bitwarden.beta" />
</shortcut>
</shortcuts>

View File

@@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- This is the SHA-256 digest for the Authenticator App debug variant:-->
<string name="known_authenticator_app_cert">13144ab52af797a88c2fe292674461ef1715e0e1e4f5f538f63f1c174696f476</string>
<string-array name="known_authenticator_app_certs">
<!-- This is the SHA-256 digest for the Authenticator App debug variant:-->
<item>13144ab52af797a88c2fe292674461ef1715e0e1e4f5f538f63f1c174696f476</item>
</string-array>
</resources>

View File

@@ -1,16 +0,0 @@
package com.x8bit.bitwarden.data.platform.manager
import com.x8bit.bitwarden.data.platform.datasource.disk.legacy.LegacyAppCenterMigrator
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
/**
* CrashLogsManager implementation for F-droid flavor builds.
*/
class CrashLogsManagerImpl(
settingsRepository: SettingsRepository,
legacyAppCenterMigrator: LegacyAppCenterMigrator,
) : CrashLogsManager {
override var isEnabled: Boolean = true
override fun trackNonFatalException(throwable: Throwable) = Unit
}

View File

@@ -0,0 +1,27 @@
package com.x8bit.bitwarden.data.platform.manager
import com.bitwarden.data.repository.model.Environment
import com.x8bit.bitwarden.BuildConfig
import com.x8bit.bitwarden.data.platform.datasource.disk.legacy.LegacyAppCenterMigrator
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
import timber.log.Timber
/**
* [LogsManager] implementation for F-droid flavor builds.
*/
class LogsManagerImpl(
settingsRepository: SettingsRepository,
legacyAppCenterMigrator: LegacyAppCenterMigrator,
) : LogsManager {
init {
if (BuildConfig.HAS_LOGS_ENABLED) {
Timber.plant(Timber.DebugTree())
}
}
override var isEnabled: Boolean = false
override fun setUserData(userId: String?, environmentType: Environment.Type) = Unit
override fun trackNonFatalException(throwable: Throwable) = Unit
}

View File

@@ -0,0 +1,12 @@
package com.x8bit.bitwarden.ui.platform.manager.review
import android.app.Activity
/**
* No-op implementation of [AppReviewManager] for F-Droid builds.
*/
class AppReviewManagerImpl(
activity: Activity,
) : AppReviewManager {
override fun promptForReview() = Unit
}

View File

@@ -15,7 +15,7 @@
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.READ_USER_DICTIONARY"/>
<!-- Protect access to AuthenticatorBridgeService using this custom permission.
Note that each build type uses a different value for knownCerts.
@@ -25,7 +25,7 @@
app. -->
<permission
android:name="${applicationId}.permission.AUTHENTICATOR_BRIDGE_SERVICE"
android:knownCerts="@string/known_authenticator_app_cert"
android:knownCerts="@array/known_authenticator_app_certs"
android:label="Bitwarden Bridge"
android:protectionLevel="signature|knownSigner"
tools:targetApi="s" />
@@ -320,6 +320,11 @@
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME" />
</intent>
<!-- To Query Chrome Beta: -->
<package android:name="com.chrome.beta" />
<!-- To Query Chrome Stable: -->
<package android:name="com.android.chrome" />
</queries>
</manifest>

View File

@@ -1,5 +1,17 @@
{
"apps": [
{
"type": "android",
"info": {
"package_name": "io.github.forkmaintainers.iceraven",
"signatures": [
{
"build": "release",
"cert_fingerprint_sha256": "9C:0D:22:37:9F:48:7B:70:A4:F9:F8:BE:C0:17:3C:F9:1A:16:44:F0:8F:93:38:5B:5B:78:2C:E3:76:60:BA:81"
}
]
}
},
{
"type": "android",
"info": {
@@ -24,6 +36,18 @@
]
}
},
{
"type": "android",
"info": {
"package_name": "org.ironfoxoss.ironfox",
"signatures": [
{
"build": "release",
"cert_fingerprint_sha256": "C5:E2:91:B5:A5:71:F9:C8:CD:9A:97:99:C2:C9:4E:02:EC:97:03:94:88:93:F2:CA:75:6D:67:B9:42:04:F9:04"
}
]
}
},
{
"type": "android",
"info": {
@@ -35,34 +59,6 @@
}
]
}
},
{
"type": "android",
"info": {
"package_name": "us.spotco.fennec_dos",
"signatures": [
{
"build": "release",
"cert_fingerprint_sha256": "26:0E:0A:49:67:8C:78:B7:0C:02:D6:53:7A:DD:3B:6D:C0:A1:71:71:BB:DE:8C:E7:5F:D4:02:6A:8A:3E:18:D2"
},
{
"build": "release",
"cert_fingerprint_sha256": "FF:81:F5:BE:56:39:65:94:EE:E7:0F:EF:28:32:25:6E:15:21:41:22:E2:BA:9C:ED:D2:60:05:FF:D4:BC:AA:A8"
}
]
}
},
{
"type": "android",
"info": {
"package_name": "us.spotco.mulch",
"signatures": [
{
"build": "release",
"cert_fingerprint_sha256": "26:0E:0A:49:67:8C:78:B7:0C:02:D6:53:7A:DD:3B:6D:C0:A1:71:71:BB:DE:8C:E7:5F:D4:02:6A:8A:3E:18:D2"
}
]
}
}
]
}

View File

@@ -160,6 +160,78 @@
]
}
},
{
"type": "android",
"info": {
"package_name": "org.mozilla.fenix",
"signatures": [
{
"build": "release",
"cert_fingerprint_sha256": "50:04:77:90:88:E7:F9:88:D5:BC:5C:C5:F8:79:8F:EB:F4:F8:CD:08:4A:1B:2A:46:EF:D4:C8:EE:4A:EA:F2:11"
}
]
}
},
{
"type": "android",
"info": {
"package_name": "org.mozilla.fenix.debug",
"signatures": [
{
"build": "userdebug",
"cert_fingerprint_sha256": "BD:AE:82:02:80:D2:AF:B7:74:94:EF:22:58:AA:78:A9:AE:A1:36:41:7E:8B:C2:3D:C9:87:75:2E:6F:48:E8:48"
}
]
}
},
{
"type": "android",
"info": {
"package_name": "org.mozilla.focus.beta",
"signatures": [
{
"build": "release",
"cert_fingerprint_sha256": "62:03:A4:73:BE:36:D6:4E:E3:7F:87:FA:50:0E:DB:C7:9E:AB:93:06:10:AB:9B:9F:A4:CA:7D:5C:1F:1B:4F:FC"
}
]
}
},
{
"type": "android",
"info": {
"package_name": "org.mozilla.focus.nightly",
"signatures": [
{
"build": "release",
"cert_fingerprint_sha256": "62:03:A4:73:BE:36:D6:4E:E3:7F:87:FA:50:0E:DB:C7:9E:AB:93:06:10:AB:9B:9F:A4:CA:7D:5C:1F:1B:4F:FC"
}
]
}
},
{
"type": "android",
"info": {
"package_name": "org.mozilla.klar",
"signatures": [
{
"build": "release",
"cert_fingerprint_sha256": "62:03:A4:73:BE:36:D6:4E:E3:7F:87:FA:50:0E:DB:C7:9E:AB:93:06:10:AB:9B:9F:A4:CA:7D:5C:1F:1B:4F:FC"
}
]
}
},
{
"type": "android",
"info": {
"package_name": "org.mozilla.reference.browser",
"signatures": [
{
"build": "release",
"cert_fingerprint_sha256": "B0:09:90:E3:0F:9D:81:5D:2E:BC:7B:9B:B2:21:CE:47:E5:C9:D5:17:AA:C7:0E:7F:D5:95:B1:E5:3E:9A:4B:14"
}
]
}
},
{
"type": "android",
"info": {
@@ -571,6 +643,142 @@
}
]
}
},
{
"type": "android",
"info": {
"package_name": "io.island.Island",
"signatures": [
{
"build": "release",
"cert_fingerprint_sha256": "D9:C3:39:AC:9C:3A:EE:E1:75:1D:85:8C:35:D9:BA:C5:CC:87:B3:CE:76:30:93:F0:F5:10:64:F5:A2:F6:9B:04"
},
{
"build": "userdebug",
"cert_fingerprint_sha256": "6C:65:BD:B0:33:F5:CE:B1:74:09:EF:F9:99:48:D5:58:9F:55:63:9A:63:78:D5:A5:00:EB:95:FC:01:BC:6D:44"
}
]
}
},
{
"type": "android",
"info": {
"package_name": "io.island.IslandCanary",
"signatures": [
{
"build": "release",
"cert_fingerprint_sha256": "90:17:13:23:45:6E:6F:39:CB:FD:CF:B2:56:BE:1D:CF:F3:BC:1C:59:8A:15:93:30:E4:97:73:D0:4C:B9:C9:05"
},
{
"build": "userdebug",
"cert_fingerprint_sha256": "6C:65:BD:B0:33:F5:CE:B1:74:09:EF:F9:99:48:D5:58:9F:55:63:9A:63:78:D5:A5:00:EB:95:FC:01:BC:6D:44"
}
]
}
},
{
"type": "android",
"info": {
"package_name": "io.island.IslandBeta",
"signatures": [
{
"build": "release",
"cert_fingerprint_sha256": "35:31:83:1A:9E:2B:21:1D:E6:AA:C3:69:4B:45:83:6E:56:09:B9:D7:D0:04:C3:1B:21:87:40:FB:77:17:38:D1"
},
{
"build": "userdebug",
"cert_fingerprint_sha256": "6C:65:BD:B0:33:F5:CE:B1:74:09:EF:F9:99:48:D5:58:9F:55:63:9A:63:78:D5:A5:00:EB:95:FC:01:BC:6D:44"
}
]
}
},
{
"type": "android",
"info": {
"package_name": "io.island.IslandDev",
"signatures": [
{
"build": "userdebug",
"cert_fingerprint_sha256": "6C:65:BD:B0:33:F5:CE:B1:74:09:EF:F9:99:48:D5:58:9F:55:63:9A:63:78:D5:A5:00:EB:95:FC:01:BC:6D:44"
}
]
}
},
{
"type": "android",
"info": {
"package_name": "io.island.island.intune",
"signatures": [
{
"build": "release",
"cert_fingerprint_sha256": "C2:38:24:15:41:20:A0:8F:C3:95:42:AC:D8:2A:E9:24:94:78:80:1E:47:FD:6C:66:2B:18:1C:28:CA:7E:59:4E"
},
{
"build": "userdebug",
"cert_fingerprint_sha256": "6C:65:BD:B0:33:F5:CE:B1:74:09:EF:F9:99:48:D5:58:9F:55:63:9A:63:78:D5:A5:00:EB:95:FC:01:BC:6D:44"
}
]
}
},
{
"type": "android",
"info": {
"package_name": "io.island.island.canary.intune",
"signatures": [
{
"build": "release",
"cert_fingerprint_sha256": "1E:16:74:BB:79:EA:09:FB:37:CF:9F:1B:07:1B:1D:51:8D:46:03:0E:D3:EE:F2:C1:4E:AD:93:9E:C6:EE:3A:4C"
},
{
"build": "userdebug",
"cert_fingerprint_sha256": "6C:65:BD:B0:33:F5:CE:B1:74:09:EF:F9:99:48:D5:58:9F:55:63:9A:63:78:D5:A5:00:EB:95:FC:01:BC:6D:44"
}
]
}
},
{
"type": "android",
"info": {
"package_name": "io.island.island.beta.intune",
"signatures": [
{
"build": "release",
"cert_fingerprint_sha256": "D2:5E:AD:F6:1C:E6:36:6C:A4:23:A4:7F:C4:DB:9B:8C:9C:8A:35:B4:B0:19:E8:D9:82:FB:D0:8A:D9:DB:49:5A"
},
{
"build": "userdebug",
"cert_fingerprint_sha256": "6C:65:BD:B0:33:F5:CE:B1:74:09:EF:F9:99:48:D5:58:9F:55:63:9A:63:78:D5:A5:00:EB:95:FC:01:BC:6D:44"
}
]
}
},
{
"type": "android",
"info": {
"package_name": "io.island.island.dev.intune",
"signatures": [
{
"build": "userdebug",
"cert_fingerprint_sha256": "6C:65:BD:B0:33:F5:CE:B1:74:09:EF:F9:99:48:D5:58:9F:55:63:9A:63:78:D5:A5:00:EB:95:FC:01:BC:6D:44"
}
]
}
},
{
"type": "android",
"info": {
"package_name": "net.quetta.browser",
"signatures": [
{
"build": "release",
"cert_fingerprint_sha256": "BE:FE:E7:31:12:6A:A5:6E:7E:FD:AE:AF:5E:F3:FA:EA:44:1C:19:CC:E0:CA:EC:42:6B:65:BB:F8:2C:59:46:80"
},
{
"build": "userdebug",
"cert_fingerprint_sha256": "F1:38:00:4F:38:04:51:D4:8A:05:2B:B3:A3:EF:17:24:23:D4:B0:D0:C8:A3:AA:DD:FB:DB:66:30:31:48:EC:A4"
}
]
}
}
]
}
}

View File

@@ -2,7 +2,7 @@ package com.x8bit.bitwarden
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.x8bit.bitwarden.data.platform.annotation.OmitFromCoverage
import com.bitwarden.core.annotation.OmitFromCoverage
/**
* An activity to be launched and then immediately closed so that the OS Shade can be collapsed

View File

@@ -4,7 +4,7 @@ import android.content.Intent
import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import com.x8bit.bitwarden.data.platform.annotation.OmitFromCoverage
import com.bitwarden.core.annotation.OmitFromCoverage
import dagger.hilt.android.AndroidEntryPoint
/**

View File

@@ -4,8 +4,8 @@ import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import com.bitwarden.core.annotation.OmitFromCoverage
import com.x8bit.bitwarden.data.autofill.manager.AutofillCompletionManager
import com.x8bit.bitwarden.data.platform.annotation.OmitFromCoverage
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach

View File

@@ -5,10 +5,10 @@ import android.content.Intent
import android.os.Build
import androidx.annotation.Keep
import androidx.core.app.AppComponentFactory
import com.bitwarden.core.annotation.OmitFromCoverage
import com.x8bit.bitwarden.data.autofill.BitwardenAutofillService
import com.x8bit.bitwarden.data.autofill.accessibility.BitwardenAccessibilityService
import com.x8bit.bitwarden.data.autofill.fido2.BitwardenFido2ProviderService
import com.x8bit.bitwarden.data.platform.annotation.OmitFromCoverage
import com.x8bit.bitwarden.data.tiles.BitwardenAutofillTileService
import com.x8bit.bitwarden.data.tiles.BitwardenGeneratorTileService
import com.x8bit.bitwarden.data.tiles.BitwardenVaultTileService

View File

@@ -1,11 +1,11 @@
package com.x8bit.bitwarden
import android.app.Application
import com.bitwarden.core.annotation.OmitFromCoverage
import com.x8bit.bitwarden.data.auth.manager.AuthRequestNotificationManager
import com.x8bit.bitwarden.data.platform.annotation.OmitFromCoverage
import com.x8bit.bitwarden.data.platform.manager.CrashLogsManager
import com.x8bit.bitwarden.data.platform.manager.NetworkConfigManager
import com.x8bit.bitwarden.data.platform.manager.LogsManager
import com.x8bit.bitwarden.data.platform.manager.event.OrganizationEventManager
import com.x8bit.bitwarden.data.platform.manager.network.NetworkConfigManager
import com.x8bit.bitwarden.data.platform.manager.restriction.RestrictionManager
import dagger.hilt.android.HiltAndroidApp
import javax.inject.Inject
@@ -19,10 +19,10 @@ class BitwardenApplication : Application() {
// Inject classes here that must be triggered on startup but are not otherwise consumed by
// other callers.
@Inject
lateinit var networkConfigManager: NetworkConfigManager
lateinit var logsManager: LogsManager
@Inject
lateinit var crashLogsManager: CrashLogsManager
lateinit var networkConfigManager: NetworkConfigManager
@Inject
lateinit var authRequestNotificationManager: AuthRequestNotificationManager

View File

@@ -6,6 +6,11 @@ package com.x8bit.bitwarden
const val LEGACY_ACCESSIBILITY_SERVICE_NAME: String =
"com.x8bit.bitwarden.Accessibility.AccessibilityService"
/**
* The short form legacy name for the accessibility service.
*/
const val LEGACY_SHORT_ACCESSIBILITY_SERVICE_NAME: String = ".Accessibility.AccessibilityService"
/**
* The legacy name for the autofill service.
*/

View File

@@ -1,6 +1,7 @@
package com.x8bit.bitwarden
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.view.KeyEvent
import android.view.MotionEvent
@@ -11,37 +12,41 @@ import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.core.os.LocaleListCompat
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.rememberNavController
import com.x8bit.bitwarden.data.autofill.accessibility.manager.AccessibilityActivityManager
import com.bitwarden.core.annotation.OmitFromCoverage
import com.x8bit.bitwarden.data.autofill.accessibility.manager.AccessibilityCompletionManager
import com.x8bit.bitwarden.data.autofill.manager.AutofillActivityManager
import com.x8bit.bitwarden.data.autofill.manager.AutofillCompletionManager
import com.x8bit.bitwarden.data.platform.annotation.OmitFromCoverage
import com.x8bit.bitwarden.data.platform.manager.util.ObserveScreenDataEffect
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
import com.x8bit.bitwarden.ui.platform.composition.LocalManagerProvider
import com.x8bit.bitwarden.ui.platform.feature.debugmenu.debugMenuDestination
import com.x8bit.bitwarden.ui.platform.feature.debugmenu.manager.DebugMenuLaunchManager
import com.x8bit.bitwarden.ui.platform.feature.debugmenu.navigateToDebugMenuScreen
import com.x8bit.bitwarden.ui.platform.feature.rootnav.RootNavScreen
import com.x8bit.bitwarden.ui.platform.feature.rootnav.ROOT_ROUTE
import com.x8bit.bitwarden.ui.platform.feature.rootnav.rootNavDestination
import com.x8bit.bitwarden.ui.platform.feature.settings.appearance.model.AppLanguage
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
import com.x8bit.bitwarden.ui.platform.util.appLanguage
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
/**
* Primary entry point for the application.
*/
@Suppress("TooManyFunctions")
@OmitFromCoverage
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private val mainViewModel: MainViewModel by viewModels()
@Inject
lateinit var accessibilityActivityManager: AccessibilityActivityManager
@Inject
lateinit var autofillActivityManager: AutofillActivityManager
@@ -57,6 +62,7 @@ class MainActivity : AppCompatActivity() {
@Inject
lateinit var debugLaunchManager: DebugMenuLaunchManager
@Suppress("LongMethod")
override fun onCreate(savedInstanceState: Bundle?) {
var shouldShowSplashScreen = true
installSplashScreen().setKeepOnScreenCondition { shouldShowSplashScreen }
@@ -70,13 +76,10 @@ class MainActivity : AppCompatActivity() {
)
}
// Within the app the language will change dynamically and will be managed
// by the OS, but we need to ensure we properly set the language when
// upgrading from older versions that handle this differently.
settingsRepository.appLanguage.localeName?.let { localeName ->
val localeList = LocaleListCompat.forLanguageTags(localeName)
AppCompatDelegate.setApplicationLocales(localeList)
}
// Within the app the theme will change dynamically and will be managed by the
// OS, but we need to ensure we properly set the values when upgrading from older versions
// that handle this differently or when the activity restarts.
AppCompatDelegate.setDefaultNightMode(settingsRepository.appTheme.osValue)
setContent {
val state by mainViewModel.stateFlow.collectAsStateWithLifecycle()
val navController = rememberNavController()
@@ -98,15 +101,43 @@ class MainActivity : AppCompatActivity() {
)
.show()
}
is MainEvent.UpdateAppLocale -> {
AppCompatDelegate.setApplicationLocales(
LocaleListCompat.forLanguageTags(event.localeName),
)
}
is MainEvent.UpdateAppTheme -> {
AppCompatDelegate.setDefaultNightMode(event.osTheme)
}
}
}
updateScreenCapture(isScreenCaptureAllowed = state.isScreenCaptureAllowed)
LocalManagerProvider {
LocalManagerProvider(featureFlagsState = state.featureFlagsState) {
ObserveScreenDataEffect(
onDataUpdate = remember(mainViewModel) {
{
mainViewModel.trySendAction(
MainAction.ResumeScreenDataReceived(it),
)
}
},
)
BitwardenTheme(theme = state.theme) {
RootNavScreen(
onSplashScreenRemoved = { shouldShowSplashScreen = false },
NavHost(
navController = navController,
)
startDestination = ROOT_ROUTE,
) {
// Nothing else should end up at this top level, we just want the ability
// to have the debug menu appear on top of the rest of the app without
// interacting with the state-based navigation used by the RootNavScreen.
rootNavDestination { shouldShowSplashScreen = false }
debugMenuDestination(
onNavigateBack = { navController.popBackStack() },
onSplashScreenRemoved = { shouldShowSplashScreen = false },
)
}
}
}
}
@@ -121,6 +152,33 @@ class MainActivity : AppCompatActivity() {
)
}
override fun onResume() {
super.onResume()
// When the app resumes check for any app specific language which may have been
// set via the device settings. Similar to the theme setting in onCreate this
// ensures we properly set the values when upgrading from older versions
// that handle this differently or when the activity restarts.
val appSpecificLanguage = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val locales: LocaleListCompat = AppCompatDelegate.getApplicationLocales()
if (locales.isEmpty) {
// App is using the system language
null
} else {
// App has specific language settings
locales.get(0)?.appLanguage
}
} else {
// For older versions, use what ever language is available from the repository.
settingsRepository.appLanguage
}
mainViewModel.trySendAction(
action = MainAction.AppSpecificLanguageUpdate(
appLanguage = appSpecificLanguage ?: AppLanguage.DEFAULT,
),
)
}
override fun onStop() {
super.onStop()
// In some scenarios on an emulator the Activity can leak when recreated

View File

@@ -4,7 +4,10 @@ import android.content.Intent
import android.os.Parcelable
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewModelScope
import com.bitwarden.ui.util.Text
import com.bitwarden.ui.util.asText
import com.bitwarden.vault.CipherView
import com.x8bit.bitwarden.data.auth.manager.AddTotpItemFromAuthenticatorManager
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
import com.x8bit.bitwarden.data.auth.repository.model.EmailTokenResult
import com.x8bit.bitwarden.data.auth.util.getCompleteRegistrationDataIntentOrNull
@@ -12,27 +15,33 @@ import com.x8bit.bitwarden.data.auth.util.getPasswordlessRequestDataIntentOrNull
import com.x8bit.bitwarden.data.autofill.accessibility.manager.AccessibilitySelectionManager
import com.x8bit.bitwarden.data.autofill.fido2.manager.Fido2CredentialManager
import com.x8bit.bitwarden.data.autofill.fido2.util.getFido2AssertionRequestOrNull
import com.x8bit.bitwarden.data.autofill.fido2.util.getFido2CredentialRequestOrNull
import com.x8bit.bitwarden.data.autofill.fido2.util.getFido2CreateCredentialRequestOrNull
import com.x8bit.bitwarden.data.autofill.fido2.util.getFido2GetCredentialsRequestOrNull
import com.x8bit.bitwarden.data.autofill.manager.AutofillSelectionManager
import com.x8bit.bitwarden.data.autofill.util.getAutofillSaveItemOrNull
import com.x8bit.bitwarden.data.autofill.util.getAutofillSelectionDataOrNull
import com.x8bit.bitwarden.data.platform.manager.AppResumeManager
import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager
import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager
import com.x8bit.bitwarden.data.platform.manager.garbage.GarbageCollectionManager
import com.x8bit.bitwarden.data.platform.manager.model.AppResumeScreenData
import com.x8bit.bitwarden.data.platform.manager.model.CompleteRegistrationData
import com.x8bit.bitwarden.data.platform.manager.model.FlagKey
import com.x8bit.bitwarden.data.platform.manager.model.SpecialCircumstance
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
import com.x8bit.bitwarden.data.platform.util.isAddTotpLoginItemFromAuthenticator
import com.x8bit.bitwarden.data.vault.manager.model.VaultStateEvent
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
import com.x8bit.bitwarden.ui.platform.base.util.Text
import com.x8bit.bitwarden.ui.platform.base.util.asText
import com.x8bit.bitwarden.ui.platform.feature.settings.appearance.model.AppLanguage
import com.x8bit.bitwarden.ui.platform.feature.settings.appearance.model.AppTheme
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
import com.x8bit.bitwarden.ui.platform.model.FeatureFlagsState
import com.x8bit.bitwarden.ui.platform.util.isAccountSecurityShortcut
import com.x8bit.bitwarden.ui.platform.util.isMyVaultShortcut
import com.x8bit.bitwarden.ui.platform.util.isPasswordGeneratorShortcut
import com.x8bit.bitwarden.ui.vault.model.TotpData
import com.x8bit.bitwarden.ui.vault.util.getTotpDataOrNull
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.delay
@@ -49,6 +58,7 @@ import java.time.Clock
import javax.inject.Inject
private const val SPECIAL_CIRCUMSTANCE_KEY = "special-circumstance"
private const val ANIMATION_REFRESH_DELAY = 500L
/**
* A view model that helps launch actions for the [MainActivity].
@@ -58,20 +68,26 @@ private const val SPECIAL_CIRCUMSTANCE_KEY = "special-circumstance"
class MainViewModel @Inject constructor(
accessibilitySelectionManager: AccessibilitySelectionManager,
autofillSelectionManager: AutofillSelectionManager,
featureFlagManager: FeatureFlagManager,
private val addTotpItemFromAuthenticatorManager: AddTotpItemFromAuthenticatorManager,
private val specialCircumstanceManager: SpecialCircumstanceManager,
private val garbageCollectionManager: GarbageCollectionManager,
private val fido2CredentialManager: Fido2CredentialManager,
private val intentManager: IntentManager,
settingsRepository: SettingsRepository,
vaultRepository: VaultRepository,
private val settingsRepository: SettingsRepository,
private val vaultRepository: VaultRepository,
private val authRepository: AuthRepository,
private val environmentRepository: EnvironmentRepository,
private val savedStateHandle: SavedStateHandle,
private val appResumeManager: AppResumeManager,
private val clock: Clock,
) : BaseViewModel<MainState, MainEvent, MainAction>(
initialState = MainState(
theme = settingsRepository.appTheme,
isScreenCaptureAllowed = settingsRepository.isScreenCaptureAllowed,
isErrorReportingDialogEnabled = featureFlagManager.getFeatureFlag(
key = FlagKey.MobileErrorReporting,
),
),
) {
private var specialCircumstance: SpecialCircumstance?
@@ -89,6 +105,12 @@ class MainViewModel @Inject constructor(
.onEach { specialCircumstance = it }
.launchIn(viewModelScope)
featureFlagManager
.getFeatureFlagFlow(key = FlagKey.MobileErrorReporting)
.map { MainAction.Internal.OnMobileErrorReportingReceive(it) }
.onEach(::sendAction)
.launchIn(viewModelScope)
accessibilitySelectionManager
.accessibilitySelectionFlow
.map { MainAction.Internal.AccessibilitySelectionReceive(it) }
@@ -104,6 +126,11 @@ class MainViewModel @Inject constructor(
.appThemeStateFlow
.onEach { trySendAction(MainAction.Internal.ThemeUpdate(it)) }
.launchIn(viewModelScope)
settingsRepository
.appLanguageStateFlow
.map { MainEvent.UpdateAppLocale(it.localeName) }
.onEach(::sendEvent)
.launchIn(viewModelScope)
settingsRepository
.isScreenCaptureAllowedStateFlow
@@ -122,8 +149,7 @@ class MainViewModel @Inject constructor(
// Switching between account states often involves some kind of animation (ex:
// account switcher) that we might want to give time to finish before triggering
// a refresh.
@Suppress("MagicNumber")
delay(500)
delay(ANIMATION_REFRESH_DELAY)
trySendAction(MainAction.Internal.CurrentUserStateChange)
}
.launchIn(viewModelScope)
@@ -135,8 +161,7 @@ class MainViewModel @Inject constructor(
is VaultStateEvent.Locked -> {
// Similar to account switching, triggering this action too soon can
// interfere with animations or navigation logic, so we will delay slightly.
@Suppress("MagicNumber")
delay(500)
delay(ANIMATION_REFRESH_DELAY)
trySendAction(MainAction.Internal.VaultUnlockStateChange)
}
@@ -160,6 +185,17 @@ class MainViewModel @Inject constructor(
}
override fun handleAction(action: MainAction) {
when (action) {
is MainAction.ReceiveFirstIntent -> handleFirstIntentReceived(action)
is MainAction.ReceiveNewIntent -> handleNewIntentReceived(action)
MainAction.OpenDebugMenu -> handleOpenDebugMenu()
is MainAction.ResumeScreenDataReceived -> handleAppResumeDataUpdated(action)
is MainAction.AppSpecificLanguageUpdate -> handleAppSpecificLanguageUpdate(action)
is MainAction.Internal -> handleInternalAction(action)
}
}
private fun handleInternalAction(action: MainAction.Internal) {
when (action) {
is MainAction.Internal.AccessibilitySelectionReceive -> {
handleAccessibilitySelectionReceive(action)
@@ -173,9 +209,28 @@ class MainViewModel @Inject constructor(
is MainAction.Internal.ScreenCaptureUpdate -> handleScreenCaptureUpdate(action)
is MainAction.Internal.ThemeUpdate -> handleAppThemeUpdated(action)
is MainAction.Internal.VaultUnlockStateChange -> handleVaultUnlockStateChange()
is MainAction.ReceiveFirstIntent -> handleFirstIntentReceived(action)
is MainAction.ReceiveNewIntent -> handleNewIntentReceived(action)
MainAction.OpenDebugMenu -> handleOpenDebugMenu()
is MainAction.Internal.OnMobileErrorReportingReceive -> {
handleOnMobileErrorReportingReceive(action)
}
}
}
private fun handleOnMobileErrorReportingReceive(
action: MainAction.Internal.OnMobileErrorReportingReceive,
) {
mutableStateFlow.update {
it.copy(isErrorReportingDialogEnabled = action.isErrorReportingEnabled)
}
}
private fun handleAppSpecificLanguageUpdate(action: MainAction.AppSpecificLanguageUpdate) {
settingsRepository.appLanguage = action.appLanguage
}
private fun handleAppResumeDataUpdated(action: MainAction.ResumeScreenDataReceived) {
when (val data = action.screenResumeData) {
null -> appResumeManager.clearResumeScreen()
else -> appResumeManager.setResumeScreen(data)
}
}
@@ -186,12 +241,14 @@ class MainViewModel @Inject constructor(
private fun handleAccessibilitySelectionReceive(
action: MainAction.Internal.AccessibilitySelectionReceive,
) {
specialCircumstanceManager.specialCircumstance = null
sendEvent(MainEvent.CompleteAccessibilityAutofill(cipherView = action.cipherView))
}
private fun handleAutofillSelectionReceive(
action: MainAction.Internal.AutofillSelectionReceive,
) {
specialCircumstanceManager.specialCircumstance = null
sendEvent(MainEvent.CompleteAutofill(cipherView = action.cipherView))
}
@@ -205,6 +262,7 @@ class MainViewModel @Inject constructor(
private fun handleAppThemeUpdated(action: MainAction.Internal.ThemeUpdate) {
mutableStateFlow.update { it.copy(theme = action.theme) }
sendEvent(MainEvent.UpdateAppTheme(osTheme = action.theme.osValue))
}
private fun handleVaultUnlockStateChange() {
@@ -234,18 +292,38 @@ class MainViewModel @Inject constructor(
val autofillSaveItem = intent.getAutofillSaveItemOrNull()
val autofillSelectionData = intent.getAutofillSelectionDataOrNull()
val shareData = intentManager.getShareDataFromIntent(intent)
val totpData = intent.getTotpDataOrNull()
val totpData: TotpData? =
// First grab TOTP URI directly from the intent data:
intent.getTotpDataOrNull()
?: run {
// Then check to see if the intent is coming from the Authenticator app:
if (intent.isAddTotpLoginItemFromAuthenticator()) {
addTotpItemFromAuthenticatorManager.pendingAddTotpLoginItemData.also {
// Clear pending add TOTP data so it is only handled once:
addTotpItemFromAuthenticatorManager.pendingAddTotpLoginItemData = null
}
} else {
null
}
}
val hasGeneratorShortcut = intent.isPasswordGeneratorShortcut
val hasVaultShortcut = intent.isMyVaultShortcut
val hasAccountSecurityShortcut = intent.isAccountSecurityShortcut
val fido2CredentialRequestData = intent.getFido2CredentialRequestOrNull()
val completeRegistrationData = intent.getCompleteRegistrationDataIntentOrNull()
val fido2CredentialAssertionRequest = intent.getFido2AssertionRequestOrNull()
val fido2CreateCredentialRequest = intent.getFido2CreateCredentialRequestOrNull()
val fido2GetCredentialsRequest = intent.getFido2GetCredentialsRequestOrNull()
val fido2AssertCredentialRequest = intent.getFido2AssertionRequestOrNull()
when {
passwordlessRequestData != null -> {
if (authRepository.activeUserId != passwordlessRequestData.userId) {
authRepository.switchAccount(passwordlessRequestData.userId)
authRepository.activeUserId?.let {
if (it != passwordlessRequestData.userId &&
!vaultRepository.isVaultUnlocked(it)
) {
// We only switch the account here if the current user's vault is not
// unlocked, otherwise prompt the user to allow us to change the account
// in the LoginApprovalScreen
authRepository.switchAccount(passwordlessRequestData.userId)
}
}
specialCircumstanceManager.specialCircumstance =
SpecialCircumstance.PasswordlessRequest(
@@ -292,28 +370,36 @@ class MainViewModel @Inject constructor(
)
}
fido2CredentialRequestData != null -> {
fido2CreateCredentialRequest != null -> {
// Set the user's verification status when a new FIDO 2 request is received to force
// explicit verification if the user's vault is unlocked when the request is
// received.
fido2CredentialManager.isUserVerified = false
fido2CredentialManager.isUserVerified =
fido2CreateCredentialRequest.isUserPreVerified
specialCircumstanceManager.specialCircumstance =
SpecialCircumstance.Fido2Save(
fido2CredentialRequest = fido2CredentialRequestData,
fido2CreateCredentialRequest = fido2CreateCredentialRequest,
)
// Switch accounts if the selected user is not the active user.
if (authRepository.activeUserId != null &&
authRepository.activeUserId != fido2CredentialRequestData.userId
authRepository.activeUserId != fido2CreateCredentialRequest.userId
) {
authRepository.switchAccount(fido2CredentialRequestData.userId)
authRepository.switchAccount(fido2CreateCredentialRequest.userId)
}
}
fido2CredentialAssertionRequest != null -> {
fido2AssertCredentialRequest != null -> {
// Set the user's verification status when a new FIDO 2 request is received to force
// explicit verification if the user's vault is unlocked when the request is
// received.
fido2CredentialManager.isUserVerified =
fido2AssertCredentialRequest.isUserPreVerified
specialCircumstanceManager.specialCircumstance =
SpecialCircumstance.Fido2Assertion(
fido2AssertionRequest = fido2CredentialAssertionRequest,
fido2AssertionRequest = fido2AssertCredentialRequest,
)
}
@@ -396,7 +482,16 @@ class MainViewModel @Inject constructor(
data class MainState(
val theme: AppTheme,
val isScreenCaptureAllowed: Boolean,
) : Parcelable
private val isErrorReportingDialogEnabled: Boolean,
) : Parcelable {
/**
* Contains all feature flags that are available to the UI.
*/
val featureFlagsState: FeatureFlagsState
get() = FeatureFlagsState(
isErrorReportingDialogEnabled = isErrorReportingDialogEnabled,
)
}
/**
* Models actions for the [MainActivity].
@@ -417,6 +512,17 @@ sealed class MainAction {
*/
data object OpenDebugMenu : MainAction()
/**
* Receive event to save the app resume screen
*/
data class ResumeScreenDataReceived(val screenResumeData: AppResumeScreenData?) : MainAction()
/**
* Receive if there is an app specific locale selection made by user
* in the device's settings.
*/
data class AppSpecificLanguageUpdate(val appLanguage: AppLanguage) : MainAction()
/**
* Actions for internal use by the ViewModel.
*/
@@ -429,6 +535,13 @@ sealed class MainAction {
val cipherView: CipherView,
) : Internal()
/**
* Indicates the Mobile Error Reporting feature flag has been updated.
*/
data class OnMobileErrorReportingReceive(
val isErrorReportingEnabled: Boolean,
) : Internal()
/**
* Indicates the user has manually selected the given [cipherView] for autofill.
*/
@@ -492,4 +605,18 @@ sealed class MainEvent {
* Show a toast with the given [message].
*/
data class ShowToast(val message: Text) : MainEvent()
/**
* Indicates that the app language has been updated.
*/
data class UpdateAppLocale(
val localeName: String?,
) : MainEvent()
/**
* Indicates that the app theme has been updated.
*/
data class UpdateAppTheme(
val osTheme: Int,
) : MainEvent()
}

View File

@@ -1,11 +1,12 @@
package com.x8bit.bitwarden.data.auth.datasource.disk
import com.bitwarden.network.model.SyncResponseJson
import com.x8bit.bitwarden.data.auth.datasource.disk.model.AccountTokensJson
import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus
import com.x8bit.bitwarden.data.auth.datasource.disk.model.PendingAuthRequestJson
import com.x8bit.bitwarden.data.auth.datasource.disk.model.UserStateJson
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
import kotlinx.coroutines.flow.Flow
import java.time.Instant
/**
* Primary access point for disk information.
@@ -171,6 +172,16 @@ interface AuthDiskSource {
pendingAuthRequest: PendingAuthRequestJson?,
)
/**
* Gets the biometrics initialization vector for the given [userId].
*/
fun getUserBiometricInitVector(userId: String): ByteArray?
/**
* Stores the biometrics initialization vector for the given [userId].
*/
fun storeUserBiometricInitVector(userId: String, iv: ByteArray?)
/**
* Gets the biometrics key for the given [userId].
*/
@@ -181,6 +192,11 @@ interface AuthDiskSource {
*/
fun storeUserBiometricUnlockKey(userId: String, biometricsKey: String?)
/**
* Gets the flow for the biometrics key for the given [userId].
*/
fun getUserBiometicUnlockKeyFlow(userId: String): Flow<String?>
/**
* Retrieves a pin-protected user key for the given [userId].
*/
@@ -198,6 +214,11 @@ interface AuthDiskSource {
inMemoryOnly: Boolean = false,
)
/**
* Retrieves a flow for the pin-protected user key for the given [userId].
*/
fun getPinProtectedUserKeyFlow(userId: String): Flow<String?>
/**
* Gets a two-factor auth token using a user's [email].
*/
@@ -318,7 +339,17 @@ interface AuthDiskSource {
fun storeShowImportLogins(userId: String, showImportLogins: Boolean?)
/**
* Emits updates that track [getShowImportLogins]. This will replay the last known value,
* Emits updates that track [getShowImportLogins]. This will replay the last known value.
*/
fun getShowImportLoginsFlow(userId: String): Flow<Boolean?>
/**
* Gets the last lock timestamp for the given [userId].
*/
fun getLastLockTimestamp(userId: String): Instant?
/**
* Stores the last lock timestamp for the given [userId].
*/
fun storeLastLockTimestamp(userId: String, lastLockTimestamp: Instant?)
}

View File

@@ -1,26 +1,27 @@
package com.x8bit.bitwarden.data.auth.datasource.disk
import android.content.SharedPreferences
import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow
import com.bitwarden.core.data.util.decodeFromStringOrNull
import com.bitwarden.data.datasource.disk.BaseEncryptedDiskSource
import com.bitwarden.network.model.SyncResponseJson
import com.x8bit.bitwarden.data.auth.datasource.disk.model.AccountTokensJson
import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus
import com.x8bit.bitwarden.data.auth.datasource.disk.model.PendingAuthRequestJson
import com.x8bit.bitwarden.data.auth.datasource.disk.model.UserStateJson
import com.x8bit.bitwarden.data.platform.datasource.disk.BaseEncryptedDiskSource
import com.x8bit.bitwarden.data.platform.datasource.disk.legacy.LegacySecureStorageMigrator
import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow
import com.x8bit.bitwarden.data.platform.util.decodeFromStringOrNull
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.onSubscription
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import java.time.Instant
import java.util.UUID
// These keys should be encrypted
private const val ACCOUNT_TOKENS_KEY = "accountTokens"
private const val AUTHENTICATOR_SYNC_SYMMETRIC_KEY = "authenticatorSyncSymmetric"
private const val AUTHENTICATOR_SYNC_UNLOCK_KEY = "authenticatorSyncUnlock"
private const val BIOMETRICS_INIT_VECTOR_KEY = "biometricInitializationVector"
private const val BIOMETRICS_UNLOCK_KEY = "userKeyBiometricUnlock"
private const val USER_AUTO_UNLOCK_KEY_KEY = "userKeyAutoUnlock"
private const val DEVICE_KEY_KEY = "deviceKey"
@@ -46,6 +47,7 @@ private const val TDE_LOGIN_COMPLETE = "tdeLoginComplete"
private const val USES_KEY_CONNECTOR = "usesKeyConnector"
private const val ONBOARDING_STATUS_KEY = "onboardingStatus"
private const val SHOW_IMPORT_LOGINS_KEY = "showImportLogins"
private const val LAST_LOCK_TIMESTAMP = "lastLockTimestamp"
/**
* Primary implementation of [AuthDiskSource].
@@ -74,6 +76,10 @@ class AuthDiskSourceImpl(
private val mutableOnboardingStatusFlowMap =
mutableMapOf<String, MutableSharedFlow<OnboardingStatus?>>()
private val mutableShowImportLoginsFlowMap = mutableMapOf<String, MutableSharedFlow<Boolean?>>()
private val mutableBiometricUnlockKeyFlowMap =
mutableMapOf<String, MutableSharedFlow<String?>>()
private val mutablePinProtectedUserKeyFlowMap =
mutableMapOf<String, MutableSharedFlow<String?>>()
private val mutableUserStateFlow = bufferedMutableSharedFlow<UserStateJson?>(replay = 1)
override var userState: UserStateJson?
@@ -138,6 +144,7 @@ class AuthDiskSourceImpl(
storePrivateKey(userId = userId, privateKey = null)
storeOrganizationKeys(userId = userId, organizationKeys = null)
storeOrganizations(userId = userId, organizations = null)
storeUserBiometricInitVector(userId = userId, iv = null)
storeUserBiometricUnlockKey(userId = userId, biometricsKey = null)
storeMasterPasswordHash(userId = userId, passwordHash = null)
storePolicies(userId = userId, policies = null)
@@ -146,6 +153,7 @@ class AuthDiskSourceImpl(
storeIsTdeLoginComplete(userId = userId, isTdeLoginComplete = null)
storeAuthenticatorSyncUnlockKey(userId = userId, authenticatorSyncUnlockKey = null)
storeShowImportLogins(userId = userId, showImportLogins = null)
storeLastLockTimestamp(userId = userId, lastLockTimestamp = null)
// Do not remove the DeviceKey or PendingAuthRequest on logout, these are persisted
// indefinitely unless the TDE flow explicitly removes them.
@@ -273,6 +281,17 @@ class AuthDiskSourceImpl(
)
}
override fun getUserBiometricInitVector(userId: String): ByteArray? =
getEncryptedString(key = BIOMETRICS_INIT_VECTOR_KEY.appendIdentifier(userId))
?.toByteArray(Charsets.ISO_8859_1)
override fun storeUserBiometricInitVector(userId: String, iv: ByteArray?) {
putEncryptedString(
key = BIOMETRICS_INIT_VECTOR_KEY.appendIdentifier(userId),
value = iv?.toString(Charsets.ISO_8859_1),
)
}
override fun getUserBiometricUnlockKey(userId: String): String? =
getEncryptedString(key = BIOMETRICS_UNLOCK_KEY.appendIdentifier(userId))
@@ -284,8 +303,13 @@ class AuthDiskSourceImpl(
key = BIOMETRICS_UNLOCK_KEY.appendIdentifier(userId),
value = biometricsKey,
)
getMutableBiometricUnlockKeyFlow(userId).tryEmit(biometricsKey)
}
override fun getUserBiometicUnlockKeyFlow(userId: String): Flow<String?> =
getMutableBiometricUnlockKeyFlow(userId)
.onSubscription { emit(getUserBiometricUnlockKey(userId = userId)) }
override fun getPinProtectedUserKey(userId: String): String? =
inMemoryPinProtectedUserKeys[userId]
?: getString(key = PIN_PROTECTED_USER_KEY_KEY.appendIdentifier(userId))
@@ -301,8 +325,13 @@ class AuthDiskSourceImpl(
key = PIN_PROTECTED_USER_KEY_KEY.appendIdentifier(userId),
value = pinProtectedUserKey,
)
getMutablePinProtectedUserKeyFlow(userId).tryEmit(pinProtectedUserKey)
}
override fun getPinProtectedUserKeyFlow(userId: String): Flow<String?> =
getMutablePinProtectedUserKeyFlow(userId)
.onSubscription { emit(getPinProtectedUserKey(userId = userId)) }
override fun getTwoFactorToken(email: String): String? =
getString(key = TWO_FACTOR_TOKEN_KEY.appendIdentifier(email))
@@ -457,6 +486,19 @@ class AuthDiskSourceImpl(
getMutableShowImportLoginsFlow(userId)
.onSubscription { emit(getShowImportLogins(userId)) }
override fun getLastLockTimestamp(userId: String): Instant? {
return getLong(key = LAST_LOCK_TIMESTAMP.appendIdentifier(userId))?.let {
Instant.ofEpochMilli(it)
}
}
override fun storeLastLockTimestamp(userId: String, lastLockTimestamp: Instant?) {
putLong(
key = LAST_LOCK_TIMESTAMP.appendIdentifier(userId),
value = lastLockTimestamp?.toEpochMilli(),
)
}
private fun generateAndStoreUniqueAppId(): String =
UUID
.randomUUID()
@@ -506,6 +548,18 @@ class AuthDiskSourceImpl(
bufferedMutableSharedFlow(replay = 1)
}
private fun getMutableBiometricUnlockKeyFlow(
userId: String,
): MutableSharedFlow<String?> = mutableBiometricUnlockKeyFlowMap.getOrPut(userId) {
bufferedMutableSharedFlow(replay = 1)
}
private fun getMutablePinProtectedUserKeyFlow(
userId: String,
): MutableSharedFlow<String?> = mutablePinProtectedUserKeyFlowMap.getOrPut(userId) {
bufferedMutableSharedFlow(replay = 1)
}
private fun migrateAccountTokens() {
userState
?.accounts

View File

@@ -1,10 +1,10 @@
package com.x8bit.bitwarden.data.auth.datasource.disk.di
import android.content.SharedPreferences
import com.bitwarden.data.datasource.disk.di.EncryptedPreferences
import com.bitwarden.data.datasource.disk.di.UnencryptedPreferences
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSourceImpl
import com.x8bit.bitwarden.data.platform.datasource.di.EncryptedPreferences
import com.x8bit.bitwarden.data.platform.datasource.di.UnencryptedPreferences
import com.x8bit.bitwarden.data.platform.datasource.disk.legacy.LegacySecureStorageMigrator
import dagger.Module
import dagger.Provides

View File

@@ -1,9 +1,14 @@
package com.x8bit.bitwarden.data.auth.datasource.disk.model
import com.x8bit.bitwarden.data.auth.datasource.network.model.KdfTypeJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.UserDecryptionOptionsJson
import com.bitwarden.data.datasource.disk.model.EnvironmentUrlDataJson
import com.bitwarden.network.model.KdfTypeJson
import com.bitwarden.network.model.UserDecryptionOptionsJson
import kotlinx.serialization.Contextual
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonNames
import java.time.ZonedDateTime
/**
* Represents the current account information for a given user.
@@ -33,6 +38,7 @@ data class AccountJson(
* @property userId The ID of the user.
* @property email The user's email address.
* @property isEmailVerified Whether or not the user's email is verified.
* @property isTwoFactorEnabled If the profile has two factor authentication enabled.
* @property name The user's name (if applicable).
* @property stamp The account's security stamp (if applicable).
* @property organizationId The ID of the associated organization (if applicable).
@@ -44,7 +50,9 @@ data class AccountJson(
* @property kdfMemory The amount of memory to use when calculating a password hash (MB).
* @property kdfParallelism The number of threads to use when calculating a password hash.
* @property userDecryptionOptions The options available to a user for decryption.
* @property creationDate The creation date of the account.
*/
@OptIn(ExperimentalSerializationApi::class)
@Serializable
data class Profile(
@SerialName("userId")
@@ -56,6 +64,9 @@ data class AccountJson(
@SerialName("emailVerified")
val isEmailVerified: Boolean?,
@SerialName("isTwoFactorEnabled")
val isTwoFactorEnabled: Boolean?,
@SerialName("name")
val name: String?,
@@ -86,8 +97,13 @@ data class AccountJson(
@SerialName("kdfParallelism")
val kdfParallelism: Int?,
@SerialName("accountDecryptionOptions")
@SerialName("userDecryptionOptions")
@JsonNames("accountDecryptionOptions")
val userDecryptionOptions: UserDecryptionOptionsJson?,
@SerialName("creationDate")
@Contextual
val creationDate: ZonedDateTime?,
)
/**

View File

@@ -7,13 +7,21 @@ import kotlinx.serialization.Serializable
* Container for the user's API tokens.
*
* @property requestId The ID of the pending Auth Request.
* @property requestPrivateKey The private of the pending Auth Request.
* @property requestPrivateKey The private key of the pending Auth Request.
* @property requestAccessCode The access code of the pending Auth Request.
* @property requestFingerprint The fingerprint of the pending Auth Request.
*/
@Serializable
data class PendingAuthRequestJson(
@SerialName("Id")
@SerialName("id")
val requestId: String,
@SerialName("PrivateKey")
@SerialName("privateKey")
val requestPrivateKey: String,
@SerialName("accessCode")
val requestAccessCode: String,
@SerialName("fingerprint")
val requestFingerprint: String,
)

View File

@@ -1,30 +0,0 @@
package com.x8bit.bitwarden.data.auth.datasource.network.api
import com.x8bit.bitwarden.data.auth.datasource.network.model.KeyConnectorKeyRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.PasswordHintRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.ResendEmailRequestJson
import com.x8bit.bitwarden.data.platform.datasource.network.util.HEADER_KEY_AUTHORIZATION
import retrofit2.http.Body
import retrofit2.http.Header
import retrofit2.http.POST
/**
* Defines raw calls under the /accounts API.
*/
interface UnauthenticatedAccountsApi {
@POST("/accounts/password-hint")
suspend fun passwordHintRequest(
@Body body: PasswordHintRequestJson,
): Result<Unit>
@POST("/two-factor/send-email-login")
suspend fun resendVerificationCodeEmail(
@Body body: ResendEmailRequestJson,
): Result<Unit>
@POST("/accounts/set-key-connector-key")
suspend fun setKeyConnectorKey(
@Body body: KeyConnectorKeyRequestJson,
@Header(HEADER_KEY_AUTHORIZATION) bearerToken: String,
): Result<Unit>
}

View File

@@ -1,19 +0,0 @@
package com.x8bit.bitwarden.data.auth.datasource.network.api
import com.x8bit.bitwarden.data.auth.datasource.network.model.OrganizationDomainSsoDetailsRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.OrganizationDomainSsoDetailsResponseJson
import retrofit2.http.Body
import retrofit2.http.POST
/**
* Defines raw calls under the /organizations API.
*/
interface UnauthenticatedOrganizationApi {
/**
* Checks for the claimed domain organization of an email for SSO purposes.
*/
@POST("/organizations/domain/sso/details")
suspend fun getClaimedDomainOrganizationDetails(
@Body body: OrganizationDomainSsoDetailsRequestJson,
): Result<OrganizationDomainSsoDetailsResponseJson>
}

View File

@@ -1,19 +1,19 @@
package com.x8bit.bitwarden.data.auth.datasource.network.di
import com.x8bit.bitwarden.data.auth.datasource.network.service.AccountsService
import com.x8bit.bitwarden.data.auth.datasource.network.service.AccountsServiceImpl
import com.x8bit.bitwarden.data.auth.datasource.network.service.AuthRequestsService
import com.x8bit.bitwarden.data.auth.datasource.network.service.AuthRequestsServiceImpl
import com.x8bit.bitwarden.data.auth.datasource.network.service.DevicesService
import com.x8bit.bitwarden.data.auth.datasource.network.service.DevicesServiceImpl
import com.x8bit.bitwarden.data.auth.datasource.network.service.HaveIBeenPwnedService
import com.x8bit.bitwarden.data.auth.datasource.network.service.HaveIBeenPwnedServiceImpl
import com.x8bit.bitwarden.data.auth.datasource.network.service.IdentityService
import com.x8bit.bitwarden.data.auth.datasource.network.service.IdentityServiceImpl
import com.x8bit.bitwarden.data.auth.datasource.network.service.NewAuthRequestService
import com.x8bit.bitwarden.data.auth.datasource.network.service.NewAuthRequestServiceImpl
import com.x8bit.bitwarden.data.auth.datasource.network.service.OrganizationService
import com.x8bit.bitwarden.data.auth.datasource.network.service.OrganizationServiceImpl
import com.bitwarden.network.service.AccountsService
import com.bitwarden.network.service.AccountsServiceImpl
import com.bitwarden.network.service.AuthRequestsService
import com.bitwarden.network.service.AuthRequestsServiceImpl
import com.bitwarden.network.service.DevicesService
import com.bitwarden.network.service.DevicesServiceImpl
import com.bitwarden.network.service.HaveIBeenPwnedService
import com.bitwarden.network.service.HaveIBeenPwnedServiceImpl
import com.bitwarden.network.service.IdentityService
import com.bitwarden.network.service.IdentityServiceImpl
import com.bitwarden.network.service.NewAuthRequestService
import com.bitwarden.network.service.NewAuthRequestServiceImpl
import com.bitwarden.network.service.OrganizationService
import com.bitwarden.network.service.OrganizationServiceImpl
import com.x8bit.bitwarden.data.platform.datasource.network.retrofit.Retrofits
import dagger.Module
import dagger.Provides

View File

@@ -1,16 +0,0 @@
package com.x8bit.bitwarden.data.auth.datasource.network.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
/**
* Response object returned when requesting organization domain SSO details.
*
* @property isSsoAvailable Whether or not SSO is available for this domain.
* @property organizationIdentifier The organization's identifier.
*/
@Serializable
data class OrganizationDomainSsoDetailsResponseJson(
@SerialName("ssoAvailable") val isSsoAvailable: Boolean,
@SerialName("organizationIdentifier") val organizationIdentifier: String,
)

View File

@@ -1,12 +0,0 @@
package com.x8bit.bitwarden.data.auth.datasource.network.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
/**
* Response body from the SSO prevalidate request.
*/
@Serializable
data class PrevalidateSsoResponseJson(
@SerialName("token") val token: String?,
)

View File

@@ -1,154 +0,0 @@
package com.x8bit.bitwarden.data.auth.datasource.network.service
import com.x8bit.bitwarden.data.auth.datasource.network.api.AuthenticatedAccountsApi
import com.x8bit.bitwarden.data.auth.datasource.network.api.AuthenticatedKeyConnectorApi
import com.x8bit.bitwarden.data.auth.datasource.network.api.UnauthenticatedAccountsApi
import com.x8bit.bitwarden.data.auth.datasource.network.api.UnauthenticatedKeyConnectorApi
import com.x8bit.bitwarden.data.auth.datasource.network.model.CreateAccountKeysRequest
import com.x8bit.bitwarden.data.auth.datasource.network.model.DeleteAccountRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.DeleteAccountResponseJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.KeyConnectorKeyRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.KeyConnectorMasterKeyRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.KeyConnectorMasterKeyResponseJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.PasswordHintRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.PasswordHintResponseJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.ResendEmailRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.ResetPasswordRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.SetPasswordRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.VerifyOtpRequestJson
import com.x8bit.bitwarden.data.platform.datasource.network.model.toBitwardenError
import com.x8bit.bitwarden.data.platform.datasource.network.util.HEADER_VALUE_BEARER_PREFIX
import com.x8bit.bitwarden.data.platform.datasource.network.util.parseErrorBodyOrNull
import kotlinx.serialization.json.Json
/**
* The default implementation of the [AccountsService].
*/
@Suppress("TooManyFunctions")
class AccountsServiceImpl(
private val unauthenticatedAccountsApi: UnauthenticatedAccountsApi,
private val authenticatedAccountsApi: AuthenticatedAccountsApi,
private val unauthenticatedKeyConnectorApi: UnauthenticatedKeyConnectorApi,
private val authenticatedKeyConnectorApi: AuthenticatedKeyConnectorApi,
private val json: Json,
) : AccountsService {
/**
* Converts the currently active account to a key-connector account.
*/
override suspend fun convertToKeyConnector(): Result<Unit> =
authenticatedAccountsApi.convertToKeyConnector()
override suspend fun createAccountKeys(
publicKey: String,
encryptedPrivateKey: String,
): Result<Unit> =
authenticatedAccountsApi.createAccountKeys(
body = CreateAccountKeysRequest(
publicKey = publicKey,
encryptedPrivateKey = encryptedPrivateKey,
),
)
override suspend fun deleteAccount(
masterPasswordHash: String?,
oneTimePassword: String?,
): Result<DeleteAccountResponseJson> =
authenticatedAccountsApi
.deleteAccount(
DeleteAccountRequestJson(
masterPasswordHash = masterPasswordHash,
oneTimePassword = oneTimePassword,
),
)
.map {
DeleteAccountResponseJson.Success
}
.recoverCatching { throwable ->
throwable
.toBitwardenError()
.parseErrorBodyOrNull<DeleteAccountResponseJson.Invalid>(
code = 400,
json = json,
)
?: throw throwable
}
override suspend fun requestOneTimePasscode(): Result<Unit> =
authenticatedAccountsApi.requestOtp()
override suspend fun verifyOneTimePasscode(passcode: String): Result<Unit> =
authenticatedAccountsApi.verifyOtp(
VerifyOtpRequestJson(
oneTimePasscode = passcode,
),
)
override suspend fun requestPasswordHint(
email: String,
): Result<PasswordHintResponseJson> =
unauthenticatedAccountsApi
.passwordHintRequest(PasswordHintRequestJson(email))
.map { PasswordHintResponseJson.Success }
.recoverCatching { throwable ->
throwable
.toBitwardenError()
.parseErrorBodyOrNull<PasswordHintResponseJson.Error>(
code = 429,
json = json,
)
?: throw throwable
}
override suspend fun resendVerificationCodeEmail(body: ResendEmailRequestJson): Result<Unit> =
unauthenticatedAccountsApi.resendVerificationCodeEmail(body = body)
override suspend fun resetPassword(body: ResetPasswordRequestJson): Result<Unit> {
return if (body.currentPasswordHash == null) {
authenticatedAccountsApi.resetTempPassword(body = body)
} else {
authenticatedAccountsApi.resetPassword(body = body)
}
}
override suspend fun setKeyConnectorKey(
accessToken: String,
body: KeyConnectorKeyRequestJson,
): Result<Unit> = unauthenticatedAccountsApi.setKeyConnectorKey(
body = body,
bearerToken = "$HEADER_VALUE_BEARER_PREFIX$accessToken",
)
override suspend fun setPassword(
body: SetPasswordRequestJson,
): Result<Unit> = authenticatedAccountsApi.setPassword(body)
override suspend fun getMasterKeyFromKeyConnector(
url: String,
accessToken: String,
): Result<KeyConnectorMasterKeyResponseJson> =
unauthenticatedKeyConnectorApi.getMasterKeyFromKeyConnector(
url = "$url/user-keys",
bearerToken = "$HEADER_VALUE_BEARER_PREFIX$accessToken",
)
override suspend fun storeMasterKeyToKeyConnector(
url: String,
masterKey: String,
): Result<Unit> =
authenticatedKeyConnectorApi.storeMasterKeyToKeyConnector(
url = "$url/user-keys",
body = KeyConnectorMasterKeyRequestJson(masterKey = masterKey),
)
override suspend fun storeMasterKeyToKeyConnector(
url: String,
accessToken: String,
masterKey: String,
): Result<Unit> =
unauthenticatedKeyConnectorApi.storeMasterKeyToKeyConnector(
url = "$url/user-keys",
bearerToken = "$HEADER_VALUE_BEARER_PREFIX$accessToken",
body = KeyConnectorMasterKeyRequestJson(masterKey = masterKey),
)
}

View File

@@ -1,34 +0,0 @@
package com.x8bit.bitwarden.data.auth.datasource.network.service
import com.x8bit.bitwarden.data.auth.datasource.network.api.AuthenticatedAuthRequestsApi
import com.x8bit.bitwarden.data.auth.datasource.network.model.AuthRequestUpdateRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.AuthRequestsResponseJson
class AuthRequestsServiceImpl(
private val authenticatedAuthRequestsApi: AuthenticatedAuthRequestsApi,
) : AuthRequestsService {
override suspend fun getAuthRequests(): Result<AuthRequestsResponseJson> =
authenticatedAuthRequestsApi.getAuthRequests()
override suspend fun getAuthRequest(
requestId: String,
): Result<AuthRequestsResponseJson.AuthRequest> =
authenticatedAuthRequestsApi.getAuthRequest(requestId = requestId)
override suspend fun updateAuthRequest(
requestId: String,
key: String,
masterPasswordHash: String?,
deviceId: String,
isApproved: Boolean,
): Result<AuthRequestsResponseJson.AuthRequest> =
authenticatedAuthRequestsApi.updateAuthRequest(
userId = requestId,
body = AuthRequestUpdateRequestJson(
key = key,
masterPasswordHash = masterPasswordHash,
deviceId = deviceId,
isApproved = isApproved,
),
)
}

View File

@@ -1,34 +0,0 @@
package com.x8bit.bitwarden.data.auth.datasource.network.service
import com.x8bit.bitwarden.data.auth.datasource.network.api.AuthenticatedDevicesApi
import com.x8bit.bitwarden.data.auth.datasource.network.api.UnauthenticatedDevicesApi
import com.x8bit.bitwarden.data.auth.datasource.network.model.TrustedDeviceKeysRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.TrustedDeviceKeysResponseJson
import com.x8bit.bitwarden.data.platform.datasource.network.util.base64UrlEncode
class DevicesServiceImpl(
private val authenticatedDevicesApi: AuthenticatedDevicesApi,
private val unauthenticatedDevicesApi: UnauthenticatedDevicesApi,
) : DevicesService {
override suspend fun getIsKnownDevice(
emailAddress: String,
deviceId: String,
): Result<Boolean> = unauthenticatedDevicesApi.getIsKnownDevice(
emailAddress = emailAddress.base64UrlEncode(),
deviceId = deviceId,
)
override suspend fun trustDevice(
appId: String,
encryptedUserKey: String,
encryptedDevicePublicKey: String,
encryptedDevicePrivateKey: String,
): Result<TrustedDeviceKeysResponseJson> = authenticatedDevicesApi.updateTrustedDeviceKeys(
appId = appId,
request = TrustedDeviceKeysRequestJson(
encryptedUserKey = encryptedUserKey,
encryptedDevicePublicKey = encryptedDevicePublicKey,
encryptedDevicePrivateKey = encryptedDevicePrivateKey,
),
)
}

View File

@@ -1,73 +0,0 @@
package com.x8bit.bitwarden.data.auth.datasource.network.service
import com.x8bit.bitwarden.data.auth.datasource.network.api.AuthenticatedAuthRequestsApi
import com.x8bit.bitwarden.data.auth.datasource.network.api.UnauthenticatedAuthRequestsApi
import com.x8bit.bitwarden.data.auth.datasource.network.model.AuthRequestRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.AuthRequestTypeJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.AuthRequestsResponseJson
import com.x8bit.bitwarden.data.platform.util.asFailure
/**
* The default implementation of the [NewAuthRequestService].
*/
class NewAuthRequestServiceImpl(
private val authenticatedAuthRequestsApi: AuthenticatedAuthRequestsApi,
private val unauthenticatedAuthRequestsApi: UnauthenticatedAuthRequestsApi,
) : NewAuthRequestService {
override suspend fun createAuthRequest(
email: String,
publicKey: String,
deviceId: String,
accessCode: String,
fingerprint: String,
authRequestType: AuthRequestTypeJson,
): Result<AuthRequestsResponseJson.AuthRequest> =
when (authRequestType) {
AuthRequestTypeJson.LOGIN_WITH_DEVICE -> {
unauthenticatedAuthRequestsApi.createAuthRequest(
deviceIdentifier = deviceId,
body = AuthRequestRequestJson(
email = email,
publicKey = publicKey,
deviceId = deviceId,
accessCode = accessCode,
fingerprint = fingerprint,
type = authRequestType,
),
)
}
AuthRequestTypeJson.UNLOCK -> {
UnsupportedOperationException("Unlock AuthRequestType is currently unsupported")
.asFailure()
}
AuthRequestTypeJson.ADMIN_APPROVAL -> {
authenticatedAuthRequestsApi.createAdminAuthRequest(
deviceIdentifier = deviceId,
body = AuthRequestRequestJson(
email = email,
publicKey = publicKey,
deviceId = deviceId,
accessCode = accessCode,
fingerprint = fingerprint,
type = authRequestType,
),
)
}
}
override suspend fun getAuthRequestUpdate(
requestId: String,
accessCode: String,
isSso: Boolean,
): Result<AuthRequestsResponseJson.AuthRequest> =
if (isSso) {
authenticatedAuthRequestsApi.getAuthRequest(requestId)
} else {
unauthenticatedAuthRequestsApi.getAuthRequestUpdate(
requestId = requestId,
accessCode = accessCode,
)
}
}

View File

@@ -8,7 +8,7 @@ import com.bitwarden.core.RegisterKeyResponse
import com.bitwarden.core.RegisterTdeKeyResponse
import com.bitwarden.crypto.HashPurpose
import com.bitwarden.crypto.Kdf
import com.bitwarden.sdk.ClientAuth
import com.bitwarden.sdk.AuthClient
import com.x8bit.bitwarden.data.auth.datasource.sdk.model.PasswordStrength
import com.x8bit.bitwarden.data.auth.datasource.sdk.util.toPasswordStrengthOrNull
import com.x8bit.bitwarden.data.auth.datasource.sdk.util.toUByte
@@ -17,7 +17,7 @@ import com.x8bit.bitwarden.data.platform.manager.SdkClientManager
/**
* Primary implementation of [AuthSdkSource] that serves as a convenience wrapper around a
* [ClientAuth].
* [AuthClient].
*/
class AuthSdkSourceImpl(
sdkClientManager: SdkClientManager,

View File

@@ -1,9 +1,9 @@
package com.x8bit.bitwarden.data.auth.datasource.sdk.util
import com.bitwarden.crypto.Kdf
import com.x8bit.bitwarden.data.auth.datasource.network.model.KdfTypeJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.KdfTypeJson.ARGON2_ID
import com.x8bit.bitwarden.data.auth.datasource.network.model.KdfTypeJson.PBKDF2_SHA256
import com.bitwarden.network.model.KdfTypeJson
import com.bitwarden.network.model.KdfTypeJson.ARGON2_ID
import com.bitwarden.network.model.KdfTypeJson.PBKDF2_SHA256
/**
* Convert a [Kdf] to a [KdfTypeJson].

View File

@@ -0,0 +1,15 @@
package com.x8bit.bitwarden.data.auth.manager
import com.x8bit.bitwarden.ui.vault.model.TotpData
/**
* Manager for keeping track of requests from the Bitwarden Authenticator app to add a TOTP
* item.
*/
interface AddTotpItemFromAuthenticatorManager {
/**
* Current pending [TotpData] to be added from the Authenticator app.
*/
var pendingAddTotpLoginItemData: TotpData?
}

View File

@@ -0,0 +1,11 @@
package com.x8bit.bitwarden.data.auth.manager
import com.x8bit.bitwarden.ui.vault.model.TotpData
/**
* Default in memory implementation for [AddTotpItemFromAuthenticatorManager].
*/
class AddTotpItemFromAuthenticatorManagerImpl : AddTotpItemFromAuthenticatorManager {
override var pendingAddTotpLoginItemData: TotpData? = null
}

View File

@@ -1,11 +1,14 @@
package com.x8bit.bitwarden.data.auth.manager
import com.bitwarden.core.AuthRequestResponse
import com.bitwarden.core.data.util.asFailure
import com.bitwarden.core.data.util.asSuccess
import com.bitwarden.core.data.util.flatMap
import com.bitwarden.network.model.AuthRequestTypeJson
import com.bitwarden.network.service.AuthRequestsService
import com.bitwarden.network.service.NewAuthRequestService
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
import com.x8bit.bitwarden.data.auth.datasource.disk.model.PendingAuthRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.AuthRequestTypeJson
import com.x8bit.bitwarden.data.auth.datasource.network.service.AuthRequestsService
import com.x8bit.bitwarden.data.auth.datasource.network.service.NewAuthRequestService
import com.x8bit.bitwarden.data.auth.datasource.sdk.AuthSdkSource
import com.x8bit.bitwarden.data.auth.manager.model.AuthRequest
import com.x8bit.bitwarden.data.auth.manager.model.AuthRequestResult
@@ -16,8 +19,7 @@ import com.x8bit.bitwarden.data.auth.manager.model.AuthRequestsUpdatesResult
import com.x8bit.bitwarden.data.auth.manager.model.CreateAuthRequestResult
import com.x8bit.bitwarden.data.auth.manager.util.isSso
import com.x8bit.bitwarden.data.auth.manager.util.toAuthRequestTypeJson
import com.x8bit.bitwarden.data.platform.util.asFailure
import com.x8bit.bitwarden.data.platform.util.flatMap
import com.x8bit.bitwarden.data.platform.error.NoActiveUserException
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.delay
@@ -50,7 +52,9 @@ class AuthRequestManagerImpl(
override fun getAuthRequestsWithUpdates(): Flow<AuthRequestsUpdatesResult> = flow {
while (currentCoroutineContext().isActive) {
when (val result = getAuthRequests()) {
AuthRequestsResult.Error -> emit(AuthRequestsUpdatesResult.Error)
is AuthRequestsResult.Error -> {
emit(AuthRequestsUpdatesResult.Error(error = result.error))
}
is AuthRequestsResult.Success -> {
emit(AuthRequestsUpdatesResult.Update(authRequests = result.authRequests))
@@ -65,16 +69,14 @@ class AuthRequestManagerImpl(
email: String,
authRequestType: AuthRequestType,
): Flow<CreateAuthRequestResult> = flow {
val initialResult = createNewAuthRequest(
val initialResult = createNewAuthRequestIfNecessary(
email = email,
authRequestType = authRequestType.toAuthRequestTypeJson(),
)
.getOrNull()
?: run {
emit(CreateAuthRequestResult.Error)
.getOrElse {
emit(CreateAuthRequestResult.Error(error = it))
return@flow
}
val authRequestResponse = initialResult.authRequestResponse
var authRequest = initialResult.authRequest
emit(CreateAuthRequestResult.Update(authRequest))
@@ -84,7 +86,7 @@ class AuthRequestManagerImpl(
newAuthRequestService
.getAuthRequestUpdate(
requestId = authRequest.id,
accessCode = authRequestResponse.accessCode,
accessCode = initialResult.accessCode,
isSso = authRequestType.isSso,
)
.map { request ->
@@ -103,7 +105,7 @@ class AuthRequestManagerImpl(
)
}
.fold(
onFailure = { emit(CreateAuthRequestResult.Error) },
onFailure = { emit(CreateAuthRequestResult.Error(error = it)) },
onSuccess = { updateAuthRequest ->
when {
updateAuthRequest.requestApproved -> {
@@ -112,7 +114,8 @@ class AuthRequestManagerImpl(
emit(
CreateAuthRequestResult.Success(
authRequest = updateAuthRequest,
authRequestResponse = authRequestResponse,
privateKey = initialResult.privateKey,
accessCode = initialResult.accessCode,
),
)
}
@@ -181,7 +184,7 @@ class AuthRequestManagerImpl(
)
}
.fold(
onFailure = { emit(AuthRequestUpdatesResult.Error) },
onFailure = { emit(AuthRequestUpdatesResult.Error(error = it)) },
onSuccess = { updateAuthRequest ->
when {
updateAuthRequest.requestApproved -> {
@@ -217,13 +220,18 @@ class AuthRequestManagerImpl(
fingerprint: String,
): Flow<AuthRequestUpdatesResult> = getAuthRequest {
when (val authRequestsResult = getAuthRequests()) {
AuthRequestsResult.Error -> AuthRequestUpdatesResult.Error
is AuthRequestsResult.Error -> {
AuthRequestUpdatesResult.Error(error = authRequestsResult.error)
}
is AuthRequestsResult.Success -> {
authRequestsResult
.authRequests
.firstOrNull { it.fingerprint == fingerprint }
?.let { AuthRequestUpdatesResult.Update(it) }
?: AuthRequestUpdatesResult.Error
?: AuthRequestUpdatesResult.Error(
error = IllegalStateException("Could not find the auth request."),
)
}
}
}
@@ -233,30 +241,28 @@ class AuthRequestManagerImpl(
): Flow<AuthRequestUpdatesResult> = getAuthRequest {
authRequestsService
.getAuthRequest(requestId)
.map { response ->
getFingerprintPhrase(response.publicKey).getOrNull()?.let { fingerprint ->
AuthRequest(
id = response.id,
publicKey = response.publicKey,
platform = response.platform,
ipAddress = response.ipAddress,
key = response.key,
masterPasswordHash = response.masterPasswordHash,
creationDate = response.creationDate,
responseDate = response.responseDate,
requestApproved = response.requestApproved ?: false,
originUrl = response.originUrl,
fingerprint = fingerprint,
)
}
.mapCatching { response ->
getFingerprintPhrase(response.publicKey)
.getOrThrow()
.let { fingerprint ->
AuthRequest(
id = response.id,
publicKey = response.publicKey,
platform = response.platform,
ipAddress = response.ipAddress,
key = response.key,
masterPasswordHash = response.masterPasswordHash,
creationDate = response.creationDate,
responseDate = response.responseDate,
requestApproved = response.requestApproved ?: false,
originUrl = response.originUrl,
fingerprint = fingerprint,
)
}
}
.fold(
onFailure = { AuthRequestUpdatesResult.Error },
onSuccess = { authRequest ->
authRequest
?.let { AuthRequestUpdatesResult.Update(it) }
?: AuthRequestUpdatesResult.Error
},
onFailure = { AuthRequestUpdatesResult.Error(error = it) },
onSuccess = { AuthRequestUpdatesResult.Update(authRequest = it) },
)
}
@@ -308,7 +314,7 @@ class AuthRequestManagerImpl(
}
}
.fold(
onFailure = { AuthRequestsResult.Error },
onFailure = { AuthRequestsResult.Error(error = it) },
onSuccess = { AuthRequestsResult.Success(authRequests = it) },
)
@@ -318,7 +324,7 @@ class AuthRequestManagerImpl(
publicKey: String,
isApproved: Boolean,
): AuthRequestResult {
val userId = activeUserId ?: return AuthRequestResult.Error
val userId = activeUserId ?: return AuthRequestResult.Error(error = NoActiveUserException())
return vaultSdkSource
.getAuthRequestKey(
publicKey = publicKey,
@@ -349,11 +355,57 @@ class AuthRequestManagerImpl(
)
}
.fold(
onFailure = { AuthRequestResult.Error },
onFailure = { AuthRequestResult.Error(error = it) },
onSuccess = { AuthRequestResult.Success(authRequest = it) },
)
}
/**
* Creates a new auth request for the given email and returns a [NewAuthRequestData].
* If the auth request type is [AuthRequestTypeJson.ADMIN_APPROVAL], check for a
* pending auth request and return it if it exists we should return that request.
*/
private suspend fun createNewAuthRequestIfNecessary(
email: String,
authRequestType: AuthRequestTypeJson,
): Result<NewAuthRequestData> {
return if (authRequestType == AuthRequestTypeJson.ADMIN_APPROVAL) {
authDiskSource
.getPendingAuthRequest(requireNotNull(activeUserId))
?.let { pendingAuthRequest ->
authRequestsService
.getAuthRequest(pendingAuthRequest.requestId)
.map {
NewAuthRequestData(
authRequest = AuthRequest(
id = it.id,
publicKey = it.publicKey,
platform = it.platform,
ipAddress = it.ipAddress,
key = it.key,
masterPasswordHash = it.masterPasswordHash,
creationDate = it.creationDate,
responseDate = it.responseDate,
requestApproved = it.requestApproved ?: false,
originUrl = it.originUrl,
fingerprint = pendingAuthRequest.requestFingerprint,
),
privateKey = pendingAuthRequest.requestPrivateKey,
accessCode = pendingAuthRequest.requestAccessCode,
)
.asSuccess()
}
.getOrNull()
}
?: createNewAuthRequest(email = email, authRequestType = authRequestType)
} else {
createNewAuthRequest(
email = email,
authRequestType = authRequestType,
)
}
}
/**
* Attempts to create a new auth request for the given email and returns a [NewAuthRequestData]
* with the [AuthRequest] and [AuthRequestResponse].
@@ -381,6 +433,8 @@ class AuthRequestManagerImpl(
pendingAuthRequest = PendingAuthRequestJson(
requestId = it.id,
requestPrivateKey = authRequestResponse.privateKey,
requestAccessCode = authRequestResponse.accessCode,
requestFingerprint = authRequestResponse.fingerprint,
),
)
}
@@ -400,14 +454,20 @@ class AuthRequestManagerImpl(
fingerprint = authRequestResponse.fingerprint,
)
}
.map { NewAuthRequestData(it, authRequestResponse) }
.map {
NewAuthRequestData(
authRequest = it,
privateKey = authRequestResponse.privateKey,
accessCode = authRequestResponse.accessCode,
)
}
}
private suspend fun getFingerprintPhrase(
publicKey: String,
): Result<String> {
val profile = authDiskSource.userState?.activeAccount?.profile
?: return IllegalStateException("No active account").asFailure()
?: return NoActiveUserException().asFailure()
return authSdkSource.getUserFingerprint(
email = profile.email,
publicKey = publicKey,
@@ -420,5 +480,6 @@ class AuthRequestManagerImpl(
*/
private data class NewAuthRequestData(
val authRequest: AuthRequest,
val authRequestResponse: AuthRequestResponse,
val privateKey: String,
val accessCode: String,
)

View File

@@ -7,13 +7,13 @@ import androidx.compose.ui.graphics.Color
import androidx.core.app.NotificationChannelCompat
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import com.bitwarden.core.annotation.OmitFromCoverage
import com.bitwarden.data.manager.DispatcherManager
import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
import com.x8bit.bitwarden.data.auth.util.createPasswordlessRequestDataIntent
import com.x8bit.bitwarden.data.autofill.util.toPendingIntentMutabilityFlag
import com.x8bit.bitwarden.data.platform.annotation.OmitFromCoverage
import com.x8bit.bitwarden.data.platform.manager.PushManager
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
import com.x8bit.bitwarden.data.platform.manager.model.PasswordlessRequestData
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.launchIn

View File

@@ -0,0 +1,8 @@
package com.x8bit.bitwarden.data.auth.manager
import com.bitwarden.network.interceptor.AuthTokenProvider
/**
* A manager class for handling authentication tokens.
*/
interface AuthTokenManager : AuthTokenProvider

View File

@@ -0,0 +1,17 @@
package com.x8bit.bitwarden.data.auth.manager
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
/**
* Default implementation of [AuthTokenManager].
*/
class AuthTokenManagerImpl(
private val authDiskSource: AuthDiskSource,
) : AuthTokenManager {
override fun getActiveAccessTokenOrNull(): String? = authDiskSource
.userState
?.activeUserId
?.let { authDiskSource.getAccountTokens(it) }
?.accessToken
}

View File

@@ -2,8 +2,8 @@ package com.x8bit.bitwarden.data.auth.manager
import com.bitwarden.core.KeyConnectorResponse
import com.bitwarden.crypto.Kdf
import com.x8bit.bitwarden.data.auth.datasource.network.model.KdfTypeJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.KeyConnectorMasterKeyResponseJson
import com.bitwarden.network.model.KdfTypeJson
import com.bitwarden.network.model.KeyConnectorMasterKeyResponseJson
/**
* Manager used to interface with a key connector.

View File

@@ -1,13 +1,13 @@
package com.x8bit.bitwarden.data.auth.manager
import com.bitwarden.core.KeyConnectorResponse
import com.bitwarden.core.data.util.flatMap
import com.bitwarden.crypto.Kdf
import com.x8bit.bitwarden.data.auth.datasource.network.model.KdfTypeJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.KeyConnectorKeyRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.KeyConnectorMasterKeyResponseJson
import com.x8bit.bitwarden.data.auth.datasource.network.service.AccountsService
import com.bitwarden.network.model.KdfTypeJson
import com.bitwarden.network.model.KeyConnectorKeyRequestJson
import com.bitwarden.network.model.KeyConnectorMasterKeyResponseJson
import com.bitwarden.network.service.AccountsService
import com.x8bit.bitwarden.data.auth.datasource.sdk.AuthSdkSource
import com.x8bit.bitwarden.data.platform.util.flatMap
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
/**

View File

@@ -1,11 +1,11 @@
package com.x8bit.bitwarden.data.auth.manager
import com.bitwarden.core.data.util.asSuccess
import com.bitwarden.core.data.util.flatMap
import com.bitwarden.crypto.TrustDeviceResponse
import com.bitwarden.network.service.DevicesService
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
import com.x8bit.bitwarden.data.auth.datasource.network.service.DevicesService
import com.x8bit.bitwarden.data.auth.manager.util.toUserStateJson
import com.x8bit.bitwarden.data.platform.util.asSuccess
import com.x8bit.bitwarden.data.platform.util.flatMap
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
/**
@@ -55,5 +55,5 @@ class TrustedDeviceManagerImpl(
authDiskSource.storeIsTdeLoginComplete(userId = userId, isTdeLoginComplete = true)
}
.also { authDiskSource.storeShouldTrustDevice(userId = userId, shouldTrustDevice = null) }
.map { Unit }
.map { }
}

View File

@@ -1,6 +1,7 @@
package com.x8bit.bitwarden.data.auth.manager
import com.x8bit.bitwarden.data.auth.manager.model.LogoutEvent
import com.x8bit.bitwarden.data.auth.repository.model.LogoutReason
import kotlinx.coroutines.flow.SharedFlow
/**
@@ -14,15 +15,14 @@ interface UserLogoutManager {
val logoutEventFlow: SharedFlow<LogoutEvent>
/**
* Completely logs out the given [userId], removing all data. If [isExpired] is true, a toast
* will be displayed letting the user know the session has expired.
* Completely logs out the given [userId], removing all data. The [reason] indicates why the
* user is being logged out.
*/
fun logout(userId: String, isExpired: Boolean = false)
fun logout(userId: String, reason: LogoutReason)
/**
* Partially logs out the given [userId]. All data for the given [userId] will be removed with
* the exception of basic account data. If [isExpired] is true, a toast will be displayed
* letting the user know the session has expired.
* the exception of basic account data. The [reason] indicates why the user is being logged out.
*/
fun softLogout(userId: String, isExpired: Boolean = false)
fun softLogout(userId: String, reason: LogoutReason)
}

View File

@@ -3,13 +3,14 @@ package com.x8bit.bitwarden.data.auth.manager
import android.content.Context
import android.widget.Toast
import androidx.annotation.StringRes
import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow
import com.bitwarden.data.manager.DispatcherManager
import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
import com.x8bit.bitwarden.data.auth.manager.model.LogoutEvent
import com.x8bit.bitwarden.data.auth.repository.model.LogoutReason
import com.x8bit.bitwarden.data.platform.datasource.disk.PushDiskSource
import com.x8bit.bitwarden.data.platform.datasource.disk.SettingsDiskSource
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow
import com.x8bit.bitwarden.data.tools.generator.datasource.disk.GeneratorDiskSource
import com.x8bit.bitwarden.data.tools.generator.datasource.disk.PasswordHistoryDiskSource
import com.x8bit.bitwarden.data.vault.datasource.disk.VaultDiskSource
@@ -19,6 +20,7 @@ import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.launch
import timber.log.Timber
/**
* Primary implementation of [UserLogoutManager].
@@ -42,9 +44,10 @@ class UserLogoutManagerImpl(
bufferedMutableSharedFlow()
override val logoutEventFlow: SharedFlow<LogoutEvent> = mutableLogoutEventFlow.asSharedFlow()
override fun logout(userId: String, isExpired: Boolean) {
override fun logout(userId: String, reason: LogoutReason) {
authDiskSource.userState ?: return
Timber.i("logout reason=$reason")
val isExpired = reason == LogoutReason.SecurityStamp
if (isExpired) {
showToast(message = R.string.login_expired)
}
@@ -64,7 +67,9 @@ class UserLogoutManagerImpl(
mutableLogoutEventFlow.tryEmit(LogoutEvent(loggedOutUserId = userId))
}
override fun softLogout(userId: String, isExpired: Boolean) {
override fun softLogout(userId: String, reason: LogoutReason) {
Timber.i("softLogout reason=$reason")
val isExpired = reason == LogoutReason.SecurityStamp
if (isExpired) {
showToast(message = R.string.login_expired)
}

View File

@@ -1,16 +1,21 @@
package com.x8bit.bitwarden.data.auth.manager.di
import android.content.Context
import com.bitwarden.data.manager.DispatcherManager
import com.bitwarden.network.service.AccountsService
import com.bitwarden.network.service.AuthRequestsService
import com.bitwarden.network.service.DevicesService
import com.bitwarden.network.service.NewAuthRequestService
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
import com.x8bit.bitwarden.data.auth.datasource.network.service.AccountsService
import com.x8bit.bitwarden.data.auth.datasource.network.service.AuthRequestsService
import com.x8bit.bitwarden.data.auth.datasource.network.service.DevicesService
import com.x8bit.bitwarden.data.auth.datasource.network.service.NewAuthRequestService
import com.x8bit.bitwarden.data.auth.datasource.sdk.AuthSdkSource
import com.x8bit.bitwarden.data.auth.manager.AddTotpItemFromAuthenticatorManager
import com.x8bit.bitwarden.data.auth.manager.AddTotpItemFromAuthenticatorManagerImpl
import com.x8bit.bitwarden.data.auth.manager.AuthRequestManager
import com.x8bit.bitwarden.data.auth.manager.AuthRequestManagerImpl
import com.x8bit.bitwarden.data.auth.manager.AuthRequestNotificationManager
import com.x8bit.bitwarden.data.auth.manager.AuthRequestNotificationManagerImpl
import com.x8bit.bitwarden.data.auth.manager.AuthTokenManager
import com.x8bit.bitwarden.data.auth.manager.AuthTokenManagerImpl
import com.x8bit.bitwarden.data.auth.manager.KeyConnectorManager
import com.x8bit.bitwarden.data.auth.manager.KeyConnectorManagerImpl
import com.x8bit.bitwarden.data.auth.manager.TrustedDeviceManager
@@ -20,7 +25,6 @@ import com.x8bit.bitwarden.data.auth.manager.UserLogoutManagerImpl
import com.x8bit.bitwarden.data.platform.datasource.disk.PushDiskSource
import com.x8bit.bitwarden.data.platform.datasource.disk.SettingsDiskSource
import com.x8bit.bitwarden.data.platform.manager.PushManager
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
import com.x8bit.bitwarden.data.tools.generator.datasource.disk.GeneratorDiskSource
import com.x8bit.bitwarden.data.tools.generator.datasource.disk.PasswordHistoryDiskSource
import com.x8bit.bitwarden.data.vault.datasource.disk.VaultDiskSource
@@ -124,4 +128,15 @@ object AuthManagerModule {
vaultSdkSource = vaultSdkSource,
dispatcherManager = dispatcherManager,
)
@Provides
@Singleton
fun providesAddTotpItemFromAuthenticatorManager(): AddTotpItemFromAuthenticatorManager =
AddTotpItemFromAuthenticatorManagerImpl()
@Provides
@Singleton
fun providesAuthTokenManager(
authDiskSource: AuthDiskSource,
): AuthTokenManager = AuthTokenManagerImpl(authDiskSource = authDiskSource)
}

View File

@@ -14,5 +14,7 @@ sealed class AuthRequestResult {
/**
* There was an error getting the user's auth requests.
*/
data object Error : AuthRequestResult()
data class Error(
val error: Throwable,
) : AuthRequestResult()
}

View File

@@ -19,7 +19,9 @@ sealed class AuthRequestUpdatesResult {
/**
* There was an error getting the user's auth requests.
*/
data object Error : AuthRequestUpdatesResult()
data class Error(
val error: Throwable,
) : AuthRequestUpdatesResult()
/**
* The auth request has been declined.

View File

@@ -14,5 +14,7 @@ sealed class AuthRequestsResult {
/**
* There was an error getting the user's auth requests.
*/
data object Error : AuthRequestsResult()
data class Error(
val error: Throwable,
) : AuthRequestsResult()
}

View File

@@ -14,5 +14,7 @@ sealed class AuthRequestsUpdatesResult {
/**
* There was an error getting the user's auth requests.
*/
data object Error : AuthRequestsUpdatesResult()
data class Error(
val error: Throwable,
) : AuthRequestsUpdatesResult()
}

View File

@@ -1,7 +1,5 @@
package com.x8bit.bitwarden.data.auth.manager.model
import com.bitwarden.core.AuthRequestResponse
/**
* Models result of creating a new login approval request.
*/
@@ -18,13 +16,16 @@ sealed class CreateAuthRequestResult {
*/
data class Success(
val authRequest: AuthRequest,
val authRequestResponse: AuthRequestResponse,
val privateKey: String,
val accessCode: String,
) : CreateAuthRequestResult()
/**
* There was a generic error getting the user's auth requests.
*/
data object Error : CreateAuthRequestResult()
data class Error(
val error: Throwable,
) : CreateAuthRequestResult()
/**
* The auth request has been declined.

View File

@@ -1,6 +1,6 @@
package com.x8bit.bitwarden.data.auth.manager.util
import com.x8bit.bitwarden.data.auth.datasource.network.model.AuthRequestTypeJson
import com.bitwarden.network.model.AuthRequestTypeJson
import com.x8bit.bitwarden.data.auth.manager.model.AuthRequestType
/**

View File

@@ -1,9 +1,9 @@
package com.x8bit.bitwarden.data.auth.manager.util
import com.bitwarden.crypto.TrustDeviceResponse
import com.bitwarden.network.model.TrustedDeviceUserDecryptionOptionsJson
import com.bitwarden.network.model.UserDecryptionOptionsJson
import com.x8bit.bitwarden.data.auth.datasource.disk.model.UserStateJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.TrustedDeviceUserDecryptionOptionsJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.UserDecryptionOptionsJson
/**
* Converts the given [TrustDeviceResponse] to an updated [UserStateJson], given the following

View File

@@ -1,16 +1,19 @@
package com.x8bit.bitwarden.data.auth.repository
import com.bitwarden.network.model.GetTokenResponseJson
import com.bitwarden.network.model.SyncResponseJson
import com.bitwarden.network.model.TwoFactorDataModel
import com.x8bit.bitwarden.data.auth.datasource.disk.model.ForcePasswordResetReason
import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus
import com.x8bit.bitwarden.data.auth.datasource.network.model.GetTokenResponseJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.TwoFactorDataModel
import com.x8bit.bitwarden.data.auth.manager.AuthRequestManager
import com.x8bit.bitwarden.data.auth.repository.model.AuthState
import com.x8bit.bitwarden.data.auth.repository.model.BreachCountResult
import com.x8bit.bitwarden.data.auth.repository.model.DeleteAccountResult
import com.x8bit.bitwarden.data.auth.repository.model.EmailTokenResult
import com.x8bit.bitwarden.data.auth.repository.model.KnownDeviceResult
import com.x8bit.bitwarden.data.auth.repository.model.LeaveOrganizationResult
import com.x8bit.bitwarden.data.auth.repository.model.LoginResult
import com.x8bit.bitwarden.data.auth.repository.model.LogoutReason
import com.x8bit.bitwarden.data.auth.repository.model.NewSsoUserResult
import com.x8bit.bitwarden.data.auth.repository.model.OrganizationDomainSsoDetailsResult
import com.x8bit.bitwarden.data.auth.repository.model.PasswordHintResult
@@ -28,6 +31,7 @@ import com.x8bit.bitwarden.data.auth.repository.model.SwitchAccountResult
import com.x8bit.bitwarden.data.auth.repository.model.UserState
import com.x8bit.bitwarden.data.auth.repository.model.ValidatePasswordResult
import com.x8bit.bitwarden.data.auth.repository.model.ValidatePinResult
import com.x8bit.bitwarden.data.auth.repository.model.VerifiedOrganizationDomainSsoDetailsResult
import com.x8bit.bitwarden.data.auth.repository.model.VerifyOtpResult
import com.x8bit.bitwarden.data.auth.repository.util.CaptchaCallbackTokenResult
import com.x8bit.bitwarden.data.auth.repository.util.DuoCallbackTokenResult
@@ -35,7 +39,6 @@ import com.x8bit.bitwarden.data.auth.repository.util.SsoCallbackResult
import com.x8bit.bitwarden.data.auth.repository.util.WebAuthResult
import com.x8bit.bitwarden.data.auth.util.YubiKeyResult
import com.x8bit.bitwarden.data.platform.datasource.network.authenticator.AuthenticatorProvider
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
@@ -228,10 +231,33 @@ interface AuthRepository : AuthenticatorProvider, AuthRequestManager {
organizationIdentifier: String,
): LoginResult
/**
* Repeat the previous login attempt but this time with New Device OTP
* information. Password is included if available to unlock the vault after
* authentication. Updated access token will be reflected in [authStateFlow].
*/
suspend fun login(
email: String,
password: String?,
newDeviceOtp: String,
captchaToken: String?,
orgIdentifier: String?,
): LoginResult
/**
* Continue the previously halted login attempt.
*/
suspend fun continueKeyConnectorLogin(): LoginResult
/**
* Cancel the previously halted login attempt.
*/
fun cancelKeyConnectorLogin()
/**
* Log out the current user.
*/
fun logout()
fun logout(reason: LogoutReason)
/**
* Requests that a one-time passcode be sent to the user's email.
@@ -250,6 +276,11 @@ interface AuthRepository : AuthenticatorProvider, AuthRequestManager {
*/
suspend fun resendVerificationCodeEmail(): ResendEmailResult
/**
* Resend the email with the new device verification code.
*/
suspend fun resendNewDeviceOtp(): ResendEmailResult
/**
* Switches to the account corresponding to the given [userId] if possible.
*/
@@ -329,6 +360,13 @@ interface AuthRepository : AuthenticatorProvider, AuthRequestManager {
email: String,
): OrganizationDomainSsoDetailsResult
/**
* Get the verified organization domain SSO details for the given [email].
*/
suspend fun getVerifiedOrganizationDomainSsoDetails(
email: String,
): VerifiedOrganizationDomainSsoDetailsResult
/**
* Prevalidates the organization identifier used in an SSO request.
*/
@@ -353,8 +391,10 @@ interface AuthRepository : AuthenticatorProvider, AuthRequestManager {
/**
* Get the password strength for the given [email] and [password] combo.
* If no value is passed for the [email] will use the active email of the current active
* account via the [userStateFlow].
*/
suspend fun getPasswordStrength(email: String, password: String): PasswordStrengthResult
suspend fun getPasswordStrength(email: String? = null, password: String): PasswordStrengthResult
/**
* Validates the master password for the current logged in user.
@@ -392,10 +432,12 @@ interface AuthRepository : AuthenticatorProvider, AuthRequestManager {
/**
* Update the value of the onboarding status for the user.
*/
fun setOnboardingStatus(userId: String, status: OnboardingStatus?)
fun setOnboardingStatus(status: OnboardingStatus)
/**
* Update the value of the showImportLogins status for the user.
* Leaves the organization that matches the given [organizationId]
*/
fun setShowImportLogins(showImportLogins: Boolean)
suspend fun leaveOrganization(
organizationId: String,
): LeaveOrganizationResult
}

View File

@@ -2,38 +2,51 @@ package com.x8bit.bitwarden.data.auth.repository
import com.bitwarden.core.AuthRequestMethod
import com.bitwarden.core.InitUserCryptoMethod
import com.bitwarden.core.InitUserCryptoRequest
import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow
import com.bitwarden.core.data.util.asFailure
import com.bitwarden.core.data.util.asSuccess
import com.bitwarden.core.data.util.flatMap
import com.bitwarden.crypto.HashPurpose
import com.bitwarden.crypto.Kdf
import com.bitwarden.data.datasource.disk.ConfigDiskSource
import com.bitwarden.data.manager.DispatcherManager
import com.bitwarden.data.repository.util.toEnvironmentUrls
import com.bitwarden.network.model.DeleteAccountResponseJson
import com.bitwarden.network.model.GetTokenResponseJson
import com.bitwarden.network.model.IdentityTokenAuthModel
import com.bitwarden.network.model.OrganizationType
import com.bitwarden.network.model.PasswordHintResponseJson
import com.bitwarden.network.model.PolicyTypeJson
import com.bitwarden.network.model.PrevalidateSsoResponseJson
import com.bitwarden.network.model.RefreshTokenResponseJson
import com.bitwarden.network.model.RegisterFinishRequestJson
import com.bitwarden.network.model.RegisterRequestJson
import com.bitwarden.network.model.RegisterResponseJson
import com.bitwarden.network.model.ResendEmailRequestJson
import com.bitwarden.network.model.ResendNewDeviceOtpRequestJson
import com.bitwarden.network.model.ResetPasswordRequestJson
import com.bitwarden.network.model.SendVerificationEmailRequestJson
import com.bitwarden.network.model.SendVerificationEmailResponseJson
import com.bitwarden.network.model.SetPasswordRequestJson
import com.bitwarden.network.model.SyncResponseJson
import com.bitwarden.network.model.TrustedDeviceUserDecryptionOptionsJson
import com.bitwarden.network.model.TwoFactorAuthMethod
import com.bitwarden.network.model.TwoFactorDataModel
import com.bitwarden.network.model.VerifyEmailTokenRequestJson
import com.bitwarden.network.model.VerifyEmailTokenResponseJson
import com.bitwarden.network.service.AccountsService
import com.bitwarden.network.service.DevicesService
import com.bitwarden.network.service.HaveIBeenPwnedService
import com.bitwarden.network.service.IdentityService
import com.bitwarden.network.service.OrganizationService
import com.bitwarden.network.util.isSslHandShakeError
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
import com.x8bit.bitwarden.data.auth.datasource.disk.model.AccountJson
import com.x8bit.bitwarden.data.auth.datasource.disk.model.AccountTokensJson
import com.x8bit.bitwarden.data.auth.datasource.disk.model.ForcePasswordResetReason
import com.x8bit.bitwarden.data.auth.datasource.disk.model.OnboardingStatus
import com.x8bit.bitwarden.data.auth.datasource.disk.model.UserStateJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.DeleteAccountResponseJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.DeviceDataModel
import com.x8bit.bitwarden.data.auth.datasource.network.model.GetTokenResponseJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.IdentityTokenAuthModel
import com.x8bit.bitwarden.data.auth.datasource.network.model.PasswordHintResponseJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.RefreshTokenResponseJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterFinishRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterResponseJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.ResendEmailRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.ResetPasswordRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.SendVerificationEmailRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.SetPasswordRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.TrustedDeviceUserDecryptionOptionsJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.TwoFactorAuthMethod
import com.x8bit.bitwarden.data.auth.datasource.network.model.TwoFactorDataModel
import com.x8bit.bitwarden.data.auth.datasource.network.model.VerifyEmailTokenRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.VerifyEmailTokenResponseJson
import com.x8bit.bitwarden.data.auth.datasource.network.service.AccountsService
import com.x8bit.bitwarden.data.auth.datasource.network.service.DevicesService
import com.x8bit.bitwarden.data.auth.datasource.network.service.HaveIBeenPwnedService
import com.x8bit.bitwarden.data.auth.datasource.network.service.IdentityService
import com.x8bit.bitwarden.data.auth.datasource.network.service.OrganizationService
import com.x8bit.bitwarden.data.auth.datasource.sdk.AuthSdkSource
import com.x8bit.bitwarden.data.auth.datasource.sdk.util.toInt
import com.x8bit.bitwarden.data.auth.datasource.sdk.util.toKdfTypeJson
@@ -46,7 +59,9 @@ import com.x8bit.bitwarden.data.auth.repository.model.BreachCountResult
import com.x8bit.bitwarden.data.auth.repository.model.DeleteAccountResult
import com.x8bit.bitwarden.data.auth.repository.model.EmailTokenResult
import com.x8bit.bitwarden.data.auth.repository.model.KnownDeviceResult
import com.x8bit.bitwarden.data.auth.repository.model.LeaveOrganizationResult
import com.x8bit.bitwarden.data.auth.repository.model.LoginResult
import com.x8bit.bitwarden.data.auth.repository.model.LogoutReason
import com.x8bit.bitwarden.data.auth.repository.model.NewSsoUserResult
import com.x8bit.bitwarden.data.auth.repository.model.OrganizationDomainSsoDetailsResult
import com.x8bit.bitwarden.data.auth.repository.model.PasswordHintResult
@@ -68,6 +83,7 @@ import com.x8bit.bitwarden.data.auth.repository.model.UserState
import com.x8bit.bitwarden.data.auth.repository.model.ValidatePasswordResult
import com.x8bit.bitwarden.data.auth.repository.model.ValidatePinResult
import com.x8bit.bitwarden.data.auth.repository.model.VaultUnlockType
import com.x8bit.bitwarden.data.auth.repository.model.VerifiedOrganizationDomainSsoDetailsResult
import com.x8bit.bitwarden.data.auth.repository.model.VerifyOtpResult
import com.x8bit.bitwarden.data.auth.repository.model.toLoginErrorResult
import com.x8bit.bitwarden.data.auth.repository.util.CaptchaCallbackTokenResult
@@ -92,26 +108,19 @@ import com.x8bit.bitwarden.data.auth.repository.util.userSwitchingChangesFlow
import com.x8bit.bitwarden.data.auth.util.KdfParamsConstants.DEFAULT_PBKDF2_ITERATIONS
import com.x8bit.bitwarden.data.auth.util.YubiKeyResult
import com.x8bit.bitwarden.data.auth.util.toSdkParams
import com.x8bit.bitwarden.data.platform.datasource.disk.ConfigDiskSource
import com.x8bit.bitwarden.data.platform.error.MissingPropertyException
import com.x8bit.bitwarden.data.platform.error.NoActiveUserException
import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager
import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager
import com.x8bit.bitwarden.data.platform.manager.LogsManager
import com.x8bit.bitwarden.data.platform.manager.PolicyManager
import com.x8bit.bitwarden.data.platform.manager.PushManager
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
import com.x8bit.bitwarden.data.platform.manager.model.FirstTimeState
import com.x8bit.bitwarden.data.platform.manager.model.FlagKey
import com.x8bit.bitwarden.data.platform.manager.util.getActivePolicies
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow
import com.x8bit.bitwarden.data.platform.util.asFailure
import com.x8bit.bitwarden.data.platform.util.asSuccess
import com.x8bit.bitwarden.data.platform.util.flatMap
import com.x8bit.bitwarden.data.vault.datasource.network.model.OrganizationType
import com.x8bit.bitwarden.data.vault.datasource.network.model.PolicyTypeJson
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.InitializeCryptoResult
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockData
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockError
@@ -164,7 +173,8 @@ class AuthRepositoryImpl(
private val userLogoutManager: UserLogoutManager,
private val policyManager: PolicyManager,
private val featureFlagManager: FeatureFlagManager,
private val firstTimeActionManager: FirstTimeActionManager,
firstTimeActionManager: FirstTimeActionManager,
logsManager: LogsManager,
pushManager: PushManager,
dispatcherManager: DispatcherManager,
) : AuthRepository,
@@ -216,6 +226,11 @@ class AuthRepositoryImpl(
*/
private var resendEmailRequestJson: ResendEmailRequestJson? = null
/**
* The information necessary to resend the verification code email for new devices.
*/
private var resendNewDeviceOtpRequestJson: ResendNewDeviceOtpRequestJson? = null
private var organizationIdentifier: String? = null
/**
@@ -224,6 +239,8 @@ class AuthRepositoryImpl(
*/
private var passwordsToCheckMap = mutableMapOf<String, String>()
private var keyConnectorResponse: GetTokenResponseJson.Success? = null
override var twoFactorResponse: GetTokenResponseJson.TwoFactorRequired? = null
override val ssoOrganizationIdentifier: String? get() = organizationIdentifier
@@ -363,6 +380,24 @@ class AuthRepositoryImpl(
featureFlagManager.getFeatureFlag(FlagKey.OnboardingCarousel)
init {
combine(
mutableHasPendingAccountAdditionStateFlow,
authDiskSource.userStateFlow,
environmentRepository.environmentStateFlow,
) { hasPendingAddition, userState, environment ->
logsManager.setUserData(
userId = userState?.activeUserId.takeUnless { hasPendingAddition },
environmentType = userState
?.activeAccount
?.settings
?.environmentUrlData
?.toEnvironmentUrls()
?.type
.takeUnless { hasPendingAddition }
?: environment.type,
)
}
.launchIn(unconfinedScope)
pushManager
.syncOrgKeysFlow
.onEach {
@@ -376,7 +411,7 @@ class AuthRepositoryImpl(
pushManager
.logoutFlow
.onEach { logout(userId = it.userId) }
.onEach { logout(userId = it.userId, reason = LogoutReason.Notification) }
.launchIn(unconfinedScope)
// When the policies for the user have been set, complete the login process.
@@ -434,7 +469,7 @@ class AuthRepositoryImpl(
masterPassword: String,
): DeleteAccountResult {
val profile = authDiskSource.userState?.activeAccount?.profile
?: return DeleteAccountResult.Error(message = null)
?: return DeleteAccountResult.Error(message = null, error = NoActiveUserException())
mutableHasPendingAccountDeletionStateFlow.value = true
return authSdkSource
.hashPassword(
@@ -468,22 +503,17 @@ class AuthRepositoryImpl(
fold(
onFailure = {
clearPendingAccountDeletion()
DeleteAccountResult.Error(message = null)
DeleteAccountResult.Error(error = it, message = null)
},
onSuccess = { response ->
when (response) {
is DeleteAccountResponseJson.Invalid -> {
clearPendingAccountDeletion()
DeleteAccountResult.Error(
message = response.validationErrors
?.values
?.firstOrNull()
?.firstOrNull(),
)
DeleteAccountResult.Error(message = response.message, error = null)
}
DeleteAccountResponseJson.Success -> {
logout()
logout(reason = LogoutReason.AccountDelete)
DeleteAccountResult.Success
}
}
@@ -491,8 +521,10 @@ class AuthRepositoryImpl(
)
override suspend fun createNewSsoUser(): NewSsoUserResult {
val account = authDiskSource.userState?.activeAccount ?: return NewSsoUserResult.Failure
val orgIdentifier = rememberedOrgIdentifier ?: return NewSsoUserResult.Failure
val account = authDiskSource.userState?.activeAccount
?: return NewSsoUserResult.Failure(error = NoActiveUserException())
val orgIdentifier = rememberedOrgIdentifier
?: return NewSsoUserResult.Failure(error = MissingPropertyException("OrgIdentifier"))
val userId = account.profile.userId
return organizationService
.getOrganizationAutoEnrollStatus(orgIdentifier)
@@ -544,7 +576,7 @@ class AuthRepositoryImpl(
}
.fold(
onSuccess = { NewSsoUserResult.Success },
onFailure = { NewSsoUserResult.Failure },
onFailure = { NewSsoUserResult.Failure(error = it) },
)
}
@@ -553,10 +585,13 @@ class AuthRepositoryImpl(
asymmetricalKey: String,
): LoginResult {
val profile = authDiskSource.userState?.activeAccount?.profile
?: return LoginResult.Error(errorMessage = null)
?: return LoginResult.Error(errorMessage = null, error = NoActiveUserException())
val userId = profile.userId
val privateKey = authDiskSource.getPrivateKey(userId = userId)
?: return LoginResult.Error(errorMessage = null)
?: return LoginResult.Error(
errorMessage = null,
error = MissingPropertyException("Private Key"),
)
checkForVaultUnlockError(
onVaultUnlockError = { error ->
@@ -603,7 +638,12 @@ class AuthRepositoryImpl(
)
}
.fold(
onFailure = { LoginResult.Error(errorMessage = null) },
onFailure = { throwable ->
when {
throwable.isSslHandShakeError() -> LoginResult.CertificateError
else -> LoginResult.Error(errorMessage = null, error = throwable)
}
},
onSuccess = { it },
)
@@ -650,7 +690,52 @@ class AuthRepositoryImpl(
orgIdentifier = orgIdentifier,
)
}
?: LoginResult.Error(errorMessage = null)
?: LoginResult.Error(
errorMessage = null,
error = MissingPropertyException("Identity Token Auth Model"),
)
override suspend fun login(
email: String,
password: String?,
newDeviceOtp: String,
captchaToken: String?,
orgIdentifier: String?,
): LoginResult = identityTokenAuthModel
?.let {
loginCommon(
email = email,
password = password,
authModel = it,
newDeviceOtp = newDeviceOtp,
captchaToken = captchaToken ?: twoFactorResponse?.captchaToken,
deviceData = twoFactorDeviceData,
orgIdentifier = orgIdentifier,
)
}
?: LoginResult.Error(
errorMessage = null,
error = MissingPropertyException("Identity Token Auth Model"),
)
override suspend fun continueKeyConnectorLogin(): LoginResult {
val response = keyConnectorResponse ?: return LoginResult.Error(
errorMessage = null,
error = MissingPropertyException("Key Connector Response"),
)
return handleLoginCommonSuccess(
loginResponse = response,
email = rememberedEmailAddress.orEmpty(),
orgIdentifier = rememberedOrgIdentifier,
password = null,
deviceData = null,
userConfirmedKeyConnector = true,
)
}
override fun cancelKeyConnectorLogin() {
keyConnectorResponse = null
}
override suspend fun login(
email: String,
@@ -698,18 +783,18 @@ class AuthRepositoryImpl(
}
}
override fun logout() {
activeUserId?.let { userId -> logout(userId) }
override fun logout(reason: LogoutReason) {
activeUserId?.let { userId -> logout(userId = userId, reason = reason) }
}
override fun logout(userId: String) {
userLogoutManager.logout(userId = userId)
override fun logout(userId: String, reason: LogoutReason) {
userLogoutManager.logout(userId = userId, reason = reason)
}
override suspend fun requestOneTimePasscode(): RequestOtpResult =
accountsService.requestOneTimePasscode()
.fold(
onFailure = { RequestOtpResult.Error(it.message) },
onFailure = { RequestOtpResult.Error(message = it.message, error = it) },
onSuccess = { RequestOtpResult.Success },
)
@@ -719,7 +804,7 @@ class AuthRepositoryImpl(
passcode = oneTimePasscode,
)
.fold(
onFailure = { VerifyOtpResult.NotVerified(it.message) },
onFailure = { VerifyOtpResult.NotVerified(errorMessage = it.message, error = it) },
onSuccess = { VerifyOtpResult.Verified },
)
@@ -727,11 +812,27 @@ class AuthRepositoryImpl(
resendEmailRequestJson
?.let { jsonRequest ->
accountsService.resendVerificationCodeEmail(body = jsonRequest).fold(
onFailure = { ResendEmailResult.Error(message = it.message) },
onFailure = { ResendEmailResult.Error(message = it.message, error = it) },
onSuccess = { ResendEmailResult.Success },
)
}
?: ResendEmailResult.Error(message = null)
?: ResendEmailResult.Error(
message = null,
error = MissingPropertyException("Resend Email Request"),
)
override suspend fun resendNewDeviceOtp(): ResendEmailResult =
resendNewDeviceOtpRequestJson
?.let { jsonRequest ->
accountsService.resendNewDeviceOtp(body = jsonRequest).fold(
onFailure = { ResendEmailResult.Error(message = it.message, error = it) },
onSuccess = { ResendEmailResult.Success },
)
}
?: ResendEmailResult.Error(
message = null,
error = MissingPropertyException("Resend New Device OTP Request"),
)
override fun switchAccount(userId: String): SwitchAccountResult {
val currentUserState = authDiskSource.userState ?: return SwitchAccountResult.NoChange
@@ -833,7 +934,10 @@ class AuthRepositoryImpl(
is RegisterResponseJson.CaptchaRequired -> {
it.validationErrors.captchaKeys.firstOrNull()
?.let { key -> RegisterResult.CaptchaRequired(captchaId = key) }
?: RegisterResult.Error(errorMessage = null)
?: RegisterResult.Error(
errorMessage = null,
error = MissingPropertyException("Captcha ID"),
)
}
is RegisterResponseJson.Success -> {
@@ -842,18 +946,11 @@ class AuthRepositoryImpl(
}
is RegisterResponseJson.Invalid -> {
RegisterResult.Error(
errorMessage = it
.validationErrors
?.values
?.firstOrNull()
?.firstOrNull()
?: it.message,
)
RegisterResult.Error(errorMessage = it.message, error = null)
}
}
},
onFailure = { RegisterResult.Error(errorMessage = null) },
onFailure = { RegisterResult.Error(errorMessage = null, error = it) },
)
}
@@ -861,11 +958,15 @@ class AuthRepositoryImpl(
return accountsService.requestPasswordHint(email).fold(
onSuccess = {
when (it) {
is PasswordHintResponseJson.Error -> PasswordHintResult.Error(it.errorMessage)
is PasswordHintResponseJson.Error -> PasswordHintResult.Error(
message = it.errorMessage,
error = null,
)
PasswordHintResponseJson.Success -> PasswordHintResult.Success
}
},
onFailure = { PasswordHintResult.Error(null) },
onFailure = { PasswordHintResult.Error(message = null, error = it) },
)
}
@@ -873,12 +974,12 @@ class AuthRepositoryImpl(
val activeAccount = authDiskSource
.userState
?.activeAccount
?: return RemovePasswordResult.Error
?: return RemovePasswordResult.Error(error = NoActiveUserException())
val profile = activeAccount.profile
val userId = profile.userId
val userKey = authDiskSource
.getUserKey(userId = userId)
?: return RemovePasswordResult.Error
?: return RemovePasswordResult.Error(error = MissingPropertyException("User Key"))
val keyConnectorUrl = organizations
.find {
it.shouldUseKeyConnector &&
@@ -886,7 +987,9 @@ class AuthRepositoryImpl(
it.type != OrganizationType.ADMIN
}
?.keyConnectorUrl
?: return RemovePasswordResult.Error
?: return RemovePasswordResult.Error(
error = MissingPropertyException("Key Connector URL"),
)
return keyConnectorManager
.migrateExistingUserToKeyConnector(
userId = userId,
@@ -904,7 +1007,7 @@ class AuthRepositoryImpl(
settingsRepository.setDefaultsIfNecessary(userId = userId)
}
.fold(
onFailure = { RemovePasswordResult.Error },
onFailure = { RemovePasswordResult.Error(error = it) },
onSuccess = { RemovePasswordResult.Success },
)
}
@@ -917,7 +1020,7 @@ class AuthRepositoryImpl(
val activeAccount = authDiskSource
.userState
?.activeAccount
?: return ResetPasswordResult.Error
?: return ResetPasswordResult.Error(error = NoActiveUserException())
val currentPasswordHash = currentPassword?.let { password ->
authSdkSource
.hashPassword(
@@ -927,7 +1030,7 @@ class AuthRepositoryImpl(
purpose = HashPurpose.SERVER_AUTHORIZATION,
)
.fold(
onFailure = { return ResetPasswordResult.Error },
onFailure = { return ResetPasswordResult.Error(error = it) },
onSuccess = { it },
)
}
@@ -973,7 +1076,7 @@ class AuthRepositoryImpl(
// Return the success.
ResetPasswordResult.Success
},
onFailure = { ResetPasswordResult.Error },
onFailure = { ResetPasswordResult.Error(error = it) },
)
}
@@ -986,7 +1089,7 @@ class AuthRepositoryImpl(
val activeAccount = authDiskSource
.userState
?.activeAccount
?: return SetPasswordResult.Error
?: return SetPasswordResult.Error(error = NoActiveUserException())
val userId = activeAccount.profile.userId
// Update the saved master password hash.
@@ -997,7 +1100,7 @@ class AuthRepositoryImpl(
kdf = activeAccount.profile.toSdkParams(),
purpose = HashPurpose.SERVER_AUTHORIZATION,
)
.getOrElse { return@setPassword SetPasswordResult.Error }
.getOrElse { return@setPassword SetPasswordResult.Error(error = it) }
return when (activeAccount.profile.forcePasswordResetReason) {
ForcePasswordResetReason.TDE_USER_WITHOUT_PASSWORD_HAS_PASSWORD_RESET_PERMISSION -> {
@@ -1047,7 +1150,7 @@ class AuthRepositoryImpl(
}
}
.flatMap {
when (vaultRepository.unlockVaultWithMasterPassword(password)) {
when (val result = vaultRepository.unlockVaultWithMasterPassword(password)) {
is VaultUnlockResult.Success -> {
enrollUserInPasswordReset(
userId = userId,
@@ -1056,11 +1159,9 @@ class AuthRepositoryImpl(
)
}
is VaultUnlockResult.AuthenticationError,
VaultUnlockResult.InvalidStateError,
VaultUnlockResult.GenericError,
-> {
IllegalStateException("Failed to unlock vault").asFailure()
is VaultUnlockError -> {
(result.error ?: IllegalStateException("Failed to unlock vault"))
.asFailure()
}
}
}
@@ -1070,7 +1171,7 @@ class AuthRepositoryImpl(
this.organizationIdentifier = null
}
.fold(
onFailure = { SetPasswordResult.Error },
onFailure = { SetPasswordResult.Error(error = it) },
onSuccess = { SetPasswordResult.Success },
)
}
@@ -1102,9 +1203,25 @@ class AuthRepositoryImpl(
OrganizationDomainSsoDetailsResult.Success(
isSsoAvailable = it.isSsoAvailable,
organizationIdentifier = it.organizationIdentifier,
verifiedDate = it.verifiedDate,
)
},
onFailure = { OrganizationDomainSsoDetailsResult.Failure },
onFailure = { OrganizationDomainSsoDetailsResult.Failure(error = it) },
)
override suspend fun getVerifiedOrganizationDomainSsoDetails(
email: String,
): VerifiedOrganizationDomainSsoDetailsResult = organizationService
.getVerifiedOrganizationDomainSsoDetails(
email = email,
)
.fold(
onSuccess = {
VerifiedOrganizationDomainSsoDetailsResult.Success(
verifiedOrganizationDomainSsoDetails = it.verifiedOrganizationDomainSsoDetails,
)
},
onFailure = { VerifiedOrganizationDomainSsoDetailsResult.Failure(error = it) },
)
override suspend fun prevalidateSso(
@@ -1114,14 +1231,23 @@ class AuthRepositoryImpl(
organizationIdentifier = organizationIdentifier,
)
.fold(
onSuccess = {
if (it.token.isNullOrBlank()) {
PrevalidateSsoResult.Failure
} else {
PrevalidateSsoResult.Success(it.token)
onSuccess = { response ->
when (response) {
is PrevalidateSsoResponseJson.Error -> {
PrevalidateSsoResult.Failure(message = response.message, error = null)
}
is PrevalidateSsoResponseJson.Success -> {
response.token
?.takeUnless { it.isBlank() }
?.let { PrevalidateSsoResult.Success(token = it) }
?: PrevalidateSsoResult.Failure(
error = MissingPropertyException("Token"),
)
}
}
},
onFailure = { PrevalidateSsoResult.Failure },
onFailure = { PrevalidateSsoResult.Failure(error = it) },
)
override fun setSsoCallbackResult(result: SsoCallbackResult) {
@@ -1135,34 +1261,39 @@ class AuthRepositoryImpl(
deviceId = authDiskSource.uniqueAppId,
)
.fold(
onFailure = { KnownDeviceResult.Error },
onSuccess = { KnownDeviceResult.Success(it) },
onFailure = { KnownDeviceResult.Error(error = it) },
onSuccess = { KnownDeviceResult.Success(isKnownDevice = it) },
)
override suspend fun getPasswordBreachCount(password: String): BreachCountResult =
haveIBeenPwnedService
.getPasswordBreachCount(password)
.fold(
onFailure = { BreachCountResult.Error },
onFailure = { BreachCountResult.Error(error = it) },
onSuccess = { BreachCountResult.Success(it) },
)
override suspend fun getPasswordStrength(
email: String,
email: String?,
password: String,
): PasswordStrengthResult =
authSdkSource
.passwordStrength(
email = email,
email = email
?: userStateFlow
.value
?.activeAccount
?.email
.orEmpty(),
password = password,
)
.fold(
onSuccess = { PasswordStrengthResult.Success(passwordStrength = it) },
onFailure = { PasswordStrengthResult.Error },
onFailure = { PasswordStrengthResult.Error(error = it) },
)
override suspend fun validatePassword(password: String): ValidatePasswordResult {
val userId = activeUserId ?: return ValidatePasswordResult.Error
val userId = activeUserId ?: return ValidatePasswordResult.Error(NoActiveUserException())
return authDiskSource
.getMasterPasswordHash(userId = userId)
?.let { masterPasswordHash ->
@@ -1174,13 +1305,13 @@ class AuthRepositoryImpl(
)
.fold(
onSuccess = { ValidatePasswordResult.Success(isValid = it) },
onFailure = { ValidatePasswordResult.Error },
onFailure = { ValidatePasswordResult.Error(error = it) },
)
}
?: run {
val encryptedKey = authDiskSource
.getUserKey(userId)
?: return ValidatePasswordResult.Error
?: return ValidatePasswordResult.Error(MissingPropertyException("UserKey"))
vaultSdkSource
.validatePasswordUserKey(
userId = userId,
@@ -1210,43 +1341,21 @@ class AuthRepositoryImpl(
.userState
?.activeAccount
?.profile
?: return ValidatePinResult.Error
val privateKey = authDiskSource
.getPrivateKey(userId = activeAccount.userId)
?: return ValidatePinResult.Error
?: return ValidatePinResult.Error(error = NoActiveUserException())
val pinProtectedUserKey = authDiskSource
.getPinProtectedUserKey(userId = activeAccount.userId)
?: return ValidatePinResult.Error
// HACK: As the SDK doesn't provide a way to directly validate the pin yet, we instead
// try to initialize the user crypto, and if it succeeds then the PIN is correct, otherwise
// the PIN is incorrect.
?: return ValidatePinResult.Error(
error = MissingPropertyException("Pin Protected User Key"),
)
return vaultSdkSource
.initializeCrypto(
.validatePin(
userId = activeAccount.userId,
request = InitUserCryptoRequest(
kdfParams = activeAccount.toSdkParams(),
email = activeAccount.email,
privateKey = privateKey,
method = InitUserCryptoMethod.Pin(
pin = pin,
pinProtectedUserKey = pinProtectedUserKey,
),
),
pin = pin,
pinProtectedUserKey = pinProtectedUserKey,
)
.fold(
onSuccess = {
when (it) {
InitializeCryptoResult.Success -> {
ValidatePinResult.Success(isValid = true)
}
is InitializeCryptoResult.AuthenticationError -> {
ValidatePinResult.Success(isValid = false)
}
}
},
onFailure = { ValidatePinResult.Error },
onSuccess = { ValidatePinResult.Success(isValid = it) },
onFailure = { ValidatePinResult.Error(error = it) },
)
}
@@ -1264,17 +1373,26 @@ class AuthRepositoryImpl(
.sendVerificationEmail(
SendVerificationEmailRequestJson(
email = email,
name = name,
name = name.takeUnless { it.isBlank() },
receiveMarketingEmails = receiveMarketingEmails,
),
)
.fold(
onSuccess = {
SendVerificationEmailResult.Success(it)
},
onFailure = {
SendVerificationEmailResult.Error(null)
when (it) {
is SendVerificationEmailResponseJson.Invalid -> {
SendVerificationEmailResult.Error(
errorMessage = it.message,
error = null,
)
}
is SendVerificationEmailResponseJson.Success -> {
SendVerificationEmailResult.Success(it.emailVerificationToken)
}
}
},
onFailure = { SendVerificationEmailResult.Error(errorMessage = null, error = it) },
)
override suspend fun validateEmailToken(email: String, token: String): EmailTokenResult {
@@ -1290,26 +1408,30 @@ class AuthRepositoryImpl(
when (val json = it) {
VerifyEmailTokenResponseJson.Valid -> EmailTokenResult.Success
is VerifyEmailTokenResponseJson.Invalid -> {
EmailTokenResult.Error(json.message)
EmailTokenResult.Error(message = json.message, error = null)
}
VerifyEmailTokenResponseJson.TokenExpired -> EmailTokenResult.Expired
}
},
onFailure = {
EmailTokenResult.Error(message = null)
},
onFailure = { EmailTokenResult.Error(message = null, error = it) },
)
}
override fun setOnboardingStatus(userId: String, status: OnboardingStatus?) {
authDiskSource.storeOnboardingStatus(userId = userId, onboardingStatus = status)
override fun setOnboardingStatus(status: OnboardingStatus) {
activeUserId?.let { userId ->
authDiskSource.storeOnboardingStatus(
userId = userId,
onboardingStatus = status,
)
}
}
override fun setShowImportLogins(showImportLogins: Boolean) {
val userId: String = activeUserId ?: return
authDiskSource.storeShowImportLogins(userId = userId, showImportLogins = showImportLogins)
}
override suspend fun leaveOrganization(organizationId: String): LeaveOrganizationResult =
organizationService.leaveOrganization(organizationId).fold(
onSuccess = { LeaveOrganizationResult.Success },
onFailure = { LeaveOrganizationResult.Error(error = it) },
)
@Suppress("CyclomaticComplexMethod")
private suspend fun validatePasswordAgainstPolicy(
@@ -1458,6 +1580,7 @@ class AuthRepositoryImpl(
* A helper function to extract the common logic of logging in through
* any of the available methods.
*/
@Suppress("LongMethod")
private suspend fun loginCommon(
email: String,
password: String? = null,
@@ -1466,6 +1589,7 @@ class AuthRepositoryImpl(
deviceData: DeviceDataModel? = null,
orgIdentifier: String? = null,
captchaToken: String?,
newDeviceOtp: String? = null,
): LoginResult = identityService
.getToken(
uniqueAppId = authDiskSource.uniqueAppId,
@@ -1473,12 +1597,20 @@ class AuthRepositoryImpl(
authModel = authModel,
twoFactorData = twoFactorData ?: getRememberedTwoFactorData(email),
captchaToken = captchaToken,
newDeviceOtp = newDeviceOtp,
)
.fold(
onFailure = {
when (configDiskSource.serverConfig?.isOfficialBitwardenServer) {
false -> LoginResult.UnofficialServerError
else -> LoginResult.Error(errorMessage = null)
onFailure = { throwable ->
when {
throwable.isSslHandShakeError() -> LoginResult.CertificateError
configDiskSource.serverConfig?.isOfficialBitwardenServer == false -> {
LoginResult.UnofficialServerError
}
else -> LoginResult.Error(
errorMessage = null,
error = throwable,
)
}
},
onSuccess = { loginResponse ->
@@ -1500,11 +1632,26 @@ class AuthRepositoryImpl(
password = password,
deviceData = deviceData,
orgIdentifier = orgIdentifier,
userConfirmedKeyConnector = false,
)
is GetTokenResponseJson.Invalid -> LoginResult.Error(
errorMessage = loginResponse.errorMessage,
)
is GetTokenResponseJson.Invalid -> {
when (loginResponse.invalidType) {
is GetTokenResponseJson.Invalid.InvalidType.NewDeviceVerification ->
handleLoginCommonNewDeviceVerification(
email = email,
authModel = authModel,
error = loginResponse.errorMessage,
)
is GetTokenResponseJson.Invalid.InvalidType.GenericInvalid -> {
LoginResult.Error(
errorMessage = loginResponse.errorMessage,
error = null,
)
}
}
}
}
},
)
@@ -1519,6 +1666,7 @@ class AuthRepositoryImpl(
password: String?,
deviceData: DeviceDataModel?,
orgIdentifier: String?,
userConfirmedKeyConnector: Boolean,
): LoginResult = userStateTransaction {
val userStateJson = loginResponse.toUserState(
previousUserState = authDiskSource.userState,
@@ -1548,6 +1696,21 @@ class AuthRepositoryImpl(
deviceData = deviceData,
)
} else if (keyConnectorUrl != null && orgIdentifier != null) {
val isNewKeyConnectorUser =
loginResponse.userDecryptionOptions?.hasMasterPassword == false &&
loginResponse.key == null &&
loginResponse.privateKey == null
val isNotConfirmed = !userConfirmedKeyConnector
// If a new KeyConnector user is logging in for the first time,
// we should ask him to confirm the domain
if (isNewKeyConnectorUser && isNotConfirmed) {
keyConnectorResponse = loginResponse
return LoginResult.ConfirmKeyConnectorDomain(
domain = keyConnectorUrl,
)
}
unlockVaultWithKeyConnectorOnLoginSuccess(
profile = profile,
keyConnectorUrl = keyConnectorUrl,
@@ -1592,15 +1755,6 @@ class AuthRepositoryImpl(
)
settingsRepository.hasUserLoggedInOrCreatedAccount = true
val shouldSetOnboardingStatus = featureFlagManager.getFeatureFlag(FlagKey.OnboardingFlow) &&
!settingsRepository.getUserHasLoggedInValue(userId = userId)
if (shouldSetOnboardingStatus) {
setOnboardingStatus(
userId = userId,
status = OnboardingStatus.NOT_STARTED,
)
}
authDiskSource.userState = userStateJson
loginResponse.key?.let {
// Only set the value if it's present, since we may have set it already
@@ -1629,6 +1783,8 @@ class AuthRepositoryImpl(
twoFactorResponse = null
resendEmailRequestJson = null
twoFactorDeviceData = null
resendNewDeviceOtpRequestJson = null
keyConnectorResponse = null
settingsRepository.setDefaultsIfNecessary(userId = userId)
settingsRepository.storeUserHasLoggedInValue(userId)
vaultRepository.syncIfNecessary()
@@ -1661,20 +1817,41 @@ class AuthRepositoryImpl(
return LoginResult.TwoFactorRequired
}
/**
* A helper method that processes the
* [GetTokenResponseJson.Invalid.InvalidType.NewDeviceVerification] when logging in.
*/
private fun handleLoginCommonNewDeviceVerification(
email: String,
authModel: IdentityTokenAuthModel,
error: String?,
): LoginResult {
identityTokenAuthModel = authModel
resendNewDeviceOtpRequestJson = ResendNewDeviceOtpRequestJson(
email = email,
passwordHash = authModel.password,
)
return LoginResult.NewDeviceVerification(error)
}
/**
* Attempt to unlock the current user's vault with key connector data.
*/
@Suppress("LongMethod")
private suspend fun unlockVaultWithKeyConnectorOnLoginSuccess(
profile: AccountJson.Profile,
keyConnectorUrl: String,
orgIdentifier: String,
loginResponse: GetTokenResponseJson.Success,
): VaultUnlockResult? =
if (loginResponse.userDecryptionOptions?.hasMasterPassword != false) {
): VaultUnlockResult? {
val key = loginResponse.key
val privateKey = loginResponse.privateKey
return if (loginResponse.userDecryptionOptions?.hasMasterPassword != false) {
// This user has a master password, so we skip the key-connector logic as it is not
// setup yet. The user can still unlock the vault with their master password.
null
} else if (loginResponse.key != null && loginResponse.privateKey != null) {
} else if (key != null && privateKey != null) {
// This is a returning user who should already have the key connector setup
keyConnectorManager
.getMasterKeyFromKeyConnector(
@@ -1684,16 +1861,16 @@ class AuthRepositoryImpl(
.map {
unlockVault(
accountProfile = profile,
privateKey = loginResponse.privateKey,
privateKey = privateKey,
initUserCryptoMethod = InitUserCryptoMethod.KeyConnector(
masterKey = it.masterKey,
userKey = loginResponse.key,
userKey = key,
),
)
}
.fold(
// If the request failed, we want to abort the login process
onFailure = { VaultUnlockResult.GenericError },
onFailure = { VaultUnlockResult.GenericError(error = it) },
onSuccess = { it },
)
} else {
@@ -1733,10 +1910,11 @@ class AuthRepositoryImpl(
}
.fold(
// If the request failed, we want to abort the login process
onFailure = { VaultUnlockResult.GenericError },
onFailure = { VaultUnlockResult.GenericError(error = it) },
onSuccess = { it },
)
}
}
/**
* Attempt to unlock the current user's vault with password data.
@@ -1770,11 +1948,13 @@ class AuthRepositoryImpl(
): VaultUnlockResult? {
// Attempt to unlock the vault with auth request if possible.
// These values will only be null during the Just-in-Time provisioning flow.
if (loginResponse.privateKey != null && loginResponse.key != null) {
val privateKey = loginResponse.privateKey
val key = loginResponse.key
if (privateKey != null && key != null) {
deviceData?.let { model ->
return unlockVault(
accountProfile = profile,
privateKey = loginResponse.privateKey,
privateKey = privateKey,
initUserCryptoMethod = InitUserCryptoMethod.AuthRequest(
requestPrivateKey = model.privateKey,
method = model
@@ -1782,7 +1962,7 @@ class AuthRepositoryImpl(
?.let {
AuthRequestMethod.MasterKey(
protectedMasterKey = model.asymmetricalKey,
authRequestKey = loginResponse.key,
authRequestKey = key,
)
}
?: AuthRequestMethod.UserKey(protectedUserKey = model.asymmetricalKey),

View File

@@ -1,11 +1,13 @@
package com.x8bit.bitwarden.data.auth.repository.di
import com.bitwarden.data.datasource.disk.ConfigDiskSource
import com.bitwarden.data.manager.DispatcherManager
import com.bitwarden.network.service.AccountsService
import com.bitwarden.network.service.DevicesService
import com.bitwarden.network.service.HaveIBeenPwnedService
import com.bitwarden.network.service.IdentityService
import com.bitwarden.network.service.OrganizationService
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
import com.x8bit.bitwarden.data.auth.datasource.network.service.AccountsService
import com.x8bit.bitwarden.data.auth.datasource.network.service.DevicesService
import com.x8bit.bitwarden.data.auth.datasource.network.service.HaveIBeenPwnedService
import com.x8bit.bitwarden.data.auth.datasource.network.service.IdentityService
import com.x8bit.bitwarden.data.auth.datasource.network.service.OrganizationService
import com.x8bit.bitwarden.data.auth.datasource.sdk.AuthSdkSource
import com.x8bit.bitwarden.data.auth.manager.AuthRequestManager
import com.x8bit.bitwarden.data.auth.manager.KeyConnectorManager
@@ -13,12 +15,11 @@ import com.x8bit.bitwarden.data.auth.manager.TrustedDeviceManager
import com.x8bit.bitwarden.data.auth.manager.UserLogoutManager
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
import com.x8bit.bitwarden.data.auth.repository.AuthRepositoryImpl
import com.x8bit.bitwarden.data.platform.datasource.disk.ConfigDiskSource
import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager
import com.x8bit.bitwarden.data.platform.manager.FirstTimeActionManager
import com.x8bit.bitwarden.data.platform.manager.LogsManager
import com.x8bit.bitwarden.data.platform.manager.PolicyManager
import com.x8bit.bitwarden.data.platform.manager.PushManager
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
@@ -60,6 +61,7 @@ object AuthRepositoryModule {
policyManager: PolicyManager,
featureFlagManager: FeatureFlagManager,
firstTimeActionManager: FirstTimeActionManager,
logsManager: LogsManager,
): AuthRepository = AuthRepositoryImpl(
accountsService = accountsService,
devicesService = devicesService,
@@ -82,5 +84,6 @@ object AuthRepositoryModule {
policyManager = policyManager,
featureFlagManager = featureFlagManager,
firstTimeActionManager = firstTimeActionManager,
logsManager = logsManager,
)
}

View File

@@ -12,5 +12,8 @@ sealed class BreachCountResult {
/**
* There was an error determining if the password has been breached.
*/
data object Error : BreachCountResult()
data class Error(
val error: Throwable,
val message: String? = null,
) : BreachCountResult()
}

View File

@@ -12,5 +12,8 @@ sealed class DeleteAccountResult {
/**
* There was an error deleting the account.
*/
data class Error(val message: String?) : DeleteAccountResult()
data class Error(
val message: String?,
val error: Throwable?,
) : DeleteAccountResult()
}

View File

@@ -18,5 +18,8 @@ sealed class EmailTokenResult {
/**
* There was an error validating the token.
*/
data class Error(val message: String?) : EmailTokenResult()
data class Error(
val message: String?,
val error: Throwable?,
) : EmailTokenResult()
}

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